diff --git a/src/spice2x/hooks/graphics/graphics.cpp b/src/spice2x/hooks/graphics/graphics.cpp index f3e19e6..0cc203b 100644 --- a/src/spice2x/hooks/graphics/graphics.cpp +++ b/src/spice2x/hooks/graphics/graphics.cpp @@ -163,6 +163,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l if (wintouchemu::INJECT_MOUSE_AS_WM_TOUCH) { // drop mouse inputs since only wintouches should be used switch (uMsg) { + case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: diff --git a/src/spice2x/misc/wintouchemu.cpp b/src/spice2x/misc/wintouchemu.cpp index 944584d..d916d56 100644 --- a/src/spice2x/misc/wintouchemu.cpp +++ b/src/spice2x/misc/wintouchemu.cpp @@ -38,6 +38,9 @@ namespace wintouchemu { bool INJECT_MOUSE_AS_WM_TOUCH = false; bool LOG_FPS = false; bool ADD_TOUCH_FLAG_PRIMARY = false; + + // state + double last_touch_event = 0.0; static inline bool is_emu_enabled() { return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR; @@ -248,7 +251,10 @@ namespace wintouchemu { touch_input->y -= SPICETOUCH_TOUCH_Y; } - // log_misc("wintouchemu", "mouse state ({}, {})", to_string(touch_input->x), to_string(touch_input->y)); + // log_misc( + // "wintouchemu", + // "mouse state ({}, {}) event={}", + // to_string(touch_input->x), to_string(touch_input->y), mouse_state.touch_event); auto valid = true; if (overlay::OVERLAY) { @@ -439,6 +445,8 @@ namespace wintouchemu { } } + const auto now = get_performance_milliseconds(); + // update touch events if (hWnd != nullptr) { @@ -458,6 +466,7 @@ namespace wintouchemu { // check if new events are available if (event_count > 0) { + last_touch_event = now; // send fake event to make the game update it's touch inputs auto wndProc = (WNDPROC) GetWindowLongPtr(hWnd, GWLP_WNDPROC); @@ -484,7 +493,12 @@ namespace wintouchemu { // value from GetTouchInputInfo or fail to read dwFlags for valid events, so it's not OK to // send empty events when the mouse button is not clicked/released if (hWnd != nullptr && USE_MOUSE) { - bool button_pressed = ((GetKeyState(VK_LBUTTON) & 0x100) != 0); + bool button_pressed = get_async_primary_mouse(); + + // if there was a touch event in the last 500 ms, don't insert new button presses + if (button_pressed && (now - last_touch_event) < 500) { + button_pressed = false; + } // figure out what kind of touch event to simulate if (button_pressed && !mouse_state.last_button_pressed) { diff --git a/src/spice2x/touch/touch.cpp b/src/spice2x/touch/touch.cpp index 43edd5f..18803dc 100644 --- a/src/spice2x/touch/touch.cpp +++ b/src/spice2x/touch/touch.cpp @@ -67,6 +67,8 @@ static const char *LOG_MODULE_NAME = "touch"; static TouchHandler *TOUCH_HANDLER = nullptr; +static bool is_mouse_message_from_touchscreen(); + TouchHandler::TouchHandler(std::string name) { log_info("touch", "Using touch handler: {}", name); } @@ -215,6 +217,30 @@ void update_card_button() { } } +static void release_all_mouse_touch_points() { + std::lock_guard lock_points(TOUCH_POINTS_M); + std::lock_guard lock_events(TOUCH_EVENTS_M); + + for (size_t x = 0; x < TOUCH_POINTS.size();) { + TouchPoint *tp = &TOUCH_POINTS[x]; + + if (tp->id == 0u) { + TouchEvent te { + .id = tp->id, + .x = tp->x, + .y = tp->y, + .type = TOUCH_UP, + .mouse = tp->mouse, + }; + add_touch_event(&te); + + TOUCH_POINTS.erase(TOUCH_POINTS.begin() + x); + } else { + x++; + } + } +} + static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { // check if touch was registered @@ -490,35 +516,22 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP // parse mouse messages switch (msg) { case WM_LBUTTONDOWN: { - // check if mouse is enabled if (SPICETOUCH_ENABLE_MOUSE) { + if (is_mouse_message_from_touchscreen()) { + return 0; + } + + // subscribe to mouse messages even when the cursor leaves the window + SetCapture(hWnd); + + // release all old events before inserting a new one + release_all_mouse_touch_points(); // lock touch points std::lock_guard lock_points(TOUCH_POINTS_M); std::lock_guard lock_events(TOUCH_EVENTS_M); - // remove all points with ID 0 - for (size_t x = 0; x < TOUCH_POINTS.size(); x++) { - TouchPoint *tp = &TOUCH_POINTS[x]; - - if (tp->id == 0u) { - - // generate touch up event - TouchEvent te { - .id = tp->id, - .x = tp->x, - .y = tp->y, - .type = TOUCH_UP, - .mouse = tp->mouse, - }; - add_touch_event(&te); - - // erase touch point - TOUCH_POINTS.erase(TOUCH_POINTS.begin() + x); - } - } - // create touch point TouchPoint tp { .id = 0, @@ -545,9 +558,11 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP break; } case WM_MOUSEMOVE: { - // check if mouse is enabled if (SPICETOUCH_ENABLE_MOUSE) { + if (is_mouse_message_from_touchscreen()) { + return 0; + } // lock touch points std::lock_guard lock_points(TOUCH_POINTS_M); @@ -582,40 +597,31 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP break; } case WM_LBUTTONUP: { - // check if mouse is enabled if (SPICETOUCH_ENABLE_MOUSE) { + if (is_mouse_message_from_touchscreen()) { + return 0; + } - // lock touch points - std::lock_guard lock_points(TOUCH_POINTS_M); - std::lock_guard lock_events(TOUCH_EVENTS_M); - - // remove all points with ID 0 - for (size_t x = 0; x < TOUCH_POINTS.size(); x++) { - TouchPoint *tp = &TOUCH_POINTS[x]; - - if (tp->id == 0u) { - - // generate touch up event - TouchEvent te { - .id = tp->id, - .x = tp->x, - .y = tp->y, - .type = TOUCH_UP, - .mouse = tp->mouse, - }; - add_touch_event(&te); - - // remove touch point - TOUCH_POINTS.erase(TOUCH_POINTS.begin() + x); - } + release_all_mouse_touch_points(); + if (GetCapture() == hWnd) { + ReleaseCapture(); } } break; } - default: + case WM_CAPTURECHANGED: + case WM_CANCELMODE: { + // to deal with window losing the capture after SetCapture + release_all_mouse_touch_points(); + if (msg == WM_CANCELMODE && GetCapture() == hWnd) { + ReleaseCapture(); + } + break; + } + default: // call original function if (SPICETOUCH_CALL_OLD_PROC && SPICETOUCH_OLD_PROC != nullptr) { return SPICETOUCH_OLD_PROC(hWnd, msg, wParam, lParam); @@ -663,6 +669,8 @@ void touch_attach_dx_hook() { // initialize touch handler touch_initialize(); + log_info("touch", "touch_attach_dx_hook: attaching SpiceTouchWndProc..."); + // add dx hook graphics_add_wnd_proc(SpiceTouchWndProc); @@ -681,6 +689,8 @@ void touch_create_wnd(HWND hWnd, bool overlay) { // initialize touch handler touch_initialize(); + log_info("touch", "touch_create_wnd: creating SPICETOUCH_TOUCH_THREAD..."); + // create thread SPICETOUCH_TOUCH_THREAD = new std::thread([hWnd, overlay]() { @@ -956,4 +966,11 @@ void update_spicetouch_window_dimensions(HWND hWnd) { SPICETOUCH_TOUCH_Y = topleft.y; SPICETOUCH_TOUCH_WIDTH = bottomright.x - topleft.x; SPICETOUCH_TOUCH_HEIGHT = bottomright.y - topleft.y; -} \ No newline at end of file +} + +// https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages +static bool is_mouse_message_from_touchscreen() { + constexpr ULONG_PTR MI_WP_SIGNATURE = 0xFF515700; + constexpr ULONG_PTR SIGNATURE_MASK = 0xFFFFFF00; + return (GetMessageExtraInfo() & SIGNATURE_MASK) == MI_WP_SIGNATURE; +}