From 36fde2527037407fc694a2d7075984b73da9a844 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 25 Aug 2023 17:08:35 +0800 Subject: [PATCH 1/8] release: Update udev rules path Signed-off-by: Daniel Schaefer --- release/postinstall.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/postinstall.sh b/release/postinstall.sh index 4df223d..6c98c81 100755 --- a/release/postinstall.sh +++ b/release/postinstall.sh @@ -1,3 +1,3 @@ #!/bin/bash -cp -u ./ledmatrix/50-framework.rules /etc/udev/rules.d/ +cp -u ./release/50-framework-inputmodule.rules /etc/udev/rules.d/ From 9840ad3fe7b315d3553a49b5ee32e75faafffaa7 Mon Sep 17 00:00:00 2001 From: Zach Feldman Date: Sun, 27 Aug 2023 10:06:39 -0400 Subject: [PATCH 2/8] Initial commit with working implementation --- Cargo.lock | 31 +++++++ Cargo.toml | 1 + dbus-monitor/Cargo.toml | 16 ++++ dbus-monitor/src/dbus_monitor.rs | 120 +++++++++++++++++++++++++ dbus-monitor/src/main.rs | 6 ++ dbus-monitor/src/utils.rs | 27 ++++++ inputmodule-control/src/commands.rs | 50 +++++++++++ inputmodule-control/src/inputmodule.rs | 11 +-- inputmodule-control/src/lib.rs | 9 ++ inputmodule-control/src/main.rs | 49 +--------- 10 files changed, 270 insertions(+), 50 deletions(-) create mode 100644 dbus-monitor/Cargo.toml create mode 100644 dbus-monitor/src/dbus_monitor.rs create mode 100644 dbus-monitor/src/main.rs create mode 100644 dbus-monitor/src/utils.rs create mode 100644 inputmodule-control/src/commands.rs create mode 100644 inputmodule-control/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0066341..8b8327b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -606,6 +606,17 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + [[package]] name = "debug-helper" version = "0.3.13" @@ -820,6 +831,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "framework-inputmodule-dbus-monitor" +version = "0.0.1" +dependencies = [ + "clap", + "dbus", + "inputmodule-control", + "libdbus-sys", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -1128,6 +1149,16 @@ version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "libloading" version = "0.7.4" diff --git a/Cargo.toml b/Cargo.toml index e86f3e3..f4eada3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "ledmatrix", "fl16-inputmodules", "inputmodule-control", + "dbus-monitor" ] # Don't build all of them by default. # Because that'll lead to all features enabled in `fl16-inputmodules` and it diff --git a/dbus-monitor/Cargo.toml b/dbus-monitor/Cargo.toml new file mode 100644 index 0000000..4849a80 --- /dev/null +++ b/dbus-monitor/Cargo.toml @@ -0,0 +1,16 @@ +[package] +edition = "2021" +name = "framework-inputmodule-dbus-monitor" +version = "0.0.1" + +[dependencies] +dbus = { version = "0.9.7", features = ["vendored"] } +clap = { version = "4.0", features = ["derive"] } + +[dependencies.libdbus-sys] +default-features = false +features = ["vendored"] +version = "0.2.5" + +[dependencies.inputmodule-control] +path = "../inputmodule-control" \ No newline at end of file diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs new file mode 100644 index 0000000..811cb1e --- /dev/null +++ b/dbus-monitor/src/dbus_monitor.rs @@ -0,0 +1,120 @@ +// Mostly taken from https://github.com/diwic/dbus-rs/blob/366a6dca3d20745f5dcfa006b1b1311c376d420e/dbus/examples/monitor.rs + +// This programs implements the equivalent of running the "dbus-monitor" tool +// modified to only search for messages in the org.freedesktop.Notifications interface +use dbus::blocking::Connection; +use dbus::channel::MatchingReceiver; +use dbus::message::MatchRule; + +use dbus::Message; +use dbus::MessageType; + +use std::process::Command; +use std::time::Duration; + +use crate::utils; + +use inputmodule_control::inputmodule::find_serialdevs; +use inputmodule_control::commands::ClapCli; +use inputmodule_control::inputmodule::{serial_commands}; +use clap::{Parser, Subcommand}; + +fn handle_message(msg: &Message) { + println!("Got message from DBus: {:?}", msg); + + run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blinking"]); + // Can't seem to get to this second command + run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]); + + println!("Message handled"); +} + +pub fn run_inputmodule_command(args: Vec<&str>){ + let bin_placeholder = vec!["bin-placeholder"]; + let full_args = [&bin_placeholder[..], &args[..]].concat(); + let args = ClapCli::parse_from(full_args); + + serial_commands(&args); +} + +pub fn run_dbus_monitor() { + // First open up a connection to the desired bus. + let conn = Connection::new_session().expect("D-Bus connection failed"); + println!("Connection to DBus session monitor opened"); + + // Second create a rule to match messages we want to receive; in this example we add no + // further requirements, so all messages will match + let rule = MatchRule::new() + .with_type(MessageType::MethodCall) + .with_interface("org.freedesktop.Notifications") + .with_member("Notify"); + + // Try matching using new scheme + let proxy = conn.with_proxy( + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + Duration::from_millis(5000), + ); + let result: Result<(), dbus::Error> = proxy.method_call( + "org.freedesktop.DBus.Monitoring", + "BecomeMonitor", + (vec![rule.match_str()], 0u32), + ); + println!("Monitoring DBus channel..."); + + match result { + // BecomeMonitor was successful, start listening for messages + Ok(_) => { + conn.start_receive( + rule, + Box::new(|msg, _| { + println!("Start listening"); + handle_message(&msg); + true + }), + ); + } + // BecomeMonitor failed, fallback to using the old scheme + Err(e) => { + eprintln!( + "Failed to BecomeMonitor: '{}', falling back to eavesdrop", + e + ); + + // First, we'll try "eavesdrop", which as the name implies lets us receive + // *all* messages, not just ours. + let rule_with_eavesdrop = { + let mut rule = rule.clone(); + rule.eavesdrop = true; + rule + }; + + let result = conn.add_match(rule_with_eavesdrop, |_: (), _, msg| { + handle_message(&msg); + true + }); + + match result { + Ok(_) => { + // success, we're now listening + } + // This can sometimes fail, for example when listening to the system bus as a non-root user. + // So, just like `dbus-monitor`, we attempt to fallback without `eavesdrop=true`: + Err(e) => { + eprintln!("Failed to eavesdrop: '{}', trying without it", e); + conn.add_match(rule, |_: (), _, msg| { + handle_message(&msg); + true + }) + .expect("add_match failed"); + } + } + } + } + + // Loop and print out all messages received (using handle_message()) as they come. + // Some can be quite large, e.g. if they contain embedded images.. + loop { + conn.process(Duration::from_millis(1000)).unwrap(); + } +} diff --git a/dbus-monitor/src/main.rs b/dbus-monitor/src/main.rs new file mode 100644 index 0000000..ddcfa2a --- /dev/null +++ b/dbus-monitor/src/main.rs @@ -0,0 +1,6 @@ +mod dbus_monitor; +mod utils; + +fn main() { + dbus_monitor::run_dbus_monitor(); +} diff --git a/dbus-monitor/src/utils.rs b/dbus-monitor/src/utils.rs new file mode 100644 index 0000000..93d4914 --- /dev/null +++ b/dbus-monitor/src/utils.rs @@ -0,0 +1,27 @@ +use std::process::Child; +use std::process::Command; +use std::time::Duration; +use std::time::Instant; + +pub fn run_command_with_timeout( + command: &str, + timeout_seconds: u64, +) -> Result> { + let mut child_process = Command::new("bash").arg("-c").arg(command).spawn()?; + + let start_time = Instant::now(); + while start_time.elapsed() < Duration::from_secs(timeout_seconds) { + if let Some(exit_status) = child_process.try_wait()? { + println!( + "Command finished before the specified duration. Exit status: {:?}", + exit_status + ); + return Ok(child_process); + } + } + + child_process.kill()?; // Attempt to kill the process + + println!("Command terminated after {} seconds", timeout_seconds); + Ok(child_process) +} diff --git a/inputmodule-control/src/commands.rs b/inputmodule-control/src/commands.rs new file mode 100644 index 0000000..fa122aa --- /dev/null +++ b/inputmodule-control/src/commands.rs @@ -0,0 +1,50 @@ +#![allow(clippy::needless_range_loop)] +#![allow(clippy::single_match)] + +use crate::inputmodule::{B1_LCD_PID, LED_MATRIX_PID}; +use crate::b1display::B1DisplaySubcommand; +use crate::c1minimal::C1MinimalSubcommand; +use crate::ledmatrix::LedMatrixSubcommand; + +use clap::{Parser, Subcommand}; + +#[derive(Subcommand, Debug)] +pub enum Commands { + LedMatrix(LedMatrixSubcommand), + B1Display(B1DisplaySubcommand), + C1Minimal(C1MinimalSubcommand), +} + +impl Commands { + pub fn to_pid(&self) -> u16 { + match self { + Self::LedMatrix(_) => LED_MATRIX_PID, + Self::B1Display(_) => B1_LCD_PID, + Self::C1Minimal(_) => 0x22, + } + } +} + +/// RAW HID and VIA commandline for QMK devices +#[derive(Parser, Debug)] +#[command(version, arg_required_else_help = true)] +pub struct ClapCli { + #[command(subcommand)] + pub command: Option, + + /// List connected HID devices + #[arg(short, long)] + pub list: bool, + + /// Verbose outputs to the console + #[arg(short, long)] + pub verbose: bool, + + /// Serial device, like /dev/ttyACM0 or COM0 + #[arg(long)] + pub serial_dev: Option, + + /// Retry connecting to the device until it works + #[arg(long)] + pub wait_for_device: bool, +} diff --git a/inputmodule-control/src/inputmodule.rs b/inputmodule-control/src/inputmodule.rs index 25baa57..81457fc 100644 --- a/inputmodule-control/src/inputmodule.rs +++ b/inputmodule-control/src/inputmodule.rs @@ -12,6 +12,7 @@ use crate::b1display::{B1Pattern, Fps, PowerMode}; use crate::c1minimal::Color; use crate::font::{convert_font, convert_symbol}; use crate::ledmatrix::{Game, GameOfLifeStartParam, Pattern}; +use crate::commands::ClapCli; const FWK_MAGIC: &[u8] = &[0x32, 0xAC]; pub const FRAMEWORK_VID: u16 = 0x32AC; @@ -99,7 +100,7 @@ fn match_serialdevs( } } -pub fn find_serialdevs(args: &crate::ClapCli, wait_for_device: bool) -> (Vec, bool) { +pub fn find_serialdevs(args: &ClapCli, wait_for_device: bool) -> (Vec, bool) { let mut serialdevs: Vec; let mut waited = false; loop { @@ -151,7 +152,7 @@ pub fn find_serialdevs(args: &crate::ClapCli, wait_for_device: bool) -> (Vec, bool) = find_serialdevs(args, args.wait_for_device); if serialdevs.is_empty() { println!("Failed to find serial devivce. Please manually specify with --serial-dev"); @@ -164,7 +165,7 @@ pub fn serial_commands(args: &crate::ClapCli) { match &args.command { // TODO: Handle generic commands without code deduplication - Some(crate::Commands::LedMatrix(ledmatrix_args)) => { + Some(crate::commands::Commands::LedMatrix(ledmatrix_args)) => { for serialdev in &serialdevs { if args.verbose { println!("Selected serialdev: {:?}", serialdev); @@ -262,7 +263,7 @@ pub fn serial_commands(args: &crate::ClapCli) { clock_cmd(&serialdevs); } } - Some(crate::Commands::B1Display(b1display_args)) => { + Some(crate::commands::Commands::B1Display(b1display_args)) => { for serialdev in &serialdevs { if args.verbose { println!("Selected serialdev: {:?}", serialdev); @@ -312,7 +313,7 @@ pub fn serial_commands(args: &crate::ClapCli) { } } } - Some(crate::Commands::C1Minimal(c1minimal_args)) => { + Some(crate::commands::Commands::C1Minimal(c1minimal_args)) => { for serialdev in &serialdevs { if args.verbose { println!("Selected serialdev: {:?}", serialdev); diff --git a/inputmodule-control/src/lib.rs b/inputmodule-control/src/lib.rs new file mode 100644 index 0000000..a421c53 --- /dev/null +++ b/inputmodule-control/src/lib.rs @@ -0,0 +1,9 @@ +#![allow(clippy::needless_range_loop)] + +mod b1display; +mod c1minimal; +mod font; +pub mod inputmodule; +mod ledmatrix; +pub mod commands; + diff --git a/inputmodule-control/src/main.rs b/inputmodule-control/src/main.rs index 8348ad7..561233d 100644 --- a/inputmodule-control/src/main.rs +++ b/inputmodule-control/src/main.rs @@ -5,55 +5,14 @@ mod c1minimal; mod font; mod inputmodule; mod ledmatrix; +mod commands; -use clap::{Parser, Subcommand}; +use clap::{Parser}; use inputmodule::find_serialdevs; -use crate::b1display::B1DisplaySubcommand; -use crate::c1minimal::C1MinimalSubcommand; -use crate::inputmodule::{serial_commands, B1_LCD_PID, LED_MATRIX_PID}; -use crate::ledmatrix::LedMatrixSubcommand; +use crate::inputmodule::serial_commands; -#[derive(Subcommand, Debug)] -enum Commands { - LedMatrix(LedMatrixSubcommand), - B1Display(B1DisplaySubcommand), - C1Minimal(C1MinimalSubcommand), -} - -impl Commands { - pub fn to_pid(&self) -> u16 { - match self { - Self::LedMatrix(_) => LED_MATRIX_PID, - Self::B1Display(_) => B1_LCD_PID, - Self::C1Minimal(_) => 0x22, - } - } -} - -/// RAW HID and VIA commandline for QMK devices -#[derive(Parser, Debug)] -#[command(version, arg_required_else_help = true)] -pub struct ClapCli { - #[command(subcommand)] - command: Option, - - /// List connected HID devices - #[arg(short, long)] - list: bool, - - /// Verbose outputs to the console - #[arg(short, long)] - verbose: bool, - - /// Serial device, like /dev/ttyACM0 or COM0 - #[arg(long)] - pub serial_dev: Option, - - /// Retry connecting to the device until it works - #[arg(long)] - wait_for_device: bool, -} +use crate::commands::ClapCli; fn main() { let args: Vec = std::env::args().collect(); From 357946c8e0c9cb5a9a73b89dbdd00c64ed3de1cb Mon Sep 17 00:00:00 2001 From: Zach Feldman Date: Mon, 9 Oct 2023 12:50:16 -0400 Subject: [PATCH 3/8] Bring up to date with main --- Cargo.lock | 4 +- Cargo.toml | 2 +- inputmodule-dbus-monitor/Cargo.toml | 7 +++ inputmodule-dbus-monitor/src/main.rs | 82 ++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 inputmodule-dbus-monitor/Cargo.toml create mode 100644 inputmodule-dbus-monitor/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 8b8327b..de8676a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1042,8 +1042,8 @@ dependencies = [ [[package]] name = "is31fl3741" -version = "0.2.1" -source = "git+https://github.com/FrameworkComputer/is31fl3741-rs?branch=sw-enablement#fb88ad1baf28aa2a61bc85ff5db83c6e2b661ed5" +version = "0.2.2" +source = "git+https://github.com/FrameworkComputer/is31fl3741-rs?branch=is31fl3743a#1814f6a838560c7553cda34cdad4692dc5da2d5f" dependencies = [ "embedded-graphics-core", "embedded-hal", diff --git a/Cargo.toml b/Cargo.toml index f4eada3..840ff91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ usbd-serial = "0.1.1" usbd-hid = "0.6.1" fugit = "0.3.7" # LED Matrix -is31fl3741 = { git = "https://github.com/FrameworkComputer/is31fl3741-rs", branch = "sw-enablement" } +is31fl3741 = { git = "https://github.com/FrameworkComputer/is31fl3741-rs", branch = "is31fl3743a" } # B1 Display st7306 = { git = "https://github.com/FrameworkComputer/st7306-rs", branch = "update-deps" } embedded-graphics = "0.8" diff --git a/inputmodule-dbus-monitor/Cargo.toml b/inputmodule-dbus-monitor/Cargo.toml new file mode 100644 index 0000000..541bb31 --- /dev/null +++ b/inputmodule-dbus-monitor/Cargo.toml @@ -0,0 +1,7 @@ +[package] +edition = "2021" +name = "inputmodule-dbus-monitor" +version = "0.0.1" + +[dependencies] +dbus = { version = "0.9.7", features = ["vendored"] } diff --git a/inputmodule-dbus-monitor/src/main.rs b/inputmodule-dbus-monitor/src/main.rs new file mode 100644 index 0000000..882c612 --- /dev/null +++ b/inputmodule-dbus-monitor/src/main.rs @@ -0,0 +1,82 @@ +// Source: https://github.com/diwic/dbus-rs/blob/master/dbus/examples/monitor.rs + +use std::time::Duration; + +use dbus::blocking::Connection; +use dbus::channel::MatchingReceiver; +use dbus::message::MatchRule; +use dbus::Message; + +// This programs implements the equivalent of running the "dbus-monitor" tool +fn main() { + // Very simple argument parsing. + let use_system_bus = std::env::args().into_iter().any(|a| a == "--system"); + + // First open up a connection to the desired bus. + let conn = (if use_system_bus { Connection::new_system() } else { Connection::new_session() }).expect("D-Bus connection failed"); + + // Second create a rule to match messages we want to receive; in this example we add no + // further requirements, so all messages will match + let rule = MatchRule::new(); + + // Try matching using new scheme + let proxy = conn.with_proxy("org.freedesktop.DBus", "/org/freedesktop/DBus", Duration::from_millis(5000)); + let result: Result<(), dbus::Error> = + proxy.method_call("org.freedesktop.DBus.Monitoring", "BecomeMonitor", (vec![rule.match_str()], 0u32)); + + match result { + // BecomeMonitor was successful, start listening for messages + Ok(_) => { + conn.start_receive( + rule, + Box::new(|msg, _| { + handle_message(&msg); + true + }), + ); + } + // BecomeMonitor failed, fallback to using the old scheme + Err(e) => { + eprintln!("Failed to BecomeMonitor: '{}', falling back to eavesdrop", e); + + // First, we'll try "eavesdrop", which as the name implies lets us receive + // *all* messages, not just ours. + let rule_with_eavesdrop = { + let mut rule = rule.clone(); + rule.eavesdrop = true; + rule + }; + + let result = conn.add_match(rule_with_eavesdrop, |_: (), _, msg| { + handle_message(&msg); + true + }); + + match result { + Ok(_) => { + // success, we're now listening + } + // This can sometimes fail, for example when listening to the system bus as a non-root user. + // So, just like `dbus-monitor`, we attempt to fallback without `eavesdrop=true`: + Err(e) => { + eprintln!("Failed to eavesdrop: '{}', trying without it", e); + conn.add_match(rule, |_: (), _, msg| { + handle_message(&msg); + true + }) + .expect("add_match failed"); + } + } + } + } + + // Loop and print out all messages received (using handle_message()) as they come. + // Some can be quite large, e.g. if they contain embedded images.. + loop { + conn.process(Duration::from_millis(1000)).unwrap(); + } +} + +fn handle_message(msg: &Message) { + println!("Got message: {:?}", msg); +} \ No newline at end of file From a81697f701c4546cb268672eee83fb9b1e41973b Mon Sep 17 00:00:00 2001 From: Zach Feldman Date: Mon, 9 Oct 2023 13:22:02 -0400 Subject: [PATCH 4/8] Add new blink n times command and use it --- dbus-monitor/src/dbus_monitor.rs | 3 +-- inputmodule-control/src/inputmodule.rs | 13 +++++++++++++ inputmodule-control/src/ledmatrix.rs | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs index 811cb1e..14bae2f 100644 --- a/dbus-monitor/src/dbus_monitor.rs +++ b/dbus-monitor/src/dbus_monitor.rs @@ -22,8 +22,7 @@ use clap::{Parser, Subcommand}; fn handle_message(msg: &Message) { println!("Got message from DBus: {:?}", msg); - run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blinking"]); - // Can't seem to get to this second command + run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]); run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]); println!("Message handled"); diff --git a/inputmodule-control/src/inputmodule.rs b/inputmodule-control/src/inputmodule.rs index 81457fc..0c2e003 100644 --- a/inputmodule-control/src/inputmodule.rs +++ b/inputmodule-control/src/inputmodule.rs @@ -190,6 +190,9 @@ pub fn serial_commands(args: &ClapCli) { if let Some(pattern) = ledmatrix_args.pattern { pattern_cmd(serialdev, pattern); } + if let Some(blink_n_times_arg) = ledmatrix_args.blink_n_times { + blink_n_cmd(&serialdevs, blink_n_times_arg); + } if ledmatrix_args.all_brightnesses { all_brightnesses_cmd(serialdev); } @@ -556,6 +559,16 @@ fn blinking_cmd(serialdevs: &Vec) { } } +fn blink_n_cmd(serialdevs: &Vec, blink_n_times: u8) { + let duration = Duration::from_millis(500); + for _ in 0..blink_n_times { + simple_cmd_multiple(serialdevs, Command::Brightness, &[0]); + thread::sleep(duration); + simple_cmd_multiple(serialdevs, Command::Brightness, &[200]); + thread::sleep(duration); + } +} + fn breathing_cmd(serialdevs: &Vec) { loop { // Go quickly from 250 to 50 diff --git a/inputmodule-control/src/ledmatrix.rs b/inputmodule-control/src/ledmatrix.rs index b5f5ac1..01e0afb 100644 --- a/inputmodule-control/src/ledmatrix.rs +++ b/inputmodule-control/src/ledmatrix.rs @@ -70,6 +70,10 @@ pub struct LedMatrixSubcommand { #[arg(long)] pub blinking: bool, + /// Blink the current pattern once a second, n times + #[arg(long)] + pub blink_n_times: Option, + /// Breathing brightness of the current pattern #[arg(long)] pub breathing: bool, From fe52e15301f83d9329533cef0f31a03a6361770e Mon Sep 17 00:00:00 2001 From: Zach Feldman Date: Mon, 9 Oct 2023 15:04:12 -0400 Subject: [PATCH 5/8] Basic string scanning functionality --- dbus-monitor/src/dbus_monitor.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs index 14bae2f..dc41d21 100644 --- a/dbus-monitor/src/dbus_monitor.rs +++ b/dbus-monitor/src/dbus_monitor.rs @@ -21,9 +21,22 @@ use clap::{Parser, Subcommand}; fn handle_message(msg: &Message) { println!("Got message from DBus: {:?}", msg); - - run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]); - run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]); + let mut iter = msg.iter_init(); + while let Some(arg) = iter.get_refarg() { + // Extract the inner value as a string and convert it to a String + if let Some(string_ref) = arg.as_str() { + let string_value: String = string_ref.to_string(); + println!("String value: {}", string_value); + + if string_value.contains("calendar.google.com"){ + run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]); + run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]); + } + } else { + println!("Not a string."); + } + iter.next(); + } println!("Message handled"); } From 978fa837f559fa2e12adf7875c5c47964c63602a Mon Sep 17 00:00:00 2001 From: Zach Feldman Date: Mon, 9 Oct 2023 15:04:31 -0400 Subject: [PATCH 6/8] Switch to debug! macro for quieter logs by default --- Cargo.lock | 2 ++ dbus-monitor/Cargo.toml | 2 ++ dbus-monitor/src/dbus_monitor.rs | 22 +++++++++++----------- dbus-monitor/src/main.rs | 5 +++++ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de8676a..6a3a0e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -837,8 +837,10 @@ version = "0.0.1" dependencies = [ "clap", "dbus", + "env_logger", "inputmodule-control", "libdbus-sys", + "log", ] [[package]] diff --git a/dbus-monitor/Cargo.toml b/dbus-monitor/Cargo.toml index 4849a80..9f09a53 100644 --- a/dbus-monitor/Cargo.toml +++ b/dbus-monitor/Cargo.toml @@ -6,6 +6,8 @@ version = "0.0.1" [dependencies] dbus = { version = "0.9.7", features = ["vendored"] } clap = { version = "4.0", features = ["derive"] } +log = "0.4" +env_logger = "0.10.0" [dependencies.libdbus-sys] default-features = false diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs index dc41d21..072fb5b 100644 --- a/dbus-monitor/src/dbus_monitor.rs +++ b/dbus-monitor/src/dbus_monitor.rs @@ -19,26 +19,26 @@ use inputmodule_control::commands::ClapCli; use inputmodule_control::inputmodule::{serial_commands}; use clap::{Parser, Subcommand}; +use log::debug; + fn handle_message(msg: &Message) { - println!("Got message from DBus: {:?}", msg); + debug!("Got message from DBus: {:?}", msg); + let mut iter = msg.iter_init(); while let Some(arg) = iter.get_refarg() { - // Extract the inner value as a string and convert it to a String if let Some(string_ref) = arg.as_str() { let string_value: String = string_ref.to_string(); - println!("String value: {}", string_value); + debug!("String value: {}", string_value); if string_value.contains("calendar.google.com"){ run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]); run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]); } - } else { - println!("Not a string."); } iter.next(); } - println!("Message handled"); + debug!("DBus Message handled"); } pub fn run_inputmodule_command(args: Vec<&str>){ @@ -52,7 +52,7 @@ pub fn run_inputmodule_command(args: Vec<&str>){ pub fn run_dbus_monitor() { // First open up a connection to the desired bus. let conn = Connection::new_session().expect("D-Bus connection failed"); - println!("Connection to DBus session monitor opened"); + debug!("Connection to DBus session monitor opened"); // Second create a rule to match messages we want to receive; in this example we add no // further requirements, so all messages will match @@ -72,7 +72,7 @@ pub fn run_dbus_monitor() { "BecomeMonitor", (vec![rule.match_str()], 0u32), ); - println!("Monitoring DBus channel..."); + debug!("Monitoring DBus channel..."); match result { // BecomeMonitor was successful, start listening for messages @@ -80,7 +80,7 @@ pub fn run_dbus_monitor() { conn.start_receive( rule, Box::new(|msg, _| { - println!("Start listening"); + debug!("Start listening"); handle_message(&msg); true }), @@ -88,7 +88,7 @@ pub fn run_dbus_monitor() { } // BecomeMonitor failed, fallback to using the old scheme Err(e) => { - eprintln!( + debug!( "Failed to BecomeMonitor: '{}', falling back to eavesdrop", e ); @@ -113,7 +113,7 @@ pub fn run_dbus_monitor() { // This can sometimes fail, for example when listening to the system bus as a non-root user. // So, just like `dbus-monitor`, we attempt to fallback without `eavesdrop=true`: Err(e) => { - eprintln!("Failed to eavesdrop: '{}', trying without it", e); + debug!("Failed to eavesdrop: '{}', trying without it", e); conn.add_match(rule, |_: (), _, msg| { handle_message(&msg); true diff --git a/dbus-monitor/src/main.rs b/dbus-monitor/src/main.rs index ddcfa2a..6406885 100644 --- a/dbus-monitor/src/main.rs +++ b/dbus-monitor/src/main.rs @@ -1,6 +1,11 @@ mod dbus_monitor; mod utils; +extern crate log; +use log::{debug}; +use env_logger; + fn main() { + env_logger::init(); dbus_monitor::run_dbus_monitor(); } From 82bf7d407be3fe4c00b4b8fa8b843442472ab4d6 Mon Sep 17 00:00:00 2001 From: Zach Feldman Date: Mon, 9 Oct 2023 15:06:43 -0400 Subject: [PATCH 7/8] RM unused modules, cargo clippy, cargo fmt --- dbus-monitor/src/dbus_monitor.rs | 22 +++++++++++---------- dbus-monitor/src/main.rs | 2 -- dbus-monitor/src/utils.rs | 27 -------------------------- inputmodule-control/src/commands.rs | 2 +- inputmodule-control/src/inputmodule.rs | 2 +- inputmodule-control/src/lib.rs | 3 +-- inputmodule-control/src/main.rs | 4 ++-- 7 files changed, 17 insertions(+), 45 deletions(-) delete mode 100644 dbus-monitor/src/utils.rs diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs index 072fb5b..271bcac 100644 --- a/dbus-monitor/src/dbus_monitor.rs +++ b/dbus-monitor/src/dbus_monitor.rs @@ -9,15 +9,11 @@ use dbus::message::MatchRule; use dbus::Message; use dbus::MessageType; -use std::process::Command; use std::time::Duration; -use crate::utils; - -use inputmodule_control::inputmodule::find_serialdevs; +use clap::Parser; use inputmodule_control::commands::ClapCli; -use inputmodule_control::inputmodule::{serial_commands}; -use clap::{Parser, Subcommand}; +use inputmodule_control::inputmodule::serial_commands; use log::debug; @@ -30,8 +26,14 @@ fn handle_message(msg: &Message) { let string_value: String = string_ref.to_string(); debug!("String value: {}", string_value); - if string_value.contains("calendar.google.com"){ - run_inputmodule_command(vec!["led-matrix", "--pattern", "all-on", "--blink-n-times", "3"]); + if string_value.contains("calendar.google.com") { + run_inputmodule_command(vec![ + "led-matrix", + "--pattern", + "all-on", + "--blink-n-times", + "3", + ]); run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]); } } @@ -41,11 +43,11 @@ fn handle_message(msg: &Message) { debug!("DBus Message handled"); } -pub fn run_inputmodule_command(args: Vec<&str>){ +pub fn run_inputmodule_command(args: Vec<&str>) { let bin_placeholder = vec!["bin-placeholder"]; let full_args = [&bin_placeholder[..], &args[..]].concat(); let args = ClapCli::parse_from(full_args); - + serial_commands(&args); } diff --git a/dbus-monitor/src/main.rs b/dbus-monitor/src/main.rs index 6406885..aa4bcc6 100644 --- a/dbus-monitor/src/main.rs +++ b/dbus-monitor/src/main.rs @@ -1,8 +1,6 @@ mod dbus_monitor; -mod utils; extern crate log; -use log::{debug}; use env_logger; fn main() { diff --git a/dbus-monitor/src/utils.rs b/dbus-monitor/src/utils.rs deleted file mode 100644 index 93d4914..0000000 --- a/dbus-monitor/src/utils.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::process::Child; -use std::process::Command; -use std::time::Duration; -use std::time::Instant; - -pub fn run_command_with_timeout( - command: &str, - timeout_seconds: u64, -) -> Result> { - let mut child_process = Command::new("bash").arg("-c").arg(command).spawn()?; - - let start_time = Instant::now(); - while start_time.elapsed() < Duration::from_secs(timeout_seconds) { - if let Some(exit_status) = child_process.try_wait()? { - println!( - "Command finished before the specified duration. Exit status: {:?}", - exit_status - ); - return Ok(child_process); - } - } - - child_process.kill()?; // Attempt to kill the process - - println!("Command terminated after {} seconds", timeout_seconds); - Ok(child_process) -} diff --git a/inputmodule-control/src/commands.rs b/inputmodule-control/src/commands.rs index fa122aa..c959c5d 100644 --- a/inputmodule-control/src/commands.rs +++ b/inputmodule-control/src/commands.rs @@ -1,9 +1,9 @@ #![allow(clippy::needless_range_loop)] #![allow(clippy::single_match)] -use crate::inputmodule::{B1_LCD_PID, LED_MATRIX_PID}; use crate::b1display::B1DisplaySubcommand; use crate::c1minimal::C1MinimalSubcommand; +use crate::inputmodule::{B1_LCD_PID, LED_MATRIX_PID}; use crate::ledmatrix::LedMatrixSubcommand; use clap::{Parser, Subcommand}; diff --git a/inputmodule-control/src/inputmodule.rs b/inputmodule-control/src/inputmodule.rs index 0c2e003..2ca8c8e 100644 --- a/inputmodule-control/src/inputmodule.rs +++ b/inputmodule-control/src/inputmodule.rs @@ -10,9 +10,9 @@ use serialport::{SerialPort, SerialPortInfo, SerialPortType}; use crate::b1display::{B1Pattern, Fps, PowerMode}; use crate::c1minimal::Color; +use crate::commands::ClapCli; use crate::font::{convert_font, convert_symbol}; use crate::ledmatrix::{Game, GameOfLifeStartParam, Pattern}; -use crate::commands::ClapCli; const FWK_MAGIC: &[u8] = &[0x32, 0xAC]; pub const FRAMEWORK_VID: u16 = 0x32AC; diff --git a/inputmodule-control/src/lib.rs b/inputmodule-control/src/lib.rs index a421c53..698e610 100644 --- a/inputmodule-control/src/lib.rs +++ b/inputmodule-control/src/lib.rs @@ -2,8 +2,7 @@ mod b1display; mod c1minimal; +pub mod commands; mod font; pub mod inputmodule; mod ledmatrix; -pub mod commands; - diff --git a/inputmodule-control/src/main.rs b/inputmodule-control/src/main.rs index 561233d..d8d9dc6 100644 --- a/inputmodule-control/src/main.rs +++ b/inputmodule-control/src/main.rs @@ -2,12 +2,12 @@ #![allow(clippy::single_match)] mod b1display; mod c1minimal; +mod commands; mod font; mod inputmodule; mod ledmatrix; -mod commands; -use clap::{Parser}; +use clap::Parser; use inputmodule::find_serialdevs; use crate::inputmodule::serial_commands; From 8d04dff630eacaf0317290e0c32e4bb21bdcb671 Mon Sep 17 00:00:00 2001 From: Zach Feldman Date: Mon, 9 Oct 2023 15:46:58 -0400 Subject: [PATCH 8/8] Add configuration file --- Cargo.lock | 63 +++++++++++++++++++++++++------- dbus-monitor/Cargo.toml | 5 ++- dbus-monitor/src/config.json | 10 +++++ dbus-monitor/src/dbus_monitor.rs | 59 +++++++++++++++++++++++------- 4 files changed, 109 insertions(+), 28 deletions(-) create mode 100644 dbus-monitor/src/config.json diff --git a/Cargo.lock b/Cargo.lock index 6a3a0e5..5dbaf95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,16 +95,15 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] @@ -134,9 +133,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys", @@ -390,33 +389,31 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.3" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", "clap_derive", - "once_cell 1.18.0", ] [[package]] name = "clap_builder" -version = "4.3.3" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", - "bitflags 1.3.2", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", @@ -839,8 +836,11 @@ dependencies = [ "dbus", "env_logger", "inputmodule-control", + "lazy_static", "libdbus-sys", "log", + "serde", + "serde_json", ] [[package]] @@ -1060,6 +1060,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + [[package]] name = "jni" version = "0.19.0" @@ -1989,6 +1995,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + [[package]] name = "same-file" version = "1.0.6" @@ -2036,6 +2048,31 @@ name = "serde" version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "serde_json" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" +dependencies = [ + "itoa", + "ryu", + "serde", +] [[package]] name = "serialport" diff --git a/dbus-monitor/Cargo.toml b/dbus-monitor/Cargo.toml index 9f09a53..ae43a17 100644 --- a/dbus-monitor/Cargo.toml +++ b/dbus-monitor/Cargo.toml @@ -5,9 +5,12 @@ version = "0.0.1" [dependencies] dbus = { version = "0.9.7", features = ["vendored"] } -clap = { version = "4.0", features = ["derive"] } +clap = { version = "4.4.6", features = ["derive"] } log = "0.4" env_logger = "0.10.0" +serde = { version="1.0", features = ["derive"] } +serde_json = "1.0" +lazy_static = "1.4" [dependencies.libdbus-sys] default-features = false diff --git a/dbus-monitor/src/config.json b/dbus-monitor/src/config.json new file mode 100644 index 0000000..f838111 --- /dev/null +++ b/dbus-monitor/src/config.json @@ -0,0 +1,10 @@ +{ + "dbus_interface": "org.freedesktop.Notifications", + "dbus_member": "Notify", + "scan_args_for": "calendar.google.com", + "run_inputmodule_commands": [ + "led-matrix --pattern all-on --blink-n-times 3", + "led-matrix --brightness 0" + ] +} + diff --git a/dbus-monitor/src/dbus_monitor.rs b/dbus-monitor/src/dbus_monitor.rs index 271bcac..05212a4 100644 --- a/dbus-monitor/src/dbus_monitor.rs +++ b/dbus-monitor/src/dbus_monitor.rs @@ -1,7 +1,9 @@ // Mostly taken from https://github.com/diwic/dbus-rs/blob/366a6dca3d20745f5dcfa006b1b1311c376d420e/dbus/examples/monitor.rs // This programs implements the equivalent of running the "dbus-monitor" tool -// modified to only search for messages in the org.freedesktop.Notifications interface +// modified to only search for messages in the interface specificied in config.json, +// and then run arbitary inputmodule-rs commands to react to them + use dbus::blocking::Connection; use dbus::channel::MatchingReceiver; use dbus::message::MatchRule; @@ -17,6 +19,40 @@ use inputmodule_control::inputmodule::serial_commands; use log::debug; +use serde::{Deserialize, Serialize}; +use serde_json; +use std::fs::File; +use std::io::Read; + +use lazy_static::lazy_static; + +#[derive(Debug, Deserialize, Serialize)] +pub struct Config { + dbus_interface: String, + dbus_member: String, + scan_args_for: String, + run_inputmodule_commands: Vec, +} + +fn read_config(file_path: &str) -> Result> { + let mut file = File::open(file_path)?; + let mut config_data = String::new(); + file.read_to_string(&mut config_data)?; + + let config: Config = serde_json::from_str(&config_data)?; + + Ok(config) +} + +lazy_static! { + pub static ref CONFIG: Config = { + // Read and deserialize the JSON configuration + let config_file = "dbus-monitor/src/config.json"; + let config = read_config(config_file).expect("Failed to read config"); + config + }; +} + fn handle_message(msg: &Message) { debug!("Got message from DBus: {:?}", msg); @@ -26,15 +62,11 @@ fn handle_message(msg: &Message) { let string_value: String = string_ref.to_string(); debug!("String value: {}", string_value); - if string_value.contains("calendar.google.com") { - run_inputmodule_command(vec![ - "led-matrix", - "--pattern", - "all-on", - "--blink-n-times", - "3", - ]); - run_inputmodule_command(vec!["led-matrix", "--brightness", "0"]); + if string_value.contains(&CONFIG.scan_args_for) { + for command in &CONFIG.run_inputmodule_commands { + let command_vec: Vec<&str> = command.split_whitespace().collect(); + run_inputmodule_command(command_vec); + } } } iter.next(); @@ -56,12 +88,11 @@ pub fn run_dbus_monitor() { let conn = Connection::new_session().expect("D-Bus connection failed"); debug!("Connection to DBus session monitor opened"); - // Second create a rule to match messages we want to receive; in this example we add no - // further requirements, so all messages will match + // Second create a rule to match messages we want to receive let rule = MatchRule::new() .with_type(MessageType::MethodCall) - .with_interface("org.freedesktop.Notifications") - .with_member("Notify"); + .with_interface(&CONFIG.dbus_interface) + .with_member(&CONFIG.dbus_member); // Try matching using new scheme let proxy = conn.with_proxy(