From 26b3b6479493c9c82bbf0dec02a87373eb9720ae Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Sat, 18 Apr 2026 16:22:00 -0700 Subject: [PATCH] rawinput, touch: discard invalid touch points from device in hybrid mode (#649) ## Link to GitHub Issue or related Pull Request, if one exists Fixes #648 ## Description of change Deal with devices in hybrid packet mode that send garbage data in second packet when it's out of bounds (outside the number of contact points). MSDN says they're supposed to be (0, 0), but some devices don't follow the spec. ## Testing I don't have a device that reports garbage values but this should fix it in theory. I tested on my Dell touch monitor which is in hybrid mode & sends 5 touches at a time, but can split across two packets to send up to 10 touches. It sends (0, 0) for the remaining slots when there are 6-9 touch points, but confirmed that there was no regression. --- src/spice2x/rawinput/touch.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/spice2x/rawinput/touch.cpp b/src/spice2x/rawinput/touch.cpp index 530acc1..efc26c0 100644 --- a/src/spice2x/rawinput/touch.cpp +++ b/src/spice2x/rawinput/touch.cpp @@ -192,8 +192,10 @@ namespace rawinput::touch { contact_count = std::max(contact_count, (size_t) hid->value_states_raw[index]); } + // https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchscreen-packet-reporting-modes // hybrid mode devices will report a contact count of 0 for subsequent reports that are // part of the same initial frame + // most laptops and touchscreens are like this - they have the contact count set to 5 but can actually report up to 10 fingers if (contact_count > 0) { if (contact_count > touch_report_count) { touch.remaining_contact_count = contact_count - touch_report_count; @@ -216,6 +218,12 @@ namespace rawinput::touch { touch_points.reserve(touch.elements_x.size()); for (size_t i = 0; i < touch.elements_x.size(); i++) { + // if there are no more touch events to handle, exit out early + if (touch_report_count == 0) { + break; + } + touch_report_count--; + // build touch point HIDTouchPoint hid_tp{}; auto pos_x = hid->value_states[touch.elements_x[i]]; @@ -247,17 +255,6 @@ namespace rawinput::touch { hid_tp.y = 1.f - hid_tp.y; } - // check if this touch point should be considered valid - // - // If "Contact count" reports there are no touch reports remaining and the X and Y - // coordinates of this touch point are zero, then this report element should be - // skipped. - if (touch_report_count == 0 && hid_tp.x == 0.f && hid_tp.y == 0.f) { - continue; - } else if (touch_report_count > 0) { - touch_report_count--; - } - // generate ID (hopefully unique) hid_tp.id = (DWORD) hid->value_states_raw[touch.elements_contact_identifier[i]]; hid_tp.id += (DWORD) (0xFFFFFF + device->id * 512);