feat(input): implement wlr-virtual-pointer-unstable-v1#96
Merged
Conversation
Advertise the wlr-virtual-pointer-unstable-v1 protocol so clients
like \`wlrctl\`, \`ydotool\` (its wayland-native mode), \`wtype\`, and
bespoke automation drivers (MCP / test harnesses) can synthesize
pointer events that feed straight into Smithay's \`PointerHandle\`.
Smithay does not ship a virtual-pointer delegate, so the
\`GlobalDispatch\`/\`Dispatch\` plumbing is hand-rolled in
\`state::virtual_pointer\`. Scope is the minimum useful subset:
motion relative displacement, applied cumulatively
motion_absolute normalized absolute position mapped to the first
output's geometry
button wl_pointer button press/release
axis accumulated via an AxisFrame
axis_source recorded on the pending AxisFrame
axis_stop stop notification on an axis
axis_discrete discrete (v120) scroll step
frame flush of the coalesced event sequence
Axis events accumulate across multiple axis / axis_source / axis_stop
/ axis_discrete requests into a single AxisFrame, committed to the
pointer on \`frame\`. This matches how real libinput pointer sequences
are batched before dispatch.
No constraints, no relative-motion reporting, no clamp to screen
bounds — those are real-pointer concerns and the client driving the
virtual pointer is assumed to be well-behaved.
Paired with the \`otto-remote-control\` Claude skill in the user's
skills directory, which documents how to drive Otto via \`wlrctl\`
(\`wlrctl pointer click\`, \`wlrctl keyboard type\`,
\`wlrctl toplevel focus\`, etc.). Otto already implements
\`virtual-keyboard-unstable-v1\` and
\`wlr-foreign-toplevel-management-unstable-v1\`; this commit completes
the trio.
Real libinput clicks run focus_window_under_cursor + layers_engine button notifications from on_pointer_button, but virtual-pointer events were bypassing that handler and dispatching to whatever surface already held pointer focus. Test harnesses couldn't use motion_absolute + click to focus a specific window. Mirror the same focus + button-down/up hooks in the Frame flush so synthesized clicks behave like real ones.
b6c8e40 to
96ecc51
Compare
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
wlr-virtual-pointer-unstable-v1protocol, enabling tools likewlrctl,wtype, and automation drivers to synthesize pointer eventsGlobalDispatch/Dispatchsince Smithay doesn't ship a virtual-pointer delegatemotion,motion_absolute,button,axis,axis_source,axis_stop,axis_discrete,frameAxisFrameand flush onframe, matching real libinput batchingfocus_window_under_cursor+layers_enginebutton hooks, so synthesized clicks behave like real onesCompletes the automation trio alongside the existing
virtual-keyboard-unstable-v1andwlr-foreign-toplevel-management-unstable-v1.Test plan
wlrctl pointer clickfocuses and clicks windowswlrctl pointer movemoves the cursorwtypetypes text via virtual keyboard (existing, unaffected)wlrctl toplevel focus <app>still works (existing, unaffected)