https://bugs.kde.org/show_bug.cgi?id=523673
--- Comment #3 from [email protected] --- I hit the same thing (KWin 6.7.4, Arch, Wayland, Huawei MateBook X Pro 2024 with an ICNT9288 touchscreen): cursor moves, keyboard works, but no client gets any wl_pointer event (no enter/button/axis) from either the touchpad or a USB mouse. It happened twice in three days, both times right after closing the lid → suspend → opening the lid. I tracked down the cause and can reproduce it every time. ## Root cause While it was broken I attached gdb to kwin_wayland: - `SeatInterface::isTouchSequence()` == true, but there was no physical touch - so `PointerInputRedirection::focusUpdatesBlocked()` == true → pointer focus stays nullptr permanently - (`areButtonsPressed()`, `isSelectingWindow()`, `isDrag()` were all false) - `TouchInputRedirection::cancel()` did nothing, because `m_activeTouchPoints` was already empty How the seat and TouchInputRedirection get out of sync: 1. A touch down happens while the screen is on. It passes the filters and `ForwardInputFilter` (or `LockScreenFilter`) calls `seat->notifyTouchDown()`. 2. The outputs turn off while that touch is still held. `Workspace` installs `DpmsInputEventFilter` (the same thing happens with `PlaceholderInputEventFilter` when the last output goes away). 3. The touch up comes in. `TouchInputRedirection::processUp()` removes the id from `m_activeTouchPoints`, then runs the filters. `DpmsInputEventFilter::touchUp()` always returns true, so `seat->notifyTouchUp()` is never called. 4. The outputs come back on and the DPMS filter is removed. The seat still holds the TouchPoint, and nothing ever clears it: `cancel()` returns early because `m_activeTouchPoints` is empty, and removing the touch device doesn't clear it either. On a laptop this is easy to hit: your hand is on the screen bezel or glass while you close the lid, KWin turns the screens off before suspend, and the finger lifts while DPMS is off. ## Reproducer (no physical hardware needed) Run as root: a uinput multitouch device (INPUT_PROP_DIRECT, ABS_MT_SLOT/TRACKING_ID/POSITION_X/Y, BTN_TOUCH), plus `kscreen-doctor` running in the user session: ``` touch down slot 0 at screen center kscreen-doctor --dpms off sleep 2 touch up slot 0 kscreen-doctor --dpms on → SeatInterface::isTouchSequence() == 1, focusUpdatesBlocked() == 1, no clicks reach any client ``` Reproduced 5/6 runs on a live 6.7.4 session (it failed once). The control run (same down/up without DPMS) is clean. The relevant code (`dpmsinputeventfilter.cpp`, `placeholderinputeventfilter.cpp`, `touch_input.cpp`, the touch parts of `input.cpp`) has the same semantics in 6.8.1 and master. Why the existing workarounds work: - A one-finger tap fixes it when the leaked id is 0. `notifyTouchDown()` overwrites `touchPoints[id]` (the `Q_ASSERT(!contains(id))` is compiled out) and the following up erases it. - A three-finger tap always fixes it. `GlobalShortcutFilter` sends a synthetic `touchCancel`, `ForwardInputFilter` calls `seat->notifyTouchCancel()`, and that clears all points. - A VT switch also fixes it (as reported here). ## Fix (with integration tests) Cancel the ongoing touch sequence before installing either filter. At that point TouchInputRedirection still has the points in `m_activeTouchPoints`, so the cancel goes through the filter chain to `ForwardInputFilter` -> `seat->notifyTouchCancel()`. The touch up that comes later is then dropped in `processUp()` because the id is no longer active. ```cpp // Workspace::requestDpmsState() } else { input()->touch()->cancel(); m_dpmsFilter = std::make_unique<DpmsInputEventFilter>(); input()->installInputEventFilter(m_dpmsFilter.get()); // Workspace::updateOutputs(), same before installing m_placeholderFilter ``` I added two integration tests to `autotests/integration/touch_input_test.cpp` (on top of v6.8.1): - `testDpmsOffDuringTouch`: touch down -> `requestDpmsState(Off)` -> touch up -> `requestDpmsState(On)`. Asserts `!seat->isTouchSequence()`, `!focusUpdatesBlocked()` and that the client gets the cancel. - `testOutputsRemovedDuringTouch`: the same with `Test::setOutputConfig({})` (placeholder output). Without the fix, both tests fail on `!waylandServer()->seat()->isTouchSequence()`. With the fix they pass. testTouchInput, testLockScreen, testOutputChanges, testScreenEdges and testPointerInput (307 tests) all pass under dbus-run-session. testPointerInput::testTabletCursorSync is flaky in my environment both with and without the patch. I also built the Arch kwin 6.8.1 package with the fix and ran it in a live session. The same uinput + DPMS reproducer came out clean 5/5. Checking each step: the seat has the touch point while the finger is held, it is cancelled as soon as DPMS turns off, and nothing is left after the up / DPMS on. The patch with the tests is attached (diff against v6.8.1, applies with `git apply`). I can open a merge request on invent if you prefer. -- You are receiving this mail because: You are watching all bug changes.
