From 4fd0c82997f644689e3af0b13f9f49f05bc0f262 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Wed, 24 Jul 2024 22:39:16 -0400 Subject: [PATCH] add dbus osk and session support The D-Bus interfaces used by Phosh are org.gnome.SessionManager for recovering critical services from crashes and sm.puri.OSK0 for manual on-screen keyboard activation. Add support for them. --- Cargo.toml | 2 + src/dbus/mod.rs | 7 +++ src/dbus/osk.rs | 99 +++++++++++++++++++++++++++++++++++++++ src/dbus/session.rs | 70 +++++++++++++++++++++++++++ src/main.rs | 79 +++++++++++++++++++++---------- src/wayland/dispatcher.rs | 56 ++++++++++++++++++++-- 6 files changed, 283 insertions(+), 30 deletions(-) create mode 100644 src/dbus/mod.rs create mode 100644 src/dbus/osk.rs create mode 100644 src/dbus/session.rs diff --git a/Cargo.toml b/Cargo.toml index 1998f76..374d6c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,14 @@ libc = "0.2.155" memmap2 = "0.9.4" polling = "3.7.2" rgb = "0.8.44" +tokio = { version = "1.38.1", features = ["macros", "rt"] } wayland-backend = "0.3.5" wayland-client = "0.31.4" wayland-protocols = { version = "0.32.2", features = ["client", "staging", "unstable"] } wayland-protocols-wlr = { version = "0.3.2", features = ["client"] } wayland-scanner = "0.31.3" xkeysym = "0.2.0" +zbus = { version = "4.4.0", default-features = false, features = ["tokio"] } [build-dependencies] bindgen = "0.69.4" diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs new file mode 100644 index 0000000..2c61dae --- /dev/null +++ b/src/dbus/mod.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Copyright (c) 2024, Richard Acayan. All rights reserved. + */ + +pub mod osk; +pub mod session; diff --git a/src/dbus/osk.rs b/src/dbus/osk.rs new file mode 100644 index 0000000..595c4e6 --- /dev/null +++ b/src/dbus/osk.rs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Copyright (c) 2024, Richard Acayan. All rights reserved. + */ + +use crate::core::Graphics; +use crate::wayland::Surface; +use crate::wayland::fractional_scale_v1::wp_fractional_scale_v1::WpFractionalScaleV1; +use crate::wayland::viewporter::wp_viewport::WpViewport; +use crate::wayland::wlr_layer_shell_unstable_v1::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; +use std::sync::Arc; +use std::sync::Mutex; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; +use wayland_client::Dispatch; +use wayland_client::protocol::wl_buffer::WlBuffer; +use wayland_client::protocol::wl_shm_pool::WlShmPool; +use wayland_client::protocol::wl_surface::WlSurface; +use zbus::Connection; +use zbus::InterfaceRef; +use zbus::Result; +use zbus::SignalContext; +use zbus::interface; + +pub struct OSK0 + + Dispatch + + Dispatch + + Dispatch + + Dispatch + + Dispatch + + 'static> { + visible: AtomicBool, + gfx: Arc>>>, + sigctx: SignalContext<'static>, + conn_wl: Arc, +} + +#[interface(name = "sm.puri.OSK0")] +impl + + Dispatch + + Dispatch + + Dispatch + + Dispatch + + Dispatch + + 'static> OSK0 { + fn get_visible(&self) -> bool + { + self.visible.load(Ordering::Relaxed) + } + + pub async fn set_visible(&self, visible: bool) + { + self.visible.store(visible, Ordering::Relaxed); + self.visible_changed(&self.sigctx).await.unwrap(); + + let mut gfx = self.gfx.lock().unwrap(); + let disp = gfx.display_mut(); + if visible { + disp.show(); + } else { + disp.hide(); + } + + self.conn_wl.flush().unwrap(); + } + + #[zbus(property)] + fn visible(&self) -> bool + { + self.visible.load(Ordering::Relaxed) + } +} + +impl + + Dispatch + + Dispatch + + Dispatch + + Dispatch + + Dispatch + + 'static> OSK0 { + pub async fn start(conn: &Connection, + gfx: Arc>>>, + conn_wl: Arc) + -> Result>> + { + let sigctx = SignalContext::new(conn, "/sm/puri/OSK0")?; + + let visible = AtomicBool::new(false); + let osk = OSK0 { visible, gfx, sigctx, conn_wl }; + + let server = conn.object_server(); + server.at("/sm/puri/OSK0", osk).await?; + conn.request_name("sm.puri.OSK0").await?; + + let osk = server.interface("/sm/puri/OSK0").await?; + + Ok(osk) + } +} diff --git a/src/dbus/session.rs b/src/dbus/session.rs new file mode 100644 index 0000000..2d48651 --- /dev/null +++ b/src/dbus/session.rs @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Copyright (c) 2024, Richard Acayan. All rights reserved. + */ + +use std::convert::TryFrom; +use std::env; +use std::sync::Arc; +use tokio::task; +use zbus::Connection; +use zbus::Result; +use zbus::export::futures_util::stream::StreamExt; +use zbus::fdo::DBusProxy; +use zbus::names::BusName; +use zbus::proxy; +use zbus::zvariant::OwnedObjectPath; + +#[proxy(interface = "org.gnome.SessionManager", + default_service = "org.gnome.SessionManager", + default_path = "/org/gnome/SessionManager")] +trait SessionManager { + fn register_client(&self, app_id: &str, client_startup_id: &str) -> Result; +} + +#[proxy(interface = "org.gnome.SessionManager.ClientPrivate")] +trait ClientPrivate { + fn end_session_response(&self, is_ok: bool, reason: &str) -> Result<()>; + + #[zbus(signal)] + fn query_end_session(&self, flags: u32); +} + +pub async fn register(conn: &Connection) -> Result<()> +{ + let manager = SessionManagerProxy::new(conn).await?; + + let autostart_id = env::var("DESKTOP_AUTOSTART_ID").unwrap_or(String::new()); + let path = manager.register_client("sm.puri.OSK0", &autostart_id).await?; + + let dbus = DBusProxy::new(conn).await?; + let name = BusName::try_from("org.gnome.SessionManager")?; + let id = dbus.get_name_owner(name).await?; + + let client_priv = ClientPrivateProxy::new(conn, id, path).await?; + let client_priv = Arc::new(client_priv); + + { + let client_priv = client_priv.clone(); + let mut stream_qes = client_priv.receive_query_end_session().await?; + + task::spawn(async move { + while stream_qes.next().await.is_some() { + let _ = client_priv.end_session_response(true, "").await; + } + }); + } + + { + let client_priv = client_priv.clone(); + let mut stream_es = client_priv.receive_query_end_session().await?; + + task::spawn(async move { + while stream_es.next().await.is_some() { + let _ = client_priv.end_session_response(true, "").await; + } + }); + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 9079f17..c8e91f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,23 +4,29 @@ */ mod core; +mod dbus; mod wayland; use crate::core::Layout; +use crate::dbus::session; +use crate::dbus::osk::OSK0; use crate::wayland::Dispatcher; use polling::Event; use polling::Events; use polling::Poller; +use std::future::pending; use std::path::Path; +use std::sync::Arc; use std::time::Instant; -use wayland_client::Connection; +use tokio::task; use wayland_client::globals; -fn main() +#[tokio::main(flavor = "current_thread")] +async fn main() { - let conn = Connection::connect_to_env().unwrap(); + let conn_wl = wayland_client::Connection::connect_to_env().unwrap(); - let (globals, mut queue) = globals::registry_queue_init::(&conn) + let (globals, mut queue) = globals::registry_queue_init::(&conn_wl) .expect("Registry required"); let layouts = Path::new("/usr/share/unfettered-keyboard/layouts"); @@ -29,29 +35,50 @@ fn main() let mut dispatcher = Dispatcher::new(layout, queue.handle(), &globals).unwrap(); let wl_evt = Event::readable(0); - let poller = Poller::new().unwrap(); - let mut events = Events::new(); - loop { - queue.flush().unwrap(); + let conn_zbus = zbus::Connection::session().await.unwrap(); - let guard = queue.prepare_read().unwrap(); - let fd = guard.connection_fd(); - let timer = dispatcher.button().next_time().map(|t| t - Instant::now()); - - unsafe { - poller.add(&fd, wl_evt).unwrap(); - } - - events.clear(); - poller.wait(&mut events, timer).unwrap(); - poller.delete(fd).unwrap(); - - if !events.is_empty() { - guard.read().unwrap(); - queue.dispatch_pending(&mut dispatcher).unwrap(); - } - - dispatcher.dispatch_timers(); + let res = session::register(&conn_zbus).await; + if let Err(e) = res { + eprintln!("warn: GNOME session registration failed: {}", e); } + + let conn_wl = Arc::new(conn_wl); + + let gfx = dispatcher.graphics(); + let osk = OSK0::start(&conn_zbus, gfx, conn_wl.clone()).await; + match osk { + Ok(k) => dispatcher.set_osk(k), + Err(e) => eprintln!("warn: bind to sm.puri.OSK0 failed: {}", e), + } + + let mut events = Events::new(); + let poller = Poller::new().unwrap(); + + task::spawn_blocking(move || { + loop { + conn_wl.flush().unwrap(); + + let guard = queue.prepare_read().unwrap(); + let fd = guard.connection_fd(); + let timer = dispatcher.button().next_time().map(|t| t - Instant::now()); + + unsafe { + poller.add(&fd, wl_evt).unwrap(); + } + + events.clear(); + poller.wait(&mut events, timer).unwrap(); + poller.delete(fd).unwrap(); + + if !events.is_empty() { + guard.read().unwrap(); + queue.dispatch_pending(&mut dispatcher).unwrap(); + } + + dispatcher.dispatch_timers(); + } + }); + + pending::<()>().await; } diff --git a/src/wayland/dispatcher.rs b/src/wayland/dispatcher.rs index 1858738..43bb02d 100644 --- a/src/wayland/dispatcher.rs +++ b/src/wayland/dispatcher.rs @@ -7,6 +7,7 @@ use crate::core::Button; use crate::core::Display; use crate::core::Graphics; use crate::core::Layout; +use crate::dbus::osk::OSK0; use crate::wayland::Seat; use crate::wayland::Surface; use crate::wayland::VirtualKeyboard; @@ -23,6 +24,7 @@ use crate::wayland::virtual_keyboard_unstable_v1::zwp_virtual_keyboard_v1; use std::io::Error; use std::sync::Arc; use std::sync::Mutex; +use tokio::task; use wayland_client::Connection; use wayland_client::Dispatch; use wayland_client::Proxy; @@ -39,10 +41,12 @@ use wayland_client::protocol::wl_shm; use wayland_client::protocol::wl_shm_pool; use wayland_client::protocol::wl_surface; use wayland_client::protocol::wl_touch; +use zbus::InterfaceRef; pub struct Dispatcher { seat: Seat, VirtualKeyboard, Self>, gfx: Arc>>>, + osk: Option>>, } impl Dispatcher { @@ -89,6 +93,7 @@ impl Dispatcher { Ok(Dispatcher { seat, gfx, + osk: None, }) } @@ -98,6 +103,17 @@ impl Dispatcher { self.seat.button() } + #[inline(always)] + pub fn graphics(&self) -> Arc>>> + { + self.gfx.clone() + } + + pub fn set_osk(&mut self, osk: InterfaceRef>) + { + self.osk = Some(osk); + } + pub fn dispatch_timers(&mut self) { self.seat.button_mut().dispatch_timers(); @@ -105,14 +121,46 @@ impl Dispatcher { fn show(&self) { - let mut gfx = self.gfx.lock().unwrap(); - gfx.display_mut().show(); + match &self.osk { + Some(osk) => { + /* + * Clone the reference from the struct so it can be given to the + * spawned task. + */ + let osk = osk.clone(); + + task::spawn(async move { + let osk = osk.get().await; + osk.set_visible(true).await; + }); + }, + None => { + let mut gfx = self.gfx.lock().unwrap(); + gfx.display_mut().show(); + }, + } } fn hide(&self) { - let mut gfx = self.gfx.lock().unwrap(); - gfx.display_mut().hide(); + match &self.osk { + Some(osk) => { + /* + * Clone the reference from the struct so it can be given to the + * spawned task. + */ + let osk = osk.clone(); + + task::spawn(async move { + let osk = osk.get().await; + osk.set_visible(false).await; + }); + }, + None => { + let mut gfx = self.gfx.lock().unwrap(); + gfx.display_mut().hide(); + }, + } } }