Skip to content

seat: drop set_cursor requests when pointer capability is absent - #526

Merged
emersion merged 1 commit into
cage-kiosk:masterfrom
dhobsd:master
Aug 18, 2026
Merged

seat: drop set_cursor requests when pointer capability is absent#526
emersion merged 1 commit into
cage-kiosk:masterfrom
dhobsd:master

Conversation

@dhobsd

@dhobsd dhobsd commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

When a pointer device is removed, cage calls wlr_cursor_unset_image() and broadcasts wl_seat.capabilities(0) to clients. However, because Wayland IPC is asynchronous, a client may legally send a lagging wl_pointer_set_cursor request before processing the revocation event.

Currently, handle_request_set_cursor and handle_request_set_shape only check if the client has focus, ignoring whether the seat actually still possesses the pointer capability. Because cage shares a single wlr_cursor object between touch and pointer devices, honoring this lagging request forcibly re-attaches the cursor surface, causing a permanent "ghost" cursor to get stranded on such displays.

This commit explicitly drops requests if the seat no longer has the WL_SEAT_CAPABILITY_POINTER flag.

@dhobsd

dhobsd commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

So, I ran into this with an RPi4 and an Acer UT272, which has a ghost input device. Indeed, using udev rules as suggested in #524 to get rid of that device works to avoid the ghost cursor on startup. However, I keep noticing the cursor appear hours or days later, and I think this is due to the issue fixed in this PR. If there's a flappy USB bus, or if the kiosk allows hotplugging USB devices, the issue gets the chance to recur.

The offending execution order:

  1. libinput may discover devices and advertise them prior to udev rules applying.
  2. cage will discover a new wlroots pointer device via handle_new_pointer and advertises it to the client.
  3. udev rules apply and cage removes the device, falling through to update_capabilities which hides the pointer via wlr_cursor_unset_image.
  4. The client receives the capabilities event and tells cage to set the cursor.
  5. handle_request_set_cursor does not check whether WL_SEAT_CAPABILITY_POINTER is still set, and blindly enables the cursor surface for a device that no longer exists, or has had its flags changed.

Because the hardware has already settled in this case, nothing will ever cause cage to call wlr_cursor_unset_image again, because the client calling wl_pointer_release is effectively invisible to cage. At this point, the pointer cannot be moved or interacted with because there are no devices referencing it.

@emersion emersion 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.

Looks good to me. Can we also mirror this check to handle_request_set_shape()?

When a pointer device is removed, cage calls wlr_cursor_unset_image()
and broadcasts wl_seat.capabilities(0) to clients. However, because
Wayland IPC is asynchronous, a client may legally send a lagging
wl_pointer_set_cursor request before processing the revocation event.

Currently, handle_request_set_cursor and handle_request_set_shape
only check if the client has focus, ignoring whether the seat actually
still possesses the pointer capability. Because cage shares a single
wlr_cursor object between touch and pointer devices, honoring this
lagging request forcibly re-attaches the cursor surface, causing a
permanent "ghost" cursor to get stranded on such displays.

This commit explicitly drops requests if the seat no longer has the
WL_SEAT_CAPABILITY_POINTER flag.
@dhobsd

dhobsd commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Aha, thanks. Done!

@emersion emersion 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.

Thanks!

@emersion
emersion merged commit 1bdf030 into cage-kiosk:master Aug 18, 2026
10 checks passed
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.

2 participants