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:
parent
397323b582
commit
4fd0c82997
6 changed files with 283 additions and 30 deletions
|
|
@ -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
7
src/dbus/mod.rs
Normal 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
99
src/dbus/osk.rs
Normal 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
70
src/dbus/session.rs
Normal 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(())
|
||||||
|
}
|
||||||
41
src/main.rs
41
src/main.rs
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue