Skip to content

A touchscreen drives the pointer: a tap is a click, a drag a drag - #628

Open
DevomB wants to merge 3 commits into
mainfrom
dwl-touch-pointer
Open

DevomB wants to merge 3 commits into
mainfrom
dwl-touch-pointer

Conversation

@DevomB

@DevomB DevomB commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Based on #207 (hardware-list, 2fafcbd), which the next batch builds on.

The gap. dwl 0.8's inputdevice() handles keyboards and pointers, and Kryptik's patch adds the lid switch. Every other device falls into /* TODO handle other input device types */. A laptop's touchscreen therefore did nothing: the kernel side was there (i2c-hid, hid-multitouch), and dwl dropped it.

What changes (tools/desktop/dwl-zone-borders.py, five exact-string edits beside the lid's):

  • A touch device is attached to dwl's cursor. The cursor's touch signals drive the pointer:
    • the first finger down moves the pointer there and presses the left button;
    • moving drags;
    • lifting or a cancel releases it;
    • a second finger is ignored.
  • The press is a wlr_pointer_button_event handed to dwl's own buttonpress, so a tap picks a window as a click does: the same focus rules, and the lock's.
  • Before the button, the surface under the touch is picked again with motionnotify(0, NULL, 0, 0, 0, 0), dwl's own focus-restoring call. dwl's motionnotify picks the surface before it moves the cursor. Without the second pick, a tap's press and release went to whatever the pointer had been resting on, possibly another zone's window, while the keyboard went to the window tapped.
  • Zones are offered no touch. WL_SEAT_CAPABILITY_TOUCH is not advertised, so the proxy and its policy are unchanged, and a zone sees only pointer events.
  • A touchscreen covers the built-in panel (an enabled eDP, LVDS or DSI output) when there is one, as a laptop's touchscreen is its own, and every screen otherwise. It is computed at each touch, so no mapping is kept that a monitor change could leave stale. One touchscreen is taken to be the laptop's, so a touch monitor on a desk drives the panel too.
  • No touch acts while the lid is shut. Closed on a dock, the panel stays on, and a touch through the lid would click and take the keyboard on a screen no one sees, and would hold off the idle lock. A touch is also dropped while a built-in panel is there but off (dark or disabled) rather than spread over the other screens. A finger already down still ends through touchup or touchcancel.
  • cleanuplisteners removes the five touch listeners with the cursor's own.

The regression check runs on the shipped image in the desktop suite:

  • tools/image/run-ovmf.sh --touch adds QEMU's virtio-multitouch-pci, bound to the GPU's first head. It refuses a QEMU without the device (it arrived in 8.1; the runners have 8.2.2).
  • tools/image/vm-drive.py touch:X,Y taps it through QMP input-send-event (mtt begin and data, then the touch button; then end). The events are bound to that head, so the button reaches the touchscreen, not the mouse.
  • build/guest-tests/gui-check.sh adds touch-picks-window:
    1. zone 0's probe window has the keyboard;
    2. untrusted's window maps beside it, unasked;
    3. the host taps the screen's left half, where dwl puts the newest window;
    4. the focus record must then name untrusted.
      The check fails if there is no touchscreen, or if zone 0 did not hold the keyboard before the tap.
  • and touch-presses-window:
    1. before the tap, the host rests the pointer on zone 0's window (GT TOUCH-HOVER-ZONE0), and zone 0's probe must say it was entered;
    2. after it, untrusted's probe must report the press and release (wlprobe cursor prints each pointer button it is sent);
    3. zone 0's probe must report none.
      Without the second pick, the press goes to zone 0's probe and the check fails.
  • eudev 3.2.15 tags the device as a touchscreen (direct, with multitouch axes), and libinput 1.30 fills in its ABS_X/Y as it does for any multitouch-only panel. The tap goes the way a laptop panel's would.
  • tools/tests/vm-drive.py checks the step's two QMP batches, and that a tap off the screen is refused before QEMU is asked.

Tests.

  • Run here:
    • dwl-zone-borders.py --check and an apply on patched dwl 0.8: all 90 edits apply, and the touch hunks land.
    • The new step's event values, from touch_events().
    • bash and Python parse the changed scripts.
    • The required-names check: 150 names, all reported.
  • Left to CI: compiling dwl (the Compositor build), and tools/tests/vm-drive.py, which needs Unix sockets this Windows host's Python lacks.
  • Left to the proof: touch-picks-window and touch-presses-window on the image.
  • Not done: a touch on a physical machine, and any guest check of the shut-lid and dark-panel drop: QEMU has no lid switch and no eDP-named output.

Not in this PR: multi-finger gestures, a touchscreen on an external monitor (it maps to the built-in panel when one is on), and pen input.

dwl 0.8 drops touch devices, so a laptop's touchscreen did nothing. dwl now
attaches one to its cursor and turns the first finger into the pointer:
down moves it there and presses the left button, moving drags, lifting
releases. The press goes through dwl's own buttonpress, so a tap picks a
window as a click does, under the same focus rules and the lock's. Zones
are offered no touch, only those pointer events, so the proxy and its
policy are unchanged. A touchscreen covers the built-in panel (eDP, LVDS
or DSI) when there is one, and every screen otherwise.

The desktop suite proves it on QEMU's virtio-multitouch: run-ovmf.sh's
--touch adds one, bound to the GPU's first head; vm-drive.py's touch: step
taps it through QMP; and gui-check's touch-picks-window has a tap give an
unasked zone window the keyboard. vm-drive's fixture checks the step's
events.
@DevomB
DevomB changed the base branch from hardware-list to main October 23, 2026 11:33
dwl's motionnotify picks the surface under the pointer before it moves the
cursor. A mouse's small steps never show it, but a touch is one jump and the
button at once: the press and release went to whatever the pointer had been
resting on, while the keyboard went to the window tapped. A tap on one
zone's window could click another's. touchpoint picks the surface again
where the touch landed, as dwl's own focus-restoring call does, before the
button goes out; a finger held down keeps its window as before.

gui-check's touch round now rests the pointer on zone 0's window first, and
holds the tap's press and release to untrusted's probe, with none for zone
0's. wlprobe's cursor mode says each pointer button it is sent.
Closed on a dock, the panel stays on, and a touch through the lid would
click, and take the keyboard, on a screen no one sees, and hold off the
idle lock. touchdown drops every touch while the lid is shut. touchpoint
drops one, too, while a built-in panel is there but off, dark or disabled,
rather than spread it over the other screens. A finger already down still
ends through touchup or touchcancel.

cleanuplisteners removes the five touch listeners with the cursor's own.
One touchscreen is taken to be the laptop's, which the comment now says.
DevomB added a commit that referenced this pull request Oct 23, 2026
…ap clicks the window under the finger, and no touch acts through a shut lid
@DevomB
DevomB marked this pull request as ready for review October 23, 2026 12:17

This branch has not been deployed

No deployments
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