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:
bicarus
2026-05-09 17:52:43 -07:00
committed by GitHub
parent eb037542b4
commit 33f2e1d4b7
3 changed files with 83 additions and 51 deletions
+66 -49
View File
@@ -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<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) {
// 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<std::mutex> lock_points(TOUCH_POINTS_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
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<std::mutex> 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<std::mutex> lock_points(TOUCH_POINTS_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);
// 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;
}
}
// 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;
}