diff --git a/src/agent.rs b/src/agent.rs index 4c516b7..23a431a 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -1,3 +1,18 @@ -pub fn run_agent() { +use zbus::{blocking, dbus_proxy, Result}; +#[dbus_proxy( + interface = "org.placeholdername.PlaceHolderName", + default_service = "org.placeholdername.PlaceHolderName", + default_path = "/org/placeholdername/PlaceHolderName" +)] +trait DbusInterface { + async fn open_window(&self) -> Result<()>; +} + +pub fn run_agent() { + let connection = blocking::Connection::session().unwrap(); + + let proxy = DbusInterfaceProxyBlocking::new(&connection).unwrap(); + + proxy.open_window().unwrap(); } diff --git a/src/cli.rs b/src/cli.rs index cbe216d..927aab2 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,15 +2,22 @@ use clap::Parser; use crate::agent::run_agent; use crate::server::run_server; -#[derive(clap::Parser)] -enum Cli { - Server, + +#[derive(Debug, clap::Parser)] +struct Cli { + #[command(subcommand)] + command: Option, +} + +#[derive(Debug, clap::Subcommand)] +enum Commands { OpenWindow, } pub fn init() { - match Cli::parse() { - Cli::Server => run_server(), - Cli::OpenWindow => run_agent() + let cli = Cli::parse(); + match &cli.command { + None => run_server(false), + Some(Commands::OpenWindow) => run_agent() }; } diff --git a/src/gtk/gtk_side.rs b/src/gtk/gtk_side.rs index 5553c6b..14b1341 100644 --- a/src/gtk/gtk_side.rs +++ b/src/gtk/gtk_side.rs @@ -4,9 +4,11 @@ use std::rc::Rc; use gtk::glib; use gtk::prelude::*; +use tokio::sync::mpsc::UnboundedReceiver; use crate::gtk::{PluginContainerContainer, PluginEventSenderContainer, PluginUiContext}; use crate::react_side::{PropertyValue, UiEvent, UiEventName, UiRequest, UiRequestData, UiResponseData, UiWidget, UiWidgetId}; +use crate::server::ServerEvent; #[derive(Debug)] pub struct GtkContext { @@ -60,6 +62,21 @@ pub(crate) fn start_request_receiver_loop( } } +pub(crate) fn start_server_event_receiver_loop( + window: gtk::Window, + mut server_event_receiver: UnboundedReceiver, +) { + glib::MainContext::default().spawn_local(async move { + while let Some(event) = server_event_receiver.recv().await { + match event { + ServerEvent::OpenWindow => { + window.set_visible(true); + } + } + } + }); +} + async fn run_request_receiver_loop( ui_context: PluginUiContext, container_container: PluginContainerContainer, diff --git a/src/gtk/gui/mod.rs b/src/gtk/gui/mod.rs index 0b26332..9c76054 100644 --- a/src/gtk/gui/mod.rs +++ b/src/gtk/gui/mod.rs @@ -78,7 +78,7 @@ impl SimpleComponent for AppModel { }, connect_is_active_notify => move |window| { if !window.is_active() { - // window.application().unwrap().quit() + window.set_visible(false); } }, match model.state { @@ -185,7 +185,7 @@ impl SimpleComponent for AppModel { AppMsg::CloseCurrentView => { match &self.state { AppState::SearchView => { - self.window.application().unwrap().quit(); + self.window.set_visible(false); } AppState::PluginView { plugin_id, .. } => { self.event_senders_container.send_event(&plugin_id, UiEvent::ViewDestroyed); diff --git a/src/server.rs b/src/server.rs index 8211d84..9793f18 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,29 +1,44 @@ +use std::cell::Cell; use std::collections::HashMap; use std::process::exit; use std::thread; +use gtk::prelude::{ApplicationExt, ApplicationExtManual, Cast, GtkApplicationExt, WidgetExt}; +use relm4::{Component, ComponentController}; use tokio::runtime::Runtime; +use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use tokio::task::LocalSet; use crate::gtk::{PluginContainerContainer, PluginEventSenderContainer, PluginUiContext, PluginUiData}; -use crate::gtk::gtk_side::start_request_receiver_loop; +use crate::gtk::gtk_side::{start_request_receiver_loop, start_server_event_receiver_loop}; use crate::gtk::gui::{AppInput, AppModel}; use crate::plugins::PluginManager; use crate::react_side::{PluginReactData, run_react}; use crate::search::{SearchIndex, SearchItem}; -struct DbusInterface; +pub enum ServerEvent { + OpenWindow +} + +struct DbusInterface { + server_event_sender: UnboundedSender, +} #[zbus::dbus_interface(name = "org.placeholdername.PlaceHolderName")] impl DbusInterface { + fn open_window(&mut self) { + self.server_event_sender.send(ServerEvent::OpenWindow).unwrap(); + } } -pub fn run_server() { +pub fn run_server(dev: bool) { let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .unwrap(); + let (server_event_sender, server_event_receiver) = tokio::sync::mpsc::unbounded_channel::(); + let mut plugin_manager = PluginManager::create(); let mut search_index = SearchIndex::create_index().unwrap(); @@ -48,7 +63,7 @@ pub fn run_server() { let (react_contexts, ui_contexts) = plugin_manager.create_all_contexts(); let zbus_connection: Result = runtime.block_on(async { - let interface = DbusInterface; + let interface = DbusInterface { server_event_sender }; let conn = zbus::ConnectionBuilder::session()? .name("org.placeholdername.PlaceHolderName")? @@ -67,12 +82,18 @@ pub fn run_server() { exit(1) }); - spawn_gtk_thread(ui_contexts, plugin_manager, search_index); + spawn_gtk_thread(dev, ui_contexts, plugin_manager, search_index, server_event_receiver); run_react_loops(&runtime, react_contexts); } -fn spawn_gtk_thread(ui_data: Vec, plugin_manager: PluginManager, search_index: SearchIndex) { +fn spawn_gtk_thread( + dev: bool, + ui_data: Vec, + plugin_manager: PluginManager, + search_index: SearchIndex, + server_event_receiver: UnboundedReceiver, +) { let handle = move || { let (contexts, event_senders): (Vec<_>, Vec<_>) = ui_data.into_iter() .map(|ui_data| { @@ -92,7 +113,11 @@ fn spawn_gtk_thread(ui_data: Vec, plugin_manager: PluginManager, s let container_container = PluginContainerContainer::new(); let event_senders_container = PluginEventSenderContainer::new(event_senders); - start_request_receiver_loop(ui_contexts, container_container.clone(), event_senders_container.clone()); + start_request_receiver_loop( + ui_contexts, + container_container.clone(), + event_senders_container.clone(), + ); let input = AppInput { search: search_index.create_handle(), @@ -101,8 +126,38 @@ fn spawn_gtk_thread(ui_data: Vec, plugin_manager: PluginManager, s event_senders_container, }; - relm4::RelmApp::from_app(relm4::gtk::Application::builder().build()) - .run::(input); + let application = gtk::Application::builder() + .build(); + + let _ = application.hold(); + + let payload = Cell::new(Some((input, server_event_receiver))); + + application.connect_activate(move |application| { + if let Some((input, server_event_receiver)) = payload.take() { + let mut controller = AppModel::builder() + .launch(input) + .detach(); + + let window = controller.widget() + .clone() + .upcast::(); + + controller.detach_runtime(); + + start_server_event_receiver_loop( + window.clone(), + server_event_receiver, + ); + + application.add_window(&window); + if dev { + window.set_visible(true); + } + } + }); + + application.run(); }; thread::Builder::new()