server: tokio/zbus/etc

This commit is contained in:
Robin Appelman 2023-06-17 18:07:04 +02:00
commit cea82db46b
10 changed files with 1602 additions and 396 deletions

View file

@ -10,9 +10,13 @@ path = "src/server.rs"
[dependencies]
main_error = "0.1.0"
dbus = { version = "0.8.2", path = "../../dbus-rs/dbus" }
glob = "0.3.0"
evdev-shortcut = { version = "0.1", path = "../../evdev-shortcut" }
evdev-shortcut = "0.1.1"
tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"] }
futures = "0.3.28"
zbus = { version = "3.13.1", features = ["tokio"], default-features = false }
tracing = "0.1.37"
tracing-subscriber = "0.3.17"
[dev-dependencies]
test-case = "1.0.0"
test-case = "3.1.0"

View file

@ -1,110 +1,71 @@
use main_error::MainError;
use dbus::blocking::LocalConnection;
use dbus::channel::Sender;
use dbus::tree::{Factory, MethodErr, Signal};
use std::collections::HashMap;
use evdev_shortcut::{Shortcut, ShortcutListener};
use evdev_shortcut::{Shortcut, ShortcutEvent, ShortcutListener, ShortcutState};
use glob::GlobError;
use std::path::PathBuf;
use std::sync::mpsc::{channel, TryRecvError};
use std::sync::Arc;
use std::time::Duration;
use futures::stream::StreamExt;
use zbus::{ConnectionBuilder, dbus_interface, fdo, SignalContext, ObjectServer};
use zbus::export::futures_util::pin_mut;
const INTERFACE: &'static str = "nl.icewind.shortcutd";
struct Register {
listener: ShortcutListener,
}
fn main() {
loop {
let _ = std::panic::catch_unwind(|| {
let _ = run();
});
#[dbus_interface(name = "nl.icewind.shortcutd")]
impl Register {
async fn register(&mut self, shortcut: &str, #[zbus(object_server)] server: &ObjectServer) -> Result<String, fdo::Error> {
match shortcut.parse::<Shortcut>() {
Ok(shortcut) => {
self.listener.add(shortcut.clone());
let path = format!("/{}", shortcut.identifier());
if let Err(e) = server.at(path.as_str(), ShortcutSignal).await {
eprintln!("{e:#}");
}
eprintln!("crashed, restarting");
std::thread::sleep(Duration::from_secs(15));
Ok(path)
}
Err(_) => Err(fdo::Error::InvalidArgs("Malformed shortcut".into())),
}
}
}
fn run() -> Result<(), MainError> {
struct ShortcutSignal;
#[dbus_interface(name = "nl.icewind.shortcutd")]
impl ShortcutSignal {
#[dbus_interface(signal)]
async fn triggered(signal_ctxt: &SignalContext<'_>, pressed: bool) -> zbus::Result<()>;
}
#[tokio::main]
async fn main() -> Result<(), MainError> {
tracing_subscriber::fmt::init();
let devices =
glob::glob("/dev/input/by-id/*-kbd")?.collect::<Result<Vec<PathBuf>, GlobError>>()?;
let listener = Arc::new(ShortcutListener::new());
let listener = ShortcutListener::new();
let shortcut_events = listener.listen(&devices)?;
let mut signals: HashMap<String, Arc<Signal<()>>> = HashMap::default();
let bus = Register {
listener,
};
let conn = ConnectionBuilder::system()?
.name("nl.icewind.shortcutd")?
.serve_at("/register", bus)?
.build()
.await?;
let shortcut_trigger_rx = listener.listen(&devices)?;
let server = conn.object_server();
let mut connection = LocalConnection::new_system()?;
connection.request_name(INTERFACE, false, true, false)?;
let (shortcut_register_tx, shortcut_register_rx) = channel();
let factory = Factory::new_fn::<()>();
let mut tree = factory
.tree(())
.add(
factory.object_path("/register", ()).introspectable().add(
factory.interface(INTERFACE, ()).add_m(
factory
.method("Register", (), move |m| {
let shortcut_str: &str = m.msg.read1()?;
match shortcut_str.parse::<Shortcut>() {
Ok(shortcut) => {
let path = shortcut.identifier();
shortcut_register_tx.send(shortcut).unwrap();
Ok(vec![m.msg.method_return().append1(format!("/{}", path))])
}
Err(_) => Err(MethodErr::invalid_arg("Malformed shortcut")),
}
})
.outarg::<&str, _>("path")
.inarg::<&str, _>("shortcut"),
),
),
)
.add(factory.object_path("/", ()).introspectable());
// Serve clients forever.
loop {
connection.process_with_tree(&tree, Duration::from_millis(50))?;
while let Ok(shortcut) = shortcut_register_rx.try_recv() {
let identifier = format!("/{}", shortcut.identifier());
listener.add(shortcut);
let signal = Arc::new(factory.signal("Triggered", ()));
signals.insert(identifier.clone(), signal.clone());
tree = tree.add(
factory
.object_path(identifier, ())
.introspectable()
.add(factory.interface(INTERFACE, ()).add_s(signal)),
);
}
match shortcut_trigger_rx.try_recv() {
Ok(shortcut) => {
let identifier = format!("/{}", shortcut.identifier());
if let Some(signal) = signals.get(&identifier) {
connection
.send(
signal
.clone()
.msg(&identifier.into(), &INTERFACE.into())
.append1(&format!("{}", shortcut)),
)
.unwrap();
}
pin_mut!(shortcut_events);
while let Some(event) = shortcut_events.next().await {
let event: ShortcutEvent = event;
let identifier = format!("/{}", event.shortcut.identifier());
if let Ok(signal_interface) = server.interface::<_, ShortcutSignal>(identifier).await {
if let Err(e) = ShortcutSignal::triggered(signal_interface.signal_context(), event.state == ShortcutState::Pressed).await {
eprintln!("{e:#}");
}
Err(TryRecvError::Disconnected) => panic!("keyboard listener crashed"),
_ => {}
}
}
Ok(())
}