diff --git a/Cargo.lock b/Cargo.lock index ab10592..3db71b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2935,6 +2935,7 @@ dependencies = [ "directories", "eframe", "epaint_default_fonts", + "gtk", "hidapi", "notify-rust", "objc2 0.6.4", diff --git a/Cargo.toml b/Cargo.toml index f0895e5..ee64ab6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,8 @@ vello_cpu = { version = "0.1", default-features = false, features = ["std", "u8_ tray-icon = { version = "0.24.2", default-features = false, features = ["gtk"] } [target.'cfg(target_os = "linux")'.dependencies] +# tray-icon's GTK menu needs gtk::init() on the main thread before it is built. +gtk = "0.18" # X11 overlay banner and foreground-window lookup. Pure Rust; no system # libraries beyond what eframe/tray-icon already require. x11rb = "0.13" diff --git a/src/desktop.rs b/src/desktop.rs index 7739574..ad35ccd 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -49,7 +49,8 @@ fn headless_requested() -> bool { fn gui_available() -> bool { #[cfg(target_os = "linux")] { - env::var_os("WAYLAND_DISPLAY").is_some() || env::var_os("DISPLAY").is_some() + (env::var_os("WAYLAND_DISPLAY").is_some() || env::var_os("DISPLAY").is_some()) + && gtk_ready() } #[cfg(not(target_os = "linux"))] { @@ -57,6 +58,20 @@ fn gui_available() -> bool { } } +/// The tray menu is built with GTK, which panics on first use unless it was +/// initialised on this thread. If it cannot start there is no tray, so Bridge +/// runs headless instead of crash-looping (issue #26). +#[cfg(target_os = "linux")] +fn gtk_ready() -> bool { + match gtk::init() { + Ok(()) => true, + Err(error) => { + tracing::warn!(%error, "GTK could not start"); + false + } + } +} + /// Serves the Bridge runtime without a window: drains notices as native /// notifications, applies update restarts, and blocks on Ctrl-C. fn run_headless( @@ -1094,7 +1109,7 @@ pub fn run() -> Result<()> { if headless_requested() || !gui_available() { if !headless_requested() { tracing::warn!( - "no display found (set WAYLAND_DISPLAY or DISPLAY for the tray panel); running headless: HTTP API, HID, and game profiles still work" + "no usable display for the tray panel (set WAYLAND_DISPLAY or DISPLAY, and GTK must start); running headless: HTTP API, HID, and game profiles still work" ); } let Some((server, snapshots, commands, updates, _panel_requests, _show_panel)) =