Skip to content

Filter pen logical collections from composite digitizer HID trees - #17

Open
testors wants to merge 2 commits into
shueber:mainfrom
testors:filter-pen-collections
Open

Filter pen logical collections from composite digitizer HID trees#17
testors wants to merge 2 commits into
shueber:mainfrom
testors:filter-pen-collections

Conversation

@testors

@testors testors commented Feb 24, 2026

Copy link
Copy Markdown

Thank you for this useful tool — it has been great for adding touch support to external monitors.

Summary

Composite digitizers (e.g. Wacom-based touchscreens with integrated active pen) can expose pen and touch data within the same HID application collection. Without filtering, pen-specific logical collections — containing pressure, tilt, and barrel elements — end up being treated as touch contacts, producing incorrect coordinates.

This PR adds a small pen-collection filter in IdentifyElements() so that only finger-touch logical collections feed into the mouse-mapping logic.

Changes

  • Add IsPenCollection() — inspects direct children of a logical collection for pen-only Digitizer-page usages (TipPressure, BarrelPressure, XTilt, YTilt, BarrelSwitch, Eraser)
  • Call IsPenCollection() in IdentifyElements() to skip pen collections before they are appended to gTouchCollectionElements
  • Guard Handle_RemovalCallback against NULL gQueue — composite digitizers trigger multiple removal callbacks per disconnect; without the guard, the second call passes NULL to IOHIDQueueStop/CFRelease, causing a crash

Notes

  • No vendor/product ID hardcoding — the filter is usage-based and benefits any composite digitizer, following the existing device-matching philosophy
  • Only direct children inspected — matches the depth used by IdentifyElements() itself
  • Conservative usage setInRange (0x32) is intentionally excluded because it appears in both pen and touch collections
  • No effect on single-digitizer touchscreens — devices without pen collections pass through unchanged

Tested with

  • Lenovo ThinkVision M14t Gen2 (Wacom digitizer, VID 0x2D1F / PID 0x53D1)
  • Single-touch, multi-touch, and pinch-to-zoom all working

Test plan

  • Verify single-finger touch → mouse cursor moves correctly
  • Verify multi-touch pinch-to-zoom works
  • Verify existing single-digitizer touchscreens are unaffected (no pen collections → no filtering)
  • Verify cable disconnect does not crash the app

Composite digitizers (e.g. Wacom-based touchscreens with integrated
active pen) can expose pen and touch data within the same HID
application collection. Without filtering, pen-specific logical
collections (containing pressure, tilt, barrel elements) end up
being treated as touch contacts, producing incorrect coordinates.

Add IsPenCollection() to detect logical collections that contain
pen-only HID usages (TipPressure, BarrelPressure, XTilt, YTilt,
BarrelSwitch, Eraser) and skip them during element identification
so only finger-touch collections feed into the mouse mapping logic.

Tested with Lenovo ThinkVision M14t Gen2 (Wacom VID 0x2D1F PID 0x53D1).
When multiple HID devices match (e.g. composite digitizers exposing
several interfaces), Handle_RemovalCallback is called once per
device. After the first call releases and NULLs gQueue, subsequent
calls would pass NULL to IOHIDQueueStop and CFRelease, causing a
crash. Add a NULL check to return early on repeated removal calls.
ajmcclary added a commit to ajmcclary/Touch-Up that referenced this pull request Jun 17, 2026
Composite digitizers (e.g. Wacom-based touchscreens with integrated
active pen) can expose pen and touch data within the same HID
application collection. Without filtering, pen-specific logical
collections — containing pressure, tilt, and barrel elements — end up
being treated as touch contacts, producing incorrect coordinates.

Add IsPenCollection(), which inspects the direct children of a logical
collection for pen-only Digitizer-page usages (TipPressure,
BarrelPressure, XTilt, YTilt, BarrelSwitch, Eraser), and call it in
IdentifyElements() to skip pen collections before they are appended to
the device's touchCollectionElements.

Adapted from shueber/Touch-Up#17 by KiHyun Kang. The PR's gQueue
NULL-guard hunk is omitted: the multiscreen refactor replaced the global
gQueue with per-device state and Handle_RemovalCallback already guards
against an unmatched device.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant