I'm building a virtual pen digitizer with HIDVirtualDevice (CoreHID, macOS 26.2, com.apple.developer.hid.virtual.device entitlement granted, Developer ID signed with the provisioning profile embedding the entitlement). The device is created and activated fine and shows up as expected:
hidutil list:
0xface 0xbeef UsagePage 13 Usage 2 Transport Virtual "Hej Stylus Virtual Pen"
AppleUserHIDEventService / AppleUserHIDEventDriver
Report descriptor (Digitizer/Pen application collection, Stylus physical collection, 7-byte input report):
05 0D Usage Page (Digitizer)
09 02 Usage (Pen)
A1 01 Collection (Application)
09 20 Usage (Stylus)
A1 00 Collection (Physical)
09 42 Usage (Tip Switch)
09 32 Usage (In Range)
15 00 25 01 75 01 95 02 81 02 ; 2 bits
95 06 81 03 ; 6 bits padding (Const)
05 01 09 30 09 31 ; Generic Desktop X, Y
16 00 00 26 FF 7F 75 10 95 02 81 02 ; 0..32767, 16 bit each
05 0D 09 30 ; Digitizer / Tip Pressure
16 00 00 26 FF 1F 75 10 95 01 81 02 ; 0..8191, 16 bit
C0
C0
I feed it a synthetic stream at 60 Hz (X sweep, Y fixed, tip switch down, in range, pressure ramping 0→8191) via dispatchInputReport(data:timestamp:) and observe the results with a global NSEvent monitor (.tabletProximity, .tabletPoint, .leftMouseDown/Up/Dragged, .mouseMoved) plus the raw CGEvent fields.
What works
tabletProximity:isEnteringProximity=1,pointingDeviceType=.pen,vendorID=0xFACE,tabletID=0xBEEF,systemTabletIDassigned.- All movement arrives as
tabletPoint/mouseMovedwithsubtype == .tabletPoint; positions are exact. - Tip switch maps to
leftMouseDown/leftMouseUpcorrectly (verified by toggling the tip bit with pressure held at max). capabilityMaskon the proximity event is0x407=NX_TABLET_CAPABILITY_DEVICEIDMASK | ABSXMASK | ABSYMASK | PRESSUREMASK— so the system declares pressure capability for this device.
What doesn't
NSEvent.pressure,kCGTabletEventPointPressureandkCGMouseEventPressureare always 0.000, on every event type, including with a constant maximum pressure value (8191).NSEvent.buttonMaskis1(pen tip) andkCGTabletEventPointButtonsis1, so the report is being parsed — the pressure field just never makes it into the tablet event data.
Things I've established / tried
- The pressure value is parsed and does influence touch/click: with tip switch held down and pressure held at 0, no mouse down is ever generated. With pressure ramping, the click happens at exactly 75 % of the logical range. Reading the open-source
IOHIDEventDriver::parseDigitizerTransducerElementexplains this:Tip Pressureis read withkIOHIDValueScaleTypeCalibrated, but only X/Y/Z elements get a calibration, and an uncalibrated element scales to −1…+1 — so raw 0…8191 becomes −1…+1 and the touch threshold (+0.5) sits at 75 %. ChangingLogical Minimumto −8191 (so raw 0…8191 maps to 0…1) moves the click to exactly 50 % — confirming the model. Pressure in the event data is still 0. - Adding
Physical Minimum/Maximumand aUnitto the pressure element: no change. hidutil monitorno longer exists on macOS 26, so I can't inspect theIOHIDEventdigitizer fields directly.- This looks like the same behaviour reported for kext-based digitizers since macOS 10.12 (developer.apple.com/forums thread "IOHIPointing dispatchAbsolutePointerEvent not works" and its sibling: "the pressure information is there from the transducer, the OS doesn't respond to it on 10.12+"). The tablet-pressure dispatch in the open-source
IOHIDEventService::dispatchDigitizerEventWithOrientationis commented out, and the userspaceIOHIDEventTranslationisn't open source, so I can't tell where the value is dropped.
Questions
- Is pen pressure from a
HIDVirtualDevice(or any generic HID digitizer handled byAppleUserHIDEventDriver) expected to reachNSEvent.pressure/kCGTabletEventPointPressureat all on current macOS? Or is that path reserved for vendor drivers posting tablet events themselves? - If it is supported: which descriptor properties does the digitizer→tablet translation require for pressure — specific usages (Transducer Index, Barrel Switch, Tilt, Twist), Report ID, Physical range/Unit on the pressure element, a Feature report, or a particular device property (e.g. something in
HIDVirtualDevice.Properties/kIOHIDDigitizer*keys)? - Is there a documented way to set element calibration for a virtual device so
Tip Pressurescales 0…1 instead of −1…+1 without abusingLogical Minimum? - Is there a supported diagnostic on macOS 26 to see the
IOHIDEventdigitizer fields (pressure, touch, event mask) that the event system builds from my reports, now thathidutil monitoris gone?
Happy to file a Feedback with the full project and a sysdiagnose if that helps.