diff --git a/README.md b/README.md index b0713e8..76d3358 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Pop Launcher +[![rustc 1.47.0](https://img.shields.io)](https://img.shields.io/badge/rustc-1.47-orange) + Modular IPC-based desktop launcher service, written in Rust. Desktop launchers may interface with this service via spawning the pop-launcher process and communicating to it via JSON IPC over the stdin and stdout pipes. The launcher service will also spawn plugins found in plugin directories on demand, based on the queries sent to the service. Using IPC enables each plugin to isolate their data from other plugin processes and frontends that are interacting with them. If a plugin crashes, the launcher will continue functioning normally, gracefully cleaning up after the crashed process. Frontends and plugins may also be written in any language. The pop-launcher will do its part to schedule the execution of these plugins in parallel, on demand. diff --git a/service/src/lib.rs b/service/src/lib.rs index 9f0fa73..0812941 100644 --- a/service/src/lib.rs +++ b/service/src/lib.rs @@ -75,8 +75,8 @@ impl Service { let name = String::from(config.name.as_ref()); - self.register_plugin(service_tx.clone(), config, regex, move |tx| { - ExternalPlugin::new(name.clone(), exec.clone(), Vec::new(), tx) + self.register_plugin(service_tx.clone(), config, regex, move |id, tx| { + ExternalPlugin::new(id, name.clone(), exec.clone(), Vec::new(), tx) }); } }; @@ -90,7 +90,7 @@ impl Service { service_tx.clone(), plugins::help::CONFIG, Some(Regex::new(plugins::help::REGEX.as_ref()).expect("failed to compile help regex")), - move |tx| HelpPlugin::new(internal.clone(), tx), + move |id, tx| HelpPlugin::new(id, internal.clone(), tx), ); let f1 = request_handler(service_tx); @@ -154,15 +154,13 @@ impl Service { } } - fn register_plugin) -> P + Send + Sync + 'static>( + fn register_plugin) -> P + Send + Sync + 'static>( &mut self, service_tx: Sender, config: PluginConfig, regex: Option, init: I, ) { - let (plugin_tx, plugin_rx) = unbounded(); - let entry = self.plugins.vacant_entry(); let id = entry.key(); @@ -175,16 +173,9 @@ impl Service { let (request_tx, request_rx) = unbounded(); let init = init.clone(); - let plugin_tx = plugin_tx.clone(); - let plugin_rx = plugin_rx.clone(); let service_tx = service_tx.clone(); smol::spawn(async move { - let mut plugin = init(plugin_tx); - - let f1 = plugin.run(request_rx); - let f2 = plugin_forwarder(id, plugin_rx, service_tx); - - future::zip(f1, f2).await; + init(id, service_tx).run(request_rx).await; }) .detach(); @@ -200,6 +191,7 @@ impl Service { } fn append(&mut self, plugin: PluginKey, append: PluginSearchResult) { + eprintln!("appending {:?}", append); self.active_search.push((plugin, append)); } @@ -229,6 +221,8 @@ impl Service { return; } + eprintln!("updating with {:?}", self.active_search); + let search_list = self.sort(); self.respond(&Response::Update(search_list)) } @@ -461,18 +455,6 @@ impl Service { } } -async fn plugin_forwarder( - plugin_id: PluginKey, - receiver: Receiver, - forwarder: Sender, -) { - while let Ok(response) = receiver.recv_async().await { - let _ = forwarder.send(Event::Response((plugin_id, response))); - } - - let _ = forwarder.send(Event::PluginExit(plugin_id)); -} - /// Handles Requests received from a frontend async fn request_handler(tx: Sender) { let mut requested_to_exit = false; diff --git a/service/src/plugins/external/mod.rs b/service/src/plugins/external/mod.rs index dae7495..9803d51 100644 --- a/service/src/plugins/external/mod.rs +++ b/service/src/plugins/external/mod.rs @@ -9,7 +9,7 @@ use std::{ }, }; -use crate::{Plugin, PluginResponse, Request}; +use crate::{Event, Plugin, PluginResponse, Request}; use async_oneshot::oneshot; use flume::Sender; use futures_lite::{AsyncWriteExt, FutureExt, StreamExt}; @@ -20,7 +20,8 @@ use smol::{ use tracing::{event, Level}; pub struct ExternalPlugin { - tx: Sender, + id: usize, + tx: Sender, name: String, pub cmd: PathBuf, pub args: Vec, @@ -30,8 +31,15 @@ pub struct ExternalPlugin { } impl ExternalPlugin { - pub fn new(name: String, cmd: PathBuf, args: Vec, tx: Sender) -> Self { + pub fn new( + id: usize, + name: String, + cmd: PathBuf, + args: Vec, + tx: Sender, + ) -> Self { Self { + id, name, tx, cmd, @@ -60,6 +68,7 @@ impl ExternalPlugin { let (trip_tx, trip_rx) = oneshot::<()>(); let tx = self.tx.clone(); let name = self.name().to_owned(); + let id = self.id; // Spawn a background task to forward JSON responses from the child process. let task = smol::spawn(async move { @@ -79,7 +88,7 @@ impl ExternalPlugin { } tracing::debug!("{}: responding with {:?}", name_, response); - let _ = tx_.send(response); + let _ = tx_.send(Event::Response((id, response))); } Err(why) => { event!(Level::ERROR, "{}: serde error: {:?}", name_, why); @@ -98,7 +107,7 @@ impl ExternalPlugin { // Ensure that a task that was searching sends a finished signal if it dies. if searching.swap(false, Ordering::SeqCst) { - let _ = tx.send(PluginResponse::Finished); + let _ = tx.send(Event::Response((id, PluginResponse::Finished))); } detached.store(true, Ordering::SeqCst); @@ -183,7 +192,10 @@ impl Plugin for ExternalPlugin { if self.query(&Request::Search(query.to_owned())).await.is_ok() { self.searching.store(true, Ordering::SeqCst); } else { - let _ = self.tx.send_async(PluginResponse::Finished).await; + let _ = self + .tx + .send_async(Event::Response((self.id, PluginResponse::Finished))) + .await; } } async fn quit(&mut self, id: u32) { diff --git a/service/src/plugins/help.rs b/service/src/plugins/help.rs index 29acc6c..d819254 100644 --- a/service/src/plugins/help.rs +++ b/service/src/plugins/help.rs @@ -20,14 +20,16 @@ pub const CONFIG: PluginConfig = PluginConfig { icon: Some(IconSource::Name(Cow::Borrowed("system-help-symbolic"))), }; pub struct HelpPlugin { + pub id: usize, pub details: Slab, pub internal: Sender, - pub tx: Sender, + pub tx: Sender, } impl HelpPlugin { - pub fn new(internal: Sender, tx: Sender) -> Self { + pub fn new(id: usize, internal: Sender, tx: Sender) -> Self { Self { + id, details: Slab::new(), internal, tx, @@ -46,7 +48,13 @@ impl Plugin for HelpPlugin { async fn activate(&mut self, id: u32) { if let Some(detail) = self.details.get(id as usize) { if let Some(help) = detail.help.as_ref() { - let _ = self.tx.send_async(PluginResponse::Fill(help.clone())).await; + let _ = self + .tx + .send_async(Event::Response(( + self.id, + PluginResponse::Fill(help.clone()), + ))) + .await; } } } @@ -69,19 +77,24 @@ impl Plugin for HelpPlugin { } for (id, detail) in self.details.iter() { if detail.help.is_some() { + let response = PluginResponse::Append(PluginSearchResult { + id: id as u32, + name: detail.name.clone(), + description: detail.description.clone(), + ..Default::default() + }); + let _ = self .tx - .send_async(PluginResponse::Append(PluginSearchResult { - id: id as u32, - name: detail.name.clone(), - description: detail.description.clone(), - ..Default::default() - })) + .send_async(Event::Response((self.id, response))) .await; } } - let _ = self.tx.send_async(PluginResponse::Finished).await; + let _ = self + .tx + .send_async(Event::Response((self.id, PluginResponse::Finished))) + .await; } async fn quit(&mut self, _id: u32) {}