Skip to content

A zone's pointer shows dwl's default cursor, never the zone's own image - #201

Merged
DevomB merged 2 commits into
mainfrom
cursor-confinement
Oct 7, 2026
Merged

DevomB merged 2 commits into
mainfrom
cursor-confinement

Conversation

@DevomB

@DevomB DevomB commented Oct 2, 2026

Copy link
Copy Markdown
Owner

While the pointer was over a zone's window, the zone could draw anywhere on the screen, over the chrome's windows and the other zones' borders included. It did this with its cursor image.

  • kryptik-wlproxy forwards wl_pointer.set_cursor unchanged, because wl_seat is on its allowlist.
  • dwl 0.8's setcursor() passed the image of whichever client had pointer focus to wlr_cursor_set_surface().
  • wlroots 0.19 puts no limit on the size of that image. One larger than the hardware cursor plane (64x64 on virtio-gpu) is drawn in software, above every scene layer. The client picks the hotspot, so it can place the image anywhere on the output.

The fix

In setcursor(), a client whose window under the pointer is zoned (c->zoneborder != unzonedcolor) gets dwl's default xcursor, never its own surface (tools/desktop/dwl-zone-borders.py). Zone 0's clients are unchanged.

The cost: zone programs lose their own cursor shapes, such as a text cursor. Over a zone's window the pointer is always the arrow.

Other ways a zone's surface could reach the cursor

setcursor() is dwl's only caller of wlr_cursor_set_surface(). The other routes are closed:

  • Drag icons need wl_data_device_manager, which the proxy hides.
  • wp_cursor_shape_manager_v1 is not on the proxy's allowlist. A shape is drawn from the compositor's own cursor theme in any case.
  • Pointer constraints, relative pointer and virtual pointer are not on the allowlist either. dwl 0.8 has no tablet support.

Back on zone 0's windows

A zone's image is never installed, so there is nothing to restore. Moving onto a border or the background sets the default cursor (motionnotify), and a zone 0 client sets its own cursor when the pointer enters its window.

Tests (desktop suite)

  • wlprobe cursor SECONDS maps a window. On every wl_pointer.enter it asks for a cursor image of 2560x1600 with the hotspot in its middle, in a colour nothing else draws (cursor_rgb). If drawn, the image covers the whole 1280x800 screen.
  • vm-drive.py has a new pointer:DX,DY step, which moves the guest's mouse through QMP. The suite moves the pointer to the top-left corner and then into the newest window, which crosses that window's border and so focuses it.
  • Zone 0 first, zone0-cursor-set: the host checks that its image covers more than half the screenshot. This shows a drawn cursor appears in a screendump, which a hardware-plane cursor would not.
  • Then untrusted, zone-cursor-asked: the host checks that not one pixel of the colour is drawn, and that the untrusted, work and chrome windows are framed as before.

A later option, not built

Cap a zone's cursor at 64x64 with the hotspot clamped inside it, and keep both up to date on every commit to the cursor surface. Zone programs would keep their own shapes, but that takes more code in dwl or the proxy.

Overlap with #191 (fullscreen bar)

Both PRs change wlprobe.c's mode check and usage line and the header comment of kryptik-chrome. Whichever merges second keeps both. With both in, the comment would read: "No zone can draw over it by itself, read its input or capture it: the proxy passes no layer shell, global input or screencopy, dwl never draws a zone's cursor image, and a zone's window covers the chrome only fullscreen, by the user's Alt+e, until the chrome opens another window."

…set, which would be drawn above every window wherever its hotspot put it
…old when a surface of its own enters an output, a cursor image among them once the compositor takes it. Zone 0's image is told, a zone's client hears of its window through its proxy, and its image must hear nothing; a screenshot from the host holds no cursor, so the pixel count could not pass
@DevomB
DevomB merged commit 0c95c87 into main Oct 7, 2026
20 checks passed
@DevomB
DevomB deleted the cursor-confinement branch October 7, 2026 04:48
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