@@ -92,24 +92,76 @@ really contains the fresh dylib, md5 vs `src/main/resources/nucleus/native/…`)
9292→ recompile ComposeNativeTray → hand off. Never compile while a demo JVM
9393is running.
9494
95- ## Linux (decide, then maybe implement)
96-
97- Transparency requires a compositing WM + ARGB visual; there is no DComp/
98- CAMetalLayer equivalent that works everywhere (X11 vs Wayland, GNOME vs
99- KDE vs bare WMs). Options:
100-
101- - ** A (pragmatic, recommended default)** : keep the opaque
102- ` TrayAppImplWindow ` . Accept animations over the window background, or
103- set Linux defaults back to ` None ` (one-line change in the
104- ` defaultTrayApp*Transition ` vals).
105- - ** B (best-effort)** : GTK layer — an undecorated, override-redirect
106- GTK window with RGBA visual when a compositor is present
107- (` gdk_screen_is_composited ` ), falling back to A otherwise. Sizeable
108- native work; only worth it if users ask.
109-
110- Outside-click on Wayland cannot use global pointer grabs from a regular
111- client — focus-loss dismissal (current behavior) is the only reliable
112- signal. Keep it.
95+ ## Linux (option B chosen — raw X11/XWayland panel, IMPLEMENTED)
96+
97+ Status 2026-07-12: macOS is DONE (Nucleus PR #302 , ` TaoStandalonePopupHostMac `
98+ + ` popup_panel.m ` ; TrayApp already routes macOS to ` TrayAppImplPanel ` ).
99+ Nucleus repo moved to ` ~/IdeaProjects/Nucleus ` . Linux option B implemented on
100+ Nucleus branch ` feat/standalone-popup-panel-linux ` (commit 1f037863): native
101+ module + host + routing + smoke test (passes on GNOME Wayland via XWayland)
102+ + GraalVM metadata + CI. Published locally as ` 2.0.0-tao-local ` ;
103+ ComposeNativeTray routes Linux → ` TrayAppImplPanel ` gated on
104+ ` isTaoStandalonePopupAvailable() ` . AWAITING: user demo validation on
105+ GNOME Wayland (topmost stacking above Wayland windows, position vs tray
106+ click coords, keyboard into text fields, outside-click dismissal), then
107+ Nucleus PR + published alpha + drop the mavenLocal pin.
108+
109+ ### Decision: raw X11 window, NOT a GTK window
110+
111+ GDK's backend is process-wide (native Wayland on Wayland sessions), so a
112+ GTK popup window would only work by forcing the whole app onto XWayland
113+ (` NUCLEUS_TAO_LINUX_RENDERER=x11 ` ). Instead the panel is a ** raw X11
114+ override-redirect ARGB32 window on its own ` XOpenDisplay ` connection** —
115+ an independent X client that works even while the app itself is a native
116+ Wayland client, via XWayland (present on effectively all desktops).
117+ Feasibility was validated on GNOME Wayland (Ubuntu, Mutter): OR window
118+ maps and moves at exact global coords, ARGB visual + desktop-GL 3.3 EGL
119+ context all work through XWayland.
120+
121+ Native Wayland (no XWayland/DISPLAY) stays unsupported: no layer-shell in
122+ vendored Tao, ` gtk_window_move ` is a no-op on xdg-toplevels, ` keep_above `
123+ ignored. In that case the panel reports unavailable and TrayApp falls back
124+ to the opaque ` TrayAppImplWindow ` .
125+
126+ ### Architecture (mirrors Windows, reuses the Linux EGL bridge)
127+
128+ - ** Visual selection** : query EGL first (alpha=8 desktop-GL configs →
129+ ` EGL_NATIVE_VISUAL_ID ` ), create the window with exactly that visual —
130+ guarantees ` NativeTaoEglBridge.nativeAttachX11 ` matches directly (no
131+ child-window fallback, alpha preserved).
132+ - ** Rendering** : reuse ` nucleus_tao_egl.c ` per-attachment API
133+ (` nativeAttachX11 ` /` nativeMakeCurrent ` /` nativePresent ` /` nativeResize ` +
134+ ` nativeGetProcAddrFunctionPointer ` ) — Linux convention is one EGL context
135+ per surface, no headless bootstrap needed. ` eglSwapInterval(0) ` +
136+ the 60 fps pacer pattern from the Windows host (don't block the Tao
137+ main thread on vsync).
138+ - ** Threading** : two X connections. Command connection owned by the Tao
139+ main thread (create/move/map/cursor/focus + EGL). Event connection owned
140+ by a dedicated per-panel thread (` XSelectInput ` on the panel XID works
141+ cross-connection); quit via ClientMessage. No ` XInitThreads ` dependency.
142+ - ** Input** : Button/Motion/scroll(buttons 4-7) → ` TaoNativeWireFormat `
143+ pointer wire; keys send the raw ** X keysym** as vkCode + Unicode
144+ codePoint; a new ` linuxNativeKeyToAwt ` in ` dispatchNativeKeyEvent `
145+ translates keysym → AWT VK (same pattern as ` macNativeKeyToAwt ` ).
146+ - ** Keyboard focus** : ` XSetInputFocus(RevertToParent) ` on click while
147+ focusable (Windows ` takeKeyboardFocus ` equivalent). If some WMs don't
148+ deliver keys to OR windows, escalate to ` XGrabKeyboard ` later.
149+ - ** Outside-click** : XI2 raw ButtonPress on the root window (the X11
150+ analog of ` WH_MOUSE_LL ` ; multiple clients allowed, no grab, doesn't
151+ consume). Fully global on X11 sessions; under XWayland only fires while
152+ X11 surfaces have input — acceptable because the tray-icon toggle and
153+ focus-loss cover the rest.
154+ - ** Scale** : ` Xft.dpi ` -derived (X clients live in the X coordinate space,
155+ which under XWayland is logical — GDK's Wayland scale would mis-size the
156+ panel). Fractional/HiDPI XWayland caveats accepted for v1.
157+
158+ New files: ` nucleus_tao_linux_popup.c ` (→ ` libnucleus_tao_linux_popup.so ` ,
159+ dlopen libX11/libXi only, linked ` -ldl ` like siblings),
160+ ` PopupNativeBridgeLinux.kt ` , ` render/TaoStandalonePopupHostLinux.kt ` ,
161+ ` render/TaoKeyLinux.kt ` , smoke test guarded on Linux + ` DISPLAY ` . Plus:
162+ route Linux in ` TaoStandalonePopup.kt ` , a public availability check for
163+ TrayApp's router, ` linux/build.sh ` step, GraalVM reachability metadata,
164+ CI ` build-natives.yaml ` + verify arrays in consumer workflows.
113165
114166## Also pending (unrelated to the port)
115167
0 commit comments