diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 60fa7df8605..326bdead210 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -1178,6 +1178,22 @@ Vector _OS::get_granted_permissions() const { return OS::get_singleton()->get_granted_permissions(); } +int _OS::get_tablet_driver_count() const { + return OS::get_singleton()->get_tablet_driver_count(); +} + +String _OS::get_tablet_driver_name(int p_driver) const { + return OS::get_singleton()->get_tablet_driver_name(p_driver); +} + +String _OS::get_current_tablet_driver() const { + return OS::get_singleton()->get_current_tablet_driver(); +} + +void _OS::set_current_tablet_driver(const String &p_driver) { + OS::get_singleton()->set_current_tablet_driver(p_driver); +} + _OS *_OS::singleton = NULL; void _OS::_bind_methods() { @@ -1371,6 +1387,13 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions); ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions); + ClassDB::bind_method(D_METHOD("get_tablet_driver_count"), &_OS::get_tablet_driver_count); + ClassDB::bind_method(D_METHOD("get_tablet_driver_name", "idx"), &_OS::get_tablet_driver_name); + ClassDB::bind_method(D_METHOD("get_current_tablet_driver"), &_OS::get_current_tablet_driver); + ClassDB::bind_method(D_METHOD("set_current_tablet_driver", "name"), &_OS::set_current_tablet_driver); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tablet_driver"), "set_current_tablet_driver", "get_current_tablet_driver"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "clipboard"), "set_clipboard", "get_clipboard"); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_screen"), "set_current_screen", "get_current_screen"); ADD_PROPERTY(PropertyInfo(Variant::INT, "exit_code"), "set_exit_code", "get_exit_code"); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 06468c342d6..22dab3cf3ee 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -359,6 +359,11 @@ public: bool request_permissions(); Vector get_granted_permissions() const; + int get_tablet_driver_count() const; + String get_tablet_driver_name(int p_driver) const; + String get_current_tablet_driver() const; + void set_current_tablet_driver(const String &p_driver); + static _OS *get_singleton() { return singleton; } _OS(); diff --git a/core/os/os.h b/core/os/os.h index 56180216cbf..9f91e4214c7 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -61,7 +61,6 @@ class OS { bool _allow_layered; bool _use_vsync; bool _vsync_via_compositor; - bool _disable_wintab; char *last_error; @@ -194,6 +193,11 @@ public: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual int get_tablet_driver_count() const { return 0; }; + virtual const char *get_tablet_driver_name(int p_driver) const { return ""; }; + virtual String get_current_tablet_driver() const { return ""; }; + virtual void set_current_tablet_driver(const String &p_driver){}; + virtual PoolStringArray get_connected_midi_inputs(); virtual void open_midi_inputs(); virtual void close_midi_inputs(); @@ -525,7 +529,6 @@ 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/OS.xml b/doc/classes/OS.xml index 82113d6b003..2504a56f85d 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -425,6 +425,24 @@ Returns the epoch time of the operating system in seconds. + + + + + Returns the total number of available tablet drivers. + [b]Note:[/b] This method is implemented on Windows. + + + + + + + + + Returns the tablet driver name for the given index. + [b]Note:[/b] This method is implemented on Windows. + + @@ -936,6 +954,9 @@ The current screen orientation. + + The current tablet drvier in use. + If [code]true[/code], vertical synchronization (Vsync) is enabled. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 42a46d31466..87adace477e 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -418,9 +418,6 @@ 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. @@ -463,6 +460,9 @@ Sets the game's main viewport width. On desktop platforms, this is the default window size. Stretch mode settings also use this as a reference when enabled. + + Specifies the tablet driver to use. If left empty, the default driver will be used. + If [code]true[/code], enables vertical synchronization. This eliminates tearing that may appear in moving scenes, at the cost of higher input latency and stuttering at lower framerates. If [code]false[/code], vertical synchronization will be disabled, however, many platforms will enforce it regardless (such as mobile platforms and HTML5). diff --git a/main/main.cpp b/main/main.cpp index 28113c00b15..4d2360930d2 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -134,7 +134,6 @@ 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 @@ -263,7 +262,13 @@ 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(" --tablet-driver Tablet input driver ("); + for (int i = 0; i < OS::get_singleton()->get_tablet_driver_count(); i++) { + if (i != 0) + OS::get_singleton()->print(", "); + OS::get_singleton()->print("'%s'", OS::get_singleton()->get_tablet_driver_name(i)); + } + OS::get_singleton()->print(") (Windows only).\n"); OS::get_singleton()->print("\n"); #endif @@ -391,6 +396,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph String video_driver = ""; String audio_driver = ""; + String tablet_driver = ""; String project_path = "."; bool upwards = false; String debug_mode; @@ -606,9 +612,26 @@ 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") { + } else if (I->get() == "--tablet-driver") { + if (I->next()) { + tablet_driver = I->next()->get(); + bool found = false; + for (int i = 0; i < OS::get_singleton()->get_tablet_driver_count(); i++) { + if (tablet_driver == OS::get_singleton()->get_tablet_driver_name(i)) { + found = true; + } + } - disable_wintab = true; + if (!found) { + OS::get_singleton()->print("Unknown tablet driver '%s', aborting.\n", tablet_driver.utf8().get_data()); + goto error; + } + + N = I->next()->next(); + } else { + OS::get_singleton()->print("Missing tablet driver argument, aborting.\n"); + goto error; + } } else if (I->get() == "--enable-vsync-via-compositor") { video_mode.vsync_via_compositor = true; @@ -1053,12 +1076,20 @@ 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); + if (tablet_driver == "") { // specified in project.godot + tablet_driver = GLOBAL_DEF_RST("display/window/tablet_driver", OS::get_singleton()->get_tablet_driver_name(0)); } - OS::get_singleton()->_disable_wintab = disable_wintab; + for (int i = 0; i < OS::get_singleton()->get_tablet_driver_count(); i++) { + if (tablet_driver == OS::get_singleton()->get_tablet_driver_name(i)) { + OS::get_singleton()->set_current_tablet_driver(OS::get_singleton()->get_tablet_driver_name(i)); + break; + } + } + + if (tablet_driver == "") { + OS::get_singleton()->set_current_tablet_driver(OS::get_singleton()->get_tablet_driver_name(0)); + } 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); @@ -1173,6 +1204,7 @@ error: video_driver = ""; audio_driver = ""; + tablet_driver = ""; project_path = ""; args.clear(); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index b7c3aff4094..71875f89acb 100755 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -206,6 +206,7 @@ WTPacketPtr OS_Windows::wintab_WTPacket = nullptr; WTEnablePtr OS_Windows::wintab_WTEnable = nullptr; // Windows Ink API +bool OS_Windows::winink_available = false; GetPointerTypePtr OS_Windows::win8p_GetPointerType = NULL; GetPointerPenInfoPtr OS_Windows::win8p_GetPointerPenInfo = NULL; @@ -373,7 +374,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) alt_mem = false; }; - if (!is_wintab_disabled() && wintab_available && wtctx) { + if ((get_current_tablet_driver() == "wintab") && wintab_available && wtctx) { wintab_WTEnable(wtctx, GET_WM_ACTIVATE_STATE(wParam, lParam)); } @@ -508,7 +509,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } break; case WT_CSRCHANGE: case WT_PROXIMITY: { - if (!is_wintab_disabled() && wintab_available && wtctx) { + if ((get_current_tablet_driver() == "wintab") && wintab_available && wtctx) { AXIS pressure; if (wintab_WTInfo(WTI_DEVICES + wtlc.lcDevice, DVC_NPRESSURE, &pressure)) { min_pressure = int(pressure.axMin); @@ -522,7 +523,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } } break; case WT_PACKET: { - if (!is_wintab_disabled() && wintab_available && wtctx) { + if ((get_current_tablet_driver() == "wintab") && wintab_available && wtctx) { PACKET packet; if (wintab_WTPacket(wtctx, wParam, &packet)) { @@ -602,7 +603,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; } - if (!win8p_GetPointerType || !win8p_GetPointerPenInfo) { + if ((get_current_tablet_driver() != "winink") || !winink_available) { break; } @@ -760,7 +761,7 @@ 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) { + if ((get_current_tablet_driver() == "wintab") && 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++; @@ -1519,8 +1520,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int if (video_mode.always_on_top) { 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) { + if ((get_current_tablet_driver() == "wintab") && wintab_available) { wintab_WTInfo(WTI_DEFSYSCTX, 0, &wtlc); wtlc.lcOptions |= CXO_MESSAGES; wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; @@ -3462,6 +3462,70 @@ Error OS_Windows::move_to_trash(const String &p_path) { return OK; } +int OS_Windows::get_tablet_driver_count() const { + return tablet_drivers.size(); +} + +const char *OS_Windows::get_tablet_driver_name(int p_driver) const { + if (p_driver < 0 || p_driver >= tablet_drivers.size()) { + return ""; + } else { + return tablet_drivers[p_driver].utf8().get_data(); + } +} + +String OS_Windows::get_current_tablet_driver() const { + return tablet_driver; +} + +void OS_Windows::set_current_tablet_driver(const String &p_driver) { + bool found = false; + for (int i = 0; i < get_tablet_driver_count(); i++) { + if (p_driver == get_tablet_driver_name(i)) { + found = true; + } + } + if (found) { + if (hWnd) { + if ((tablet_driver == "wintab") && wintab_available && wtctx) { + wintab_WTEnable(wtctx, false); + wintab_WTClose(wtctx); + wtctx = 0; + } + if ((p_driver == "wintab") && 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; + } + wintab_WTEnable(wtctx, true); + } else { + print_verbose("WinTab context creation failed."); + } + } + } + tablet_driver = p_driver; + } else { + ERR_PRINT("Unknown tablet driver " + p_driver + "."); + } +}; + OS_Windows::OS_Windows(HINSTANCE _hInstance) { drop_events = false; @@ -3490,11 +3554,21 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { wintab_available = wintab_WTOpen && wintab_WTClose && wintab_WTInfo && wintab_WTPacket && wintab_WTEnable; } + if (wintab_available) { + tablet_drivers.push_back("wintab"); + } + //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"); win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo"); + + winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo; + } + + if (winink_available) { + tablet_drivers.push_back("winink"); } hInstance = _hInstance; diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 5ee50c66f92..562473cf454 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -241,6 +241,9 @@ typedef struct { class JoypadWindows; class OS_Windows : public OS { + String tablet_driver; + Vector tablet_drivers; + // WinTab API static bool wintab_available; static WTOpenPtr wintab_WTOpen; @@ -250,6 +253,7 @@ class OS_Windows : public OS { static WTEnablePtr wintab_WTEnable; // Windows Ink API + static bool winink_available; static GetPointerTypePtr win8p_GetPointerType; static GetPointerPenInfoPtr win8p_GetPointerPenInfo; @@ -414,6 +418,11 @@ public: virtual VideoMode get_video_mode(int p_screen = 0) const; virtual void get_fullscreen_mode_list(List *p_list, int p_screen = 0) const; + virtual int get_tablet_driver_count() const; + virtual const char *get_tablet_driver_name(int p_driver) const; + virtual String get_current_tablet_driver() const; + virtual void set_current_tablet_driver(const String &p_driver); + virtual int get_screen_count() const; virtual int get_current_screen() const; virtual void set_current_screen(int p_screen);