Skip to content

Merge in Overlay Overhaul#2

Merged
SudoWatson merged 29 commits into
linux-supportfrom
linux-support-overlay-merge
Jul 20, 2026
Merged

Merge in Overlay Overhaul#2
SudoWatson merged 29 commits into
linux-supportfrom
linux-support-overlay-merge

Conversation

@SudoWatson

Copy link
Copy Markdown
Owner

No description provided.

petoncle and others added 29 commits July 17, 2026 19:18
…e a new grid in when it replaces one mid fade-out
- **Grid drawn with Qt, with animations and styling**: The grid is now rendered with Qt, like the hints and the indicator, instead of the old GDI drawing. The grid has an optional transition animation when it moves or resizes, and configurable line opacity and background fill. The animation is enabled by default with a 100ms duration.
    ```properties
    grid-mode.grid.transition-animation-enabled=true
    grid-mode.grid.transition-animation-duration-millis=100
    grid-mode.grid.fade-animation-enabled=true
    grid-mode.grid.fade-animation-duration-millis=100
    grid-mode.grid.line-opacity=1.0
    grid-mode.grid.background-color=#FF0000
    grid-mode.grid.background-opacity=0.1
    ```

https://github.com/user-attachments/assets/b395505e-9a06-4ec8-b361-81323c7b3fb4

- **Injected event fingerprinting**: mousemaster now tags its own injected input with a fingerprint, so it can tell its own events apart from events injected by other software (like kanata).
…ve hint grids

hint.grid-cell-sizing=fit divides the hint area into exactly
grid-max-row/column-count cells (pixel sizes ignored); grid-area=last-selected-hint-cell
narrows the grid to the last selected cell. Together they enable neru-style
recursive grids built as one mode per depth level. Adds neru.properties and an
author.properties recursive-hint chain, plus a reference-doc note.
…tation paths at the nested cellSizing record

In author configuration, hint3 zoomed would break.
Our Linux-only qt/ZoomWindow (now being retired in favor of the
shared ScreenshotWidget from petoncle/main's overlay refactor) had a
hardware-confirmed fix: clamp the zoom source rect to the captured
pixmap's bounds and shrink the target rect by the same scale, so
regions with no captured pixels stay black instead of leaving a
transparent gap near screen edges/corners. ScreenshotWidget lacked
this. Port it in before consolidating Linux onto this shared class,
since Linux uses it as the sole, persistent zoom-rendering mechanism
(no live-magnifier handoff like Windows has), so the gap would be
visible in steady state rather than only during a brief transition.
Replace our hand-rolled qt/GridWindow, qt/HintMeshWindow, qt/ZoomWindow
(deleted in the previous commit) with the same platform-agnostic
GridRenderer, HintMeshRenderer, IndicatorRenderer, and ScreenshotWidget
that WindowsOverlay now uses post-refactor. LinuxOverlay becomes a thin
adapter: it owns the renderer instances, supplies TransparentWindow via
the existing X11-flagged base class, and handles only X11-specific
window management (real setTopmost() via Qt raise(), plus applying the
same window flags to GridRenderer's/ScreenshotWidget's own widgets,
which - being shared with Windows - only set FramelessWindowHint
themselves and rely on the platform to add topmost/click-through
styling, the way Windows does via native WS_EX_* calls).

This fixes a real bug for free: our old HintMeshWindow silently dropped
the Zoom parameter passed to setHintMesh(), which HANDOFF.md root-caused
as the cause of second-level hint overlays rendering at screen-center
instead of the zoomed location, and third-level/history-mode hints
being unreadably small. HintMeshRenderer correctly transforms hint-box
geometry by Zoom internally.

It also adds a real mouse indicator to Linux for the first time -
setIndicator()/hideIndicator() were previously complete no-op stubs.
Since MouseController has no cursorVisualCenter()/mouseSize() lookup on
Linux, a fixed cursor-size fallback is used for now (mirrors Windows'
own fallback-when-lookup-fails pattern); a real XFixesGetCursorImage
lookup is a follow-up. Active-screen resolution reuses the existing,
already platform-agnostic ScreenManager.nearestScreenContaining()
rather than adding new X11 code.

The one-tick-deferred screenshot-capture mechanism for zoom (hide our
own windows, grab the screen one tick later once the hide is actually
flushed, since Linux has no capture-exclusion API like Windows'
WDA_EXCLUDEFROMCAPTURE) is preserved, just re-targeted at the new
renderers' windows instead of the old bespoke classes.

LinuxPlatform gains lastMouseX()/lastMouseY() getters (exposing the
XQueryPointer-polled position already tracked for Fix 4's mouse-move
listener notifications) for the indicator glue, forwards mouse moves
to LinuxOverlay.mouseMoved() for live indicator repositioning (Windows
does this via its low-level hook; X11 has no such hook), and now calls
overlay.setTopmost() on a periodic timer mirroring WindowsPlatform's
own 200ms re-assertion against WMs that don't fully respect
WindowStaysOnTopHint.
GridRenderer/HintMeshRenderer's hide() calls never actually unmap the
underlying window - they only clear the drawn content and reset
opacity, leaving the real window mapped indefinitely once first shown
(GridRenderer's widget spans the entire virtual desktop;
HintMeshRenderer's per-screen windows span entire screens). This is
safe by design on Windows, where WS_EX_TRANSPARENT is applied once at
HWND creation, making the window permanently click-through regardless
of its "logical" visibility.

The Linux port never added the Qt equivalent
(WA_TransparentForMouseEvents), so the first time any grid or hint
mesh was shown, its window became a permanent, invisible, full-desktop
click blocker - swallowing every click (both our own synthetic
XTestFakeButtonEvent clicks and real physical clicks) from that point
forward, in every mode including idle-mode, until the process was
killed.

Fix: set WA_TransparentForMouseEvents in TransparentWindow's
constructor (covers indicator + hint-mesh windows, both created via
TransparentWindow) and in LinuxOverlay.applyX11OverlayFlags() (covers
GridRenderer's widget and ScreenshotWidget, which are shared with
Windows and don't extend TransparentWindow).

Found via hardware testing: clicking (both program-driven and
physical trackpad) stopped working entirely as soon as any hint or
grid overlay had been shown once, in any mode afterward.
Qt's WA_TransparentForMouseEvents attribute (added in the previous
commit) turned out not to be enough on its own for these frameless,
override-redirect (X11BypassWindowManagerHint) windows - confirmed via
hardware testing that clicks still didn't pass through.

Add real, guaranteed click-through by clearing each overlay window's
X11 input shape directly via the XShape extension
(XShapeCombineRectangles with a null/empty rectangle list on
ShapeInput) - the same mechanism compositors and other click-through
overlay tools use, independent of Qt's platform-plugin behavior for
this specific window configuration.

Applied uniformly to all four overlay window types via
applyX11OverlayFlags(): the indicator window, each per-screen hint
mesh window (via a new createStyledHintMeshWindow() factory passed to
HintMeshRenderer, mirroring WindowsOverlay's own factory pattern),
GridRenderer's widget, and ScreenshotWidget.
@SudoWatson
SudoWatson merged commit eba6795 into linux-support Jul 20, 2026
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.

2 participants