diff options
-rw-r--r-- | platform/windows/detect.py | 11 | ||||
-rw-r--r-- | platform/windows/os_windows.cpp | 50 | ||||
-rw-r--r-- | platform/windows/os_windows.h | 7 | ||||
-rw-r--r-- | platform/x11/detect.py | 9 | ||||
-rw-r--r-- | platform/x11/os_x11.cpp | 160 | ||||
-rw-r--r-- | platform/x11/os_x11.h | 11 |
6 files changed, 224 insertions, 24 deletions
diff --git a/platform/windows/detect.py b/platform/windows/detect.py index fbb02c9d1b..890decf962 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -64,6 +64,10 @@ def get_opts(): return [ ('mingw_prefix_32', 'MinGW prefix (Win32)', mingw32), ('mingw_prefix_64', 'MinGW prefix (Win64)', mingw64), + # Targeted Windows version: 7 (and later), minimum supported version + # XP support dropped after EOL due to missing API for IPv6 and other issues + # Vista support dropped after EOL due to GH-10243 + ('target_win_version', 'Targeted Windows version, >= 0x0601 (Windows 7)', '0x0601'), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), ] @@ -97,11 +101,6 @@ def configure(env): env.Append(CPPPATH=['#platform/windows']) - # Targeted Windows version: 7 (and later), minimum supported version - # XP support dropped after EOL due to missing API for IPv6 and other issues - # Vista support dropped after EOL due to GH-10243 - winver = "0x0601" - if (os.name == "nt" and os.getenv("VCINSTALLDIR")): # MSVC env['ENV']['TMP'] = os.environ['TMP'] @@ -175,7 +174,7 @@ def configure(env): env.Append(CCFLAGS=['/DWASAPI_ENABLED']) env.Append(CCFLAGS=['/DTYPED_METHOD_BIND']) env.Append(CCFLAGS=['/DWIN32']) - env.Append(CCFLAGS=['/DWINVER=%s' % winver, '/D_WIN32_WINNT=%s' % winver]) + env.Append(CCFLAGS=['/DWINVER=%s' % env['target_win_version'], '/D_WIN32_WINNT=%s' % env['target_win_version']]) if env["bits"] == "64": env.Append(CCFLAGS=['/D_WIN64']) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 9bcbb6ddb6..d62ecc6016 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -73,22 +73,17 @@ static String format_error_message(DWORD id) { LPWSTR messageBuffer = NULL; size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); + NULL, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); - String msg = "Error "+itos(id)+": "+String(messageBuffer,size); + String msg = "Error " + itos(id) + ": " + String(messageBuffer, size); LocalFree(messageBuffer); return msg; - } - - extern HINSTANCE godot_hinstance; - - void RedirectIOToConsole() { int hConHandle; @@ -228,7 +223,17 @@ bool OS_Windows::can_draw() const { #define SIGNATURE_MASK 0xFFFFFF00 #define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE) -void OS_Windows::_touch_event(bool p_pressed, int p_x, int p_y, int idx) { +void OS_Windows::_touch_event(bool p_pressed, float p_x, float p_y, int idx) { + + // Defensive + if (touch_state.has(idx) == p_pressed) + return; + + if (p_pressed) { + touch_state.insert(idx, Vector2(p_x, p_y)); + } else { + touch_state.erase(idx); + } Ref<InputEventScreenTouch> event; event.instance(); @@ -241,7 +246,17 @@ void OS_Windows::_touch_event(bool p_pressed, int p_x, int p_y, int idx) { } }; -void OS_Windows::_drag_event(int p_x, int p_y, int idx) { +void OS_Windows::_drag_event(float p_x, float p_y, int idx) { + + Map<int, Vector2>::Element *curr = touch_state.find(idx); + // Defensive + if (!curr) + return; + + if (curr->get() == Vector2(p_x, p_y)) + return; + + curr->get() = Vector2(p_x, p_y); Ref<InputEventScreenDrag> event; event.instance(); @@ -271,6 +286,13 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) { ReleaseCapture(); } + + // Release every touch to avoid sticky points + for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) { + _touch_event(false, E->get().x, E->get().y, E->key()); + } + touch_state.clear(); + break; } case WM_ACTIVATE: // Watch For Window Activate Message @@ -674,7 +696,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) print_line("input lang change"); } break; -#if WINVER >= 0x0601 // for windows 7 case WM_TOUCH: { BOOL bHandled = FALSE; @@ -687,10 +708,10 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) //do something with each touch input entry if (ti.dwFlags & TOUCHEVENTF_MOVE) { - _drag_event(ti.x / 100, ti.y / 100, ti.dwID); + _drag_event(ti.x / 100.0f, ti.y / 100.0f, ti.dwID); } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) { - _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN != 0, ti.x / 100, ti.y / 100, ti.dwID); + _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, ti.x / 100.0f, ti.y / 100.0f, ti.dwID); }; } bHandled = TRUE; @@ -708,7 +729,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } break; -#endif case WM_DEVICECHANGE: { joypad->probe_joypads(); @@ -1092,7 +1112,7 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int tme.dwHoverTime = HOVER_DEFAULT; TrackMouseEvent(&tme); - //RegisterTouchWindow(hWnd, 0); // Windows 7 + RegisterTouchWindow(hWnd, 0); _ensure_user_data_dir(); @@ -1206,6 +1226,7 @@ void OS_Windows::finalize() { memdelete(joypad); memdelete(input); + touch_state.clear(); visual_server->finish(); memdelete(visual_server); @@ -1608,7 +1629,6 @@ void OS_Windows::_update_window_style(bool repaint) { Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { - DLL_DIRECTORY_COOKIE cookie; if (p_also_set_library_path) { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index e98f8277df..af1ccd4446 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -117,6 +117,7 @@ class OS_Windows : public OS { InputDefault *input; JoypadWindows *joypad; + Map<int, Vector2> touch_state; PowerWindows *power_manager; @@ -132,8 +133,8 @@ class OS_Windows : public OS { CrashHandler crash_handler; - void _drag_event(int p_x, int p_y, int idx); - void _touch_event(bool p_pressed, int p_x, int p_y, int idx); + void _drag_event(float p_x, float p_y, int idx); + void _touch_event(bool p_pressed, float p_x, float p_y, int idx); void _update_window_style(bool repaint = true); @@ -212,7 +213,7 @@ public: virtual void set_borderless_window(int p_borderless); virtual bool get_borderless_window(); - virtual Error open_dynamic_library(const String p_path, void *&p_library_handle,bool p_also_set_library_path=false); + virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); virtual Error close_dynamic_library(void *p_library_handle); virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false); diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 3d07851c4f..d7dbe71da4 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -55,6 +55,7 @@ def get_opts(): BoolVariable('pulseaudio', 'Detect & use pulseaudio', True), BoolVariable('udev', 'Use udev for gamepad connection callbacks', False), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), + BoolVariable('touch', 'Enable touch events', True), ] @@ -141,6 +142,14 @@ def configure(env): env.ParseConfig('pkg-config xinerama --cflags --libs') env.ParseConfig('pkg-config xrandr --cflags --libs') + if (env['touch']): + x11_error = os.system("pkg-config xi --modversion > /dev/null ") + if (x11_error): + print("xi not found.. cannot build with touch. Aborting.") + sys.exit(255) + env.ParseConfig('pkg-config xi --cflags --libs') + env.Append(CPPFLAGS=['-DTOUCH_ENABLED']) + # FIXME: Check for existence of the libs before parsing their flags with pkg-config if not env['builtin_openssl']: diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index d1aa129e77..bf63921b06 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -178,6 +178,50 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au } } +#ifdef TOUCH_ENABLED + if (!XQueryExtension(x11_display, "XInputExtension", &touch.opcode, &event_base, &error_base)) { + fprintf(stderr, "XInput extension not available"); + } else { + // 2.2 is the first release with multitouch + int xi_major = 2; + int xi_minor = 2; + if (XIQueryVersion(x11_display, &xi_major, &xi_minor) != Success) { + fprintf(stderr, "XInput 2.2 not available (server supports %d.%d)\n", xi_major, xi_minor); + touch.opcode = 0; + } else { + int dev_count; + XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count); + + for (int i = 0; i < dev_count; i++) { + XIDeviceInfo *dev = &info[i]; + if (!dev->enabled) + continue; + /*if (dev->use != XIMasterPointer) + continue;*/ + + bool direct_touch = false; + for (int j = 0; j < dev->num_classes; j++) { + if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) { + direct_touch = true; + printf("%d) %d %s\n", i, dev->attachment, dev->name); + break; + } + } + if (direct_touch) { + touch.devices.push_back(dev->deviceid); + fprintf(stderr, "Using touch device: %s\n", dev->name); + } + } + + XIFreeDeviceInfo(info); + + if (!touch.devices.size()) { + fprintf(stderr, "No suitable touch device found\n"); + } + } + } +#endif + xim = XOpenIM(x11_display, NULL, NULL, NULL); if (xim == NULL) { @@ -308,6 +352,32 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr); +#ifdef TOUCH_ENABLED + if (touch.devices.size()) { + + // Must be alive after this block + static unsigned char mask_data[XIMaskLen(XI_LASTEVENT)] = {}; + + touch.event_mask.deviceid = XIAllMasterDevices; + touch.event_mask.mask_len = sizeof(mask_data); + touch.event_mask.mask = mask_data; + + XISetMask(touch.event_mask.mask, XI_TouchBegin); + XISetMask(touch.event_mask.mask, XI_TouchUpdate); + XISetMask(touch.event_mask.mask, XI_TouchEnd); + XISetMask(touch.event_mask.mask, XI_TouchOwnership); + + XISelectEvents(x11_display, x11_window, &touch.event_mask, 1); + + XIClearMask(touch.event_mask.mask, XI_TouchOwnership); + + // Grab touch devices to avoid OS gesture interference + for (int i = 0; i < touch.devices.size(); ++i) { + XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask); + } + } +#endif + /* set the titlebar name */ XStoreName(x11_display, x11_window, "Godot"); @@ -487,6 +557,10 @@ void OS_X11::finalize() { #ifdef JOYDEV_ENABLED memdelete(joypad); #endif +#ifdef TOUCH_ENABLED + touch.devices.clear(); + touch.state.clear(); +#endif memdelete(input); visual_server->finish(); @@ -1435,6 +1509,69 @@ void OS_X11::process_xevents() { continue; } +#ifdef TOUCH_ENABLED + if (XGetEventData(x11_display, &event.xcookie)) { + + if (event.xcookie.extension == touch.opcode) { + + XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data; + int index = event_data->detail; + Vector2 pos = Vector2(event_data->event_x, event_data->event_y); + + switch (event_data->evtype) { + + case XI_TouchBegin: // Fall-through + XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch); + + case XI_TouchEnd: { + + bool is_begin = event_data->evtype == XI_TouchBegin; + + Ref<InputEventScreenTouch> st; + st.instance(); + st->set_index(index); + st->set_position(pos); + st->set_pressed(is_begin); + + if (is_begin) { + if (touch.state.has(index)) // Defensive + break; + touch.state[index] = pos; + input->parse_input_event(st); + } else { + if (!touch.state.has(index)) // Defensive + break; + touch.state.erase(index); + input->parse_input_event(st); + } + } break; + + case XI_TouchUpdate: { + + Map<int, Vector2>::Element *curr_pos_elem = touch.state.find(index); + if (!curr_pos_elem) { // Defensive + break; + } + + if (curr_pos_elem->value() != pos) { + + Ref<InputEventScreenDrag> sd; + sd.instance(); + sd->set_index(index); + sd->set_position(pos); + sd->set_relative(pos - curr_pos_elem->value()); + input->parse_input_event(sd); + + curr_pos_elem->value() = pos; + } + } break; + } + } + + XFreeEventData(x11_display, &event.xcookie); + } +#endif + switch (event.type) { case Expose: Main::force_redraw(); @@ -1477,6 +1614,12 @@ void OS_X11::process_xevents() { ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime); } +#ifdef TOUCH_ENABLED + // Grab touch devices to avoid OS gesture interference + for (int i = 0; i < touch.devices.size(); ++i) { + XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask); + } +#endif if (xic) { XSetICFocus(xic); } @@ -1493,6 +1636,23 @@ void OS_X11::process_xevents() { } XUngrabPointer(x11_display, CurrentTime); } +#ifdef TOUCH_ENABLED + // Ungrab touch devices so input works as usual while we are unfocused + for (int i = 0; i < touch.devices.size(); ++i) { + XIUngrabDevice(x11_display, touch.devices[i], CurrentTime); + } + + // Release every pointer to avoid sticky points + for (Map<int, Vector2>::Element *E = touch.state.front(); E; E = E->next()) { + + Ref<InputEventScreenTouch> st; + st.instance(); + st->set_index(E->key()); + st->set_position(E->get()); + input->parse_input_event(st); + } + touch.state.clear(); +#endif if (xic) { XUnsetICFocus(xic); } diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index a74e6ee5f3..84dff2e089 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -48,6 +48,9 @@ #include <X11/Xlib.h> #include <X11/extensions/Xrandr.h> #include <X11/keysym.h> +#ifdef TOUCH_ENABLED +#include <X11/extensions/XInput2.h> +#endif // Hints for X11 fullscreen typedef struct { @@ -117,6 +120,14 @@ class OS_X11 : public OS_Unix { Point2i last_click_pos; uint64_t last_click_ms; uint32_t last_button_state; +#ifdef TOUCH_ENABLED + struct { + int opcode; + Vector<int> devices; + XIEventMask event_mask; + Map<int, Vector2> state; + } touch; +#endif unsigned int get_mouse_button_state(unsigned int p_x11_state); void get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state); |