From 90ca235af1816ad3d1c507508d8ccd1c958af8db Mon Sep 17 00:00:00 2001 From: Moritz Wolf Date: Tue, 17 Mar 2026 21:06:26 +0100 Subject: [PATCH] feat: add Linux/Fedora terminal support with Wayland detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes terminal spawning on Fedora and other RPM-based distributions where `x-terminal-emulator` (a Debian/Ubuntu mechanism) does not exist. Changes: - Replace hardcoded terminal list in `open_terminal` with a new `find_terminal_emulator()` helper that probes installed terminals in order of preference: x-terminal-emulator → gnome-terminal → konsole → xfce4-terminal → alacritty → kitty → tilix → xterm → urxvt - Add Wayland awareness: under a pure Wayland session (WAYLAND_DISPLAY set, DISPLAY unset) X11-only terminals (xterm, urxvt) are skipped to avoid launch failures when XWayland is not running - Use correct argument separator per terminal ("--" for gnome-terminal, konsole, kitty; "-e" for everything else) - Update README with Fedora/DNF build dependencies, cronie setup, and a Wayland compatibility note Tested on Fedora 43 with GNOME (Wayland + XWayland), producing a working .rpm and .AppImage via `pnpm tauri build`. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 26 ++++++++++++++++- src-tauri/src/lib.rs | 67 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index cc5cf53..80d2a64 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,29 @@ sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file libssl-dev curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` +**Linux** (Fedora/RPM): +```bash +sudo dnf install -y \ + webkit2gtk4.1-devel \ + openssl-devel \ + gtk3-devel \ + libayatana-appindicator-gtk3-devel \ + librsvg2-devel \ + curl wget file gcc make + +# Install Rust +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +source ~/.cargo/env + +# Enable cron scheduling support +sudo dnf install -y cronie +sudo systemctl enable --now crond +``` + +> **Fedora note:** If `libayatana-appindicator-gtk3-devel` is not found, try `libappindicator-gtk3-devel`. If `webkit2gtk4.1-devel` is missing, also install `libsoup3-devel`. + +> **Wayland:** ATM supports Wayland natively via gnome-terminal or konsole. If you are running a pure Wayland session (no XWayland), ensure one of these is installed. + **Build for production**: @@ -221,7 +244,8 @@ pnpm tauri build |----------|--------|---------| | Windows | Full support | PowerShell | | macOS | Full support | bash (Terminal.app) | -| Linux | Supported | bash | +| Linux (Debian/Ubuntu) | Supported | gnome-terminal, xterm, or any installed emulator | +| Linux (Fedora/RPM) | Supported | konsole, gnome-terminal, alacritty, kitty, xterm | --- diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3ca68de..0232168 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -254,6 +254,42 @@ fn delete_scheduled_task(task_name: String) -> Result { } } +/// Finds an available terminal emulator, preferring Wayland-native ones when running +/// under a pure Wayland session (WAYLAND_DISPLAY set, DISPLAY unset). +#[cfg(target_os = "linux")] +fn find_terminal_emulator() -> Option<&'static str> { + let wayland = std::env::var("WAYLAND_DISPLAY").is_ok(); + let x11 = std::env::var("DISPLAY").is_ok(); + let pure_wayland = wayland && !x11; + + // X11-only terminals that won't work without XWayland + const X11_ONLY: &[&str] = &["xterm", "urxvt"]; + + // Ordered by preference: Debian compat first, then common DEs, then fallbacks + const CANDIDATES: &[&str] = &[ + "x-terminal-emulator", // Debian/Ubuntu alternatives system + "gnome-terminal", // GNOME (native Wayland) + "konsole", // KDE (native Wayland, default on Fedora KDE) + "xfce4-terminal", // XFCE + "alacritty", // Modern, widespread + "kitty", // Modern, widespread + "tilix", // GNOME alternative + "xterm", // X11 fallback + "urxvt", // X11 fallback + ]; + + CANDIDATES.iter().find(|&&term| { + if pure_wayland && X11_ONLY.contains(&term) { + return false; + } + StdCommand::new("which") + .arg(term) + .output() + .map(|o| o.status.success()) + .unwrap_or(false) + }).copied() +} + /// Opens a visible terminal window running the given script. #[tauri::command] fn open_terminal(script_path: String) -> Result<(), String> { @@ -287,21 +323,22 @@ fn open_terminal(script_path: String) -> Result<(), String> { #[cfg(target_os = "linux")] { - let terminals = [ - ("x-terminal-emulator", vec!["-e", &script_path]), - ("gnome-terminal", vec!["--", &script_path]), - ("xterm", vec!["-e", &script_path]), - ]; - let mut launched = false; - for (term, args) in &terminals { - if StdCommand::new(term).args(args).spawn().is_ok() { - launched = true; - break; - } - } - if !launched { - return Err("No terminal emulator found".into()); - } + let term = find_terminal_emulator().ok_or_else(|| { + "No terminal emulator found. Install gnome-terminal, konsole, xterm, or another terminal emulator.".to_string() + })?; + + // Terminals that use "--" as argument separator instead of "-e" + const DASH_DASH_TERMS: &[&str] = &["gnome-terminal", "konsole", "kitty"]; + let args: Vec<&str> = if DASH_DASH_TERMS.contains(&term) { + vec!["--", &script_path] + } else { + vec!["-e", &script_path] + }; + + StdCommand::new(term) + .args(&args) + .spawn() + .map_err(|e| format!("Failed to open terminal '{}': {}", term, e))?; } Ok(())