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"
|
||||
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"
|
||||
|
|
|
|||
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 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::<Dispatcher>(&conn)
|
||||
let (globals, mut queue) = globals::registry_queue_init::<Dispatcher>(&conn_wl)
|
||||
.expect("Registry required");
|
||||
|
||||
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 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 {
|
||||
queue.flush().unwrap();
|
||||
conn_wl.flush().unwrap();
|
||||
|
||||
let guard = queue.prepare_read().unwrap();
|
||||
let fd = guard.connection_fd();
|
||||
|
|
@ -54,4 +78,7 @@ fn main()
|
|||
|
||||
dispatcher.dispatch_timers();
|
||||
}
|
||||
});
|
||||
|
||||
pending::<()>().await;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Surface<Self>, VirtualKeyboard, Self>,
|
||||
gfx: Arc<Mutex<Graphics<Surface<Self>>>>,
|
||||
osk: Option<InterfaceRef<OSK0<Self>>>,
|
||||
}
|
||||
|
||||
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<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)
|
||||
{
|
||||
self.seat.button_mut().dispatch_timers();
|
||||
|
|
@ -105,14 +121,46 @@ impl Dispatcher {
|
|||
|
||||
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();
|
||||
gfx.display_mut().show();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
gfx.display_mut().hide();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue