Skip to content

[EXPERIMENT — DO NOT MERGE] Linux buddy floater via XWayland - #239

Draft
itsdestin wants to merge 2 commits into
masterfrom
fix/linux-xwayland-floater
Draft

[EXPERIMENT — DO NOT MERGE] Linux buddy floater via XWayland#239
itsdestin wants to merge 2 commits into
masterfrom
fix/linux-xwayland-floater

Conversation

@itsdestin

Copy link
Copy Markdown
Owner

⚠️ NOT PRODUCTION-READY — experiment only, do not merge

Opened as a draft to preserve the findings and keep the branch runnable.
The floater genuinely works under XWayland, but adopting this is a bigger
commitment than it looks (see Drawbacks). Shelved by Destin 2026-07-23.

What this proves

The existing three-window buddy floater works on Linux/Wayland if the app runs on XWayland. No overlay, no new architecture — the same code that ships on Windows/macOS. Visually confirmed on KDE: mascot renders crisply, transparency clean, real positioning, keepAbove=true, no GPU crash.

This is the follow-up to PR #214, which merged the one-window overlay dormant after setIgnoreMouseEvents turned out to be a total no-op on native Wayland.

What it takes (both required)

Need Mechanism Notes
Route to XWayland real argv flag electron . --ozone-platform=x11 app.commandLine.appendSwitch('ozone-platform','x11') is silently ineffective on Electron 41 — the process still boots on the native Wayland ozone backend. ELECTRON_OZONE_PLATFORM_HINT=x11 is also ineffective. Only the argv flag works.
Stop the GPU crash --use-angle=vulkan (YOUCODED_ANGLE=vulkan) Default ANGLE GL backend SIGSEGVs the GPU process (exit_code=139) at EGL_CreateWindowSurface — 3 crashes/launch. Vulkan (RADV): 0. Machine-specific → deliberately not defaulted.

The earlier force-high-performance-gpu hypothesis was wrong — it was the ANGLE GL backend.

Bug found and fixed along the way

Under 1.5× fractional scaling on XWayland, setPosition() inflates a frameless window's size by a rounding error on every call. moveMascot fires one per pointermove → compounding.

Isolated probe (scaleFactor 1.5, 40 moves each):

Method Before After
setPosition 334×490 373×529 — grew
setBounds w/ fixed size 334×490 334×489 — stable
setMinimumSize+setMaximumSize 334×488 373×527 — grew anyway

Real-world: chat ballooned to 1851×1526 (bigger than the 1707×1067 logical screen), windows drifted apart, KWin popped a geometry tip mid-drag. Fixed with BuddyWindowManager.place(), which re-asserts each window's known fixed size via setBounds on every move — Linux-gated, so Windows/macOS keep the cheaper setPosition path.

Drawbacks (why this is shelved)

  • Whole-process decision. The ozone backend is chosen once at launch for the entire app — you cannot run the main window on native Wayland and only the buddy on XWayland. Adopting this puts the whole app on XWayland.
  • Production enablement is unimplemented. The in-code appendSwitch does nothing; shipping needs a self re-exec or a launcher/.desktop entry carrying the flag. Dev-only today.
  • Sharpness is compositor-specific — sharp on KDE, famously blurry on GNOME under fractional scaling. Does not generalize from one machine.
  • Mixed-DPI multi-monitor breaks — XWayland has one global scale; native Wayland scales per-monitor.
  • GPU flag untested off this hardware (Nvidia/Intel/older mesa).
  • Legacy backend, weaker IME support, and gives back some of Wayland's app isolation.

If resumed

Preferred direction is finding a native Wayland path. If XWayland is ever adopted it should sit behind a user toggle, not be forced on all Linux users. Open follow-ups: production enablement mechanism; a pinning test for the size-inflation fix (currently guarded only by the isolated probe); an unexplained green line artifact on the chat edge; a benign-looking No suitable EGL configs found warning.

Testing

280 files / 3090 tests passed, 0 failed (also stubs app.isReady in two test mocks that the ozone block broke at module load — recovers 12 tests).

Full findings: docs/active/investigations/2026-07-23-buddy-overlay-wayland-presentation.md in the youcoded-dev workspace.

🤖 Generated with Claude Code

itsdestin and others added 2 commits July 23, 2026 13:01
*** NOT PRODUCTION-READY — experiment only, do not ship as-is. ***

Native Wayland forbids a client from positioning its own windows, which is the
buddy floater's entire model (main-process setPosition per drag frame), so the
floater cannot work there. Under XWayland the existing three-window floater
works: real positioning, KWin keepAbove, clean transparency, sharp at 1.5x on
KDE.

