diff --git a/src-tauri/src/app.rs b/src-tauri/src/app.rs index 7b53eea..0ca865e 100644 --- a/src-tauri/src/app.rs +++ b/src-tauri/src/app.rs @@ -108,6 +108,10 @@ async fn setup(app: &mut App) -> Result<(), Box> { setup_errors.push("Failed to register hotkeys. Hotkey settings have been disabled.".into()); } + let desktop_is_gnome = std::env::var("XDG_CURRENT_DESKTOP") + .map(|names| names.split(':').any(|n| n == "GNOME")) + .unwrap_or(false); + // if session is empty, this is probably the first launch, so don't autohide if !conf.start_minimized || is_first_launch { app.get_window("main") @@ -115,7 +119,7 @@ async fn setup(app: &mut App) -> Result<(), Box> { .show()?; } - let state = AppState::new(conf, session, pool, setup_errors); + let state = AppState::new(conf, session, pool, setup_errors, desktop_is_gnome); app.manage(state); Ok(()) } diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index cd30af1..e6af889 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -39,13 +39,22 @@ impl Visibility { .ok_or(WindowError::NoMainWindow)?; self.leases += 1; + // `original` represents the visibility of the window before any leases were acquired + // None means we don't know, Some(false) means it was previously hidden, + // Some(true) means it was previously visible if self.original.is_none() { let is_visible = window.is_visible()?; self.original = Some(is_visible); - if !is_visible { - window.show()?; - } } + + let state = app.state::(); + if matches!(self.original, Some(true)) && state.desktop_is_gnome { + // Gnome has a really annoying "focus-stealing prevention" behavior means we + // can't just pop up when the window is already visible, so to work around it + // we hide and then immediately unhide the window + window.hide()?; + } + window.show()?; window.set_focus()?; let (tx, rx) = oneshot::channel(); @@ -95,8 +104,9 @@ pub struct AppState { pub request_count: RwLock, pub waiting_requests: RwLock>>, pub pending_terminal_request: RwLock, - // setup_errors is never modified and so doesn't need to be wrapped in RwLock + // these are never modified and so don't need to be wrapped in RwLocks pub setup_errors: Vec, + pub desktop_is_gnome: bool, pool: sqlx::SqlitePool, visibility: RwLock, } @@ -107,6 +117,7 @@ impl AppState { session: Session, pool: SqlitePool, setup_errors: Vec, + desktop_is_gnome: bool, ) -> AppState { AppState { config: RwLock::new(config), @@ -115,6 +126,7 @@ impl AppState { waiting_requests: RwLock::new(HashMap::new()), pending_terminal_request: RwLock::new(false), setup_errors, + desktop_is_gnome, pool, visibility: RwLock::new(Visibility::new()), }