diff --git a/Cargo.lock b/Cargo.lock index 36f5922..b3a21f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3700,7 +3700,7 @@ dependencies = [ [[package]] name = "scythe" -version = "0.2.8" +version = "0.2.9" dependencies = [ "ashpd", "cpal", diff --git a/Cargo.toml b/Cargo.toml index 8162217..176ac1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "scythe" -version = "0.2.8" +version = "0.2.9" edition = "2024" [[bin]] @@ -53,4 +53,6 @@ windows = { version = "0.58.0", features = [ "Win32_UI_Controls", "Win32_System_Threading", "Win32_System_LibraryLoader", + "Win32_System_Console", + "Win32_UI_Shell", ] } diff --git a/packaging/windows/scythe-installer.nsi b/packaging/windows/scythe-installer.nsi index f74291a..6806935 100644 --- a/packaging/windows/scythe-installer.nsi +++ b/packaging/windows/scythe-installer.nsi @@ -27,11 +27,11 @@ RequestExecutionLevel user Section "Scythe" SecMain ; Terminate any running instances so locked executables and DLLs can be updated cleanly - nsExec::Exec 'cmd.exe /C taskkill /F /IM scythe-ui.exe /T >nul 2>&1' - nsExec::Exec 'cmd.exe /C taskkill /F /IM scythe-daemon.exe /T >nul 2>&1' - nsExec::Exec 'cmd.exe /C taskkill /F /IM vrec-ui.exe /T >nul 2>&1' - nsExec::Exec 'cmd.exe /C taskkill /F /IM vrec-daemon.exe /T >nul 2>&1' - nsExec::Exec 'powershell -NoProfile -NonInteractive -Command "Get-Process -Name scythe-ui, scythe-daemon, vrec-ui, vrec-daemon -ErrorAction SilentlyContinue | Stop-Process -Force"' + nsExec::Exec 'taskkill /F /IM scythe-ui.exe /T' + nsExec::Exec 'taskkill /F /IM scythe-daemon.exe /T' + nsExec::Exec 'taskkill /F /IM vrec-ui.exe /T' + nsExec::Exec 'taskkill /F /IM vrec-daemon.exe /T' + nsExec::Exec 'powershell -NoProfile -NonInteractive -WindowStyle Hidden -Command "Get-Process -Name scythe-ui, scythe-daemon, vrec-ui, vrec-daemon -ErrorAction SilentlyContinue | Stop-Process -Force"' Sleep 1500 SetOverwrite try @@ -52,11 +52,11 @@ Section "Scythe" SecMain SectionEnd Section "Uninstall" - nsExec::Exec 'cmd.exe /C taskkill /F /IM scythe-ui.exe /T >nul 2>&1' - nsExec::Exec 'cmd.exe /C taskkill /F /IM scythe-daemon.exe /T >nul 2>&1' - nsExec::Exec 'cmd.exe /C taskkill /F /IM vrec-ui.exe /T >nul 2>&1' - nsExec::Exec 'cmd.exe /C taskkill /F /IM vrec-daemon.exe /T >nul 2>&1' - nsExec::Exec 'powershell -NoProfile -NonInteractive -Command "Get-Process -Name scythe-ui, scythe-daemon, vrec-ui, vrec-daemon -ErrorAction SilentlyContinue | Stop-Process -Force"' + nsExec::Exec 'taskkill /F /IM scythe-ui.exe /T' + nsExec::Exec 'taskkill /F /IM scythe-daemon.exe /T' + nsExec::Exec 'taskkill /F /IM vrec-ui.exe /T' + nsExec::Exec 'taskkill /F /IM vrec-daemon.exe /T' + nsExec::Exec 'powershell -NoProfile -NonInteractive -WindowStyle Hidden -Command "Get-Process -Name scythe-ui, scythe-daemon, vrec-ui, vrec-daemon -ErrorAction SilentlyContinue | Stop-Process -Force"' Sleep 1500 Delete "$DESKTOP\scythe.lnk" diff --git a/packaging/windows/scythe-standalone.nsi b/packaging/windows/scythe-standalone.nsi index 8bd53a4..81e63e9 100644 --- a/packaging/windows/scythe-standalone.nsi +++ b/packaging/windows/scythe-standalone.nsi @@ -9,11 +9,11 @@ RequestExecutionLevel user Section ; Terminate any running instances so locked executables and DLLs can be updated cleanly - nsExec::Exec 'cmd.exe /C taskkill /F /IM scythe-ui.exe /T >nul 2>&1' - nsExec::Exec 'cmd.exe /C taskkill /F /IM scythe-daemon.exe /T >nul 2>&1' - nsExec::Exec 'cmd.exe /C taskkill /F /IM vrec-ui.exe /T >nul 2>&1' - nsExec::Exec 'cmd.exe /C taskkill /F /IM vrec-daemon.exe /T >nul 2>&1' - nsExec::Exec 'powershell -NoProfile -NonInteractive -Command "Get-Process -Name scythe-ui, scythe-daemon, vrec-ui, vrec-daemon -ErrorAction SilentlyContinue | Stop-Process -Force"' + nsExec::Exec 'taskkill /F /IM scythe-ui.exe /T' + nsExec::Exec 'taskkill /F /IM scythe-daemon.exe /T' + nsExec::Exec 'taskkill /F /IM vrec-ui.exe /T' + nsExec::Exec 'taskkill /F /IM vrec-daemon.exe /T' + nsExec::Exec 'powershell -NoProfile -NonInteractive -WindowStyle Hidden -Command "Get-Process -Name scythe-ui, scythe-daemon, vrec-ui, vrec-daemon -ErrorAction SilentlyContinue | Stop-Process -Force"' Sleep 1500 SetOverwrite try diff --git a/src/bin/scythe-daemon.rs b/src/bin/scythe-daemon.rs index e054f96..c68907b 100644 --- a/src/bin/scythe-daemon.rs +++ b/src/bin/scythe-daemon.rs @@ -21,6 +21,12 @@ fn ensure_wayland_env() { #[tokio::main] #[allow(unused_assignments)] async fn main() -> Result<(), Box> { + #[cfg(target_os = "windows")] + unsafe { + use windows::Win32::System::Console::FreeConsole; + let _ = FreeConsole(); + } + ensure_wayland_env(); #[cfg(unix)] @@ -496,11 +502,16 @@ async fn main() -> Result<(), Box> { let _ = m.finalize(); } if let Ok(exe) = std::env::current_exe() { - let _ = std::process::Command::new(exe) - .stdin(std::process::Stdio::null()) + let mut cmd = std::process::Command::new(exe); + cmd.stdin(std::process::Stdio::null()) .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::null()) - .spawn(); + .stderr(std::process::Stdio::null()); + #[cfg(target_os = "windows")] + { + use std::os::windows::process::CommandExt; + cmd.creation_flags(0x08000000); + } + let _ = cmd.spawn(); } std::thread::sleep(std::time::Duration::from_millis(150)); std::process::exit(0); diff --git a/src/bin/scythe-ui.rs b/src/bin/scythe-ui.rs index aff4a58..b273527 100644 --- a/src/bin/scythe-ui.rs +++ b/src/bin/scythe-ui.rs @@ -283,9 +283,19 @@ fn handle_save_replay() -> Result<(), Box> } fn main() -> Result<(), Box> { + let args: Vec = env::args().collect(); + + #[cfg(target_os = "windows")] + unsafe { + let is_cli = args.iter().any(|a| a == "--status" || a == "--help" || a == "-h"); + if !is_cli { + use windows::Win32::System::Console::FreeConsole; + let _ = FreeConsole(); + } + } + ensure_wayland_env(); - let args: Vec = env::args().collect(); if args.len() > 1 { match args[1].as_str() { "--save" => { diff --git a/src/overlay.rs b/src/overlay.rs index d2efd44..26529ab 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -51,13 +51,34 @@ pub fn spawn_toast(title: &str, subtitle: &str, icon: ToastIcon) { let icon_owned = icon_str.to_string(); std::thread::spawn(move || { - let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("scythe-ui")); - let _ = std::process::Command::new(exe) - .args(["--toast", &title_owned, &subtitle_owned, &icon_owned]) + let exe = if let Ok(mut p) = std::env::current_exe() { + p.pop(); + #[cfg(target_os = "windows")] + let ui = p.join("scythe-ui.exe"); + #[cfg(not(target_os = "windows"))] + let ui = p.join("scythe-ui"); + if ui.exists() { + ui + } else { + std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("scythe-ui")) + } + } else { + std::path::PathBuf::from("scythe-ui") + }; + let mut cmd = std::process::Command::new(&exe); + if let Some(parent) = exe.parent() { + cmd.current_dir(parent); + } + cmd.args(["--toast", &title_owned, &subtitle_owned, &icon_owned]) .stdin(std::process::Stdio::null()) .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::null()) - .spawn(); + .stderr(std::process::Stdio::null()); + #[cfg(target_os = "windows")] + { + use std::os::windows::process::CommandExt; + cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW + } + let _ = cmd.spawn(); }); } diff --git a/src/overlay_egui.rs b/src/overlay_egui.rs index 161419b..c1784e3 100644 --- a/src/overlay_egui.rs +++ b/src/overlay_egui.rs @@ -48,14 +48,19 @@ fn format_egui_key(key: egui::Key) -> Option { } fn probe_duration_sec(path: &std::path::Path) -> f32 { - let out = std::process::Command::new("ffprobe") - .args([ - "-v", "error", - "-show_entries", "format=duration", - "-of", "default=noprint_wrappers=1:nokey=1", - ]) - .arg(path) - .output(); + let mut cmd = std::process::Command::new("ffprobe"); + cmd.args([ + "-v", "error", + "-show_entries", "format=duration", + "-of", "default=noprint_wrappers=1:nokey=1", + ]) + .arg(path); + #[cfg(target_os = "windows")] + { + use std::os::windows::process::CommandExt; + cmd.creation_flags(0x08000000); + } + let out = cmd.output(); if let Ok(out) = out { let text = String::from_utf8_lossy(&out.stdout); if let Ok(val) = text.trim().parse::() { @@ -93,6 +98,11 @@ fn trim_clip( "-avoid_negative_ts", "make_zero", ]) .arg(&out_path); + #[cfg(target_os = "windows")] + { + use std::os::windows::process::CommandExt; + cmd.creation_flags(0x08000000); + } let res = cmd.output().map_err(|e| format!("Failed to spawn ffmpeg: {}", e))?; if res.status.success() { @@ -108,11 +118,21 @@ fn play_clip(path: &std::path::Path) { std::thread::spawn(move || { #[cfg(target_os = "windows")] { - use std::os::windows::process::CommandExt; - let _ = std::process::Command::new("cmd") - .args(["/C", "start", "", &p.to_string_lossy()]) - .creation_flags(0x08000000) - .spawn(); + use windows::core::HSTRING; + use windows::Win32::UI::Shell::ShellExecuteW; + use windows::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; + + let path_str = p.to_string_lossy().to_string(); + unsafe { + let _ = ShellExecuteW( + None, + windows::core::w!("open"), + &HSTRING::from(&path_str), + None, + None, + SW_SHOWNORMAL, + ); + } } #[cfg(not(target_os = "windows"))] { @@ -263,7 +283,7 @@ fn pick_folder(current_dir: &str, tx: Sender, is_active: Arc clean_cur, clean_cur ); let mut cmd = std::process::Command::new("powershell.exe"); - cmd.args(["-NoProfile", "-STA", "-Command", &script]); + cmd.args(["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-STA", "-Command", &script]); cmd.creation_flags(0x08000000); if let Ok(out) = cmd.output() { let sel = String::from_utf8_lossy(&out.stdout).trim().to_string(); diff --git a/src/updater.rs b/src/updater.rs index 843db35..3c509df 100644 --- a/src/updater.rs +++ b/src/updater.rs @@ -123,11 +123,20 @@ pub fn open_browser_url(url: &str) { std::thread::spawn(move || { #[cfg(target_os = "windows")] { - use std::os::windows::process::CommandExt; - let _ = std::process::Command::new("cmd") - .args(["/C", "start", "", &u]) - .creation_flags(0x08000000) - .spawn(); + use windows::core::HSTRING; + use windows::Win32::UI::Shell::ShellExecuteW; + use windows::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; + + unsafe { + let _ = ShellExecuteW( + None, + windows::core::w!("open"), + &HSTRING::from(&u), + None, + None, + SW_SHOWNORMAL, + ); + } } #[cfg(target_os = "macos")] {