Two things are required to actually get there, both verified 2026-07-23:

1. A REAL argv flag `electron . --ozone-platform=x11`.
   app.commandLine.appendSwitch('ozone-platform','x11') is SILENTLY INEFFECTIVE
   on Electron 41 — the process still comes up on the native Wayland ozone
   backend. ELECTRON_OZONE_PLATFORM_HINT=x11 is also ineffective. Only the argv
   flag flips it. The in-code block is kept as intent + kill switch
   (YOUCODED_OZONE=wayland) but on its own changes nothing; the comment says so.
   Dev gets the flag via the dev:main script. Production enablement would need a
   self re-exec or a launcher entry carrying the flag — NOT IMPLEMENTED.

2. --use-angle=vulkan (YOUCODED_ANGLE=vulkan). The default ANGLE GL backend
   SIGSEGVs the GPU process (exit_code=139) at EGL_CreateWindowSurface under
   XWayland on this AMD/mesa stack — 3 crashes per launch. Vulkan: 0 crashes.
   Machine-specific, so it is NOT defaulted; it must be passed explicitly.

Also stubs app.isReady in two test mocks — the ozone block calls it at module
load, which broke those suites from loading (recovers 12 tests).

Why not production-ready: the ozone backend is a whole-process, launch-time
choice (you cannot run the main app native-Wayland and only the buddy on
XWayland); XWayland sharpness under fractional scaling is compositor-specific
(fine on KDE, blurry on GNOME); mixed-DPI multi-monitor breaks (one global
scale); the GPU flag is untested off this machine. If adopted it should sit
behind a user toggle, not be forced on all Linux users.

Full findings + drawbacks:
docs/active/investigations/2026-07-23-buddy-overlay-wayland-presentation.md
(youcoded-dev workspace)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…le inflation

Under XWayland with fractional display scaling (KDE at 1.5x), Electron's
setPosition() inflates a frameless window's size by a DIP<->physical rounding
error on EVERY call. moveMascot fires one per pointermove, so it compounds
across a drag: the chat window ballooned to 1851x1526 — larger than the whole
1707x1067 logical screen — the mascot/chat/bar drifted apart, and KWin popped a
geometry tip mid-drag. This is the "chat keeps getting bigger / things don't
stay together" jank.

Isolated probe at scaleFactor 1.5, 40 moves each:

  setPosition                          334x490 -> 373x529  (grew)
  setBounds with explicit fixed size   334x490 -> 334x489  (stable)
  setMinimumSize+setMaximumSize        334x488 -> 373x527  (grew anyway)

So min/max size does NOT clamp this — only re-asserting the bounds does.

Adds BuddyWindowManager.place(win, x, y), which re-asserts the window's known
fixed size (MASCOT_SIZE / CHAT_SIZE / BAR_SIZE — the same constants main.ts
builds the windows with) via setBounds on every move. All buddy setPosition
calls route through it, including the glide animation. Gated to Linux so
Windows/macOS keep the cheaper setPosition path, whose per-move DWM cost is
called out in moveMascot; neither platform inflates.

Verified after the fix: mascot stays at exactly 112x112 across drags, no
ballooning, and the windows track together.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
itsdestin added a commit to itsdestin/youcoded-dev that referenced this pull request Jul 23, 2026
Records the outcome of the XWayland route (option A from the buddy-overlay
investigation): the existing three-window floater WORKS under XWayland — real
positioning, keepAbove, clean transparency, sharp at 1.5x on KDE, visually
confirmed. Not shipped; draft PR itsdestin/youcoded#239 marks it
NOT PRODUCTION-READY.

Captured findings:
- Only a REAL argv --ozone-platform=x11 flips the backend. appendSwitch() and
  ELECTRON_OZONE_PLATFORM_HINT are both silently ineffective on Electron 41.
- --use-angle=vulkan eliminates a GPU-process SIGSEGV at EGL_CreateWindowSurface
  (default ANGLE GL: 3 crashes/launch; vulkan: 0). Machine-specific.
- New Electron bug: at 1.5x fractional scale setPosition() inflates frameless
  windows on every call (chat ballooned to 1851x1526, group drifted apart).
  setBounds with an explicit fixed size pins it; min/max size does NOT.
- Drawbacks that keep it shelved: whole-process launch-time backend choice,
  unimplemented production enablement, compositor-specific sharpness, broken
  mixed-DPI multi-monitor, untested GPU flag off this hardware.

Decision: prefer a native-Wayland path; if XWayland is adopted, hide it behind
a user toggle rather than forcing it on all Linux users.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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