diff --git a/applets/cosmic-app-list/Cargo.lock b/applets/cosmic-app-list/Cargo.lock index 1c5ad52a..c0a5c54b 100644 --- a/applets/cosmic-app-list/Cargo.lock +++ b/applets/cosmic-app-list/Cargo.lock @@ -231,6 +231,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + [[package]] name = "calloop" version = "0.10.4" @@ -399,6 +405,7 @@ dependencies = [ "ron", "rust-embed", "serde", + "shlex", "tokio", "xdg", ] @@ -1864,6 +1871,18 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.42.0", +] + [[package]] name = "mutate_once" version = "0.1.1" @@ -2641,6 +2660,21 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "simplecss" version = "0.2.1" @@ -2926,8 +2960,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" dependencies = [ "autocfg", + "bytes", + "libc", + "mio", "num_cpus", "pin-project-lite", + "signal-hook-registry", "tokio-macros", "windows-sys 0.42.0", ] diff --git a/applets/cosmic-app-list/Cargo.toml b/applets/cosmic-app-list/Cargo.toml index 312f9e88..7b7ca80c 100644 --- a/applets/cosmic-app-list/Cargo.toml +++ b/applets/cosmic-app-list/Cargo.toml @@ -19,10 +19,11 @@ xdg = "2.4" pretty_env_logger = "0.4" calloop = "0.10" nix = "0.26" +shlex = "1.1.0" anyhow = "1.0" serde = { version = "1.0", features = ["derive"] } log = "0.4" -tokio = { version = "1.17.0", features = ["sync", "rt", "rt-multi-thread", "macros"] } +tokio = { version = "1.17.0", features = ["sync", "rt", "rt-multi-thread", "macros", "process"] } itertools = "*" cosmic-panel-config = {git = "https://github.com/pop-os/cosmic-panel", no-default-features = true} freedesktop-desktop-entry = "0.5.0" diff --git a/applets/cosmic-app-list/src/app.rs b/applets/cosmic-app-list/src/app.rs index 0c4a6482..9d205e93 100644 --- a/applets/cosmic-app-list/src/app.rs +++ b/applets/cosmic-app-list/src/app.rs @@ -45,6 +45,7 @@ struct Toplevel { toplevels: Vec<(ZcosmicToplevelHandleV1, ToplevelInfo)>, app_id: String, icon_path: PathBuf, + exec: String, } #[derive(Clone, Default)] @@ -67,14 +68,15 @@ enum Message { Favorite(String), UnFavorite(String), TogglePopup(usize), - Activate(Option), + Activate(ZcosmicToplevelHandleV1), + Exec(String), Quit(ZcosmicToplevelHandleV1), Errored(String), Ignore, NewSeat(WlSeat), RemovedSeat(WlSeat), } -fn icon_for_app_ids(mut app_ids: Vec) -> Vec<(String, PathBuf)> { +fn icon_exec_for_app_ids(mut app_ids: Vec) -> Vec<(String, PathBuf, String)> { let mut ret = freedesktop_desktop_entry::Iter::new(freedesktop_desktop_entry::default_paths()) .filter_map(|path| { std::fs::read_to_string(&path).ok().and_then(|input| { @@ -85,7 +87,7 @@ fn icon_for_app_ids(mut app_ids: Vec) -> Vec<(String, PathBuf)> { .with_size(128) .with_cache() .find() - .map(|buf| (id, buf)) + .map(|buf| (id, buf, de.exec().unwrap_or_default().to_string())) } else { None } @@ -96,7 +98,7 @@ fn icon_for_app_ids(mut app_ids: Vec) -> Vec<(String, PathBuf)> { ret.append( &mut app_ids .into_iter() - .map(|id| (id, Default::default())) + .map(|id| (id, Default::default(), Default::default())) .collect_vec(), ); ret @@ -112,12 +114,13 @@ impl Application for CosmicAppList { let config = config::AppListConfig::load().unwrap_or_default(); ( CosmicAppList { - toplevel_list: icon_for_app_ids(config.favorites.clone()) + toplevel_list: icon_exec_for_app_ids(config.favorites.clone()) .into_iter() .map(|e| Toplevel { toplevels: Default::default(), app_id: e.0, icon_path: e.1, + exec: e.2, }) .collect(), config, @@ -161,7 +164,7 @@ impl Application for CosmicAppList { let _ = self.config.remove_favorite(id); } Message::Activate(handle) => { - if let (Some(tx), Some(seat), Some(handle)) = (self.toplevel_sender.as_ref(), self.seat.as_ref(), handle) { + if let (Some(tx), Some(seat)) = (self.toplevel_sender.as_ref(), self.seat.as_ref()) { let _ = tx.send(ToplevelRequest::Activate(handle, seat.clone())); } } @@ -181,13 +184,14 @@ impl Application for CosmicAppList { { self.toplevel_list[i].toplevels.push((handle, info)); } else { - let (app_id, icon_name) = - icon_for_app_ids(vec![info.app_id.clone()]).remove(0); + let (app_id, icon_path, exec) = + icon_exec_for_app_ids(vec![info.app_id.clone()]).remove(0); self.toplevel_list.push(Toplevel { toplevels: vec![(handle, info)], app_id, - icon_path: icon_name, + icon_path, + exec }); // TODO better way of setting window size? let pixel_size = self.applet_helper.suggested_icon_size(); @@ -298,6 +302,20 @@ impl Application for CosmicAppList { Message::RemovedSeat(_) => { self.seat.take(); }, + Message::Exec(exec_str) => { + let mut exec = shlex::Shlex::new(&exec_str); + let mut cmd = match exec.next() { + Some(cmd) if !cmd.contains("=") => tokio::process::Command::new(cmd), + _ => return Command::none(), + }; + for arg in exec { + // TODO handle "%" args here if necessary? + if !arg.starts_with("%") { + cmd.arg(arg); + } + } + let _ = cmd.spawn(); + }, } Command::none() } @@ -315,6 +333,7 @@ impl Application for CosmicAppList { toplevels, app_id, icon_path, + exec, }, )| { let icon = if icon_path.extension() == Some(&OsStr::new("svg")) { @@ -371,7 +390,7 @@ impl Application for CosmicAppList { // TODO tooltip on hover let icon_button = cosmic::widget::button(Button::Text) .custom(vec![icon_wrapper]) - .on_press(Message::Activate(toplevels.first().map(|t| t.0.clone()))) + .on_press(toplevels.first().map(|t| Message::Activate(t.0.clone())).unwrap_or_else(|| Message::Exec(exec.clone()))) .padding(8) .into(); if self.config.favorites.contains(&app_id) {