The window cannot be moved by touch #9

Closed
opened 2026-10-13 03:22:11 +00:00 by asxpi · 0 comments

On Linux with the app's own title bar, the window cannot be moved with a finger on a touchscreen: dragging the header bar by touch does nothing. With a mouse it moves as expected. Seen on GNOME (Wayland, mutter 50.2) with the 0.4.7 Flatpak built from fix/touch-resize (#8); nothing in that branch touches the move path, so it is most likely the same in every build.

Why

Two things are missing, either one enough to stop it:

  1. The page never asks for a move on touch. ui/src/views/window-drag.ts starts a move on mousedown followed by a mousemove with the button held past 4px. WebKitGTK sends no mouse events while a finger drags; it only synthesizes a click when the finger lifts. So frame.startDragging() is never called. A Wayland log of a test session shows no xdg_toplevel.move after any touch, and one after every mouse drag.
  2. tao's move is mouse-only. startDragging() ends in tao 0.35.3's WindowRequest::DragWindow (src/platform_impl/linux/event_loop.rs:336), which calls begin_move_drag(1, x, y, 0) on the seat's pointer device. Started during a touch, GTK would send the move with a mouse serial, and the compositor would refuse it.

What a fix needs

  • In the page: start the move from pointer events with pointerType === 'touch' (pointerdown, then pointermove past the threshold) on the data-window-drag regions, alongside the mouse path.
  • In the shell: a command that starts the move for the touchscreen device, e.g. gdk_window_begin_move_drag_for_device() with the device and position of the touch in progress, so the request carries the live touch's serial. Exactly one request per touch.

Careful with mutter

Until mutter has a NULL check in touch_can_grab_surface() (#7), a second move or resize request carrying the serial of a touch that mutter has already cancelled crashes GNOME Shell, and starting a move is what makes mutter cancel the touch. So the shell side must send the request once per touch and never repeat it, and it is best tested on a patched mutter first.

Also seen

With the touch-resize handlers blocked (#8), every finger lift still makes tao's button-press handler (event_loop.rs:521) answer GTK's emulated click with xdg_toplevel.resize(seat, 0 or a stale mouse serial, 1), a top-edge resize. The compositor refuses it and it carries no touch serial, so it is harmless, but it is noise worth reporting to tao along with its touch resizing.

On Linux with the app's own title bar, the window cannot be moved with a finger on a touchscreen: dragging the header bar by touch does nothing. With a mouse it moves as expected. Seen on GNOME (Wayland, mutter 50.2) with the 0.4.7 Flatpak built from `fix/touch-resize` (#8); nothing in that branch touches the move path, so it is most likely the same in every build. ## Why Two things are missing, either one enough to stop it: 1. **The page never asks for a move on touch.** `ui/src/views/window-drag.ts` starts a move on `mousedown` followed by a `mousemove` with the button held past 4px. WebKitGTK sends no mouse events while a finger drags; it only synthesizes a click when the finger lifts. So `frame.startDragging()` is never called. A Wayland log of a test session shows no `xdg_toplevel.move` after any touch, and one after every mouse drag. 2. **tao's move is mouse-only.** `startDragging()` ends in tao 0.35.3's `WindowRequest::DragWindow` (`src/platform_impl/linux/event_loop.rs:336`), which calls `begin_move_drag(1, x, y, 0)` on the seat's *pointer* device. Started during a touch, GTK would send the move with a mouse serial, and the compositor would refuse it. ## What a fix needs - In the page: start the move from pointer events with `pointerType === 'touch'` (`pointerdown`, then `pointermove` past the threshold) on the `data-window-drag` regions, alongside the mouse path. - In the shell: a command that starts the move for the touchscreen device, e.g. `gdk_window_begin_move_drag_for_device()` with the device and position of the touch in progress, so the request carries the live touch's serial. Exactly one request per touch. ## Careful with mutter Until mutter has a NULL check in `touch_can_grab_surface()` (#7), a second move or resize request carrying the serial of a touch that mutter has already cancelled crashes GNOME Shell, and starting a move is what makes mutter cancel the touch. So the shell side must send the request once per touch and never repeat it, and it is best tested on a patched mutter first. ## Also seen With the touch-resize handlers blocked (#8), every finger lift still makes tao's `button-press` handler (`event_loop.rs:521`) answer GTK's emulated click with `xdg_toplevel.resize(seat, 0 or a stale mouse serial, 1)`, a top-edge resize. The compositor refuses it and it carries no touch serial, so it is harmless, but it is noise worth reporting to tao along with its touch resizing.
ann closed this issue 2026-10-15 18:00:52 +00:00
ann referenced this issue from a commit 2026-10-27 11:52:18 +00:00
Android lock fixes from the final audit of 2026-10-27 (findings 1, 3, 4, 7, 8, 9, 10, 11): every lock now runs on DeviceLock's one worker and looks at the store itself (storage info) when its turn comes, so a screen-off heard while the silent reopen is still opening the tier closes it again instead of being dropped (H1), and a reopen or a fingerprint open that ends with the phone already off or locked is closed again at once (DeviceLock.opened, called from MainActivity.presence after Relock.keep); behind a secure lock an open split store closes at every lock whether or not a copy of K2 is held, the copy deciding only whether the unlock is silent, and with no copy the page is told __anotherDeviceLocked('fingerprint') and the gate says why it asks (M3, new string in en ru uk et es); a close libtacky holds back for a call or a transfer (close_main closed 0) sets DeviceLock.closeHeld, the bridge's deviceClosePending(status) makes the page take that unlocked store as call (shellStatus in store.begin and recheck), so a fresh window after a swipe during a call gets the fingerprint, and open-main over the held close releases K2 through the prompt and calls the close off, or opens the tier with it where the close went meanwhile (M4); the page publishes its drafts and answers deviceLockFlushed after a request round trip before close_main goes (at most a second, #11); the gate offers the fingerprint after RELOCK_WAIT_MS (15 s) of a reopen that never comes (#10); Relock.take sleeps for its retry outside the object's lock (#9); EnrolmentProbe counts an unexpected Keystore exception on a made probe as a change, fail closed (#8); the headers of DeviceLock, Relock and docs/push.md now say K2 passes through JVM strings that are released, not zeroed (#7, K2 over JNI as bytes left for later). Ringing still counts as busy in libtacky's MainTierBusy (libtacky's, not changed here). Tests: RelockTest 8 new (screen off during the reopen with the keyguard locked and within the lock delay, both failing on the old code; an open ending behind the lock; a held close keeping a fresh window at the fingerprint and called off by the prompt, or opened by it where it went; the close waiting for the page's drafts; a take's retry not holding drop), DeviceLockTest and WatchesTest updated for the close without a copy, KeystoreSealersTest for the fail-closed probe; device-lock.test.ts and storage-gate.test.ts 6 new; JVM free 485, gms 488, all passing; make check STRICT=1 clean; make test-ui 380 files, 4168 passed, 8 skipped; make test-shell ok.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
another-im/anotherim-js#9
No description provided.