diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index 36dbca3a3fd..e0ad1833409 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -646,7 +646,7 @@ String InputEventMouseMotion::as_text() const { button_mask_string = itos(get_button_mask()); break; } - return "InputEventMouseMotion : button_mask=" + button_mask_string + ", position=(" + String(get_position()) + "), relative=(" + String(get_relative()) + "), speed=(" + String(get_speed()) + ")"; + return "InputEventMouseMotion : button_mask=" + button_mask_string + ", position=(" + String(get_position()) + "), relative=(" + String(get_relative()) + "), speed=(" + String(get_speed()) + "), pressure=(" + rtos(get_pressure()) + "), tilt=(" + String(get_tilt()) + ")"; } bool InputEventMouseMotion::accumulate(const Ref &p_event) { diff --git a/core/os/os.h b/core/os/os.h index 89b3414b3eb..4bab7eeb741 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -61,6 +61,7 @@ class OS { bool _allow_layered; bool _use_vsync; bool _vsync_via_compositor; + bool _disable_wintab; char *last_error; @@ -528,6 +529,7 @@ public: bool is_layered_allowed() const { return _allow_layered; } bool is_hidpi_allowed() const { return _allow_hidpi; } + bool is_wintab_disabled() const { return _disable_wintab; } void set_restart_on_exit(bool p_restart, const List &p_restart_arguments); bool is_restart_on_exit_set() const; diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 8dfd82a2e94..e1f33b93509 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -417,6 +417,9 @@ Position offset for tooltips, relative to the mouse cursor's hotspot. + + Disables WinTab API and always use Windows Ink API for the pen input (Windows only). + If [code]true[/code], allows HiDPI display on Windows and macOS. This setting has no effect on desktop Linux, as DPI-awareness fallbacks are not supported there. diff --git a/main/main.cpp b/main/main.cpp index b91f4ba5f32..b3dc453bee0 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -134,6 +134,7 @@ static bool init_always_on_top = false; static bool init_use_custom_pos = false; static Vector2 init_custom_pos; static bool force_lowdpi = false; +static bool disable_wintab = false; // Debug @@ -262,6 +263,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" --no-window Disable window creation (Windows only). Useful together with --script.\n"); OS::get_singleton()->print(" --enable-vsync-via-compositor When vsync is enabled, vsync via the OS' window compositor (Windows only).\n"); OS::get_singleton()->print(" --disable-vsync-via-compositor Disable vsync via the OS' window compositor (Windows only).\n"); + OS::get_singleton()->print(" --disable-wintab Disable WinTab API and always use Windows Ink API for the pen input (Windows only).\n"); OS::get_singleton()->print("\n"); #endif @@ -604,6 +606,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (I->get() == "--no-window") { // disable window creation (Windows only) OS::get_singleton()->set_no_window_mode(true); + } else if (I->get() == "--disable-wintab") { + + disable_wintab = true; } else if (I->get() == "--enable-vsync-via-compositor") { video_mode.vsync_via_compositor = true; @@ -1048,6 +1053,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->_vsync_via_compositor = video_mode.vsync_via_compositor; + if (!disable_wintab) { + // No "--disable_wintab" option + disable_wintab = GLOBAL_DEF("display/window/disable_wintab_api", false); + } + + OS::get_singleton()->_disable_wintab = disable_wintab; + OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/per_pixel_transparency/allowed", false); video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency/enabled", false); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index e24586cd8d5..7db4f9899e2 100755 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -70,10 +70,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #define WM_TOUCH 576 #endif -#ifndef WM_POINTERUPDATE -#define WM_POINTERUPDATE 0x0245 -#endif - #if defined(__GNUC__) // Workaround GCC warning from -Wcast-function-type. #define GetProcAddress (void *)GetProcAddress @@ -201,6 +197,15 @@ BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) { } } +// WinTab API +bool OS_Windows::wintab_available = false; +WTOpenPtr OS_Windows::wintab_WTOpen = nullptr; +WTClosePtr OS_Windows::wintab_WTClose = nullptr; +WTInfoPtr OS_Windows::wintab_WTInfo = nullptr; +WTPacketPtr OS_Windows::wintab_WTPacket = nullptr; +WTEnablePtr OS_Windows::wintab_WTEnable = nullptr; + +// Windows Ink API GetPointerTypePtr OS_Windows::win8p_GetPointerType = NULL; GetPointerPenInfoPtr OS_Windows::win8p_GetPointerPenInfo = NULL; @@ -368,7 +373,11 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) alt_mem = false; }; - return 0; // Return To The Message Loop + if (!is_wintab_disabled() && wintab_available && wtctx) { + wintab_WTEnable(wtctx, GET_WM_ACTIVATE_STATE(wParam, lParam)); + } + + return 0; // Return To The Message Loop } case WM_GETMINMAXINFO: { if (video_mode.resizable && !video_mode.fullscreen) { @@ -446,6 +455,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mm->set_control(control_mem); mm->set_shift(shift_mem); mm->set_alt(alt_mem); + mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f); mm->set_button_mask(last_button_state); @@ -496,6 +506,42 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } delete[] lpb; } break; + case WT_CSRCHANGE: + case WT_PROXIMITY: { + if (!is_wintab_disabled() && wintab_available && wtctx) { + AXIS pressure; + if (wintab_WTInfo(WTI_DEVICES + wtlc.lcDevice, DVC_NPRESSURE, &pressure)) { + min_pressure = int(pressure.axMin); + max_pressure = int(pressure.axMax); + } + AXIS orientation[3]; + if (wintab_WTInfo(WTI_DEVICES + wtlc.lcDevice, DVC_ORIENTATION, &orientation)) { + tilt_supported = orientation[0].axResolution && orientation[1].axResolution; + } + return 0; + } + } break; + case WT_PACKET: { + if (!is_wintab_disabled() && wintab_available && wtctx) { + PACKET packet; + if (wintab_WTPacket(wtctx, wParam, &packet)) { + + float pressure = float(packet.pkNormalPressure - min_pressure) / float(max_pressure - min_pressure); + last_pressure = pressure; + last_pressure_update = 0; + + double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math_PI / 180); + double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math_PI / 180)); + + if (tilt_supported) { + last_tilt = Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)); + } else { + last_tilt = Vector2(); + } + } + return 0; + } + } break; case WM_POINTERUPDATE: { if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { break; @@ -568,8 +614,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mm->set_shift((wParam & MK_SHIFT) != 0); mm->set_alt(alt_mem); - mm->set_pressure((wParam & MK_LBUTTON) ? 1.0f : 0.0f); - mm->set_button_mask(last_button_state); POINT coords; //client coords @@ -661,6 +705,22 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mm->set_shift((wParam & MK_SHIFT) != 0); mm->set_alt(alt_mem); + if (!is_wintab_disabled() && wintab_available && wtctx) { + // Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not update recently. + if (last_pressure_update < 10) { + last_pressure_update++; + } else { + last_tilt = Vector2(); + last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f; + } + } else { + last_tilt = Vector2(); + last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f; + } + + mm->set_pressure(last_pressure); + mm->set_tilt(last_tilt); + mm->set_button_mask(last_button_state); mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); @@ -1425,6 +1485,39 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } + if (!is_wintab_disabled() && wintab_available) { + wintab_WTInfo(WTI_DEFSYSCTX, 0, &wtlc); + wtlc.lcOptions |= CXO_MESSAGES; + wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; + wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE; + wtlc.lcPktMode = 0; + wtlc.lcOutOrgX = 0; + wtlc.lcOutExtX = wtlc.lcInExtX; + wtlc.lcOutOrgY = 0; + wtlc.lcOutExtY = -wtlc.lcInExtY; + wtctx = wintab_WTOpen(hWnd, &wtlc, false); + if (wtctx) { + wintab_WTEnable(wtctx, true); + AXIS pressure; + if (wintab_WTInfo(WTI_DEVICES + wtlc.lcDevice, DVC_NPRESSURE, &pressure)) { + min_pressure = int(pressure.axMin); + max_pressure = int(pressure.axMax); + } + AXIS orientation[3]; + if (wintab_WTInfo(WTI_DEVICES + wtlc.lcDevice, DVC_ORIENTATION, &orientation)) { + tilt_supported = orientation[0].axResolution && orientation[1].axResolution; + } + } else { + ERR_PRINT("WinTab context creation falied."); + } + } else { + wtctx = 0; + } + + last_pressure = 0; + last_pressure_update = 0; + last_tilt = Vector2(); + #if defined(OPENGL_ENABLED) bool gles3_context = true; @@ -3396,7 +3489,19 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { window_focused = true; console_visible = IsWindowVisible(GetConsoleWindow()); - //Note: Functions for pen input, available on Windows 8+ + //Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. + HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll"); + if (wintab_lib) { + wintab_WTOpen = (WTOpenPtr)GetProcAddress(wintab_lib, "WTOpenW"); + wintab_WTClose = (WTClosePtr)GetProcAddress(wintab_lib, "WTClose"); + wintab_WTInfo = (WTInfoPtr)GetProcAddress(wintab_lib, "WTInfoW"); + wintab_WTPacket = (WTPacketPtr)GetProcAddress(wintab_lib, "WTPacket"); + wintab_WTEnable = (WTEnablePtr)GetProcAddress(wintab_lib, "WTEnable"); + + wintab_available = wintab_WTOpen && wintab_WTClose && wintab_WTInfo && wintab_WTPacket && wintab_WTEnable; + } + + //Note: Windows Ink API for pen input, available on Windows 8+ only. HMODULE user32_lib = LoadLibraryW(L"user32.dll"); if (user32_lib) { win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); @@ -3425,6 +3530,11 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { } OS_Windows::~OS_Windows() { + if (wintab_available && wtctx) { + wintab_WTClose(wtctx); + wtctx = 0; + } + if (is_layered_allowed() && layered_window) { DeleteObject(hBitmap); DeleteDC(hDC_dib); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 981005913cc..21bd8c3c915 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -54,7 +54,87 @@ #include #include #include +// WinTab API +#define WT_PACKET 0x7FF0 +#define WT_PROXIMITY 0x7FF5 +#define WT_INFOCHANGE 0x7FF6 +#define WT_CSRCHANGE 0x7FF7 +#define WTI_DEFSYSCTX 4 +#define WTI_DEVICES 100 +#define DVC_NPRESSURE 15 +#define DVC_TPRESSURE 16 +#define DVC_ORIENTATION 17 +#define DVC_ROTATION 18 + +#define CXO_MESSAGES 0x0004 +#define PK_NORMAL_PRESSURE 0x0400 +#define PK_TANGENT_PRESSURE 0x0800 +#define PK_ORIENTATION 0x1000 + +typedef struct tagLOGCONTEXTW { + WCHAR lcName[40]; + UINT lcOptions; + UINT lcStatus; + UINT lcLocks; + UINT lcMsgBase; + UINT lcDevice; + UINT lcPktRate; + DWORD lcPktData; + DWORD lcPktMode; + DWORD lcMoveMask; + DWORD lcBtnDnMask; + DWORD lcBtnUpMask; + LONG lcInOrgX; + LONG lcInOrgY; + LONG lcInOrgZ; + LONG lcInExtX; + LONG lcInExtY; + LONG lcInExtZ; + LONG lcOutOrgX; + LONG lcOutOrgY; + LONG lcOutOrgZ; + LONG lcOutExtX; + LONG lcOutExtY; + LONG lcOutExtZ; + DWORD lcSensX; + DWORD lcSensY; + DWORD lcSensZ; + BOOL lcSysMode; + int lcSysOrgX; + int lcSysOrgY; + int lcSysExtX; + int lcSysExtY; + DWORD lcSysSensX; + DWORD lcSysSensY; +} LOGCONTEXTW; + +typedef struct tagAXIS { + LONG axMin; + LONG axMax; + UINT axUnits; + DWORD axResolution; +} AXIS; + +typedef struct tagORIENTATION { + int orAzimuth; + int orAltitude; + int orTwist; +} ORIENTATION; + +typedef struct tagPACKET { + int pkNormalPressure; + int pkTangentPressure; + ORIENTATION pkOrientation; +} PACKET; + +typedef HANDLE(WINAPI *WTOpenPtr)(HWND p_window, LOGCONTEXTW *p_ctx, BOOL p_enable); +typedef BOOL(WINAPI *WTClosePtr)(HANDLE p_ctx); +typedef UINT(WINAPI *WTInfoPtr)(UINT p_category, UINT p_index, LPVOID p_output); +typedef BOOL(WINAPI *WTPacketPtr)(HANDLE p_ctx, UINT p_param, LPVOID p_packets); +typedef BOOL(WINAPI *WTEnablePtr)(HANDLE p_ctx, BOOL p_enable); + +// Windows Ink API #ifndef POINTER_STRUCTURES #define POINTER_STRUCTURES @@ -133,6 +213,10 @@ typedef struct tagPOINTER_PEN_INFO { #endif +#ifndef WM_POINTERUPDATE +#define WM_POINTERUPDATE 0x0245 +#endif + typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type); typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info); @@ -156,10 +240,28 @@ typedef struct { class JoypadWindows; class OS_Windows : public OS { + // WinTab API + static bool wintab_available; + static WTOpenPtr wintab_WTOpen; + static WTClosePtr wintab_WTClose; + static WTInfoPtr wintab_WTInfo; + static WTPacketPtr wintab_WTPacket; + static WTEnablePtr wintab_WTEnable; + // Windows Ink API static GetPointerTypePtr win8p_GetPointerType; static GetPointerPenInfoPtr win8p_GetPointerPenInfo; + HANDLE wtctx; + LOGCONTEXTW wtlc; + int min_pressure; + int max_pressure; + bool tilt_supported; + + int last_pressure_update; + float last_pressure; + Vector2 last_tilt; + enum { KEY_EVENT_BUFFER_SIZE = 512 };