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.
This commit is contained in:
Richard Acayan 2024-07-24 22:39:16 -04:00
parent 397323b582
commit 4fd0c82997
6 changed files with 283 additions and 30 deletions

View file

@ -13,12 +13,14 @@ libc = "0.2.155"
memmap2 = "0.9.4" memmap2 = "0.9.4"
polling = "3.7.2" polling = "3.7.2"
rgb = "0.8.44" rgb = "0.8.44"
tokio = { version = "1.38.1", features = ["macros", "rt"] }
wayland-backend = "0.3.5" wayland-backend = "0.3.5"
wayland-client = "0.31.4" wayland-client = "0.31.4"
wayland-protocols = { version = "0.32.2", features = ["client", "staging", "unstable"] } wayland-protocols = { version = "0.32.2", features = ["client", "staging", "unstable"] }
wayland-protocols-wlr = { version = "0.3.2", features = ["client"] } wayland-protocols-wlr = { version = "0.3.2", features = ["client"] }
wayland-scanner = "0.31.3" wayland-scanner = "0.31.3"
xkeysym = "0.2.0" xkeysym = "0.2.0"
zbus = { version = "4.4.0", default-features = false, features = ["tokio"] }
[build-dependencies] [build-dependencies]
bindgen = "0.69.4" bindgen = "0.69.4"

7
src/dbus/mod.rs Normal file
View file

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

99
src/dbus/osk.rs Normal file
View file

@ -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<T: Dispatch<WlBuffer, u32>
+ Dispatch<WlShmPool, u32>
+ Dispatch<WlSurface, ()>
+ Dispatch<ZwlrLayerSurfaceV1, ()>
+ Dispatch<WpFractionalScaleV1, ()>
+ Dispatch<WpViewport, ()>
+ 'static> {
visible: AtomicBool,
gfx: Arc<Mutex<Graphics<Surface<T>>>>,
sigctx: SignalContext<'static>,
conn_wl: Arc<wayland_client::Connection>,
}
#[interface(name = "sm.puri.OSK0")]
impl<T: Dispatch<WlBuffer, u32>
+ Dispatch<WlShmPool, u32>
+ Dispatch<WlSurface, ()>
+ Dispatch<ZwlrLayerSurfaceV1, ()>
+ Dispatch<WpFractionalScaleV1, ()>
+ Dispatch<WpViewport, ()>
+ 'static> OSK0<T> {
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<T: Dispatch<WlBuffer, u32>
+ Dispatch<WlShmPool, u32>
+ Dispatch<WlSurface, ()>
+ Dispatch<ZwlrLayerSurfaceV1, ()>
+ Dispatch<WpFractionalScaleV1, ()>
+ Dispatch<WpViewport, ()>
+ 'static> OSK0<T> {
pub async fn start(conn: &Connection,
gfx: Arc<Mutex<Graphics<Surface<T>>>>,
conn_wl: Arc<wayland_client::Connection>)
-> Result<InterfaceRef<OSK0<T>>>
{
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)
}
}

70
src/dbus/session.rs Normal file
View file

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

View file

@ -4,23 +4,29 @@
*/ */
mod core; mod core;
mod dbus;
mod wayland; mod wayland;
use crate::core::Layout; use crate::core::Layout;
use crate::dbus::session;
use crate::dbus::osk::OSK0;
use crate::wayland::Dispatcher; use crate::wayland::Dispatcher;
use polling::Event; use polling::Event;
use polling::Events; use polling::Events;
use polling::Poller; use polling::Poller;
use std::future::pending;
use std::path::Path; use std::path::Path;
use std::sync::Arc;
use std::time::Instant; use std::time::Instant;
use wayland_client::Connection; use tokio::task;
use wayland_client::globals; 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::<Dispatcher>(&conn) let (globals, mut queue) = globals::registry_queue_init::<Dispatcher>(&conn_wl)
.expect("Registry required"); .expect("Registry required");
let layouts = Path::new("/usr/share/unfettered-keyboard/layouts"); let layouts = Path::new("/usr/share/unfettered-keyboard/layouts");
@ -29,11 +35,29 @@ fn main()
let mut dispatcher = Dispatcher::new(layout, queue.handle(), &globals).unwrap(); let mut dispatcher = Dispatcher::new(layout, queue.handle(), &globals).unwrap();
let wl_evt = Event::readable(0); let wl_evt = Event::readable(0);
let poller = Poller::new().unwrap();
let mut events = Events::new();
let conn_zbus = zbus::Connection::session().await.unwrap();
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 { loop {
queue.flush().unwrap(); conn_wl.flush().unwrap();
let guard = queue.prepare_read().unwrap(); let guard = queue.prepare_read().unwrap();
let fd = guard.connection_fd(); let fd = guard.connection_fd();
@ -54,4 +78,7 @@ fn main()
dispatcher.dispatch_timers(); dispatcher.dispatch_timers();
} }
});
pending::<()>().await;
} }

View file

@ -7,6 +7,7 @@ use crate::core::Button;
use crate::core::Display; use crate::core::Display;
use crate::core::Graphics; use crate::core::Graphics;
use crate::core::Layout; use crate::core::Layout;
use crate::dbus::osk::OSK0;
use crate::wayland::Seat; use crate::wayland::Seat;
use crate::wayland::Surface; use crate::wayland::Surface;
use crate::wayland::VirtualKeyboard; 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::io::Error;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use tokio::task;
use wayland_client::Connection; use wayland_client::Connection;
use wayland_client::Dispatch; use wayland_client::Dispatch;
use wayland_client::Proxy; 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_shm_pool;
use wayland_client::protocol::wl_surface; use wayland_client::protocol::wl_surface;
use wayland_client::protocol::wl_touch; use wayland_client::protocol::wl_touch;
use zbus::InterfaceRef;
pub struct Dispatcher { pub struct Dispatcher {
seat: Seat<Surface<Self>, VirtualKeyboard, Self>, seat: Seat<Surface<Self>, VirtualKeyboard, Self>,
gfx: Arc<Mutex<Graphics<Surface<Self>>>>, gfx: Arc<Mutex<Graphics<Surface<Self>>>>,
osk: Option<InterfaceRef<OSK0<Self>>>,
} }
impl Dispatcher { impl Dispatcher {
@ -89,6 +93,7 @@ impl Dispatcher {
Ok(Dispatcher { Ok(Dispatcher {
seat, seat,
gfx, gfx,
osk: None,
}) })
} }
@ -98,6 +103,17 @@ impl Dispatcher {
self.seat.button() self.seat.button()
} }
#[inline(always)]
pub fn graphics(&self) -> Arc<Mutex<Graphics<Surface<Self>>>>
{
self.gfx.clone()
}
pub fn set_osk(&mut self, osk: InterfaceRef<OSK0<Self>>)
{
self.osk = Some(osk);
}
pub fn dispatch_timers(&mut self) pub fn dispatch_timers(&mut self)
{ {
self.seat.button_mut().dispatch_timers(); self.seat.button_mut().dispatch_timers();
@ -105,14 +121,46 @@ impl Dispatcher {
fn show(&self) fn show(&self)
{ {
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(); let mut gfx = self.gfx.lock().unwrap();
gfx.display_mut().show(); gfx.display_mut().show();
},
}
} }
fn hide(&self) fn hide(&self)
{ {
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(); let mut gfx = self.gfx.lock().unwrap();
gfx.display_mut().hide(); gfx.display_mut().hide();
},
}
} }
} }