mirror of
https://codeberg.org/icewind/shortcutd.git
synced 2026-06-03 09:14:07 +02:00
dynamic per shortcut signals
This commit is contained in:
parent
f7c7953464
commit
f5095b25c8
2 changed files with 121 additions and 75 deletions
|
|
@ -126,6 +126,13 @@ impl Shortcut {
|
|||
key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identifier(&self) -> String {
|
||||
self.to_string()
|
||||
.replace('<', "")
|
||||
.replace('>', "")
|
||||
.replace('-', "_")
|
||||
}
|
||||
}
|
||||
|
||||
impl Shortcut {
|
||||
|
|
@ -145,8 +152,8 @@ impl Shortcut {
|
|||
|
||||
#[cfg(test)]
|
||||
mod triggered_tests {
|
||||
use crate::keyboard::{Key, Modifier, Shortcut};
|
||||
use std::collections::HashSet;
|
||||
use crate::keyboard::{Key, Shortcut};
|
||||
|
||||
use test_case::test_case;
|
||||
|
||||
#[test_case("<Ctrl>-KeyP", & [] => false)]
|
||||
|
|
|
|||
179
src/main.rs
179
src/main.rs
|
|
@ -1,19 +1,67 @@
|
|||
use crate::keyboard::{Key, Modifier, Shortcut, ShortcutListener};
|
||||
use crate::keyboard::{Shortcut, ShortcutListener};
|
||||
use evdev::Device;
|
||||
use main_error::MainError;
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use dbus::blocking::LocalConnection;
|
||||
use dbus::channel::Sender;
|
||||
use dbus::tree::{Factory, MethodErr};
|
||||
use std::error::Error;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use dbus::tree::{DataType, Factory, MethodErr, MethodType, Signal, Tree};
|
||||
use dbus::Message;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
mod keyboard;
|
||||
|
||||
const INTERFACE: &'static str = "nl.icewind.shortcutd";
|
||||
|
||||
struct MutateTree<M: MethodType<D>, D: DataType> {
|
||||
factory: Factory<M, D>,
|
||||
tree: Arc<Mutex<Option<Tree<M, D>>>>,
|
||||
}
|
||||
|
||||
impl<M: MethodType<D> + 'static, D: DataType + 'static> MutateTree<M, D> {
|
||||
fn factory(&self) -> &Factory<M, D> {
|
||||
&self.factory
|
||||
}
|
||||
|
||||
fn mutate_tree(&self, f: impl FnOnce(&Factory<M, D>, Tree<M, D>) -> Tree<M, D>) {
|
||||
let tree = self.tree.lock().unwrap().take().unwrap();
|
||||
self.tree.lock().unwrap().replace(f(&self.factory, tree));
|
||||
}
|
||||
|
||||
pub fn start_receive<C>(&self, connection: &C)
|
||||
where
|
||||
C: dbus::channel::MatchingReceiver<F = Box<dyn FnMut(Message, &C) -> bool>>
|
||||
+ dbus::channel::Sender,
|
||||
{
|
||||
let tree = self.tree.clone();
|
||||
connection.start_receive(
|
||||
dbus::message::MatchRule::new_method_call(),
|
||||
Box::new(move |msg, c| {
|
||||
let tree_opt = tree.lock().unwrap();
|
||||
let tree = tree_opt.as_ref().unwrap();
|
||||
if let Some(replies) = tree.handle(&msg) {
|
||||
for r in replies {
|
||||
let _ = c.send(r);
|
||||
}
|
||||
}
|
||||
true
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn new(factory: Factory<M, D>, data: D::Tree) -> Self {
|
||||
let tree = factory.tree(data);
|
||||
MutateTree {
|
||||
factory,
|
||||
tree: Arc::new(Mutex::new(Some(tree))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), MainError> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let device = if args.len() > 1 {
|
||||
|
|
@ -23,89 +71,80 @@ fn main() -> Result<(), MainError> {
|
|||
return Ok(());
|
||||
};
|
||||
|
||||
// let shortcut = Shortcut::new(vec![Modifier::Ctrl, Modifier::Ctrl], Key::KeyP);
|
||||
let listener = Arc::new(ShortcutListener::new());
|
||||
|
||||
let listener = ShortcutListener::new();
|
||||
// listener.add(shortcut);
|
||||
let mut signals: HashMap<String, Arc<Signal<()>>> = HashMap::default();
|
||||
|
||||
let rx = listener.listen(device);
|
||||
|
||||
// Let's start by starting up a connection to the session bus and request a name.
|
||||
let mut connection: LocalConnection = LocalConnection::new_session()?;
|
||||
connection.request_name("nl.icewind.shortcutd", false, true, false)?;
|
||||
let mut connection = LocalConnection::new_session()?;
|
||||
connection.request_name(INTERFACE, false, true, false)?;
|
||||
|
||||
// The choice of factory tells us what type of tree we want,
|
||||
// and if we want any extra data inside. We pick the simplest variant.
|
||||
let f = Factory::new_fn::<()>();
|
||||
let mtree = Arc::new(MutateTree::new(Factory::new_fn::<()>(), ()));
|
||||
|
||||
// We create the signal first, since we'll need it in both inside the method callback
|
||||
// and when creating the tree.
|
||||
let signal = Arc::new(f.signal("ShortcutTriggered", ()).sarg::<&str, _>("sender"));
|
||||
let signal2 = signal.clone();
|
||||
let signal3 = signal.clone();
|
||||
let (new_shortcut_tx, new_shortcut_rx) = channel();
|
||||
|
||||
// We create a tree with one object path inside and make that path introspectable.
|
||||
let tree = f
|
||||
.tree(())
|
||||
.add(
|
||||
f.object_path("/shortcut", ()).introspectable().add(
|
||||
// We add an interface to the object path...
|
||||
f.interface("nl.icewind.shortcutd", ())
|
||||
.add_m(
|
||||
// ...and a method inside the interface.
|
||||
f.method("Register", (), move |m| {
|
||||
// This is the callback that will be called when another peer on the bus calls our method.
|
||||
// the callback receives "MethodInfo" struct and can return either an error, or a list of
|
||||
// messages to send back.
|
||||
mtree.mutate_tree(move |factory, tree| {
|
||||
let new_shortcut_tx = new_shortcut_tx.clone();
|
||||
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()?;
|
||||
|
||||
let name: &str = m.msg.read1()?;
|
||||
match Shortcut::from_str(name) {
|
||||
match shortcut_str.parse::<Shortcut>() {
|
||||
Ok(shortcut) => {
|
||||
let s = format!("Ok: {}", shortcut);
|
||||
listener.add(shortcut);
|
||||
let mret = m.msg.method_return().append1(s);
|
||||
Ok(vec![mret])
|
||||
}
|
||||
Err(_) => Err(MethodErr::invalid_arg("Failed to parse shortcut")),
|
||||
}
|
||||
// let s = format!("Hello {}!", name);
|
||||
// let mret = m.msg.method_return().append1(s);
|
||||
//
|
||||
// // let sig = signal
|
||||
// // .msg(m.path.get_name(), m.iface.get_name())
|
||||
// // .append1(&*name);
|
||||
//
|
||||
// // Two messages will be returned - one is the method return (and should always be there),
|
||||
// // and in our case we also have a signal we want to send at the same time.
|
||||
// Ok(vec![mret, sig])
|
||||
listener.add(shortcut.clone());
|
||||
let path = shortcut.identifier();
|
||||
new_shortcut_tx.send(shortcut).unwrap();
|
||||
|
||||
// Our method has one output argument and one input argument.
|
||||
Ok(vec![m.msg.method_return().append1(format!("/{}", path))])
|
||||
}
|
||||
Err(_) => Err(MethodErr::invalid_arg("Malformed shortcut")),
|
||||
}
|
||||
})
|
||||
.outarg::<&str, _>("reply")
|
||||
.inarg::<&str, _>("name"), // We also add the signal to the interface. This is mainly for introspection.
|
||||
)
|
||||
.add_s(signal2), // Also add the root path, to help introspection from debugging tools.
|
||||
.outarg::<&str, _>("path")
|
||||
.inarg::<&str, _>("shortcut"),
|
||||
),
|
||||
),
|
||||
)
|
||||
.add(f.object_path("/", ()).introspectable());
|
||||
.add(factory.object_path("/", ()).introspectable())
|
||||
});
|
||||
|
||||
// We add the tree to the connection so that incoming method calls will be handled.
|
||||
tree.start_receive(&connection);
|
||||
mtree.start_receive(&connection);
|
||||
|
||||
// Serve clients forever.
|
||||
loop {
|
||||
connection.process(Duration::from_millis(50))?;
|
||||
|
||||
while let Ok(shortcut) = rx.try_recv() {
|
||||
connection
|
||||
.send(
|
||||
signal3
|
||||
.msg(&"/shortcut".into(), &"nl.icewind.shortcutd".into())
|
||||
.append1(&format!("{}", shortcut)),
|
||||
while let Ok(shortcut) = new_shortcut_rx.try_recv() {
|
||||
let identifier = format!("/{}", shortcut.identifier());
|
||||
let signal = Arc::new(mtree.factory().signal("Triggered", ()));
|
||||
signals.insert(identifier.clone(), signal.clone());
|
||||
|
||||
mtree.mutate_tree(move |factory, tree| {
|
||||
tree.add(
|
||||
factory
|
||||
.object_path(identifier, ())
|
||||
.introspectable()
|
||||
.add(factory.interface(INTERFACE, ()).add_s(signal)),
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
while let Ok(shortcut) = rx.try_recv() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue