diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 78172ed..5e19203 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y libxi-dev libxtst-dev libpulse-dev + run: sudo apt-get update && sudo apt-get install -y libxi-dev libudev-dev libxtst-dev libpulse-dev libxkbcommon-dev libinput-dev - name: Build run: cargo build --verbose - name: Run tests diff --git a/Cargo.lock b/Cargo.lock index 57087e7..9014bb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,21 +77,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -138,83 +123,12 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" -[[package]] -name = "cocoa" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "667fdc068627a2816b9ff831201dd9864249d6ee8d190b9532357f1fc0f61ea7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation 0.9.3", - "core-graphics 0.21.0", - "foreign-types", - "libc", - "objc", -] - [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "core-foundation" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" -dependencies = [ - "core-foundation-sys 0.7.0", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys 0.8.4", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core-graphics" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.7.0", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a67c4378cf203eace8fb6567847eb641fd6ff933c1145a115c6ee820ebb978" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.3", - "foreign-types", - "libc", -] - [[package]] name = "directories-next" version = "2.0.0" @@ -255,16 +169,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "epoll" -version = "4.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79" -dependencies = [ - "bitflags 2.4.1", - "libc", -] - [[package]] name = "errno" version = "0.3.5" @@ -275,44 +179,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "evdev-rs" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b92abc30d5fd1e4f6440dee4d626abc68f4a9b5014dc1de575901e23c2e02321" -dependencies = [ - "bitflags 1.3.2", - "evdev-sys", - "libc", - "log", -] - -[[package]] -name = "evdev-sys" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14ead42b547b15d47089c1243d907bcf0eb94e457046d3b315a26ac9c9e9ea6d" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "fs2" version = "0.4.3" @@ -353,23 +219,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] -name = "inotify" +name = "input" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46dd0a94b393c730779ccfd2a872b67b1eb67be3fc33082e733bdb38b5fde4d4" +checksum = "e6e74cd82cedcd66db78742a8337bdc48f188c4d2c12742cbc5cd85113f0b059" dependencies = [ "bitflags 1.3.2", - "inotify-sys", + "input-sys", + "io-lifetimes", + "libc", + "log", + "udev", +] + +[[package]] +name = "input-sys" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f6c2a17e8aba7217660e32863af87b0febad811d4b8620ef76b386603fddc2" +dependencies = [ "libc", ] [[package]] -name = "inotify-sys" -version = "0.1.5" +name = "io-lifetimes" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ + "hermit-abi", "libc", + "windows-sys", ] [[package]] @@ -392,12 +272,6 @@ dependencies = [ "either", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.149" @@ -431,6 +305,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "linux-raw-sys" version = "0.4.10" @@ -443,21 +327,21 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -478,15 +362,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - [[package]] name = "pkg-config" version = "0.3.27" @@ -512,19 +387,21 @@ dependencies = [ [[package]] name = "push2talk" -version = "1.0.2" +version = "1.1.0" dependencies = [ "clap", "directories-next", "env_logger", "fs2", + "input", "itertools", + "libc", "log", "pulsectl-rs", - "rdev", "signal-hook", "strum", "strum_macros", + "xkbcommon", ] [[package]] @@ -536,26 +413,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rdev" -version = "0.5.3" -source = "git+https://github.com/cyrinux/rdev?branch=main#962feee063a1147e2a08307b637c44d39c40410e" -dependencies = [ - "cocoa", - "core-foundation 0.7.0", - "core-foundation-sys 0.7.0", - "core-graphics 0.19.2", - "epoll", - "evdev-rs", - "inotify", - "lazy_static", - "libc", - "strum", - "strum_macros", - "winapi", - "x11", -] - [[package]] name = "redox_syscall" version = "0.2.16" @@ -719,6 +576,17 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "udev" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebdbbd670373442a12fe9ef7aeb53aec4147a5a27a00bbc3ab639f08f48191a" +dependencies = [ + "libc", + "libudev-sys", + "pkg-config", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -835,11 +703,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "x11" -version = "2.21.0" +name = "xkbcommon" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" dependencies = [ "libc", - "pkg-config", + "memmap2", + "xkeysym", ] + +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" diff --git a/Cargo.toml b/Cargo.toml index 2c6bbd4..76b3aaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "push2talk" -version = "1.0.2" +version = "1.1.0" edition = "2021" authors = ["Cyril Levis", "Maxim Baz"] categories = ["gui", "audio"] @@ -11,18 +11,17 @@ license = "ISC" homepage = "https://github.com/cyrinux/push2talk" repository = "https://github.com/cyrinux/push2talk" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] clap = { version = "4.4.7", features = ["derive"] } directories-next = "2.0.0" env_logger = "0.10.0" fs2 = "0.4.3" +input = "0.8.3" itertools = "0.11.0" +libc = "0.2.149" log = "0.4.20" pulsectl-rs = { git = "https://github.com/cyrinux/pulsectl-rs", branch="main"} -rdev = { git = "https://github.com/cyrinux/rdev", branch="main", default-features = false, features = ["evdev-rs", "unstable_grab"] } signal-hook = "0.3.17" strum = "0.25.0" strum_macros = "0.25.3" +xkbcommon = "0.7.0" diff --git a/README.md b/README.md index 6a8ab98..dba9a44 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ sudo usermod -a -G input $USER ## 🎤 Usage -- To set keybind compose of one or two keys, use env var, eg: `env PUSH2TALK_KEYBIND="ControlLeft,KeyO" push2talk` or `env PUSH2TALK_KEYBIND="MetaRight" push2talk`. - +- To get the code name of the keys you want to use, start in trace mode: `env RUST_LOG=trace push2talk`. +- To set keybind compose of one or two keys, use env var, eg: `env PUSH2TALK_KEYBIND="Control_L,Space" push2talk` or `env PUSH2TALK_KEYBIND="Super_R" push2talk`. - To get more log: `RUST_LOG=debug push2talk`. - To specify an unique source to manage, use the env var, eg: `env PUSH2TALK_SOURCE="OpenComm by Shokz" push2talk`. - There is also a systemd unit provided. `systemctl --user start push2talk.service` diff --git a/src/main.rs b/src/main.rs index 6ef7139..f81846f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,19 @@ use clap::Parser; use directories_next::BaseDirs; use fs2::FileExt; +use input::event::keyboard::KeyState::*; +use input::event::keyboard::KeyboardEventTrait; +use input::{Libinput, LibinputInterface}; use itertools::Itertools; +use libc::{O_RDWR, O_WRONLY}; use log::{debug, info}; use pulsectl::controllers::types::DeviceInfo; use pulsectl::controllers::{DeviceControl, SourceController}; -use rdev::{grab, Event, EventType, Key}; use signal_hook::flag; use std::error::Error; -use std::fs::OpenOptions; +use std::fs::{File, OpenOptions}; +use std::os::unix::{fs::OpenOptionsExt, io::OwnedFd}; +use std::path::Path; use std::path::PathBuf; use std::process::Command; use std::{ @@ -20,6 +25,24 @@ use std::{ }, thread, time, }; +use xkbcommon::xkb; +use xkbcommon::xkb::Keysym; + +struct Push2TalkLibinput; +impl LibinputInterface for Push2TalkLibinput { + fn open_restricted(&mut self, path: &Path, flags: i32) -> Result { + OpenOptions::new() + .custom_flags(flags) + .read(true) + .write((flags & O_WRONLY != 0) | (flags & O_RDWR != 0)) + .open(path) + .map(|file| file.into()) + .map_err(|err| err.raw_os_error().unwrap()) + } + fn close_restricted(&mut self, fd: OwnedFd) { + let _ = File::from(fd); + } +} #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -48,7 +71,9 @@ fn main() -> Result<(), Box> { .args(["-SIGUSR1", "push2talk"]) .spawn() .expect("Can't pause push2talk"); + println!("Toggle pause."); + return Ok(()); } @@ -61,6 +86,20 @@ fn main() -> Result<(), Box> { // Initialize logging setup_logging(); + // Init libinput + let mut libinput_context = Libinput::new_with_udev(Push2TalkLibinput); + libinput_context + .udev_assign_seat("seat0") + .map_err(|e| format!("Can't connect to libinput on seat0: {e:?}"))?; + + // Create context + let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + + // Load keymap informations + let keymap = + xkb::Keymap::new_from_names(&xkb_context, "", "", "", "", None, xkb::COMPILE_NO_FLAGS) + .unwrap(); + // Parse and validate keybinding environment variable let keybind_parsed = parse_keybind()?; validate_keybind(&keybind_parsed)?; @@ -84,41 +123,80 @@ fn main() -> Result<(), Box> { let sig_pause = Arc::new(AtomicBool::new(false)); register_signal(&sig_pause)?; - // Define the callback for key events - let callback = move |event: Event| -> Option { - let check_keybind = |key: Key, pressed: bool| -> bool { - match key { - k if Some(k) == second_key => second_key_pressed.set(pressed), - k if k == first_key => first_key_pressed.set(pressed), - _ => {} - } - !first_key_pressed.get() || second_key.is_some() && !second_key_pressed.get() - }; - - let (key, pressed) = match event.event_type { - EventType::KeyPress(key) => (key, true), - EventType::KeyRelease(key) => (key, false), - _ => return Some(event), - }; - - let should_mute = check_keybind(key, pressed); - if should_mute != last_mute.get() { - info!("Toggle mute: {}", should_mute); - last_mute.set(should_mute); - set_sources(should_mute, &source).ok(); - } + // Create the state tracker + let xkb_state = xkb::State::new(&keymap); - Some(event) - }; + // Main event loop, toggles state based on signals and key events + let mut is_running = true; - // Pause for a moment before starting the main loop - thread::sleep(time::Duration::from_secs(1)); + // Check keybind closure + let check_keybind = |key: Keysym, pressed: bool| -> bool { + match key { + k if Some(k) == second_key => second_key_pressed.set(pressed), + k if k == first_key => first_key_pressed.set(pressed), + _ => {} + } + !first_key_pressed.get() || second_key.is_some() && !second_key_pressed.get() + }; // Start the application info!("Push2talk started"); - main_loop(callback, &sig_pause); + loop { + if sig_pause.swap(false, Ordering::Relaxed) { + is_running = !is_running; + info!( + "Receive SIGUSR1 signal, {}", + if is_running { "resuming" } else { "pausing" } + ) + } - Ok(()) + if !is_running { + thread::sleep(time::Duration::from_secs(1)); + continue; + } + + libinput_context.dispatch()?; + for event in libinput_context.by_ref() { + handle_event(event, &xkb_state, &check_keybind, &last_mute, &source); + } + } +} + +fn handle_event( + event: input::Event, + xkb_state: &xkb::State, + check_keybind: &dyn Fn(Keysym, bool) -> bool, + last_mute: &Cell, + source: &Option, +) { + if let input::Event::Keyboard(key_event) = event { + let keysym = get_keysym(&key_event, xkb_state); + let pressed = check_pressed(&key_event); + log::trace!( + "Key {}: {}", + if pressed { "pressed" } else { "released" }, + xkb::keysym_get_name(keysym) + ); + let should_mute = check_keybind(keysym, pressed); + if should_mute != last_mute.get() { + info!("Toggle {}", if should_mute { "mute" } else { "unmute" }); + last_mute.set(should_mute); + set_sources(should_mute, source).ok(); + } + } +} + +fn get_keysym(key_event: &input::event::KeyboardEvent, xkb_state: &xkb::State) -> Keysym { + // libinput's keycodes are offset by 8 from XKB keycodes + let keycode = key_event.key() + 8; + xkb_state.key_get_one_sym(keycode.into()) +} + +fn check_pressed(state: &input::event::KeyboardEvent) -> bool { + match state.key_state() { + Released => false, + Pressed => true, + } } fn list_devices() -> Result<(), Box> { @@ -153,19 +231,28 @@ fn setup_logging() { ); } -fn parse_keybind() -> Result, Box> { - env::var("PUSH2TALK_KEYBIND") - .unwrap_or("ControlLeft,Space".to_string()) +fn parse_keybind() -> Result, Box> { + let keybind = env::var("PUSH2TALK_KEYBIND") + .unwrap_or("Control_L,Space".to_string()) .split(',') - .map(|k| k.parse().map_err(|_| format!("Unknown key: {k}").into())) - .collect() + .map(|k| xkb::keysym_from_name(k, xkb::KEYSYM_CASE_INSENSITIVE)) + .collect::>(); + + if keybind + .iter() + .any(|k| *k == xkb::keysym_from_name("KEY_NoSymbol", xkb::KEYSYM_CASE_INSENSITIVE)) + { + return Err("Unable to parse keybind".into()); + } + + Ok(keybind) } fn parse_source() -> Option { env::var_os("PUSH2TALK_SOURCE").map(|v| v.into_string().unwrap_or_default()) } -fn validate_keybind(keybind: &[Key]) -> Result<(), Box> { +fn validate_keybind(keybind: &[Keysym]) -> Result<(), Box> { match keybind.len() { 1 | 2 => Ok(()), n => Err(format!("Expected 1 or 2 keys for PUSH2TALK_KEYBIND, got {n}").into()), @@ -179,29 +266,6 @@ fn register_signal(sig_pause: &Arc) -> Result<(), Box> { Ok(()) } -fn main_loop( - callback: impl Fn(Event) -> Option + 'static + Clone, - sig_pause: &Arc, -) { - // Main event loop, toggles state based on signals and key events - let mut is_running = true; - loop { - if sig_pause.swap(false, Ordering::Relaxed) { - is_running = !is_running; - info!("Receive SIGUSR1 signal, is running: {is_running}"); - } - - if !is_running { - thread::sleep(time::Duration::from_secs(1)); - continue; - } - - if grab(callback.clone()).is_err() { - thread::sleep(time::Duration::from_secs(1)); - } - } -} - fn set_sources(mute: bool, source: &Option) -> Result<(), Box> { let mut handler = SourceController::create()?; let sources = handler.list_devices()?; @@ -241,36 +305,69 @@ mod tests { use super::*; #[test] - fn test_parse_keybind() { - std::env::set_var("PUSH2TALK_KEYBIND", "ShiftLeft,ShiftRight"); - let parsed_keys = parse_keybind().unwrap(); - assert_eq!(parsed_keys, vec![Key::ShiftLeft, Key::ShiftRight]); + fn test_parse_keybind_default() { + // Assuming default keybinds are Control_L and Space + std::env::remove_var("PUSH2TALK_KEYBIND"); + let keybind = parse_keybind().unwrap(); + assert_eq!(keybind.len(), 2); + // Assuming default keybinds are Control_L and Space + assert_eq!( + keybind[0], + xkb::keysym_from_name("Control_L", xkb::KEYSYM_CASE_INSENSITIVE) + ); + assert_eq!( + keybind[1], + xkb::keysym_from_name("Space", xkb::KEYSYM_CASE_INSENSITIVE) + ); } #[test] - fn test_validate_keybind_empty() { - assert!(validate_keybind(&[]).is_err()); + fn test_parse_keybind_with_2_valid_keys() { + std::env::set_var("PUSH2TALK_KEYBIND", "Control_L,O"); + let keybind = parse_keybind().unwrap(); + assert_eq!(keybind.len(), 2); + assert_eq!( + keybind[0], + xkb::keysym_from_name("Control_L", xkb::KEYSYM_CASE_INSENSITIVE) + ); + assert_eq!( + keybind[1], + xkb::keysym_from_name("O", xkb::KEYSYM_CASE_INSENSITIVE) + ); + std::env::remove_var("PUSH2TALK_KEYBIND"); } #[test] - fn test_validate_keybind_too_many() { - assert!(validate_keybind(&[Key::ShiftLeft, Key::ShiftRight, Key::AltGr]).is_err()); + fn test_parse_keybind_with_invalid_key() { + std::env::set_var("PUSH2TALK_KEYBIND", "InvalidKey"); + assert!(parse_keybind().is_err()); + std::env::remove_var("PUSH2TALK_KEYBIND"); } #[test] - fn test_validate_keybind_single_key() { - assert!(validate_keybind(&[Key::ShiftLeft]).is_ok()); + fn test_validate_keybind_with_2_keys_is_valid() { + let keybind = vec![ + xkb::keysym_from_name("Control_L", xkb::KEYSYM_CASE_INSENSITIVE), + xkb::keysym_from_name("Space", xkb::KEYSYM_CASE_INSENSITIVE), + ]; + assert!(validate_keybind(&keybind).is_ok()); } #[test] - fn test_validate_keybind_two_keys() { - assert!(validate_keybind(&[Key::ShiftLeft, Key::ShiftRight]).is_ok()); + fn test_validate_keybind_with_3_keys_is_invalid() { + let keybind = vec![ + xkb::keysym_from_name("Control_L", xkb::KEYSYM_CASE_INSENSITIVE), + xkb::keysym_from_name("Space", xkb::KEYSYM_CASE_INSENSITIVE), + xkb::keysym_from_name("Shift_R", xkb::KEYSYM_CASE_INSENSITIVE), + ]; + assert!(validate_keybind(&keybind).is_err()); } #[test] fn test_parse_source_valid() { std::env::set_var("PUSH2TALK_SOURCE", "SourceName"); assert_eq!(parse_source(), Some("SourceName".to_string())); + std::env::remove_var("PUSH2TALK_SOURCE"); } #[test] @@ -284,32 +381,4 @@ mod tests { let flag = Arc::new(AtomicBool::new(false)); assert!(register_signal(&flag).is_ok()); } - - // #[test] - // fn test_set_sources_mute_true() { - // let mut handler = SourceController::create().unwrap(); - // let devices = handler.list_devices().unwrap(); - // let sources = devices - // .iter() - // .filter(|dev| dev.description.is_some()) - // .map(|dev| dev.description.clone().unwrap()) - // .collect::>(); - - // let source_option = sources.first().cloned(); - // assert!(set_sources(true, &source_option).is_ok()); - // } - - // #[test] - // fn test_set_sources_mute_false() { - // let mut handler = SourceController::create().unwrap(); - // let devices = handler.list_devices().unwrap(); - // let sources = devices - // .iter() - // .filter(|dev| dev.description.is_some()) - // .map(|dev| dev.description.clone().unwrap()) - // .collect::>(); - - // let source_option = sources.first().cloned(); - // assert!(set_sources(false, &source_option).is_ok()); - // } }