mirror of
https://codeberg.org/icewind/shortcutd.git
synced 2026-06-04 01:34:08 +02:00
dbus deamon wip
This commit is contained in:
parent
8feb933041
commit
107c21b6a5
5 changed files with 851 additions and 559 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1,20 +1,27 @@
|
|||
use evdev::Device;
|
||||
pub use keycodes::Key;
|
||||
use parse_display::{Display, FromStr, ParseError};
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Display};
|
||||
use std::str::FromStr;
|
||||
use std::sync::mpsc::{channel, Receiver};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
mod keycodes;
|
||||
|
||||
pub use keycodes::Key;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Display, FromStr)]
|
||||
pub enum Modifier {
|
||||
AnyAlt,
|
||||
Alt,
|
||||
LeftAlt,
|
||||
RightAlt,
|
||||
AnyCtrl,
|
||||
Ctrl,
|
||||
LeftCtrl,
|
||||
RightCtrl,
|
||||
AnyShift,
|
||||
Shift,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
AnyMeta,
|
||||
Meta,
|
||||
LeftMeta,
|
||||
RightMeta,
|
||||
}
|
||||
|
|
@ -22,31 +29,93 @@ pub enum Modifier {
|
|||
impl Modifier {
|
||||
pub fn is_modifier(&self, key: Key) -> bool {
|
||||
match self {
|
||||
Modifier::AnyAlt => key == Key::KeyLeftAlt || key == Key::KeyRightAlt,
|
||||
Modifier::Alt => key == Key::KeyLeftAlt || key == Key::KeyRightAlt,
|
||||
Modifier::LeftAlt => key == Key::KeyLeftAlt,
|
||||
Modifier::RightAlt => key == Key::KeyRightAlt,
|
||||
Modifier::AnyCtrl => key == Key::KeyLeftCtrl || key == Key::KeyRightCtrl,
|
||||
Modifier::Ctrl => key == Key::KeyLeftCtrl || key == Key::KeyRightCtrl,
|
||||
Modifier::LeftCtrl => key == Key::KeyLeftCtrl,
|
||||
Modifier::RightCtrl => key == Key::KeyRightCtrl,
|
||||
Modifier::AnyMeta => key == Key::KeyLeftMeta || key == Key::KeyRightMeta,
|
||||
Modifier::Meta => key == Key::KeyLeftMeta || key == Key::KeyRightMeta,
|
||||
Modifier::LeftMeta => key == Key::KeyLeftMeta,
|
||||
Modifier::RightMeta => key == Key::KeyRightMeta,
|
||||
Modifier::AnyShift => key == Key::KeyLeftshift || key == Key::KeyRightshift,
|
||||
Modifier::Shift => key == Key::KeyLeftshift || key == Key::KeyRightshift,
|
||||
Modifier::LeftShift => key == Key::KeyLeftshift,
|
||||
Modifier::RightShift => key == Key::KeyRightshift,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ModifierList(Vec<Modifier>);
|
||||
|
||||
impl Display for ModifierList {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for modifier in self.0.iter() {
|
||||
write!(f, "<{}>", modifier)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ModifierList {
|
||||
type Err = ParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(ModifierList(
|
||||
s.split('>')
|
||||
.filter(|part| !part.is_empty())
|
||||
.map(|part| {
|
||||
if !part.starts_with('<') {
|
||||
Err(ParseError::with_message("Invalid modifier"))
|
||||
} else {
|
||||
Ok(part[1..].parse::<Modifier>()?)
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<Modifier>, ParseError>>()?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Display, FromStr)]
|
||||
#[display("{modifiers}-{key}")]
|
||||
pub struct Shortcut {
|
||||
pub modifiers: Vec<Modifier>,
|
||||
pub key: Key,
|
||||
modifiers: ModifierList,
|
||||
key: Key,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shortcut_format() {
|
||||
assert_eq!(
|
||||
"<Ctrl>-KeyP",
|
||||
format!("{}", Shortcut::new(vec![Modifier::Ctrl], Key::KeyP))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"<LeftCtrl><LeftAlt>-KeyLeft",
|
||||
format!(
|
||||
"{}",
|
||||
Shortcut::new(vec![Modifier::LeftCtrl, Modifier::LeftAlt], Key::KeyLeft)
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Shortcut::from_str("<LeftCtrl><LeftAlt>-KeyLeft").unwrap(),
|
||||
Shortcut::new(vec![Modifier::LeftCtrl, Modifier::LeftAlt], Key::KeyLeft)
|
||||
);
|
||||
}
|
||||
|
||||
impl Shortcut {
|
||||
pub fn new(modifiers: Vec<Modifier>, key: Key) -> Self {
|
||||
Shortcut {
|
||||
modifiers: ModifierList(modifiers),
|
||||
key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Shortcut {
|
||||
pub fn is_triggered(&self, active_keys: &HashSet<Key>) -> bool {
|
||||
for modifier in &self.modifiers {
|
||||
for modifier in &self.modifiers.0 {
|
||||
if active_keys.iter().any(|key| modifier.is_modifier(*key)) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -56,4 +125,60 @@ impl Shortcut {
|
|||
|
||||
active_keys.contains(&self.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShortcutListener {
|
||||
shortcuts: Arc<Mutex<HashSet<Shortcut>>>,
|
||||
}
|
||||
|
||||
impl ShortcutListener {
|
||||
pub fn new() -> Self {
|
||||
ShortcutListener {
|
||||
shortcuts: Arc::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listen(&self, mut device: Device) -> Receiver<Shortcut> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let shortcuts = self.shortcuts.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut active_keys = HashSet::new();
|
||||
|
||||
loop {
|
||||
let mut got_event = false;
|
||||
|
||||
for ev in device.events().unwrap() {
|
||||
got_event = true;
|
||||
|
||||
if let Ok(key) = Key::try_from(ev.code) {
|
||||
match ev.value {
|
||||
1 => active_keys.insert(key),
|
||||
0 => active_keys.remove(&key),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if got_event {
|
||||
for shortcut in shortcuts.lock().unwrap().iter() {
|
||||
if shortcut.is_triggered(&active_keys) {
|
||||
tx.send(shortcut.clone()).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rx
|
||||
}
|
||||
|
||||
pub fn add(&self, shortcut: Shortcut) {
|
||||
self.shortcuts.lock().unwrap().insert(shortcut);
|
||||
}
|
||||
|
||||
pub fn remove(&self, shortcut: Shortcut) {
|
||||
self.shortcuts.lock().unwrap().remove(&shortcut);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
112
src/main.rs
112
src/main.rs
|
|
@ -1,45 +1,109 @@
|
|||
use crate::keyboard::{Key, Modifier, Shortcut, ShortcutListener};
|
||||
use evdev::Device;
|
||||
use main_error::MainError;
|
||||
use std::collections::HashSet;
|
||||
use crate::keyboard::{Key, Shortcut, Modifier};
|
||||
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 std::time::Duration;
|
||||
|
||||
mod keyboard;
|
||||
|
||||
fn main() -> Result<(), MainError> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let mut device = if args.len() > 1 {
|
||||
let device = if args.len() > 1 {
|
||||
Device::open(&args[1])?
|
||||
} else {
|
||||
eprintln!("Usage {} </dev/input/...>", args[0]);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut keys = HashSet::new();
|
||||
let shortcut = Shortcut {
|
||||
modifiers: vec![Modifier::AnyCtrl, Modifier::AnyCtrl],
|
||||
key: Key::KeyP
|
||||
};
|
||||
// let shortcut = Shortcut::new(vec![Modifier::Ctrl, Modifier::Ctrl], Key::KeyP);
|
||||
|
||||
let listener = ShortcutListener::new();
|
||||
// listener.add(shortcut);
|
||||
|
||||
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)?;
|
||||
|
||||
// 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::<()>();
|
||||
|
||||
// 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();
|
||||
|
||||
// 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.
|
||||
|
||||
let name: &str = m.msg.read1()?;
|
||||
match Shortcut::from_str(name) {
|
||||
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])
|
||||
|
||||
// Our method has one output argument and one input argument.
|
||||
})
|
||||
.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.
|
||||
),
|
||||
)
|
||||
.add(f.object_path("/", ()).introspectable());
|
||||
|
||||
// We add the tree to the connection so that incoming method calls will be handled.
|
||||
tree.start_receive(&connection);
|
||||
|
||||
// Serve clients forever.
|
||||
loop {
|
||||
let mut got_event = false;
|
||||
connection.process(Duration::from_millis(50))?;
|
||||
|
||||
for ev in device.events()? {
|
||||
got_event = true;
|
||||
|
||||
if let Ok(key) = Key::try_from(ev.code) {
|
||||
match ev.value {
|
||||
1 => keys.insert(key),
|
||||
0 => keys.remove(&key),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if got_event {
|
||||
if shortcut.is_triggered(&keys) {
|
||||
println!("triggered");
|
||||
}
|
||||
while let Ok(shortcut) = rx.try_recv() {
|
||||
connection
|
||||
.send(
|
||||
signal3
|
||||
.msg(&"/shortcut".into(), &"nl.icewind.shortcutd".into())
|
||||
.append1(&format!("{}", shortcut)),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue