Skip to content

AppKit: Add tablet tool support#4593

Open
bytesandwich wants to merge 2 commits into
rust-windowing:masterfrom
bytesandwich:macos-tablet-tool
Open

AppKit: Add tablet tool support#4593
bytesandwich wants to merge 2 commits into
rust-windowing:masterfrom
bytesandwich:macos-tablet-tool

Conversation

@bytesandwich

@bytesandwich bytesandwich commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Add support to winit-appkit for tablet tools (pen/eraser) with rich inputs like pressure and tilt.

/begin Edit: (explain the issues with accessing data )

Apple's implementation has two difficulties we need to work around. The first is limited acccess to information about whether the event is from a tablet or a mouse (NSEvent.subtype), and the second is limited access information about kind of pointer tool created the event (NSEvent.pointingdevicetype).

  1. subtype: whether this is a mouse or tablet or just "tablet proximity" kind of event is not available (trying to access it throws an exception) on mouse_enter/mouse_exit.
Screenshot 2026-07-06 at 12 56 51 PM

So mouse_enter and mouse_exit have to be unknown pointer type in appkit.

  1. pointingdevicetype: which kind of "pointer" tool (mouse, pen, erasor, ...) that information is only available on the "proximity type" events for that device that happen when a pen tablet sense the pen is "entering nearby" or "exiting the sensible area" so relatively a pre-start-drawing and post-end-drawing kind of event.
Screenshot 2026-07-06 at 12 28 10 PM

So we keep that information cached in the ViewState.

/end edit

Tilt gets converted from -1,1 to -90,90. y is negated, since AppKit is y-up and winit documents positive y as toward the user.

  • Tested on all platforms changed
    Tested with a toy app that tracks a Wacom tablet pressure (sphere size) and tilt (the vector)
Screenshot 2026-06-09 at 8 41 47 PM
  • Added an entry to the changelog module if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality

@bytesandwich bytesandwich requested a review from madsmtm as a code owner June 10, 2026 01:27

@kchibisov kchibisov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just by looking into docs for tablet, it seems that tablet events has a property of

NSTabletPointEventSubtype or NSTabletPoint

Also, how handling works if you hover with tablet over window and also use mouse? is mouse treated as tablet in such case?

Comment thread winit-appkit/src/view.rs Outdated
Comment thread winit-appkit/src/view.rs Outdated
@bytesandwich

bytesandwich commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the review!

how handling works if you hover with tablet over window and also use mouse? is mouse treated as tablet in such case?

Good catch and yeah I needed to change something. Firstly this is an edge case and I think relatively minor. But still I think this is kind of a bummer so I put it in the edited main comment as well.
The root problem is we just can't get the subtype information in mouse_enter/mouse_exited. .subtype is documented to throw for "non mouse events", and I checked with a simple example that .subtype()unfortunately throws in mouse_enter/exit. If we don't report unknown, then we can get bad behavior out of what you suggested like this: if the pen just hovers (and sets the cached ViewState's tablet_tool) and then the user moves the mouse so there are enter/exit events from it, those mouse enter/exit events they will get the tablet tool cache value and that's not right.

I changed it to kind=unknown for now, because that seems to be the only 100% correct option and winit is a foundation library.
It could go back to always "Mouse" if you want, but always Mouse isn't right for tablets and actually might cause problems for people who have tablets and mice working around each other.

Besides mouse_enter/mouse_exited though, the mouse isn't treated like the tablet with this PR change. If they are both active at the same time then they both try to move the cursor. They both send the same .type values (like MouseMoved or LeftMouseDown), but the .subtype values are distinct (MouseEvent or TabletPoint) so you can tell which device an event came from. I'll share a log example below.

Just by looking into docs for tablet, it seems that tablet events has a property of NSTabletPointEventSubtype or NSTabletPoint

Right, and I think we need the subtype one here. The tablet NSEvents look the same as mouse events if you only look at .type (NSEventType) but the .subtype field (NSEventSubtype) has the tablet details we want - whether it's:

  • a TabletPoint event
  • a TabletProximity event
  • or MouseEvent.

The .type field will be MouseEntered or MouseMoved etc for tablets and mice.

Inline comments addressed

I also addressed the inline comments. The device-type chain is now a match, and the button mapping is extracted into a tablet_button helper.

Appendix - just extra info if it helps

Here are summarized logs of (NSEvent) event.type and (NSEvent) event.subtype for just my wacom tablet pen being used:

* .type = MouseEntered
* .type = MouseMoved // pen crosses into proximity (near the surface)
	.subtype = TabletProximity (entering=true device=Pen)
* .type = MouseMoved // pen hovering above the surface (force = 0.0)
	.subtype = TabletPoint
* .type = LeftMouseDown // pen makes contact (force > 0)
	.subtype = TabletPoint
* .type = LeftMouseDragged // pen moves while in contact
	.subtype = TabletPoint
* .type = LeftMouseUp // pen lifts off the surface
	.subtype = TabletPoint
* .type = MouseMoved // pen hovering again (force = 0.0)
	.subtype = TabletPoint
* .type = MouseMoved // pen leaves proximity
	.subtype = TabletProximity (entering=false device=Pen)

Here are summarized logs of (NSEvent) event.type and (NSEvent) event.subtype for just a mouse being used:

* .type = MouseEntered (no subtype)
* .type = MouseMoved // moving the mouse (hover, no button)
	.subtype = MouseEvent
* .type = LeftMouseDown // button press
	.subtype = MouseEvent
* .type = LeftMouseDragged // move while button held
	.subtype = MouseEvent
* .type = LeftMouseUp // button release
	.subtype = MouseEvent
* .type = MouseMoved // moving the mouse afterward
	.subtype = MouseEvent

Here are summarized logs of (NSEvent) event.type and (NSEvent) event.subtype for using the mouse and pen at the same time

(hover the pen near the surface (no contact) then use the mouse)

* .type = MouseMoved 
	.subtype=TabletProximity entering=true  device=Pen
* .type = MouseMoved 
	.subtype=TabletPoint (hover sample, force 0)

// now use the mouse while pen is hovering. Note that mouse appears normal.
* .type = MouseMoved       
	.subtype=MouseEvent
* .type = LeftMouseDown    
	.subtype=MouseEvent
* .type = LeftMouseDragged 
	.subtype=MouseEvent
* .type = LeftMouseUp      
	.subtype=MouseEvent
* .type = MouseMoved       
	.subtype=MouseEvent

@bytesandwich bytesandwich requested a review from kchibisov July 7, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants