mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
rawinput, touch: attempt to remove touchscreens injecting mouse events (#687)
## Link to GitHub Issue or related Pull Request, if one exists Fixes #562 ## Description of change ### rawinput touch fixes In rawinput touch handler, if the mouse events (`WM_LBUTTONDOWN`, `WM_MOUSEMOVE`, `WM_LBUTTONUP`) originated from a touch screen, drop them on the floor instead of treating them as touch events. This is an attempt to fix phantom touch issues we see with some screens. Fix another issue where, if mouse is clicked inside the window, and then dragged outside the window and button is released, touch point (id 0) continues to be held. ### wintouchemu fixes If we detected a touch input recently (500ms), ignore new mouse clicks. This isn't perfect, but hopefully it helps with filtering out mouse clicks originating from touch screens. Also, switch over to detecting the primary mouse button correctly, not just the left mouse button. ### notes Future investigation: are there really touchscreens that report a mouse HID capability & insert mouse events? We could have a way to filter them at rawinput level but it'll be ugly. ## Testing Tried jubeat, beatstream..
This commit is contained in:
@@ -163,6 +163,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
|
|||||||
if (wintouchemu::INJECT_MOUSE_AS_WM_TOUCH) {
|
if (wintouchemu::INJECT_MOUSE_AS_WM_TOUCH) {
|
||||||
// drop mouse inputs since only wintouches should be used
|
// drop mouse inputs since only wintouches should be used
|
||||||
switch (uMsg) {
|
switch (uMsg) {
|
||||||
|
case WM_MOUSEMOVE:
|
||||||
case WM_LBUTTONDOWN:
|
case WM_LBUTTONDOWN:
|
||||||
case WM_LBUTTONUP:
|
case WM_LBUTTONUP:
|
||||||
case WM_MBUTTONDOWN:
|
case WM_MBUTTONDOWN:
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ namespace wintouchemu {
|
|||||||
bool LOG_FPS = false;
|
bool LOG_FPS = false;
|
||||||
bool ADD_TOUCH_FLAG_PRIMARY = false;
|
bool ADD_TOUCH_FLAG_PRIMARY = false;
|
||||||
|
|
||||||
|
// state
|
||||||
|
double last_touch_event = 0.0;
|
||||||
|
|
||||||
static inline bool is_emu_enabled() {
|
static inline bool is_emu_enabled() {
|
||||||
return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR;
|
return FORCE || !is_touch_available("wintouchemu::is_emu_enabled") || GRAPHICS_SHOW_CURSOR;
|
||||||
}
|
}
|
||||||
@@ -248,7 +251,10 @@ namespace wintouchemu {
|
|||||||
touch_input->y -= SPICETOUCH_TOUCH_Y;
|
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;
|
auto valid = true;
|
||||||
if (overlay::OVERLAY) {
|
if (overlay::OVERLAY) {
|
||||||
@@ -439,6 +445,8 @@ namespace wintouchemu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto now = get_performance_milliseconds();
|
||||||
|
|
||||||
// update touch events
|
// update touch events
|
||||||
if (hWnd != nullptr) {
|
if (hWnd != nullptr) {
|
||||||
|
|
||||||
@@ -458,6 +466,7 @@ namespace wintouchemu {
|
|||||||
|
|
||||||
// check if new events are available
|
// check if new events are available
|
||||||
if (event_count > 0) {
|
if (event_count > 0) {
|
||||||
|
last_touch_event = now;
|
||||||
|
|
||||||
// send fake event to make the game update it's touch inputs
|
// send fake event to make the game update it's touch inputs
|
||||||
auto wndProc = (WNDPROC) GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
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
|
// 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
|
// send empty events when the mouse button is not clicked/released
|
||||||
if (hWnd != nullptr && USE_MOUSE) {
|
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
|
// figure out what kind of touch event to simulate
|
||||||
if (button_pressed && !mouse_state.last_button_pressed) {
|
if (button_pressed && !mouse_state.last_button_pressed) {
|
||||||
|
|||||||
+65
-48
@@ -67,6 +67,8 @@ static const char *LOG_MODULE_NAME = "touch";
|
|||||||
|
|
||||||
static TouchHandler *TOUCH_HANDLER = nullptr;
|
static TouchHandler *TOUCH_HANDLER = nullptr;
|
||||||
|
|
||||||
|
static bool is_mouse_message_from_touchscreen();
|
||||||
|
|
||||||
TouchHandler::TouchHandler(std::string name) {
|
TouchHandler::TouchHandler(std::string name) {
|
||||||
log_info("touch", "Using touch handler: {}", 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<std::mutex> lock_points(TOUCH_POINTS_M);
|
||||||
|
std::lock_guard<std::mutex> 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) {
|
static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||||
|
|
||||||
// check if touch was registered
|
// check if touch was registered
|
||||||
@@ -490,35 +516,22 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
|
|||||||
// parse mouse messages
|
// parse mouse messages
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case WM_LBUTTONDOWN: {
|
case WM_LBUTTONDOWN: {
|
||||||
|
|
||||||
// check if mouse is enabled
|
// check if mouse is enabled
|
||||||
if (SPICETOUCH_ENABLE_MOUSE) {
|
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
|
// lock touch points
|
||||||
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
||||||
std::lock_guard<std::mutex> lock_events(TOUCH_EVENTS_M);
|
std::lock_guard<std::mutex> 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
|
// create touch point
|
||||||
TouchPoint tp {
|
TouchPoint tp {
|
||||||
.id = 0,
|
.id = 0,
|
||||||
@@ -545,9 +558,11 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WM_MOUSEMOVE: {
|
case WM_MOUSEMOVE: {
|
||||||
|
|
||||||
// check if mouse is enabled
|
// check if mouse is enabled
|
||||||
if (SPICETOUCH_ENABLE_MOUSE) {
|
if (SPICETOUCH_ENABLE_MOUSE) {
|
||||||
|
if (is_mouse_message_from_touchscreen()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// lock touch points
|
// lock touch points
|
||||||
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
||||||
@@ -582,40 +597,31 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WM_LBUTTONUP: {
|
case WM_LBUTTONUP: {
|
||||||
|
|
||||||
// check if mouse is enabled
|
// check if mouse is enabled
|
||||||
if (SPICETOUCH_ENABLE_MOUSE) {
|
if (SPICETOUCH_ENABLE_MOUSE) {
|
||||||
|
if (is_mouse_message_from_touchscreen()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// lock touch points
|
release_all_mouse_touch_points();
|
||||||
std::lock_guard<std::mutex> lock_points(TOUCH_POINTS_M);
|
if (GetCapture() == hWnd) {
|
||||||
std::lock_guard<std::mutex> lock_events(TOUCH_EVENTS_M);
|
ReleaseCapture();
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
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
|
// call original function
|
||||||
if (SPICETOUCH_CALL_OLD_PROC && SPICETOUCH_OLD_PROC != nullptr) {
|
if (SPICETOUCH_CALL_OLD_PROC && SPICETOUCH_OLD_PROC != nullptr) {
|
||||||
return SPICETOUCH_OLD_PROC(hWnd, msg, wParam, lParam);
|
return SPICETOUCH_OLD_PROC(hWnd, msg, wParam, lParam);
|
||||||
@@ -663,6 +669,8 @@ void touch_attach_dx_hook() {
|
|||||||
// initialize touch handler
|
// initialize touch handler
|
||||||
touch_initialize();
|
touch_initialize();
|
||||||
|
|
||||||
|
log_info("touch", "touch_attach_dx_hook: attaching SpiceTouchWndProc...");
|
||||||
|
|
||||||
// add dx hook
|
// add dx hook
|
||||||
graphics_add_wnd_proc(SpiceTouchWndProc);
|
graphics_add_wnd_proc(SpiceTouchWndProc);
|
||||||
|
|
||||||
@@ -681,6 +689,8 @@ void touch_create_wnd(HWND hWnd, bool overlay) {
|
|||||||
// initialize touch handler
|
// initialize touch handler
|
||||||
touch_initialize();
|
touch_initialize();
|
||||||
|
|
||||||
|
log_info("touch", "touch_create_wnd: creating SPICETOUCH_TOUCH_THREAD...");
|
||||||
|
|
||||||
// create thread
|
// create thread
|
||||||
SPICETOUCH_TOUCH_THREAD = new std::thread([hWnd, overlay]() {
|
SPICETOUCH_TOUCH_THREAD = new std::thread([hWnd, overlay]() {
|
||||||
|
|
||||||
@@ -957,3 +967,10 @@ void update_spicetouch_window_dimensions(HWND hWnd) {
|
|||||||
SPICETOUCH_TOUCH_WIDTH = bottomright.x - topleft.x;
|
SPICETOUCH_TOUCH_WIDTH = bottomright.x - topleft.x;
|
||||||
SPICETOUCH_TOUCH_HEIGHT = bottomright.y - topleft.y;
|
SPICETOUCH_TOUCH_HEIGHT = bottomright.y - topleft.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user