fix(linux): custom window chrome no longer stacks with the native frame on Wayland #51

Open
jared wants to merge 1 commits from fix/linux-custom-chrome into main
Owner

Reported on CachyOS (KDE Plasma, Wayland): turning on Custom window chrome (beta) kept KDE's frame and drew the Lotus titlebar inside it, so there were two sets of close, minimize and maximize buttons. Both sets worked.

Cause

tao applies set_decorations(false) at runtime with gtk_window_set_decorated. On native Wayland, GTK3 negotiates server- or client-side decorations with the compositor only when the window is created; a later set_decorated doesn't renegotiate. So KDE kept drawing its frame. Turning the setting off would have hit the same problem the other way: the Lotus titlebar goes away, but the window stays frameless until restart, leaving no titlebar at all.

Fix

  • The choice is saved natively (custom-chrome in the app config dir, like start-minimized). On Linux the main window is built with or without the frame from it. Building undecorated is the path that works on KDE and GNOME Wayland.
  • set_custom_chrome still toggles at runtime where that works: Windows (unchanged, including the Mica handling), macOS, and Linux on X11 / XWayland. On native Wayland it only saves the choice, which applies on the next launch.
  • It now returns whether the frame is stripped right now. cinny (LotusGuild/cinny fix/linux-custom-chrome) shows its titlebar only then, so there are never two titlebars or none. Older cinny ignores the return value. Newer cinny with an older desktop falls back to the setting.
  • The native-Wayland check moved out of gpu_workarounds.rs into a shared gtk_native_wayland helper. decide still behaves the same, and all its existing tests pass unchanged.

Tests

  • cargo test --lib: 38/38, including a new runtime_toggle_works test.
  • rustfmt is clean on the touched files.
  • clippy is clean on Linux and on x86_64-pc-windows-gnu.

On CachyOS after this ships

Turn the setting on: after the reload you get the native frame only (no duplicate). Restart: the Lotus titlebar only. Turning it off works the same way in reverse.

Known limit, unchanged by this PR: a frameless GTK window on Wayland has no resize edges. Resize with the compositor shortcut (KDE: Alt + right-drag) or by maximizing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA

Reported on CachyOS (KDE Plasma, Wayland): turning on **Custom window chrome (beta)** kept KDE's frame and drew the Lotus titlebar inside it, so there were two sets of close, minimize and maximize buttons. Both sets worked. ## Cause tao applies `set_decorations(false)` at runtime with `gtk_window_set_decorated`. On native Wayland, GTK3 negotiates server- or client-side decorations with the compositor only when the window is created; a later `set_decorated` doesn't renegotiate. So KDE kept drawing its frame. Turning the setting *off* would have hit the same problem the other way: the Lotus titlebar goes away, but the window stays frameless until restart, leaving no titlebar at all. ## Fix - The choice is saved natively (`custom-chrome` in the app config dir, like `start-minimized`). **On Linux the main window is built with or without the frame from it.** Building undecorated is the path that works on KDE and GNOME Wayland. - `set_custom_chrome` still toggles at runtime where that works: Windows (unchanged, including the Mica handling), macOS, and Linux on X11 / XWayland. On native Wayland it only saves the choice, which applies on the next launch. - It now **returns whether the frame is stripped right now**. cinny (LotusGuild/cinny `fix/linux-custom-chrome`) shows its titlebar only then, so there are never two titlebars or none. Older cinny ignores the return value. Newer cinny with an older desktop falls back to the setting. - The native-Wayland check moved out of `gpu_workarounds.rs` into a shared `gtk_native_wayland` helper. `decide` still behaves the same, and all its existing tests pass unchanged. ## Tests - `cargo test --lib`: 38/38, including a new `runtime_toggle_works` test. - rustfmt is clean on the touched files. - clippy is clean on Linux and on `x86_64-pc-windows-gnu`. ## On CachyOS after this ships Turn the setting on: after the reload you get the native frame only (no duplicate). Restart: the Lotus titlebar only. Turning it off works the same way in reverse. Known limit, unchanged by this PR: a frameless GTK window on Wayland has no resize edges. Resize with the compositor shortcut (KDE: Alt + right-drag) or by maximizing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
jared added 1 commit 2026-11-09 21:43:23 -05:00
On Linux / native Wayland GTK negotiates decorations with the compositor
only when the window is created, so `set_decorations(false)` at runtime
left KDE's frame in place and the Lotus titlebar was drawn inside it
(two sets of close/min/max buttons). Turning it off would have left a
window with no titlebar at all until restart.

- The choice is saved natively (app config dir `custom-chrome`, like
  `start-minimized`). On Linux the main window is built with or without
  the frame from it.
- `set_custom_chrome` still toggles at runtime where that works
  (Windows, macOS, Linux on X11 / XWayland); on native Wayland it only
  saves the choice, which applies next launch.
- It now returns whether the frame is stripped right now, so the web
  client can show its titlebar only then.
- The native-Wayland check moved out of gpu_workarounds into a shared
  `gtk_native_wayland` helper (behaviour unchanged).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
You are not authorized to merge this pull request.
This pull request can be merged automatically.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix/linux-custom-chrome:fix/linux-custom-chrome
git checkout fix/linux-custom-chrome
Sign in to join this conversation.