From 6e3afd19fd00b5bfcce12822657bfee7cd334817 Mon Sep 17 00:00:00 2001 From: Cristian Date: Sun, 18 Oct 2026 06:04:04 +0200 Subject: [PATCH] fix(linux): enable floating window frame by default on GNOME --- docs/content/docs/faq.cn.mdx | 6 ++-- docs/content/docs/faq.mdx | 6 ++-- src-tauri/src/lib.rs | 60 +++++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/docs/content/docs/faq.cn.mdx b/docs/content/docs/faq.cn.mdx index ead94a147d..07fe54f69b 100644 --- a/docs/content/docs/faq.cn.mdx +++ b/docs/content/docs/faq.cn.mdx @@ -35,15 +35,15 @@ description: DBX 常见问题:连接数据保存在哪里、如何备份、如 ## Linux 窗口闪烁或内容消失 -Linux 主窗口默认使用不透明背景,避免部分 KDE、显卡驱动与 WebKitGTK 的透明窗口合成兼容问题。不会因此关闭 GPU 加速或改变 Wayland/X11 后端。 +在 GNOME 且系统已开启窗口合成时,Linux 主窗口会绘制带圆角、阴影的浮动边框;在其他桌面(KDE、XFCE、Cinnamon 等)默认使用不透明背景,避免部分显卡驱动与 WebKitGTK 的透明窗口合成兼容问题。两种模式都不会关闭 GPU 加速或改变 Wayland/X11 后端。 -如果确认当前环境支持透明窗口,可以显式启用带圆角、阴影的浮动边框: +可以通过环境变量覆盖默认行为。在其他桌面强制启用浮动边框: ```bash DBX_LINUX_FLOATING_FRAME=1 dbx ``` -只有环境变量值为 `1` 且系统已开启窗口合成时才启用。若启用后出现闪烁、内容消失或必须缩放窗口才能重绘,请完全退出 DBX(包括托盘),移除该环境变量或使用 `DBX_LINUX_FLOATING_FRAME=0 dbx` 重新启动。AppImage 用户请将 `dbx` 替换为实际的 AppImage 文件路径。 +若在 GNOME 上出现闪烁、内容消失或必须缩放窗口才能重绘,请完全退出 DBX(包括托盘),并使用 `DBX_LINUX_FLOATING_FRAME=0 dbx` 重新启动以强制使用不透明窗口。没有窗口合成时始终不启用浮动边框。AppImage 用户请将 `dbx` 替换为实际的 AppImage 文件路径。 如果默认的不透明窗口仍有问题,请在反馈中提供 DBX 版本、安装方式、KDE/Plasma 版本、Wayland/X11 会话类型、显卡及驱动信息,以及系统 WebKitGTK 版本。 diff --git a/docs/content/docs/faq.mdx b/docs/content/docs/faq.mdx index 864d7049ad..8756539d2b 100644 --- a/docs/content/docs/faq.mdx +++ b/docs/content/docs/faq.mdx @@ -35,15 +35,15 @@ description: Frequently asked questions about DBX, including where connection da ## Linux Window Flickering or Missing Content -The Linux main window uses an opaque background by default to avoid transparent-window compositing compatibility problems with some KDE, graphics driver and WebKitGTK combinations. This does not disable GPU acceleration or change the Wayland/X11 backend. +The Linux main window draws a rounded, shadowed floating frame on GNOME when a compositing manager is running. On other desktops (KDE, XFCE, Cinnamon, ...) it uses an opaque background by default to avoid transparent-window compositing problems with some graphics driver and WebKitGTK combinations. Neither mode disables GPU acceleration or changes the Wayland/X11 backend. -If your environment supports transparent windows, you can explicitly enable the floating frame with rounded corners and a shadow: +The default can be overridden with an environment variable. To force the floating frame on another desktop: ```bash DBX_LINUX_FLOATING_FRAME=1 dbx ``` -The frame is enabled only when the variable is exactly `1` and a compositing manager is available. If enabling it causes flickering, missing content or redraws only after resizing, quit DBX completely, including its tray process, and remove the variable or restart with `DBX_LINUX_FLOATING_FRAME=0 dbx`. For AppImage installations, replace `dbx` with the actual AppImage path. +To force the opaque window on GNOME, for example if you see flickering, missing content or redraws only after resizing, quit DBX completely, including its tray process, and restart with `DBX_LINUX_FLOATING_FRAME=0 dbx`. The frame is never used without a compositing manager. For AppImage installations, replace `dbx` with the actual AppImage path. If the default opaque window still has problems, include the DBX version, installation format, KDE/Plasma version, Wayland/X11 session type, graphics hardware and driver details, and system WebKitGTK version in your report. diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index b64507e331..13df161c88 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -176,21 +176,45 @@ pub(crate) fn linux_compositing_available() -> bool { gtk::gdk::Screen::default().is_some_and(|screen| screen.is_composited()) } +/// Desktops where the transparent floating frame is known to render correctly. Others (notably +/// KDE, which shows flickering or missing content with some driver/WebKitGTK combinations) keep +/// the opaque window unless the user forces the frame on. #[cfg_attr(not(target_os = "linux"), allow(dead_code))] -fn should_enable_linux_floating_frame(compositing: bool, opt_in: Option<&std::ffi::OsStr>) -> bool { - compositing && opt_in == Some(std::ffi::OsStr::new("1")) +fn linux_desktop_supports_floating_frame(current_desktop: Option<&std::ffi::OsStr>) -> bool { + current_desktop + .map(|value| value.to_string_lossy().split(':').any(|name| name.eq_ignore_ascii_case("GNOME"))) + .unwrap_or(false) +} + +/// `DBX_LINUX_FLOATING_FRAME=1|0` is an optional override; without it the desktop decides. +#[cfg_attr(not(target_os = "linux"), allow(dead_code))] +fn should_enable_linux_floating_frame( + compositing: bool, + override_value: Option<&std::ffi::OsStr>, + current_desktop: Option<&std::ffi::OsStr>, +) -> bool { + if !compositing { + return false; + } + match override_value.and_then(|value| value.to_str()) { + Some("1") => true, + Some("0") => false, + _ => linux_desktop_supports_floating_frame(current_desktop), + } } /// The Linux config declares the main window with `create: false` so its transparency can -/// be explicitly opted into at runtime when a compositor is available. +/// depend at runtime on the desktop and on whether a compositor is available. #[cfg(target_os = "linux")] fn create_linux_main_window(app: &tauri::App) -> tauri::Result<()> { let Some(config) = app.config().app.windows.iter().find(|window| window.label == "main").cloned() else { return Ok(()); }; let compositing = linux_compositing_available(); - let opt_in = std::env::var_os("DBX_LINUX_FLOATING_FRAME"); - let floating_frame = should_enable_linux_floating_frame(compositing, opt_in.as_deref()); + let override_value = std::env::var_os("DBX_LINUX_FLOATING_FRAME"); + let current_desktop = std::env::var_os("XDG_CURRENT_DESKTOP"); + let floating_frame = + should_enable_linux_floating_frame(compositing, override_value.as_deref(), current_desktop.as_deref()); log::info!("[WINDOW] Linux main window compositing={compositing}; floating_frame={floating_frame}"); let mut builder = tauri::WebviewWindowBuilder::from_config(app.handle(), &config)?.transparent(floating_frame); if floating_frame { @@ -1205,22 +1229,30 @@ mod tests { } #[test] - fn linux_floating_frame_is_disabled_by_default() { - assert!(!should_enable_linux_floating_frame(true, None)); - assert!(!should_enable_linux_floating_frame(false, None)); + fn linux_floating_frame_is_enabled_by_default_on_gnome_only() { + for desktop in ["GNOME", "ubuntu:GNOME", "gnome", "GNOME-Classic:GNOME"] { + assert!(should_enable_linux_floating_frame(true, None, Some(OsStr::new(desktop))), "{desktop}"); + } + for desktop in ["KDE", "XFCE", "X-Cinnamon", "sway", "", "GNOMEISH"] { + assert!(!should_enable_linux_floating_frame(true, None, Some(OsStr::new(desktop))), "{desktop}"); + } + assert!(!should_enable_linux_floating_frame(true, None, None)); } #[test] - fn linux_floating_frame_requires_explicit_opt_in() { - assert!(should_enable_linux_floating_frame(true, Some(OsStr::new("1")))); - for value in ["", "0", "false", "true", "auto", "yes", "01"] { - assert!(!should_enable_linux_floating_frame(true, Some(OsStr::new(value)))); + fn linux_floating_frame_override_wins_over_desktop() { + assert!(should_enable_linux_floating_frame(true, Some(OsStr::new("1")), Some(OsStr::new("KDE")))); + assert!(!should_enable_linux_floating_frame(true, Some(OsStr::new("0")), Some(OsStr::new("GNOME")))); + for value in ["", "true", "auto", "01"] { + assert!(should_enable_linux_floating_frame(true, Some(OsStr::new(value)), Some(OsStr::new("GNOME")))); + assert!(!should_enable_linux_floating_frame(true, Some(OsStr::new(value)), Some(OsStr::new("KDE")))); } } #[test] - fn linux_floating_frame_requires_compositing_even_when_opted_in() { - assert!(!should_enable_linux_floating_frame(false, Some(OsStr::new("1")))); + fn linux_floating_frame_requires_compositing() { + assert!(!should_enable_linux_floating_frame(false, Some(OsStr::new("1")), Some(OsStr::new("GNOME")))); + assert!(!should_enable_linux_floating_frame(false, None, Some(OsStr::new("GNOME")))); } #[test]