diff --git a/src/bin/scythe-daemon.rs b/src/bin/scythe-daemon.rs index 0a56fa3..3227653 100644 --- a/src/bin/scythe-daemon.rs +++ b/src/bin/scythe-daemon.rs @@ -36,6 +36,21 @@ fn ensure_wayland_env() { env::set_var("DBUS_SESSION_BUS_ADDRESS", format!("unix:path={}", bus_path)); } } + if env::var("HYPRLAND_INSTANCE_SIGNATURE").is_err() { + let hypr_dir = std::path::Path::new(&runtime_dir).join("hypr"); + if let Ok(entries) = std::fs::read_dir(&hypr_dir) { + for entry in entries.flatten() { + if entry.path().is_dir() { + let sig = entry.file_name().to_string_lossy().to_string(); + if !sig.is_empty() { + env::set_var("HYPRLAND_INSTANCE_SIGNATURE", &sig); + println!("Auto-detected Hyprland instance signature: {}", sig); + break; + } + } + } + } + } if env::var("XDG_CURRENT_DESKTOP").is_err() { env::set_var("XDG_CURRENT_DESKTOP", "Hyprland"); } diff --git a/src/bin/scythe-ui.rs b/src/bin/scythe-ui.rs index 551dd8b..e8deb07 100644 --- a/src/bin/scythe-ui.rs +++ b/src/bin/scythe-ui.rs @@ -29,6 +29,20 @@ fn ensure_wayland_env() { env::set_var("DBUS_SESSION_BUS_ADDRESS", format!("unix:path={}", bus_path)); } } + if env::var("HYPRLAND_INSTANCE_SIGNATURE").is_err() { + let hypr_dir = std::path::Path::new(&runtime_dir).join("hypr"); + if let Ok(entries) = std::fs::read_dir(&hypr_dir) { + for entry in entries.flatten() { + if entry.path().is_dir() { + let sig = entry.file_name().to_string_lossy().to_string(); + if !sig.is_empty() { + env::set_var("HYPRLAND_INSTANCE_SIGNATURE", &sig); + break; + } + } + } + } + } if env::var("XDG_CURRENT_DESKTOP").is_err() && env::var("WAYLAND_DISPLAY").is_ok() { env::set_var("XDG_CURRENT_DESKTOP", "Hyprland"); } @@ -114,6 +128,53 @@ fn get_ui_cmd() -> std::process::Command { cmd } +fn check_and_toggle_overlay() -> bool { + let pid_path = scythe::ipc::get_overlay_pid_path(); + if pid_path.exists() { + if let Ok(content) = std::fs::read_to_string(&pid_path) { + if let Ok(pid) = content.trim().parse::() { + #[cfg(unix)] + { + let exists = unsafe { libc::kill(pid, 0) == 0 }; + if exists && pid != std::process::id() as i32 { + let proc_cmd = format!("/proc/{}/cmdline", pid); + let is_scythe = std::fs::read_to_string(&proc_cmd) + .map(|cmd| cmd.contains("scythe") || cmd.contains("vrec")) + .unwrap_or(false); + if is_scythe { + // Overlay already open: toggle it closed instantly! + unsafe { libc::kill(pid, libc::SIGTERM) }; + let _ = std::fs::remove_file(&pid_path); + return true; + } + } + } + } + } + let _ = std::fs::remove_file(&pid_path); + } + + let my_pid = std::process::id().to_string(); + let _ = std::fs::write(&pid_path, my_pid); + false +} + +fn ensure_daemon_running_async() { + #[cfg(unix)] + { + use std::os::unix::net::UnixStream; + if UnixStream::connect(scythe::ipc::get_socket_path()).is_ok() + || UnixStream::connect(scythe::ipc::get_legacy_socket_path()).is_ok() { + return; + } + } + let _ = get_daemon_cmd() + .stdin(std::process::Stdio::null()) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .spawn(); +} + fn ensure_daemon_running() { if query_status().is_err() { let _ = get_daemon_cmd() @@ -122,7 +183,7 @@ fn ensure_daemon_running() { .stderr(std::process::Stdio::null()) .spawn(); for _ in 0..15 { - std::thread::sleep(std::time::Duration::from_millis(100)); + std::thread::sleep(std::time::Duration::from_millis(80)); if query_status().is_ok() { break; } @@ -219,17 +280,29 @@ fn main() -> Result<(), Box> { return send_with_notification(Command::SaveReplay, "Replay saved"); } "--menu" => { - ensure_daemon_running(); + ensure_daemon_running_async(); ensure_hotkeys_running(); - #[cfg(not(target_os = "linux"))] + if check_and_toggle_overlay() { + return Ok(()); + } scythe::overlay_egui::run_egui_overlay(); - #[cfg(target_os = "linux")] - scythe::overlay::show_menu_overlay(); return Ok(()); } "--egui" => { + ensure_daemon_running_async(); + ensure_hotkeys_running(); + if check_and_toggle_overlay() { + return Ok(()); + } + scythe::overlay_egui::run_egui_overlay(); + return Ok(()); + } + "--gtk" => { ensure_daemon_running(); ensure_hotkeys_running(); + #[cfg(target_os = "linux")] + scythe::overlay::show_menu_overlay(); + #[cfg(not(target_os = "linux"))] scythe::overlay_egui::run_egui_overlay(); return Ok(()); } @@ -278,6 +351,7 @@ fn main() -> Result<(), Box> { println!("Usage:"); println!(" scythe-ui Open the interactive overlay UI menu (default)"); println!(" scythe-ui --menu Open the interactive overlay UI menu"); + println!(" scythe-ui --gtk Open legacy GTK layer-shell overlay (Linux)"); println!(" scythe-ui --hotkeys Run global hotkey manager in background"); println!(" scythe-ui --status Query current daemon status"); println!(" scythe-ui --save Save instant replay and show notification"); @@ -292,12 +366,12 @@ fn main() -> Result<(), Box> { _ => {} } } else { - ensure_daemon_running(); + ensure_daemon_running_async(); ensure_hotkeys_running(); - #[cfg(not(target_os = "linux"))] + if check_and_toggle_overlay() { + return Ok(()); + } scythe::overlay_egui::run_egui_overlay(); - #[cfg(target_os = "linux")] - scythe::overlay::show_menu_overlay(); return Ok(()); } diff --git a/src/ipc.rs b/src/ipc.rs index 20b08c7..439b749 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -178,3 +178,20 @@ pub fn query_status() -> Result std::path::PathBuf { + #[cfg(unix)] + { + let runtime_dir = std::env::var("XDG_RUNTIME_DIR") + .unwrap_or_else(|_| format!("/run/user/{}", unsafe { libc::getuid() })); + std::path::PathBuf::from(runtime_dir).join("scythe-overlay.pid") + } + #[cfg(not(unix))] + { + std::env::temp_dir().join("scythe-overlay.pid") + } +} + +pub fn clean_overlay_pid() { + let _ = std::fs::remove_file(get_overlay_pid_path()); +} diff --git a/src/overlay.rs b/src/overlay.rs index e2105f3..af29366 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -1007,14 +1007,14 @@ pub fn show_menu_overlay() { } /* Frosted Settings Panel */ .settings-panel { - background-color: rgba(15, 22, 32, 0.90); + background-color: #0f1620; border: 1px solid rgba(255, 255, 255, 0.16); border-radius: 18px; padding: 20px 26px; box-shadow: 0px 24px 64px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.2); } .settings-section-card { - background-color: rgba(24, 32, 46, 0.55); + background-color: #18202e; border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 12px; padding: 14px 18px; @@ -1140,7 +1140,7 @@ pub fn show_menu_overlay() { window.connect_key_press_event(move |_, key| { if key.keyval() == gdk::keys::constants::Escape { w_esc.close(); - gtk::glib::Propagation::Stop + std::process::exit(0); } else { gtk::glib::Propagation::Proceed } diff --git a/src/overlay_egui.rs b/src/overlay_egui.rs index 9bb1fab..be3ba0c 100644 --- a/src/overlay_egui.rs +++ b/src/overlay_egui.rs @@ -1495,10 +1495,10 @@ impl eframe::App for ScytheOverlayApp { self.replay_dropdown_open = false; self.record_dropdown_open = false; self.update_window_size(ctx); - } else if self.current_view != ShadowPlayView::MainHud { - self.switch_view(ShadowPlayView::MainHud, ctx); } else { ctx.send_viewport_cmd(egui::ViewportCommand::Close); + crate::ipc::clean_overlay_pid(); + std::process::exit(0); } } @@ -1538,4 +1538,6 @@ pub fn run_egui_overlay() { options, Box::new(|_cc| Ok(Box::new(ScytheOverlayApp::new()))), ); + + crate::ipc::clean_overlay_pid(); }