Main application as service. Window hidden by default, shown using dbus

This commit is contained in:
Exidex 2023-08-22 21:56:47 +02:00
parent 97d154f8b4
commit 7b93ed6d49
5 changed files with 112 additions and 18 deletions

View file

@ -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();
}

View file

@ -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<Commands>,
}
#[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()
};
}

View file

@ -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<ServerEvent>,
) {
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,

View file

@ -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);

View file

@ -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<ServerEvent>,
}
#[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::<ServerEvent>();
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<zbus::Connection, zbus::Error> = 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<PluginUiData>, plugin_manager: PluginManager, search_index: SearchIndex) {
fn spawn_gtk_thread(
dev: bool,
ui_data: Vec<PluginUiData>,
plugin_manager: PluginManager,
search_index: SearchIndex,
server_event_receiver: UnboundedReceiver<ServerEvent>,
) {
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<PluginUiData>, 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<PluginUiData>, plugin_manager: PluginManager, s
event_senders_container,
};
relm4::RelmApp::from_app(relm4::gtk::Application::builder().build())
.run::<AppModel>(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::<gtk::Window>();
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()