feat: expose transparent + clickThrough windows, watermark demo - #563
Merged
kdroidFilter merged 12 commits intoAug 17, 2026
Merged
Conversation
…ndow Per-pixel window transparency (#416) was only reachable through the Tao backend's own DecoratedWindow composable. Forward the flag through both nucleus-application overloads and TaoDecoratedWindowAdapter; the AWT backend ignores it, like undecorated.
On Windows, a DecoratedWindow(transparent = true) with custom chrome still applied the themed DWM 1px frame (DWMWA_BORDER_COLOR), the drop shadow margin and the rounded clip — all tracing the rectangular HWND and betraying the content-defined shape. Reuse the borderlessChrome DWM treatment for fully transparent windows; hit-testing (title-bar drag, resize borders) is unaffected. Linux/Windows Compose CSD stroke is skipped for transparent windows for the same reason.
…ents Plumb tao's set_ignore_cursor_events through the JNI bridge and expose it as TaoWindow.setIgnoreCursorEvents plus a reactive clickThrough parameter on both the Tao and backend-agnostic DecoratedWindow composables (WS_EX_TRANSPARENT|WS_EX_LAYERED on Windows, NSWindow.ignoresMouseEvents on macOS, empty GDK input region on Linux). On Windows, tao only flips the ex-styles; a WS_EX_LAYERED window renders nothing until its layering attributes are initialised, so the native handler calls SetLayeredWindowAttributes(alpha=255) — full alpha keeps per-pixel transparency driven by DWM blur-behind.
Animated NUCLEUS watermark in a transparent, undecorated, always-on-top, click-through, never-focused window pinned to a screen corner, hidden from the taskbar and controlled entirely from a ComposeNativeTray icon (corner selection + quit). The tray icon is fed as image files (iconPath/windowsIconPath as file: URIs): the composable-icon overloads crash on the skiko shipped with Compose 1.12 (NoSuchMethodError on Image.encodeToData), and java.net.URI rejects raw Windows paths.
An NSWindow stays bound to the Space it was created in, and a GTK window to its workspace, so the watermark overlay vanished on macOS/Linux as soon as the user switched desktop. Windows needed nothing: a WS_EX_TOOLWINDOW (hiddenFromDock) window is not tracked by the Virtual Desktop Manager and already shows everywhere. Wire tao's set_visible_on_all_workspaces (CanJoinAllSpaces / gtk stick, no-op on Windows) as a reactive DecoratedWindow parameter, and move the new overlay flags to the end of the public parameter lists so existing positional call sites keep their meaning.
Vendored tao patch 0006. `CursorIgnoreEvents` installed a 1x1 rectangle at the origin instead of an empty region, so the top-left pixel of a click-through window stayed clickable, and it cleared the shape on the GtkWidget while installing it on the GdkWindow — which never undid it, so click-through could not be switched back off. Both branches now go through the same GdkWindow; the clear passes a NULL region (the only form that survives a later resize, and one the safe binding cannot express).
`clickThrough` was a no-op on Linux: the LaunchedEffect applies it at the first composition, but every DecoratedWindow is created hidden and shown only after the first paint, and the GDK input region is bound to the GdkWindow alive when it is installed — the window swaps it on the way to its first map, taking the region with it. Measured on GNOME/XWayland: the window kept a full-size ShapeInput rectangle and swallowed every click, while the same call made after the window was mapped landed correctly. TaoWindow now remembers the requested state and replays it from show(), queued behind the show on the same event loop.
…no-op alwaysOnTop and visibleOnAllWorkspaces silently did nothing on native Wayland — the protocol has neither client-side stacking nor workspaces, so gtk_window_set_keep_above and gtk_window_stick are ignored. They now log once per window, mirroring what hiddenFromDock already did, and point at NUCLEUS_TAO_LINUX_RENDERER=x11. The check reads the surface kind the native side reports for that window rather than sniffing the environment, so it stays correct per window.
Wayland has no protocol for client-side stacking, programmatic positioning or workspace stickiness, so an overlay that needs them had only one way out: NUCLEUS_TAO_LINUX_RENDERER=x11, which drags the entire app onto XWayland. GTK drives several GdkDisplays from one process and one main loop, so `forceX11` opens the X server named by DISPLAY once and re-homes just that window's GdkWindow onto it. The rest of the app keeps its Wayland surfaces. Reported per window through the new TaoWindow.isNativeWaylandSurface, which reads the live surface instead of sniffing the environment, so a window that could not be re-homed (no XWayland) says so and logs a warning. Exposed on the Tao and backend-agnostic DecoratedWindow, and — together with transparent / clickThrough / visibleOnAllWorkspaces — on the Material 2, Material 3 and Jewel wrappers, which had no way to reach any of them. The Material wrappers also gain `undecorated`, without which `transparent` has no borderless window to sit on.
…ists Resolving WindowPosition.Aligned needs the monitor work area, which Linux queries through the native window — and Tao creates that asynchronously on its event loop. A JVM start is slow enough that the window is already there at first composition; a GraalVM native-image start beats it, the query returns nothing, and since the effect only re-runs when state.position changes the window stayed wherever the WM had centred it until the app moved it by hand. Retries over ~10 frames, then gives up rather than looping forever so a genuinely unavailable monitor query cannot wedge the effect.
The demo was a single overlay with a tray menu. It now opens a Material 3 control window — toggle the overlay, pick the corner from a miniature screen, and read which surface each window actually landed on. On Linux that makes the demo the mixed case it is meant to show: the control window keeps the session's Wayland surface while the overlay takes an X11 one through forceX11, instead of pushing the whole app onto XWayland.
kdroidFilter
marked this pull request as ready for review
August 17, 2026 16:24
Three lint regressions from the previous commits, all mechanical: the hiddenFromDock guard grew a fifth condition (fold forceX11 into the X11 predicate it belongs to, which also reads better), the Aligned retry constants landed between an existing KDoc and its declaration, and the demo's imports were out of order.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
transparenton the backend-agnosticDecoratedWindow(nucleus-application): forwards the Tao backend's per-pixel transparency flag (Fully transparent Windows #416) through both overloads andTaoDecoratedWindowAdapter; ignored by the AWT backend, likeundecorated. API dumps regenerated.DWMWA_BORDER_COLOR), the drop-shadow margin, the rounded clip and tao's style-level undecorated shadow — all tracing the rectangular HWND. Transparent windows now reuse theborderlessChromeDWM treatment (hit-testing untouched) and dropwith_undecorated_shadowon Windows; the Linux/Windows Compose CSD stroke is skipped too. macOS keeps its content-shaped AppKit shadow.clickThroughwindows: plumbs tao'sset_ignore_cursor_eventsthrough the JNI bridge (TaoWindow.setIgnoreCursorEvents) and exposes a reactiveclickThroughparameter on the Tao and backend-agnosticDecoratedWindowcomposables. On Windows tao only flipsWS_EX_TRANSPARENT|WS_EX_LAYERED; a layered window renders nothing until its layering attributes are initialised, so the native handler callsSetLayeredWindowAttributes(alpha=255).examples/watermark-demo: animated NUCLEUS watermark in a transparent, undecorated, always-on-top, click-through, never-focused, taskbar-hidden window pinned to a screen corner, driven entirely from a ComposeNativeTray icon (corner selection viaWindowPosition.Aligned+ quit). Documents two ComposeNativeTray 2.1.0 workarounds: composable-icon overloads crash on Compose 1.12's skiko (NoSuchMethodErroronImage.encodeToData), andiconPathmust be afile:URI becausejava.net.URIrejects raw Windows paths.Test plan
:decorated-window-tao/:nucleus-application: compile, tests, ktlint, detekt, apiCheckclickThrough = trueand clicks fall through; window never takes focusencodeToDatacrash on Compose 1.12