diff --git a/Cargo.lock b/Cargo.lock index 5607e70..d1a8f5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,9 +110,9 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "ahash" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if 1.0.0", "getrandom", @@ -280,9 +280,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f" [[package]] name = "arrayref" @@ -335,6 +335,23 @@ dependencies = [ "zbus", ] +[[package]] +name = "ashpd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01992ad7774250d5b7fe214e2676cb99bf92564436d8135ab44fe815e71769a9" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.5.1" @@ -791,10 +808,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" +checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" dependencies = [ + "jobserver", "libc", ] @@ -1012,9 +1030,9 @@ dependencies = [ [[package]] name = "const-random" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" dependencies = [ "const-random-macro", ] @@ -1127,7 +1145,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -1144,7 +1162,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "quote", "syn 1.0.109", @@ -1164,6 +1182,7 @@ dependencies = [ "i18n-embed-fl", "image 0.24.9", "lexical-sort", + "libc", "libcosmic", "log", "mime_guess", @@ -1173,6 +1192,7 @@ dependencies = [ "paste", "rust-embed", "serde", + "shlex", "smol_str", "systemicons", "tempfile", @@ -1186,7 +1206,7 @@ dependencies = [ [[package]] name = "cosmic-text" version = "0.11.2" -source = "git+https://github.com/pop-os/cosmic-text.git#22e61965aa38ab0bd64bbdddf3848ae891edceba" +source = "git+https://github.com/pop-os/cosmic-text.git#a53a0b3a8c085143470a9d26ac2c2911cc479033" dependencies = [ "bitflags 2.4.2", "fontdb", @@ -1208,7 +1228,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "almost", "cosmic-config", @@ -2662,7 +2682,7 @@ dependencies = [ [[package]] name = "iced" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "iced_accessibility", "iced_core", @@ -2677,7 +2697,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "accesskit", "accesskit_winit", @@ -2686,7 +2706,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "bitflags 1.3.2", "log", @@ -2703,7 +2723,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "futures", "iced_core", @@ -2716,7 +2736,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -2740,7 +2760,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2752,7 +2772,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "iced_core", "iced_futures", @@ -2762,7 +2782,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "iced_core", "once_cell", @@ -2772,7 +2792,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "bytemuck", "cosmic-text", @@ -2789,7 +2809,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -2808,7 +2828,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "iced_renderer", "iced_runtime", @@ -2822,7 +2842,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "iced_graphics", "iced_runtime", @@ -3032,6 +3052,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + [[package]] name = "jpeg-decoder" version = "0.1.22" @@ -3166,10 +3195,10 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#ce45af20f8f1d7c803b20c8af37fdd3adac641e2" +source = "git+https://github.com/pop-os/libcosmic.git#be00b0ec4beb36e38aedab8ee5fd7976bf5dd141" dependencies = [ "apply", - "ashpd", + "ashpd 0.7.0", "cosmic-config", "cosmic-theme", "css-color", @@ -3977,9 +4006,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "open" -version = "5.1.0" +version = "5.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2588edf622de56e7a1fed57bf203344f63c03f3d43472ba0434a92373c8f27" +checksum = "68b3fbb0d52bf0cbb5225ba3d2c303aa136031d43abff98284332a9981ecddec" dependencies = [ "is-wsl", "libc", @@ -4581,9 +4610,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -4598,9 +4627,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "renderdoc-sys" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "resvg" @@ -4625,7 +4654,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0d8ab342bcc5436e04d3a4c1e09e17d74958bfaddf8d5fad6f85607df0f994f" dependencies = [ - "ashpd", + "ashpd 0.6.8", "block", "dispatch", "glib-sys", diff --git a/Cargo.toml b/Cargo.toml index 4f539a7..1e93b61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,11 +18,13 @@ image = "0.24" once_cell = "1.19" open = "5.0.2" lexical-sort = "0.3.1" +libc = "0.2" log = "0.4" mime_guess = "2" notify = "6" paste = "1.0" serde = { version = "1", features = ["serde_derive"] } +shlex = { version = "1.3" } tokio = { version = "1" } trash = "3.2.0" xdg = { version = "2.5.2", optional = true } diff --git a/i18n/en/cosmic_files.ftl b/i18n/en/cosmic_files.ftl index 06384f7..8ec0aa8 100644 --- a/i18n/en/cosmic_files.ftl +++ b/i18n/en/cosmic_files.ftl @@ -72,6 +72,7 @@ light = Light new-file = New file new-folder = New folder open-with = Open with +open-in-terminal = Open in terminal move-to-trash = Move to trash restore-from-trash = Restore from trash diff --git a/src/app.rs b/src/app.rs index f7f7141..d168bd7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -13,7 +13,10 @@ use cosmic::{ window, Alignment, Event, Length, }, style, theme, - widget::{self, segmented_button}, + widget::{ + self, + segmented_button::{self, Entity}, + }, Application, ApplicationExt, Element, }; use notify::Watcher; @@ -30,8 +33,9 @@ use crate::{ config::{AppTheme, Config, IconSizes, TabConfig, CONFIG_VERSION}, fl, home_dir, key_bind::{key_binds, KeyBind}, - menu, + menu, mime_app, operation::Operation, + spawn_detached::spawn_detached, tab::{self, ItemMetadata, Location, Tab}, }; @@ -58,6 +62,7 @@ pub enum Action { NewFile, NewFolder, Open, + OpenTerminal, OpenWith, Operations, Paste, @@ -78,7 +83,7 @@ pub enum Action { } impl Action { - pub fn message(self, entity_opt: Option) -> Message { + pub fn message(self, entity_opt: Option) -> Message { match self { Action::About => Message::ToggleContextPage(ContextPage::About), Action::Copy => Message::Copy(entity_opt), @@ -95,6 +100,7 @@ impl Action { Action::NewFile => Message::NewItem(entity_opt, false), Action::NewFolder => Message::NewItem(entity_opt, true), Action::Open => Message::TabMessage(entity_opt, tab::Message::Open), + Action::OpenTerminal => Message::OpenTerminal(entity_opt), Action::OpenWith => Message::ToggleContextPage(ContextPage::OpenWith), Action::Operations => Message::ToggleContextPage(ContextPage::Operations), Action::Paste => Message::Paste(entity_opt), @@ -125,34 +131,35 @@ impl Action { pub enum Message { AppTheme(AppTheme), Config(Config), - Copy(Option), - Cut(Option), + Copy(Option), + Cut(Option), DialogCancel, DialogComplete, DialogUpdate(DialogPage), - EditLocation(Option), + EditLocation(Option), Key(Modifiers, Key), LaunchUrl(String), Modifiers(Modifiers), - MoveToTrash(Option), - NewItem(Option, bool), + MoveToTrash(Option), + NewItem(Option, bool), NotifyEvent(notify::Event), NotifyWatcher(WatcherWrapper), - Paste(Option), + OpenTerminal(Option), + Paste(Option), PendingComplete(u64), PendingError(u64, String), PendingProgress(u64, f32), - Rename(Option), - RestoreFromTrash(Option), + Rename(Option), + RestoreFromTrash(Option), SystemThemeModeChange(cosmic_theme::ThemeMode), - TabActivate(segmented_button::Entity), + TabActivate(Entity), TabNext, TabPrev, - TabClose(Option), + TabClose(Option), TabConfig(TabConfig), - TabMessage(Option, tab::Message), + TabMessage(Option, tab::Message), TabNew, - TabRescan(segmented_button::Entity, Vec), + TabRescan(Entity, Vec), ToggleContextPage(ContextPage), WindowClose, WindowNew, @@ -256,11 +263,7 @@ impl App { self.pending_operations.insert(id, (operation, 0.0)); } - fn rescan_tab( - &mut self, - entity: segmented_button::Entity, - location: Location, - ) -> Command { + fn rescan_tab(&mut self, entity: Entity, location: Location) -> Command { let icon_sizes = self.config.tab.icon_sizes; Command::perform( async move { @@ -643,7 +646,7 @@ impl Application for App { Some(&self.nav_model) } - fn on_nav_select(&mut self, entity: segmented_button::Entity) -> Command { + fn on_nav_select(&mut self, entity: Entity) -> Command { let location_opt = self.nav_model.data::(entity).clone(); if let Some(location) = location_opt { @@ -864,6 +867,44 @@ impl Application for App { log::warn!("message did not contain notify watcher"); } }, + Message::OpenTerminal(entity_opt) => { + if let Some(terminal) = mime_app::terminal() { + let mut paths = Vec::new(); + let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); + if let Some(tab) = self.tab_model.data_mut::(entity) { + if let Location::Path(path) = &tab.location { + if let Some(items) = tab.items_opt() { + for item in items.iter() { + if item.selected { + paths.push(item.path.clone()); + } + } + } + if paths.is_empty() { + paths.push(path.clone()); + } + } + } + for path in paths { + if let Some(mut command) = terminal.command(None) { + command.current_dir(&path); + match spawn_detached(&mut command) { + Ok(()) => {} + Err(err) => { + log::warn!( + "failed to launch terminal {:?} in {:?}: {}", + terminal.id, + path, + err + ) + } + } + } else { + log::warn!("failed to get command for {:?}", terminal.id); + } + } + } + } Message::Paste(_entity_opt) => { log::warn!("TODO: PASTE"); } diff --git a/src/lib.rs b/src/lib.rs index a76c206..36174aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ mod mime_app; mod mime_icon; mod mouse_area; mod operation; +mod spawn_detached; mod tab; pub fn home_dir() -> PathBuf { diff --git a/src/menu.rs b/src/menu.rs index e7ae618..bc0f13d 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -57,9 +57,18 @@ pub fn context_menu<'a>( .on_press(tab::Message::ContextAction(action)) }; - let selected = tab - .items_opt() - .map_or(0, |items| items.iter().filter(|x| x.selected).count()); + let mut selected_dir = 0; + let mut selected = 0; + tab.items_opt().map(|items| { + for item in items.iter() { + if item.selected { + selected += 1; + if item.metadata.is_dir() { + selected_dir += 1; + } + } + } + }); let mut children: Vec> = Vec::new(); match tab.location { @@ -68,6 +77,10 @@ pub fn context_menu<'a>( children.push(menu_item(fl!("open"), Action::Open).into()); if selected == 1 { children.push(menu_item(fl!("open-with"), Action::OpenWith).into()); + if selected_dir == 1 { + children + .push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal).into()); + } } children.push(horizontal_rule(1).into()); children.push(menu_item(fl!("rename"), Action::Rename).into()); @@ -85,6 +98,7 @@ pub fn context_menu<'a>( //TODO: have things like properties but they apply to the folder? children.push(menu_item(fl!("new-file"), Action::NewFile).into()); children.push(menu_item(fl!("new-folder"), Action::NewFolder).into()); + children.push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal).into()); children.push(horizontal_rule(1).into()); children.push(menu_item(fl!("select-all"), Action::SelectAll).into()); children.push(menu_item(fl!("paste"), Action::Paste).into()); diff --git a/src/mime_app.rs b/src/mime_app.rs index 7062351..d69807c 100644 --- a/src/mime_app.rs +++ b/src/mime_app.rs @@ -6,7 +6,7 @@ use cosmic::desktop; use cosmic::widget; pub use mime_guess::Mime; use once_cell::sync::Lazy; -use std::{collections::HashMap, path::PathBuf, sync::Mutex, time::Instant}; +use std::{collections::HashMap, path::PathBuf, process, sync::Mutex, time::Instant}; #[derive(Clone, Debug)] pub struct MimeApp { @@ -18,6 +18,33 @@ pub struct MimeApp { pub is_default: bool, } +impl MimeApp { + //TODO: move to libcosmic, support multiple files + pub fn command(&self, path_opt: Option) -> Option { + let args_vec: Vec = self.exec.as_deref().and_then(shlex::split)?; + let mut args = args_vec.iter(); + let mut command = process::Command::new(args.next()?); + for arg in args { + if arg.starts_with('%') { + match arg.as_str() { + "%f" | "%F" | "%u" | "%U" => { + if let Some(path) = &path_opt { + command.arg(path); + } + } + _ => { + log::warn!("unsupported Exec code {:?} in {:?}", arg, self.id); + return None; + } + } + } else { + command.arg(arg); + } + } + Some(command) + } +} + #[cfg(feature = "desktop")] impl From<&desktop::DesktopEntryData> for MimeApp { fn from(app: &desktop::DesktopEntryData) -> Self { @@ -46,12 +73,14 @@ fn filename_eq(path_opt: &Option, filename: &str) -> bool { pub struct MimeAppCache { cache: HashMap>, + terminals: Vec, } impl MimeAppCache { pub fn new() -> Self { let mut mime_app_cache = Self { cache: HashMap::new(), + terminals: Vec::new(), }; mime_app_cache.reload(); mime_app_cache @@ -66,6 +95,7 @@ impl MimeAppCache { let start = Instant::now(); self.cache.clear(); + self.terminals.clear(); //TODO: get proper locale? let locale = None; @@ -83,6 +113,12 @@ impl MimeAppCache { apps.push(MimeApp::from(app)); } } + for category in app.categories.iter() { + if category == "TerminalEmulator" { + self.terminals.push(MimeApp::from(app)); + break; + } + } } // Load mimeapps.list files @@ -202,3 +238,22 @@ pub fn mime_apps(mime: &Mime) -> Vec { let mime_app_cache = MIME_APP_CACHE.lock().unwrap(); mime_app_cache.get(mime) } + +pub fn terminal() -> Option { + let mime_app_cache = MIME_APP_CACHE.lock().unwrap(); + + //TODO: consider rules in https://github.com/Vladimir-csp/xdg-terminal-exec + + // Look for and return preferred terminals + //TODO: fallback order beyond cosmic-term? + for id in &["com.system76.CosmicTerm"] { + for terminal in mime_app_cache.terminals.iter() { + if &terminal.id == id { + return Some(terminal.clone()); + } + } + } + + // Return whatever was the first terminal found + mime_app_cache.terminals.first().map(|x| x.clone()) +} diff --git a/src/spawn_detached.rs b/src/spawn_detached.rs new file mode 100644 index 0000000..d4d345b --- /dev/null +++ b/src/spawn_detached.rs @@ -0,0 +1,37 @@ +use std::{io, process}; + +// This code is from the open crate and retains its MIT license. +pub fn spawn_detached(command: &mut process::Command) -> io::Result<()> { + command + .stdin(process::Stdio::null()) + .stdout(process::Stdio::null()) + .stderr(process::Stdio::null()); + + #[cfg(unix)] + unsafe { + use std::os::unix::process::CommandExt as _; + + command.pre_exec(move || { + match libc::fork() { + -1 => return Err(io::Error::last_os_error()), + 0 => (), + _ => libc::_exit(0), + } + + if libc::setsid() == -1 { + return Err(io::Error::last_os_error()); + } + + Ok(()) + }); + } + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + const CREATE_NO_WINDOW: u32 = 0x08000000; + command.creation_flags(CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW); + } + + command.spawn().map(|_| ()) +}