Skip to content

desklets: stop grabbing the pointer where a window covers them - #13979

Open
Fantu wants to merge 1 commit into
linuxmint:masterfrom
Fantu:desklet-input-region
Open

desklets: stop grabbing the pointer where a window covers them#13979
Fantu wants to merge 1 commit into
linuxmint:masterfrom
Fantu:desklet-input-region

Conversation

@Fantu

@Fantu Fantu commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes the long-standing "desklets steal mouse input from the window on top of them" behaviour: #12567, and previously reported in #1746, #3701, #9190 and #10315 (also Debian #952972, and linuxmint/cinnamon-spices-desklets#1390).

The problem

Whether desklets are part of the stage input region was decided by a 500 ms timeout asking a single global question — is the pointer sitting on a window? — and applying the answer to every desklet at once:

mouseTrackTimoutId = Mainloop.timeout_add(500, checkMouseTracking);

function hasMouseWindow(){
    let window = global.display.get_pointer_window(null);
    return window && window.window_type !== Meta.WindowType.DESKTOP;
}

The stacking at the position being clicked was never consulted, so a desklet under a window can stay in the input region and take the pointer events aimed at that window. Two things can then happen, and both are in the bug reports:

  • the event is swallowed by the window actor that gets picked above the desklet, so the window looks dead over the desklet's rectangle;
  • the desklet gets it while the window actor above is being animated away (during a minimize, for instance), which is how a desklet ends up stuck to the pointer as if it were being dragged.

The cached mouseTrackEnabled flag made it worse: _trackMouse()/_untrackMouse() only ran when the aggregate answer flipped, so a desklet created or re-created while the cache said "tracked" was never untracked afterwards. That matches the reports of a window staying uninteractive over a desklet for hours, until the session is restarted, and it is also why acceptDrop()/cancelDrag() had to poison the cache with mouseTrackEnabled = -1 to force a refresh.

The change

Decide it per desklet, from the window geometry: a desklet that a window overlaps is taken out of the input region, and it goes back in as soon as nothing covers it. The input region is a list of rectangles, so a desklet partly behind a window can only be all in or all out of it: keeping it in is exactly what takes the events away from that window, so the visible part of a partly covered desklet is no longer clickable. That is a deliberate trade - the window on top staying usable matters more, and the behaviour is predictable instead of depending on where the pointer happened to be half a second earlier.

Desklets raised above the windows keep the input region, as they hold a modal grab anyway.

The check also runs on restacked, not only on the timeout, so raising, minimizing or unminimizing a window is noticed at once instead of up to half a second later. The timeout stays as the backstop for window moves and resizes.

Testing

Tested on a Debian sid VM (Cinnamon 6.6.9, X11, clock desklet at 400,300 172x94, a gnome-terminal window moved over it, clicks driven with xdotool, state read back through org.Cinnamon.Eval). The exact same diff applies to 6.6.9 and to master. Each case checks whether the desklet is in the input region and whether it received the click:

case before after
window covering the whole desklet, pointer arriving from the desktop, right click on the covered spot desklet in the input region, the click never reaches the terminal (no context menu at all) desklet out of the input region, the terminal's context menu opens
window overlapping the desklet by a few pixels only, same gesture same failure: a sliver of desklet left uncovered is enough desklet out of the input region, the terminal's context menu opens
window raised from minimized, right click 50 ms later on the covered spot click stolen by the desklet in 2 runs out of 3 not stolen, 3 runs out of 3
show desktop, back, right click without moving the pointer click lost the terminal's context menu opens
no window above the desklet desklet clickable desklet clickable

One race remains, and it is inherent to updating an input region after the fact: a click issued in the very same instant as the window is raised, before the main loop can run, is still misrouted. It is reproducible only by sending both in the same batch of X requests — a 50 ms delay is already enough for the new code to behave — whereas the old code was wrong for up to 500 ms.

Whether desklets belong to the stage input region was decided by
polling, every 500 ms, whether the pointer was sitting on a window, and
the answer was applied to every desklet at once. The stacking at the
pointer position was never consulted, so a desklet under a window could
stay in the input region and take the pointer events aimed at that
window: the event is either swallowed by the window actor picked above
the desklet, or handed to the desklet itself while the window actor
above is being animated away, which is why the desklet sometimes ends up
stuck to the pointer as if dragged.

Decide it per desklet from the window geometry instead: a desklet a
window overlaps is taken out of the input region. The input region is
made of rectangles, so a desklet partly behind a window can only be all
in or all out of it, and keeping it in is what takes the events away
from that window; the visible part of such a desklet is no longer
clickable, which is a fair trade for the window above it staying usable.
Check on 'restacked' as well as on the timeout, so that raising,
minimizing or unminimizing a window is noticed right away instead of up
to half a second later.

This also drops the mouseTrackEnabled cache, which had to be poisoned by
hand after a desklet drag to force a refresh, and which left a desklet
permanently in the input region when it was recreated while the cached
state said the desklets were tracked - the likely reason some reports
describe a window that stays uninteractive over a desklet until the
session is restarted.

Assisted-by: Claude Code:claude-opus-5
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