diff --git a/.github/workflows/check-and-lint.yaml b/.github/workflows/check-and-lint.yaml new file mode 100644 index 0000000..2fada22 --- /dev/null +++ b/.github/workflows/check-and-lint.yaml @@ -0,0 +1,54 @@ +on: + push: + branches: + - main + +name: Check and Lint + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libudev-dev + - uses: Swatinem/rust-cache@v2 + - name: "cargo check" + run: cargo check + + format: + name: Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - uses: Swatinem/rust-cache@v2 + - name: "cargo fmt" + uses: mbrobbel/rustfmt-check@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libudev-dev + - uses: Swatinem/rust-cache@v2 + - name: "cargo clippy" + run: cargo clippy --all-features --workspace --tests --no-deps diff --git a/.github/workflows/check-pr.yaml b/.github/workflows/check-pr.yaml new file mode 100644 index 0000000..1822c2d --- /dev/null +++ b/.github/workflows/check-pr.yaml @@ -0,0 +1,21 @@ +on: pull_request + +name: Check Pull Request + +jobs: + format: + name: Format + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + - name: "cargo fmt" + uses: mbrobbel/rustfmt-check@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + mode: review diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64bf0eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/src/active_client.rs b/src/active_client.rs index c1f54ca..e16e377 100644 --- a/src/active_client.rs +++ b/src/active_client.rs @@ -1,9 +1,9 @@ +use crate::udev_monitor::{Client, Environment, Server}; use crate::Config; -use crate::udev_monitor::{Client, Server, Environment}; use serde_json; -use swayipc_async::Connection; use std::process::{Command, Stdio}; -use x11rb::protocol::xproto::{get_property, get_input_focus, Atom, AtomEnum}; +use swayipc_async::Connection; +use x11rb::protocol::xproto::{get_input_focus, get_property, Atom, AtomEnum}; pub async fn get_active_window(environment: &Environment, config: &Vec) -> Client { match &environment.server { @@ -11,10 +11,19 @@ pub async fn get_active_window(environment: &Environment, config: &Vec) let server_str = server.as_str(); match server_str { "Hyprland" => { - let query = Command::new("hyprctl").args(["activewindow", "-j"]).output().unwrap(); - if let Ok(reply) = serde_json::from_str::(std::str::from_utf8(query.stdout.as_slice()).unwrap()) { - let active_window = Client::Class(reply["class"].to_string().replace("\"", "")); - if let Some(_) = config.iter().find(|&x| x.associations.client == active_window) { + let query = Command::new("hyprctl") + .args(["activewindow", "-j"]) + .output() + .unwrap(); + if let Ok(reply) = serde_json::from_str::( + std::str::from_utf8(query.stdout.as_slice()).unwrap(), + ) { + let active_window = + Client::Class(reply["class"].to_string().replace("\"", "")); + if let Some(_) = config + .iter() + .find(|&x| x.associations.client == active_window) + { active_window } else { Client::Default @@ -22,33 +31,37 @@ pub async fn get_active_window(environment: &Environment, config: &Vec) } else { Client::Default } - }, + } "sway" => { let mut connection = Connection::new().await.unwrap(); - let active_window = match connection.get_tree().await.unwrap().find_focused(|window| window.focused) { - Some(window) => { - match window.app_id { - Some(id) => Client::Class(id), - None => Client::Class(window.window_properties.unwrap().class.unwrap()) - } + let active_window = match connection + .get_tree() + .await + .unwrap() + .find_focused(|window| window.focused) + { + Some(window) => match window.app_id { + Some(id) => Client::Class(id), + None => Client::Class(window.window_properties.unwrap().class.unwrap()), }, - None => Client::Default + None => Client::Default, }; - if let Some(_) = config.iter().find(|&x| x.associations.client == active_window) { + if let Some(_) = config + .iter() + .find(|&x| x.associations.client == active_window) + { active_window } else { Client::Default } - }, + } "KDE" => { let (user, running_as_root) = if let Ok(sudo_user) = environment.sudo_user.clone() { (Option::Some(sudo_user), true) - } - else if let Ok(user) = environment.user.clone() { + } else if let Ok(user) = environment.user.clone() { (Option::Some(user), false) - } - else { + } else { (Option::None, false) }; let active_window = { @@ -60,7 +73,12 @@ pub async fn get_active_window(environment: &Environment, config: &Vec) .arg("kdotool getactivewindow getwindowclassname") .output() .unwrap(); - Client::Class(std::str::from_utf8(output.stdout.as_slice()).unwrap().trim().to_string()) + Client::Class( + std::str::from_utf8(output.stdout.as_slice()) + .unwrap() + .trim() + .to_string(), + ) } else { let output = Command::new("sh") .arg("-c") @@ -68,46 +86,69 @@ pub async fn get_active_window(environment: &Environment, config: &Vec) .stderr(Stdio::null()) .output() .unwrap(); - Client::Class(std::str::from_utf8(output.stdout.as_slice()).unwrap().trim().to_string()) + Client::Class( + std::str::from_utf8(output.stdout.as_slice()) + .unwrap() + .trim() + .to_string(), + ) } } else { Client::Default } }; - if let Some(_) = config.iter().find(|&x| x.associations.client == active_window) { + if let Some(_) = config + .iter() + .find(|&x| x.associations.client == active_window) + { active_window } else { Client::Default } - }, + } "x11" => { let connection = x11rb::connect(None).unwrap().0; - let focused_window = get_input_focus(&connection) - .unwrap().reply().unwrap().focus; - let (wm_class, string): (Atom, Atom) = (AtomEnum::WM_CLASS.into(), AtomEnum::STRING.into()); - let class = get_property(&connection, false, focused_window, wm_class, string, 0, u32::MAX) - .unwrap().reply().unwrap().value; + let focused_window = + get_input_focus(&connection).unwrap().reply().unwrap().focus; + let (wm_class, string): (Atom, Atom) = + (AtomEnum::WM_CLASS.into(), AtomEnum::STRING.into()); + let class = get_property( + &connection, + false, + focused_window, + wm_class, + string, + 0, + u32::MAX, + ) + .unwrap() + .reply() + .unwrap() + .value; if let Some(middle) = class.iter().position(|&byte| byte == 0) { let class = class.split_at(middle).1; let mut class = &class[1..]; if class.last() == Some(&0) { - class = &class[..class.len() -1]; + class = &class[..class.len() - 1]; } - let active_window = Client::Class(std::str::from_utf8(class).unwrap().to_string()); - if let Some(_) = config.iter().find(|&x| x.associations.client == active_window) { - active_window + let active_window = + Client::Class(std::str::from_utf8(class).unwrap().to_string()); + if let Some(_) = config + .iter() + .find(|&x| x.associations.client == active_window) + { + active_window } else { Client::Default } } else { Client::Default } - }, - _ => Client::Default + } + _ => Client::Default, } - }, + } Server::Unsupported => Client::Default, Server::Failed => Client::Default, } } - diff --git a/src/config.rs b/src/config.rs index e07ee90..a6a25b7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,7 @@ -use std::{collections::HashMap, str::FromStr}; +use crate::udev_monitor::Client; use evdev::Key; use serde; -use crate::udev_monitor::Client; - +use std::{collections::HashMap, str::FromStr}; #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] pub enum Event { @@ -31,7 +30,7 @@ pub enum Axis { BTN_TL2, BTN_TR2, ABS_WHEEL_CW, - ABS_WHEEL_CCW + ABS_WHEEL_CCW, } impl FromStr for Axis { @@ -92,10 +91,13 @@ pub struct RawConfig { impl RawConfig { fn new_from_file(file: &str) -> Self { - println!("Parsing config file:\n{:?}\n", file.rsplit_once("/").unwrap().1); + println!( + "Parsing config file:\n{:?}\n", + file.rsplit_once("/").unwrap().1 + ); let file_content: String = std::fs::read_to_string(file).unwrap(); - let raw_config: RawConfig = toml::from_str(&file_content) - .expect("Couldn't parse config file."); + let raw_config: RawConfig = + toml::from_str(&file_content).expect("Couldn't parse config file."); let remap = raw_config.remap; let commands = raw_config.commands; let settings = raw_config.settings; @@ -162,8 +164,10 @@ fn parse_raw_config(raw_config: RawConfig) -> (Bindings, HashMap all: Vec::new(), }; let custom_modifiers: Vec = parse_modifiers(&settings, "CUSTOM_MODIFIERS"); - let lstick_activation_modifiers: Vec = parse_modifiers(&settings, "LSTICK_ACTIVATION_MODIFIERS"); - let rstick_activation_modifiers: Vec = parse_modifiers(&settings, "RSTICK_ACTIVATION_MODIFIERS"); + let lstick_activation_modifiers: Vec = + parse_modifiers(&settings, "LSTICK_ACTIVATION_MODIFIERS"); + let rstick_activation_modifiers: Vec = + parse_modifiers(&settings, "RSTICK_ACTIVATION_MODIFIERS"); mapped_modifiers.custom.extend(custom_modifiers); mapped_modifiers.custom.extend(lstick_activation_modifiers); @@ -192,30 +196,54 @@ fn parse_raw_config(raw_config: RawConfig) -> (Bindings, HashMap } if let Ok(event) = Axis::from_str(event) { if !bindings.remap.contains_key(&Event::Axis(event)) { - bindings.remap.insert(Event::Axis(event), HashMap::from([(modifiers, output)])); + bindings + .remap + .insert(Event::Axis(event), HashMap::from([(modifiers, output)])); } else { - bindings.remap.get_mut(&Event::Axis(event)).unwrap().insert(modifiers, output); + bindings + .remap + .get_mut(&Event::Axis(event)) + .unwrap() + .insert(modifiers, output); } } else if let Ok(event) = Key::from_str(event) { if !bindings.remap.contains_key(&Event::Key(event)) { - bindings.remap.insert(Event::Key(event), HashMap::from([(modifiers, output)])); + bindings + .remap + .insert(Event::Key(event), HashMap::from([(modifiers, output)])); } else { - bindings.remap.get_mut(&Event::Key(event)).unwrap().insert(modifiers, output); + bindings + .remap + .get_mut(&Event::Key(event)) + .unwrap() + .insert(modifiers, output); } } } else { let modifiers: Vec = Vec::new(); if let Ok(event) = Axis::from_str(input.as_str()) { if !bindings.remap.contains_key(&Event::Axis(event)) { - bindings.remap.insert(Event::Axis(event), HashMap::from([(modifiers, output)])); + bindings + .remap + .insert(Event::Axis(event), HashMap::from([(modifiers, output)])); } else { - bindings.remap.get_mut(&Event::Axis(event)).unwrap().insert(modifiers, output); + bindings + .remap + .get_mut(&Event::Axis(event)) + .unwrap() + .insert(modifiers, output); } } else if let Ok(event) = Key::from_str(input.as_str()) { if !bindings.remap.contains_key(&Event::Key(event)) { - bindings.remap.insert(Event::Key(event), HashMap::from([(modifiers, output)])); + bindings + .remap + .insert(Event::Key(event), HashMap::from([(modifiers, output)])); } else { - bindings.remap.get_mut(&Event::Key(event)).unwrap().insert(modifiers, output); + bindings + .remap + .get_mut(&Event::Key(event)) + .unwrap() + .insert(modifiers, output); } } } @@ -241,39 +269,64 @@ fn parse_raw_config(raw_config: RawConfig) -> (Bindings, HashMap } if let Ok(event) = Axis::from_str(event) { if !bindings.commands.contains_key(&Event::Axis(event)) { - bindings.commands.insert(Event::Axis(event), HashMap::from([(modifiers, output)])); + bindings + .commands + .insert(Event::Axis(event), HashMap::from([(modifiers, output)])); } else { - bindings.commands.get_mut(&Event::Axis(event)).unwrap().insert(modifiers, output); + bindings + .commands + .get_mut(&Event::Axis(event)) + .unwrap() + .insert(modifiers, output); } } else if let Ok(event) = Key::from_str(event) { if !bindings.commands.contains_key(&Event::Key(event)) { - bindings.commands.insert(Event::Key(event), HashMap::from([(modifiers, output)])); + bindings + .commands + .insert(Event::Key(event), HashMap::from([(modifiers, output)])); } else { - bindings.commands.get_mut(&Event::Key(event)).unwrap().insert(modifiers, output); + bindings + .commands + .get_mut(&Event::Key(event)) + .unwrap() + .insert(modifiers, output); } } } else { let modifiers: Vec = Vec::new(); if let Ok(event) = Axis::from_str(input.as_str()) { if !bindings.commands.contains_key(&Event::Axis(event)) { - bindings.commands.insert(Event::Axis(event), HashMap::from([(modifiers, output)])); + bindings + .commands + .insert(Event::Axis(event), HashMap::from([(modifiers, output)])); } else { - bindings.commands.get_mut(&Event::Axis(event)).unwrap().insert(modifiers, output); + bindings + .commands + .get_mut(&Event::Axis(event)) + .unwrap() + .insert(modifiers, output); } } else if let Ok(event) = Key::from_str(input.as_str()) { if !bindings.commands.contains_key(&Event::Key(event)) { - bindings.commands.insert(Event::Key(event), HashMap::from([(modifiers, output)])); + bindings + .commands + .insert(Event::Key(event), HashMap::from([(modifiers, output)])); } else { - bindings.commands.get_mut(&Event::Key(event)).unwrap().insert(modifiers, output); + bindings + .commands + .get_mut(&Event::Key(event)) + .unwrap() + .insert(modifiers, output); } } } } - mapped_modifiers.custom.sort(); mapped_modifiers.custom.dedup(); - mapped_modifiers.all.extend(mapped_modifiers.default.clone()); + mapped_modifiers + .all + .extend(mapped_modifiers.default.clone()); mapped_modifiers.all.extend(mapped_modifiers.custom.clone()); mapped_modifiers.all.sort(); mapped_modifiers.all.dedup(); @@ -296,8 +349,7 @@ pub fn parse_modifiers(settings: &HashMap, parameter: &str) -> V } } custom_modifiers - }, + } None => Vec::new(), } } - diff --git a/src/event_reader.rs b/src/event_reader.rs index 9acf0a7..bf9054e 100644 --- a/src/event_reader.rs +++ b/src/event_reader.rs @@ -1,13 +1,20 @@ -use std::{sync::Arc, option::Option, str::FromStr, future::Future, pin::Pin, process::{Command, Stdio}}; -use tokio::sync::Mutex; -use tokio_stream::StreamExt; -use fork::{fork, Fork, setsid}; -use evdev::{EventStream, Key, RelativeAxisType, AbsoluteAxisType, EventType, InputEvent}; +use crate::active_client::*; +use crate::config::{parse_modifiers, Associations, Axis, Event}; +use crate::udev_monitor::Environment; use crate::virtual_devices::VirtualDevices; use crate::Config; -use crate::config::{Event, Axis, Associations, parse_modifiers}; -use crate::active_client::*; -use crate::udev_monitor::Environment; +use evdev::{AbsoluteAxisType, EventStream, EventType, InputEvent, Key, RelativeAxisType}; +use fork::{fork, setsid, Fork}; +use std::{ + future::Future, + option::Option, + pin::Pin, + process::{Command, Stdio}, + str::FromStr, + sync::Arc, +}; +use tokio::sync::Mutex; +use tokio_stream::StreamExt; struct Stick { function: String, @@ -43,7 +50,7 @@ pub struct EventReader { } impl EventReader { - pub fn new ( + pub fn new( config: Vec, stream: Arc>, modifiers: Arc>>, @@ -51,20 +58,55 @@ impl EventReader { environment: Environment, ) -> Self { let mut position_vector: Vec = Vec::new(); - for i in [0, 0] {position_vector.push(i)}; + for i in [0, 0] { + position_vector.push(i) + } let lstick_position = Arc::new(Mutex::new(position_vector.clone())); let rstick_position = Arc::new(Mutex::new(position_vector.clone())); let device_is_connected: Arc> = Arc::new(Mutex::new(true)); let active_layout: Arc> = Arc::new(Mutex::new(0)); - let current_config: Arc> = Arc::new(Mutex::new(config.iter().find(|&x| x.associations == Associations::default()).unwrap().clone())); + let current_config: Arc> = Arc::new(Mutex::new( + config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .clone(), + )); let virt_dev = Arc::new(Mutex::new(VirtualDevices::new())); - let lstick_function = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("LSTICK").unwrap_or(&"cursor".to_string()).to_string(); - let lstick_sensitivity: u64 = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("LSTICK_SENSITIVITY").unwrap_or(&"0".to_string()).parse::().expect("Invalid value for LSTICK_SENSITIVITY, please use an integer value >= 0"); - let lstick_deadzone: i32 = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("LSTICK_DEADZONE").unwrap_or(&"5".to_string()).parse::().expect("Invalid value for LSTICK_DEADZONE, please use an integer between 0 and 128."); - let lstick_activation_modifiers: Vec = parse_modifiers(&config.iter().find(|&x| x.associations == Associations::default()).unwrap().settings, "LSTICK_ACTIVATION_MODIFIERS"); + let lstick_function = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("LSTICK") + .unwrap_or(&"cursor".to_string()) + .to_string(); + let lstick_sensitivity: u64 = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("LSTICK_SENSITIVITY") + .unwrap_or(&"0".to_string()) + .parse::() + .expect("Invalid value for LSTICK_SENSITIVITY, please use an integer value >= 0"); + let lstick_deadzone: i32 = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("LSTICK_DEADZONE") + .unwrap_or(&"5".to_string()) + .parse::() + .expect("Invalid value for LSTICK_DEADZONE, please use an integer between 0 and 128."); + let lstick_activation_modifiers: Vec = parse_modifiers( + &config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings, + "LSTICK_ACTIVATION_MODIFIERS", + ); let lstick = Stick { function: lstick_function, sensitivity: lstick_sensitivity, @@ -72,13 +114,40 @@ impl EventReader { activation_modifiers: lstick_activation_modifiers, }; - let rstick_function: String = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("RSTICK").unwrap_or(&"scroll".to_string()).to_string(); - let rstick_sensitivity: u64 = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("RSTICK_SENSITIVITY").unwrap_or(&"0".to_string()).parse::().expect("Invalid value for RSTICK_SENSITIVITY, please use an integer value >= 0"); - let rstick_deadzone: i32 = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("RSTICK_DEADZONE").unwrap_or(&"5".to_string()).parse::().expect("Invalid value for RSTICK_DEADZONE, please use an integer between 0 and 128."); - let rstick_activation_modifiers: Vec = parse_modifiers(&config.iter().find(|&x| x.associations == Associations::default()).unwrap().settings, "RSTICK_ACTIVATION_MODIFIERS"); + let rstick_function: String = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("RSTICK") + .unwrap_or(&"scroll".to_string()) + .to_string(); + let rstick_sensitivity: u64 = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("RSTICK_SENSITIVITY") + .unwrap_or(&"0".to_string()) + .parse::() + .expect("Invalid value for RSTICK_SENSITIVITY, please use an integer value >= 0"); + let rstick_deadzone: i32 = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("RSTICK_DEADZONE") + .unwrap_or(&"5".to_string()) + .parse::() + .expect("Invalid value for RSTICK_DEADZONE, please use an integer between 0 and 128."); + let rstick_activation_modifiers: Vec = parse_modifiers( + &config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings, + "RSTICK_ACTIVATION_MODIFIERS", + ); let rstick = Stick { function: rstick_function, sensitivity: rstick_sensitivity, @@ -86,23 +155,66 @@ impl EventReader { activation_modifiers: rstick_activation_modifiers, }; - let axis_16_bit: bool = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("16_BIT_AXIS").unwrap_or(&"false".to_string()).parse().expect("16_BIT_AXIS can only be true or false."); + let axis_16_bit: bool = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("16_BIT_AXIS") + .unwrap_or(&"false".to_string()) + .parse() + .expect("16_BIT_AXIS can only be true or false."); - let chain_only: bool = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("CHAIN_ONLY").unwrap_or(&"true".to_string()).parse().expect("CHAIN_ONLY can only be true or false."); + let chain_only: bool = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("CHAIN_ONLY") + .unwrap_or(&"true".to_string()) + .parse() + .expect("CHAIN_ONLY can only be true or false."); - let invert_cursor_axis: bool = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("INVERT_CURSOR_AXIS").unwrap_or(&"false".to_string()).parse().expect("INVERT_CURSOR_AXIS can only be true or false."); + let invert_cursor_axis: bool = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("INVERT_CURSOR_AXIS") + .unwrap_or(&"false".to_string()) + .parse() + .expect("INVERT_CURSOR_AXIS can only be true or false."); - let invert_scroll_axis: bool = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("INVERT_SCROLL_AXIS").unwrap_or(&"false".to_string()).parse().expect("INVERT_SCROLL_AXIS can only be true or false."); + let invert_scroll_axis: bool = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("INVERT_SCROLL_AXIS") + .unwrap_or(&"false".to_string()) + .parse() + .expect("INVERT_SCROLL_AXIS can only be true or false."); - let layout_switcher: Key = Key::from_str(config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("LAYOUT_SWITCHER").unwrap_or(&"BTN_0".to_string())).expect("LAYOUT_SWITCHER is not a valid Key."); + let layout_switcher: Key = Key::from_str( + config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("LAYOUT_SWITCHER") + .unwrap_or(&"BTN_0".to_string()), + ) + .expect("LAYOUT_SWITCHER is not a valid Key."); - let notify_layout_switch: bool = config.iter().find(|&x| x.associations == Associations::default()).unwrap() - .settings.get("NOTIFY_LAYOUT_SWITCH").unwrap_or(&"false".to_string()).parse().expect("NOTIFY_LAYOUT_SWITCH can only be true or false."); + let notify_layout_switch: bool = config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("NOTIFY_LAYOUT_SWITCH") + .unwrap_or(&"false".to_string()) + .parse() + .expect("NOTIFY_LAYOUT_SWITCH can only be true or false."); let settings = Settings { lstick, @@ -131,16 +243,25 @@ impl EventReader { } pub async fn start(&self) { - println!("{:?} detected, reading events.\n", self.config.iter().find(|&x| x.associations == Associations::default()).unwrap().name); - tokio::join!( - self.event_loop(), - self.cursor_loop(), - self.scroll_loop(), + println!( + "{:?} detected, reading events.\n", + self.config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .name ); + tokio::join!(self.event_loop(), self.cursor_loop(), self.scroll_loop(),); } pub async fn event_loop(&self) { - let (mut dpad_values, mut lstick_values, mut rstick_values, mut triggers_values, mut abs_wheel_position) = ((0, 0), (0, 0), (0, 0), (0, 0), 0); + let ( + mut dpad_values, + mut lstick_values, + mut rstick_values, + mut triggers_values, + mut abs_wheel_position, + ) = ((0, 0), (0, 0), (0, 0), (0, 0), 0); let switcher: Key = self.settings.layout_switcher; let mut stream = self.stream.lock().await; let mut max_abs_wheel = 0; @@ -149,293 +270,531 @@ impl EventReader { if state.maximum > max_abs_wheel { max_abs_wheel = state.maximum; } - }; + } } while let Some(Ok(event)) = stream.next().await { - match (event.event_type(), RelativeAxisType(event.code()), AbsoluteAxisType(event.code())) { - (EventType::KEY, _, _) => { - match Key(event.code()) { - Key::BTN_TL2 | Key::BTN_TR2 => {}, - key if key == switcher && event.value() == 1 => { self.change_active_layout().await }, - _ => self.convert_event(event, Event::Key(Key(event.code())), event.value(), false).await, + match ( + event.event_type(), + RelativeAxisType(event.code()), + AbsoluteAxisType(event.code()), + ) { + (EventType::KEY, _, _) => match Key(event.code()) { + Key::BTN_TL2 | Key::BTN_TR2 => {} + key if key == switcher && event.value() == 1 => { + self.change_active_layout().await + } + _ => { + self.convert_event( + event, + Event::Key(Key(event.code())), + event.value(), + false, + ) + .await } }, - (EventType::RELATIVE, RelativeAxisType::REL_WHEEL | RelativeAxisType::REL_WHEEL_HI_RES, _) => { - match event.value() { - -1 => { - self.convert_event(event, Event::Axis(Axis::SCROLL_WHEEL_DOWN), 1, true).await; - }, - 1 => { - self.convert_event(event, Event::Axis(Axis::SCROLL_WHEEL_UP), 1, true).await; - }, - _ => {} + ( + EventType::RELATIVE, + RelativeAxisType::REL_WHEEL | RelativeAxisType::REL_WHEEL_HI_RES, + _, + ) => match event.value() { + -1 => { + self.convert_event(event, Event::Axis(Axis::SCROLL_WHEEL_DOWN), 1, true) + .await; } + 1 => { + self.convert_event(event, Event::Axis(Axis::SCROLL_WHEEL_UP), 1, true) + .await; + } + _ => {} }, (_, _, AbsoluteAxisType::ABS_HAT0X) => { match event.value() { -1 => { - self.convert_event(event, Event::Axis(Axis::BTN_DPAD_LEFT), 1, false).await; + self.convert_event(event, Event::Axis(Axis::BTN_DPAD_LEFT), 1, false) + .await; dpad_values.0 = -1; - }, + } 1 => { - self.convert_event(event, Event::Axis(Axis::BTN_DPAD_RIGHT), 1, false).await; + self.convert_event(event, Event::Axis(Axis::BTN_DPAD_RIGHT), 1, false) + .await; dpad_values.0 = 1; - }, + } 0 => { match dpad_values.0 { - -1 => self.convert_event(event, Event::Axis(Axis::BTN_DPAD_LEFT), 0, false).await, - 1 => self.convert_event(event, Event::Axis(Axis::BTN_DPAD_RIGHT), 0, false).await, - _ => {}, + -1 => { + self.convert_event( + event, + Event::Axis(Axis::BTN_DPAD_LEFT), + 0, + false, + ) + .await + } + 1 => { + self.convert_event( + event, + Event::Axis(Axis::BTN_DPAD_RIGHT), + 0, + false, + ) + .await + } + _ => {} } dpad_values.0 = 0; - }, + } _ => {} }; - }, + } (_, _, AbsoluteAxisType::ABS_HAT0Y) => { match event.value() { -1 => { - self.convert_event(event, Event::Axis(Axis::BTN_DPAD_UP), 1, false).await; + self.convert_event(event, Event::Axis(Axis::BTN_DPAD_UP), 1, false) + .await; dpad_values.1 = -1; - }, + } 1 => { - self.convert_event(event, Event::Axis(Axis::BTN_DPAD_DOWN), 1, false).await; + self.convert_event(event, Event::Axis(Axis::BTN_DPAD_DOWN), 1, false) + .await; dpad_values.1 = 1; - }, + } 0 => { match dpad_values.1 { - -1 => self.convert_event(event, Event::Axis(Axis::BTN_DPAD_UP), 0, false).await, - 1 => self.convert_event(event, Event::Axis(Axis::BTN_DPAD_DOWN), 0, false).await, - _ => {}, + -1 => { + self.convert_event( + event, + Event::Axis(Axis::BTN_DPAD_UP), + 0, + false, + ) + .await + } + 1 => { + self.convert_event( + event, + Event::Axis(Axis::BTN_DPAD_DOWN), + 0, + false, + ) + .await + } + _ => {} } dpad_values.1 = 0; - }, + } _ => {} }; - }, + } (EventType::ABSOLUTE, _, AbsoluteAxisType::ABS_X | AbsoluteAxisType::ABS_Y) => { match self.settings.lstick.function.as_str() { "cursor" | "scroll" => { - let axis_value = self.get_axis_value(&event, &self.settings.lstick.deadzone).await; + let axis_value = self + .get_axis_value(&event, &self.settings.lstick.deadzone) + .await; let mut lstick_position = self.lstick_position.lock().await; lstick_position[event.code() as usize] = axis_value; - }, + } "bind" => { - let axis_value = self.get_axis_value(&event, &self.settings.lstick.deadzone).await; - let clamped_value = - if axis_value < 0 { -1 } - else if axis_value > 0 { 1 } - else { 0 }; + let axis_value = self + .get_axis_value(&event, &self.settings.lstick.deadzone) + .await; + let clamped_value = if axis_value < 0 { + -1 + } else if axis_value > 0 { + 1 + } else { + 0 + }; match AbsoluteAxisType(event.code()) { - AbsoluteAxisType::ABS_Y => { - match clamped_value { - -1 if lstick_values.1 != -1 => { - self.convert_event(event, Event::Axis(Axis::LSTICK_UP), 1, false).await; - lstick_values.1 = -1 - }, - 1 if lstick_values.1 != 1 => { - self.convert_event(event, Event::Axis(Axis::LSTICK_DOWN), 1, false).await; - lstick_values.1 = 1 - }, - 0 => if lstick_values.1 != 0 { + AbsoluteAxisType::ABS_Y => match clamped_value { + -1 if lstick_values.1 != -1 => { + self.convert_event( + event, + Event::Axis(Axis::LSTICK_UP), + 1, + false, + ) + .await; + lstick_values.1 = -1 + } + 1 if lstick_values.1 != 1 => { + self.convert_event( + event, + Event::Axis(Axis::LSTICK_DOWN), + 1, + false, + ) + .await; + lstick_values.1 = 1 + } + 0 => { + if lstick_values.1 != 0 { match lstick_values.1 { - -1 => self.convert_event(event, Event::Axis(Axis::LSTICK_UP), 0, false).await, - 1 => self.convert_event(event, Event::Axis(Axis::LSTICK_DOWN), 0, false).await, - _ => {}, + -1 => { + self.convert_event( + event, + Event::Axis(Axis::LSTICK_UP), + 0, + false, + ) + .await + } + 1 => { + self.convert_event( + event, + Event::Axis(Axis::LSTICK_DOWN), + 0, + false, + ) + .await + } + _ => {} } lstick_values.1 = 0; - }, - _ => {}, + } } + _ => {} }, - AbsoluteAxisType::ABS_X => { - match clamped_value { - -1 if lstick_values.0 != -1 => { - self.convert_event(event, Event::Axis(Axis::LSTICK_LEFT), 1, false).await; - lstick_values.0 = -1 - }, - 1 => if lstick_values.0 != 1 { - self.convert_event(event, Event::Axis(Axis::LSTICK_RIGHT), 1, false).await; + AbsoluteAxisType::ABS_X => match clamped_value { + -1 if lstick_values.0 != -1 => { + self.convert_event( + event, + Event::Axis(Axis::LSTICK_LEFT), + 1, + false, + ) + .await; + lstick_values.0 = -1 + } + 1 => { + if lstick_values.0 != 1 { + self.convert_event( + event, + Event::Axis(Axis::LSTICK_RIGHT), + 1, + false, + ) + .await; lstick_values.0 = 1 - }, - 0 => if lstick_values.0 != 0 { + } + } + 0 => { + if lstick_values.0 != 0 { match lstick_values.0 { - -1 => self.convert_event(event, Event::Axis(Axis::LSTICK_LEFT), 0, false).await, - 1 => self.convert_event(event, Event::Axis(Axis::LSTICK_RIGHT), 0, false).await, - _ => {}, + -1 => { + self.convert_event( + event, + Event::Axis(Axis::LSTICK_LEFT), + 0, + false, + ) + .await + } + 1 => { + self.convert_event( + event, + Event::Axis(Axis::LSTICK_RIGHT), + 0, + false, + ) + .await + } + _ => {} } lstick_values.0 = 0; - }, - _ => {}, + } } + _ => {} }, - _ => {}, + _ => {} } - }, - _ => {}, + } + _ => {} } - }, + } (EventType::ABSOLUTE, _, AbsoluteAxisType::ABS_RX | AbsoluteAxisType::ABS_RY) => { match self.settings.rstick.function.as_str() { "cursor" | "scroll" => { - let axis_value = self.get_axis_value(&event, &self.settings.rstick.deadzone).await; + let axis_value = self + .get_axis_value(&event, &self.settings.rstick.deadzone) + .await; let mut rstick_position = self.rstick_position.lock().await; - rstick_position[event.code() as usize -3] = axis_value; - }, + rstick_position[event.code() as usize - 3] = axis_value; + } "bind" => { - let axis_value = self.get_axis_value(&event, &self.settings.rstick.deadzone).await; - let clamped_value = if axis_value < 0 { -1 } - else if axis_value > 0 { 1 } - else { 0 }; + let axis_value = self + .get_axis_value(&event, &self.settings.rstick.deadzone) + .await; + let clamped_value = if axis_value < 0 { + -1 + } else if axis_value > 0 { + 1 + } else { + 0 + }; match AbsoluteAxisType(event.code()) { - AbsoluteAxisType::ABS_RY => { - match clamped_value { - -1 => if rstick_values.1 != -1 { - self.convert_event(event, Event::Axis(Axis::RSTICK_UP), 1, false).await; + AbsoluteAxisType::ABS_RY => match clamped_value { + -1 => { + if rstick_values.1 != -1 { + self.convert_event( + event, + Event::Axis(Axis::RSTICK_UP), + 1, + false, + ) + .await; rstick_values.1 = -1 - }, - 1 => if rstick_values.1 != 1 { - self.convert_event(event, Event::Axis(Axis::RSTICK_DOWN), 1, false).await; + } + } + 1 => { + if rstick_values.1 != 1 { + self.convert_event( + event, + Event::Axis(Axis::RSTICK_DOWN), + 1, + false, + ) + .await; rstick_values.1 = 1 - }, - 0 => if rstick_values.1 != 0 { + } + } + 0 => { + if rstick_values.1 != 0 { match rstick_values.1 { - -1 => self.convert_event(event, Event::Axis(Axis::RSTICK_UP), 0, false).await, - 1 => self.convert_event(event, Event::Axis(Axis::RSTICK_DOWN), 0, false).await, - _ => {}, + -1 => { + self.convert_event( + event, + Event::Axis(Axis::RSTICK_UP), + 0, + false, + ) + .await + } + 1 => { + self.convert_event( + event, + Event::Axis(Axis::RSTICK_DOWN), + 0, + false, + ) + .await + } + _ => {} } rstick_values.1 = 0; - }, - _ => {}, + } } + _ => {} }, - AbsoluteAxisType::ABS_RX => { - match clamped_value { - -1 if rstick_values.0 != -1 => { - self.convert_event(event, Event::Axis(Axis::RSTICK_LEFT), 1, false).await; - rstick_values.0 = -1 - }, - 1 => if rstick_values.0 != 1 { - self.convert_event(event, Event::Axis(Axis::RSTICK_RIGHT), 1, false).await; + AbsoluteAxisType::ABS_RX => match clamped_value { + -1 if rstick_values.0 != -1 => { + self.convert_event( + event, + Event::Axis(Axis::RSTICK_LEFT), + 1, + false, + ) + .await; + rstick_values.0 = -1 + } + 1 => { + if rstick_values.0 != 1 { + self.convert_event( + event, + Event::Axis(Axis::RSTICK_RIGHT), + 1, + false, + ) + .await; rstick_values.0 = 1 - }, - 0 => if rstick_values.0 != 0 { + } + } + 0 => { + if rstick_values.0 != 0 { match rstick_values.0 { - -1 => self.convert_event(event, Event::Axis(Axis::RSTICK_LEFT), 0, false).await, - 1 => self.convert_event(event, Event::Axis(Axis::RSTICK_RIGHT), 0, false).await, - _ => {}, + -1 => { + self.convert_event( + event, + Event::Axis(Axis::RSTICK_LEFT), + 0, + false, + ) + .await + } + 1 => { + self.convert_event( + event, + Event::Axis(Axis::RSTICK_RIGHT), + 0, + false, + ) + .await + } + _ => {} } rstick_values.0 = 0; - }, - _ => {}, + } } + _ => {} }, - _ => {}, + _ => {} } - }, - _ => {}, + } + _ => {} } - }, + } (EventType::ABSOLUTE, _, AbsoluteAxisType::ABS_Z) => { match (event.value(), triggers_values.0) { (0, 1) => { - self.convert_event(event, Event::Axis(Axis::BTN_TL2), 0, false).await; + self.convert_event(event, Event::Axis(Axis::BTN_TL2), 0, false) + .await; triggers_values.0 = 0; - }, + } (_, 0) => { - self.convert_event(event, Event::Axis(Axis::BTN_TL2), 1, false).await; + self.convert_event(event, Event::Axis(Axis::BTN_TL2), 1, false) + .await; triggers_values.0 = 1; - }, - _ => {}, + } + _ => {} } - }, + } (EventType::ABSOLUTE, _, AbsoluteAxisType::ABS_RZ) => { match (event.value(), triggers_values.1) { (0, 1) => { - self.convert_event(event, Event::Axis(Axis::BTN_TR2), 0, false).await; + self.convert_event(event, Event::Axis(Axis::BTN_TR2), 0, false) + .await; triggers_values.1 = 0; - }, + } (_, 0) => { - self.convert_event(event, Event::Axis(Axis::BTN_TR2), 1, false).await; + self.convert_event(event, Event::Axis(Axis::BTN_TR2), 1, false) + .await; triggers_values.1 = 1; - }, - _ => {}, + } + _ => {} } - }, + } (EventType::ABSOLUTE, _, AbsoluteAxisType::ABS_WHEEL) => { let value = event.value(); if value != 0 && abs_wheel_position != 0 { let gap = value - abs_wheel_position; - if gap < -max_abs_wheel/2 { - self.convert_event(event, Event::Axis(Axis::ABS_WHEEL_CW), 1, true).await; - } else if gap > max_abs_wheel/2 { - self.convert_event(event, Event::Axis(Axis::ABS_WHEEL_CCW), 1, true).await; + if gap < -max_abs_wheel / 2 { + self.convert_event(event, Event::Axis(Axis::ABS_WHEEL_CW), 1, true) + .await; + } else if gap > max_abs_wheel / 2 { + self.convert_event(event, Event::Axis(Axis::ABS_WHEEL_CCW), 1, true) + .await; } else if value > abs_wheel_position { - self.convert_event(event, Event::Axis(Axis::ABS_WHEEL_CW), 1, true).await; + self.convert_event(event, Event::Axis(Axis::ABS_WHEEL_CW), 1, true) + .await; } else if value < abs_wheel_position { - self.convert_event(event, Event::Axis(Axis::ABS_WHEEL_CCW), 1, true).await; + self.convert_event(event, Event::Axis(Axis::ABS_WHEEL_CCW), 1, true) + .await; } } abs_wheel_position = value; - }, + } (EventType::ABSOLUTE, _, AbsoluteAxisType::ABS_MISC) => { - if event.value() == 0 { abs_wheel_position = 0 }; - }, + if event.value() == 0 { + abs_wheel_position = 0 + }; + } _ => self.emit_default_event(event).await, } } let mut device_is_connected = self.device_is_connected.lock().await; *device_is_connected = false; - println!("Disconnected device \"{}\".\n", self.current_config.lock().await.name); + println!( + "Disconnected device \"{}\".\n", + self.current_config.lock().await.name + ); } - async fn convert_event(&self, default_event: InputEvent, event: Event, value: i32, send_zero: bool) { - if value == 1 { + async fn convert_event( + &self, + default_event: InputEvent, + event: Event, + value: i32, + send_zero: bool, + ) { + if value == 1 { self.update_config().await; - }; - let config = self.current_config.lock().await; + }; + let config = self.current_config.lock().await; let modifiers = self.modifiers.lock().await.clone(); if let Some(map) = config.bindings.remap.get(&event) { if let Some(event_list) = map.get(&modifiers) { - self.emit_event(event_list, value, &modifiers, &config, modifiers.is_empty(), !modifiers.is_empty()).await; + self.emit_event( + event_list, + value, + &modifiers, + &config, + modifiers.is_empty(), + !modifiers.is_empty(), + ) + .await; if send_zero { let modifiers = self.modifiers.lock().await.clone(); - self.emit_event(event_list, 0, &modifiers, &config, modifiers.is_empty(), !modifiers.is_empty()).await; + self.emit_event( + event_list, + 0, + &modifiers, + &config, + modifiers.is_empty(), + !modifiers.is_empty(), + ) + .await; } - return + return; } if let Some(event_list) = map.get(&vec![Event::Hold]) { if !modifiers.is_empty() || self.settings.chain_only == false { - self.emit_event(event_list, value, &modifiers, &config, false, false).await; - return + self.emit_event(event_list, value, &modifiers, &config, false, false) + .await; + return; } } if let Some(map) = config.bindings.commands.get(&event) { if let Some(command_list) = map.get(&modifiers) { - if value == 1 { self.spawn_subprocess(command_list).await }; - return + if value == 1 { + self.spawn_subprocess(command_list).await + }; + return; } } if let Some(event_list) = map.get(&Vec::new()) { - self.emit_event(event_list, value, &modifiers, &config, true, false).await; + self.emit_event(event_list, value, &modifiers, &config, true, false) + .await; if send_zero { let modifiers = self.modifiers.lock().await.clone(); - self.emit_event(event_list, 0, &modifiers, &config, true, false).await; + self.emit_event(event_list, 0, &modifiers, &config, true, false) + .await; } - return + return; } } if let Some(map) = config.bindings.commands.get(&event) { if let Some(command_list) = map.get(&modifiers) { - if value == 1 { self.spawn_subprocess(command_list).await }; - return + if value == 1 { + self.spawn_subprocess(command_list).await + }; + return; } } - self.emit_nonmapped_event(default_event, event, value, &modifiers, &config).await; + self.emit_nonmapped_event(default_event, event, value, &modifiers, &config) + .await; } - async fn emit_event(&self, event_list: &Vec, value: i32, modifiers: &Vec, config: &Config, release_keys: bool, ignore_modifiers: bool) { + async fn emit_event( + &self, + event_list: &Vec, + value: i32, + modifiers: &Vec, + config: &Config, + release_keys: bool, + ignore_modifiers: bool, + ) { let mut virt_dev = self.virt_dev.lock().await; let mut modifier_was_activated = self.modifier_was_activated.lock().await; if release_keys { @@ -448,34 +807,46 @@ impl EventReader { } else if ignore_modifiers { for key in modifiers.iter() { if let Event::Key(key) = key { - let virtual_event: InputEvent = InputEvent::new_now(EventType::KEY, key.code(), 0); + let virtual_event: InputEvent = + InputEvent::new_now(EventType::KEY, key.code(), 0); virt_dev.keys.emit(&[virtual_event]).unwrap(); } } } for key in event_list { if release_keys { - self.toggle_modifiers(Event::Key(*key), value, &config).await; + self.toggle_modifiers(Event::Key(*key), value, &config) + .await; } if config.mapped_modifiers.custom.contains(&Event::Key(*key)) { if value == 0 && !*modifier_was_activated { - let virtual_event: InputEvent = InputEvent::new_now(EventType::KEY, key.code(), 1); + let virtual_event: InputEvent = + InputEvent::new_now(EventType::KEY, key.code(), 1); virt_dev.keys.emit(&[virtual_event]).unwrap(); - let virtual_event: InputEvent = InputEvent::new_now(EventType::KEY, key.code(), 0); + let virtual_event: InputEvent = + InputEvent::new_now(EventType::KEY, key.code(), 0); virt_dev.keys.emit(&[virtual_event]).unwrap(); *modifier_was_activated = true; } else if value == 1 { *modifier_was_activated = false; } } else { - let virtual_event: InputEvent = InputEvent::new_now(EventType::KEY, key.code(), value); + let virtual_event: InputEvent = + InputEvent::new_now(EventType::KEY, key.code(), value); virt_dev.keys.emit(&[virtual_event]).unwrap(); *modifier_was_activated = true; } } } - async fn emit_nonmapped_event(&self, default_event: InputEvent, event: Event, value: i32, modifiers: &Vec, config: &Config) { + async fn emit_nonmapped_event( + &self, + default_event: InputEvent, + event: Event, + value: i32, + modifiers: &Vec, + config: &Config, + ) { let mut virt_dev = self.virt_dev.lock().await; let mut modifier_was_activated = self.modifier_was_activated.lock().await; let released_keys: Vec = self.released_keys(&modifiers, &config).await; @@ -487,9 +858,11 @@ impl EventReader { self.toggle_modifiers(event, value, &config).await; if config.mapped_modifiers.custom.contains(&event) { if value == 0 && !*modifier_was_activated { - let virtual_event: InputEvent = InputEvent::new_now(default_event.event_type(), default_event.code(), 1); + let virtual_event: InputEvent = + InputEvent::new_now(default_event.event_type(), default_event.code(), 1); virt_dev.keys.emit(&[virtual_event]).unwrap(); - let virtual_event: InputEvent = InputEvent::new_now(default_event.event_type(), default_event.code(), 0); + let virtual_event: InputEvent = + InputEvent::new_now(default_event.event_type(), default_event.code(), 0); virt_dev.keys.emit(&[virtual_event]).unwrap(); *modifier_was_activated = true; } else if value == 1 { @@ -500,11 +873,11 @@ impl EventReader { match default_event.event_type() { EventType::KEY => { virt_dev.keys.emit(&[default_event]).unwrap(); - }, + } EventType::RELATIVE => { virt_dev.axis.emit(&[default_event]).unwrap(); - }, - _ => {}, + } + _ => {} } } } @@ -514,49 +887,44 @@ impl EventReader { EventType::KEY => { let mut virt_dev = self.virt_dev.lock().await; virt_dev.keys.emit(&[event]).unwrap(); - }, + } EventType::RELATIVE => { let mut virt_dev = self.virt_dev.lock().await; virt_dev.axis.emit(&[event]).unwrap(); - }, - _ => {}, + } + _ => {} } } async fn spawn_subprocess(&self, command_list: &Vec) { let mut modifier_was_activated = self.modifier_was_activated.lock().await; *modifier_was_activated = true; - let (user, running_as_root) = - if let Ok(sudo_user) = &self.environment.sudo_user { - (Option::Some(sudo_user), true) - } - else if let Ok(user) = &self.environment.user { - (Option::Some(user), false) - } - else { - (Option::None, false) - }; + let (user, running_as_root) = if let Ok(sudo_user) = &self.environment.sudo_user { + (Option::Some(sudo_user), true) + } else if let Ok(user) = &self.environment.user { + (Option::Some(user), false) + } else { + (Option::None, false) + }; if let Some(user) = user { for command in command_list { if running_as_root { match fork() { - Ok(Fork::Child) => { - match fork() { - Ok(Fork::Child) => { - setsid().unwrap(); - Command::new("runuser") - .args([user, "-c", command]) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn() - .unwrap(); - std::process::exit(0); - } - Ok(Fork::Parent(_)) => std::process::exit(0), - Err(_) => std::process::exit(1), + Ok(Fork::Child) => match fork() { + Ok(Fork::Child) => { + setsid().unwrap(); + Command::new("runuser") + .args([user, "-c", command]) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .unwrap(); + std::process::exit(0); } - } + Ok(Fork::Parent(_)) => std::process::exit(0), + Err(_) => std::process::exit(1), + }, Ok(Fork::Parent(_)) => (), Err(_) => std::process::exit(1), } @@ -577,7 +945,7 @@ impl EventReader { async fn get_axis_value(&self, event: &InputEvent, deadzone: &i32) -> i32 { let distance_from_center: i32 = match self.settings.axis_16_bit { false => (event.value() as i32 - 128) * 200, - _ => event.value() as i32 + _ => event.value() as i32, }; if distance_from_center.abs() <= deadzone * 200 { 0 @@ -585,7 +953,7 @@ impl EventReader { (distance_from_center + 2000 - 1) / 2000 } } - + async fn toggle_modifiers(&self, modifier: Event, value: i32, config: &Config) { let mut modifiers = self.modifiers.lock().await; if config.mapped_modifiers.all.contains(&modifier) { @@ -594,9 +962,9 @@ impl EventReader { modifiers.push(modifier); modifiers.sort(); modifiers.dedup(); - }, + } 0 => modifiers.retain(|&x| x != modifier), - _ => {}, + _ => {} } } } @@ -615,14 +983,22 @@ impl EventReader { let mut active_layout = self.active_layout.lock().await; let active_window = get_active_window(&self.environment, &self.config).await; loop { - if *active_layout == 3 { *active_layout = 0 } - else { *active_layout += 1 }; - if let Some(_) = self.config.iter().find(|&x| x.associations.layout == *active_layout && x.associations.client == active_window) { - break + if *active_layout == 3 { + *active_layout = 0 + } else { + *active_layout += 1 + }; + if let Some(_) = self.config.iter().find(|&x| { + x.associations.layout == *active_layout && x.associations.client == active_window + }) { + break; }; } if self.settings.notify_layout_switch { - let notify = vec![String::from(format!("notify-send -t 500 'Makima' 'Switching to layout {}'", *active_layout))]; + let notify = vec![String::from(format!( + "notify-send -t 500 'Makima' 'Switching to layout {}'", + *active_layout + ))]; self.spawn_subprocess(¬ify).await; } } @@ -639,23 +1015,32 @@ impl EventReader { Some(config) => { let mut current_config = self.current_config.lock().await; *current_config = config.clone(); - }, + } None => { self.change_active_layout().await; self.update_config().await; - }, + } }; }) } pub async fn cursor_loop(&self) { - let (cursor, sensitivity, activation_modifiers) = if self.settings.lstick.function.as_str() == "cursor" { - ("left", self.settings.lstick.sensitivity, self.settings.lstick.activation_modifiers.clone()) - } else if self.settings.rstick.function.as_str() == "cursor" { - ("right", self.settings.rstick.sensitivity, self.settings.rstick.activation_modifiers.clone()) - } else { - ("disabled", 0, vec![]) - }; + let (cursor, sensitivity, activation_modifiers) = + if self.settings.lstick.function.as_str() == "cursor" { + ( + "left", + self.settings.lstick.sensitivity, + self.settings.lstick.activation_modifiers.clone(), + ) + } else if self.settings.rstick.function.as_str() == "cursor" { + ( + "right", + self.settings.rstick.sensitivity, + self.settings.rstick.activation_modifiers.clone(), + ) + } else { + ("disabled", 0, vec![]) + }; if sensitivity != 0 { while *self.device_is_connected.lock().await { { @@ -664,16 +1049,20 @@ impl EventReader { } else if cursor == "right" { self.rstick_position.lock().await } else { - break + break; }; if stick_position[0] != 0 || stick_position[1] != 0 { let modifiers = self.modifiers.lock().await; if activation_modifiers.len() == 0 || activation_modifiers == *modifiers { - let (x_coord, y_coord) = - if self.settings.invert_cursor_axis { (-stick_position[0], -stick_position[1]) } - else { (stick_position[0], stick_position[1]) }; - let virtual_event_x: InputEvent = InputEvent::new_now(EventType::RELATIVE, 0, x_coord); - let virtual_event_y: InputEvent = InputEvent::new_now(EventType::RELATIVE, 1, y_coord); + let (x_coord, y_coord) = if self.settings.invert_cursor_axis { + (-stick_position[0], -stick_position[1]) + } else { + (stick_position[0], stick_position[1]) + }; + let virtual_event_x: InputEvent = + InputEvent::new_now(EventType::RELATIVE, 0, x_coord); + let virtual_event_y: InputEvent = + InputEvent::new_now(EventType::RELATIVE, 1, y_coord); let mut virt_dev = self.virt_dev.lock().await; virt_dev.axis.emit(&[virtual_event_x]).unwrap(); virt_dev.axis.emit(&[virtual_event_y]).unwrap(); @@ -683,18 +1072,27 @@ impl EventReader { tokio::time::sleep(std::time::Duration::from_millis(sensitivity)).await; } } else { - return + return; } } pub async fn scroll_loop(&self) { - let (scroll, sensitivity, activation_modifiers) = if self.settings.lstick.function.as_str() == "scroll" { - ("left", self.settings.lstick.sensitivity, self.settings.lstick.activation_modifiers.clone()) - } else if self.settings.rstick.function.as_str() == "scroll" { - ("right", self.settings.rstick.sensitivity, self.settings.rstick.activation_modifiers.clone()) - } else { - ("disabled", 0, vec![]) - }; + let (scroll, sensitivity, activation_modifiers) = + if self.settings.lstick.function.as_str() == "scroll" { + ( + "left", + self.settings.lstick.sensitivity, + self.settings.lstick.activation_modifiers.clone(), + ) + } else if self.settings.rstick.function.as_str() == "scroll" { + ( + "right", + self.settings.rstick.sensitivity, + self.settings.rstick.activation_modifiers.clone(), + ) + } else { + ("disabled", 0, vec![]) + }; if sensitivity != 0 { while *self.device_is_connected.lock().await { { @@ -703,16 +1101,20 @@ impl EventReader { } else if scroll == "right" { self.rstick_position.lock().await } else { - break + break; }; if stick_position[0] != 0 || stick_position[1] != 0 { let modifiers = self.modifiers.lock().await; if activation_modifiers.len() == 0 || activation_modifiers == *modifiers { - let (x_coord, y_coord) = - if self.settings.invert_scroll_axis { (-stick_position[0], -stick_position[1]) } - else { (stick_position[0], stick_position[1]) }; - let virtual_event_x: InputEvent = InputEvent::new_now(EventType::RELATIVE, 12, x_coord); - let virtual_event_y: InputEvent = InputEvent::new_now(EventType::RELATIVE, 11, y_coord); + let (x_coord, y_coord) = if self.settings.invert_scroll_axis { + (-stick_position[0], -stick_position[1]) + } else { + (stick_position[0], stick_position[1]) + }; + let virtual_event_x: InputEvent = + InputEvent::new_now(EventType::RELATIVE, 12, x_coord); + let virtual_event_y: InputEvent = + InputEvent::new_now(EventType::RELATIVE, 11, y_coord); let mut virt_dev = self.virt_dev.lock().await; virt_dev.axis.emit(&[virtual_event_x]).unwrap(); virt_dev.axis.emit(&[virtual_event_y]).unwrap(); @@ -722,8 +1124,7 @@ impl EventReader { tokio::time::sleep(std::time::Duration::from_millis(sensitivity)).await; } } else { - return + return; } } } - diff --git a/src/main.rs b/src/main.rs index acc415d..e8fa3ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,14 @@ +mod active_client; mod config; -mod virtual_devices; mod event_reader; mod udev_monitor; -mod active_client; +mod virtual_devices; +use crate::udev_monitor::*; +use config::Config; use std::env; use tokio; -use config::Config; use tokio::task::JoinHandle; -use crate::udev_monitor::*; - #[tokio::main] async fn main() { @@ -23,20 +22,21 @@ async fn main() { std::process::exit(0); } } - }, + } Err(_) => { let user_home = match env::var("HOME") { - Ok(user_home) if user_home == "/root".to_string() => { - match env::var("SUDO_USER") { - Ok(sudo_user) => format!("/home/{}", sudo_user), - _ => user_home, - } + Ok(user_home) if user_home == "/root".to_string() => match env::var("SUDO_USER") { + Ok(sudo_user) => format!("/home/{}", sudo_user), + _ => user_home, }, Ok(user_home) => user_home, _ => "/root".to_string(), }; let default_config_path = format!("{}/.config/makima", user_home); - println!("\nMAKIMA_CONFIG environment variable is not set, defaulting to {:?}.\n", default_config_path); + println!( + "\nMAKIMA_CONFIG environment variable is not set, defaulting to {:?}.\n", + default_config_path + ); match std::fs::read_dir(default_config_path) { Ok(dir) => dir, _ => { @@ -44,14 +44,15 @@ async fn main() { std::process::exit(0); } } - }, + } }; let mut config_files: Vec = Vec::new(); for file in config_path { let filename: String = file.as_ref().unwrap().file_name().into_string().unwrap(); if filename.ends_with(".toml") && !filename.starts_with(".") { let name: String = filename.split(".toml").collect::>()[0].to_string(); - let config_file: Config = Config::new_from_file(file.unwrap().path().to_str().unwrap(), name); + let config_file: Config = + Config::new_from_file(file.unwrap().path().to_str().unwrap(), name); config_files.push(config_file); } } diff --git a/src/udev_monitor.rs b/src/udev_monitor.rs index 4521f17..6f67eda 100644 --- a/src/udev_monitor.rs +++ b/src/udev_monitor.rs @@ -1,12 +1,11 @@ -use std::{sync::Arc, path::Path, process::Command, env}; +use crate::config::{Associations, Event}; +use crate::event_reader::EventReader; +use crate::Config; +use evdev::{Device, EventStream}; +use std::{env, path::Path, process::Command, sync::Arc}; use tokio::sync::Mutex; use tokio::task::JoinHandle; use tokio_stream::StreamExt; -use evdev::{Device, EventStream}; -use crate::Config; -use crate::config::{Event, Associations}; -use crate::event_reader::EventReader; - #[derive(Debug, Default, Eq, PartialEq, Hash, Clone)] pub enum Client { @@ -32,11 +31,15 @@ pub struct Environment { pub async fn start_monitoring_udev(config_files: Vec, mut tasks: Vec>) { let environment = set_environment(); launch_tasks(&config_files, &mut tasks, environment.clone()); - let mut monitor = tokio_udev::AsyncMonitorSocket::new ( - tokio_udev::MonitorBuilder::new().unwrap() - .match_subsystem(std::ffi::OsStr::new("input")).unwrap() - .listen().unwrap() - ).unwrap(); + let mut monitor = tokio_udev::AsyncMonitorSocket::new( + tokio_udev::MonitorBuilder::new() + .unwrap() + .match_subsystem(std::ffi::OsStr::new("input")) + .unwrap() + .listen() + .unwrap(), + ) + .unwrap(); while let Some(Ok(event)) = monitor.next().await { if is_mapped(&event.device(), &config_files) { println!("---------------------\n\nReinitializing...\n"); @@ -49,15 +52,27 @@ pub async fn start_monitoring_udev(config_files: Vec, mut tasks: Vec, tasks: &mut Vec>, environment: Environment) { +pub fn launch_tasks( + config_files: &Vec, + tasks: &mut Vec>, + environment: Environment, +) { let modifiers: Arc>> = Arc::new(Mutex::new(Default::default())); let modifier_was_activated: Arc> = Arc::new(Mutex::new(true)); let user_has_access = match Command::new("groups").output() { - Ok(groups) if std::str::from_utf8(&groups.stdout.as_slice()).unwrap().contains("input") => { + Ok(groups) + if std::str::from_utf8(&groups.stdout.as_slice()) + .unwrap() + .contains("input") => + { println!("Evdev permissions available.\nScanning for event devices with a matching config file...\n"); true - }, - Ok(groups) if std::str::from_utf8(&groups.stdout.as_slice()).unwrap().contains("root") => { + } + Ok(groups) + if std::str::from_utf8(&groups.stdout.as_slice()) + .unwrap() + .contains("root") => + { println!("Root permissions available.\nScanning for event devices with a matching config file...\n"); true } @@ -65,11 +80,13 @@ pub fn launch_tasks(config_files: &Vec, tasks: &mut Vec>, println!("Warning: user has no access to event devices, Makima might not be able to detect all connected devices.\n\ Note: Run Makima with 'sudo -E makima' or as a system service. Refer to the docs for more info. Continuing...\n"); false - }, + } Err(_) => { - println!("Warning: unable to determine if user has access to event devices. Continuing...\n"); + println!( + "Warning: unable to determine if user has access to event devices. Continuing...\n" + ); false - }, + } }; let devices: evdev::EnumerateDevices = evdev::enumerate(); let mut devices_found = 0; @@ -79,7 +96,7 @@ pub fn launch_tasks(config_files: &Vec, tasks: &mut Vec>, let split_config_name = config.name.split("::").collect::>(); let associated_device_name = split_config_name[0]; if associated_device_name == device.1.name().unwrap().replace("/", "") { - let (window_class, layout) = match split_config_name.len() { + let (window_class, layout) = match split_config_name.len() { 1 => (Client::Default, 0), 2 => { if let Ok(layout) = split_config_name[1].parse::() { @@ -87,7 +104,7 @@ pub fn launch_tasks(config_files: &Vec, tasks: &mut Vec>, } else { (Client::Class(split_config_name[1].to_string()), 0) } - }, + } 3 => { if let Ok(layout) = split_config_name[1].parse::() { (Client::Class(split_config_name[2].to_string()), layout) @@ -97,24 +114,37 @@ pub fn launch_tasks(config_files: &Vec, tasks: &mut Vec>, println!("Warning: unable to parse layout number in {}, treating it as default.", config.name); (Client::Default, 0) } - }, + } _ => { println!("Warning: too many arguments in config file name {}, treating it as default.", config.name); (Client::Default, 0) - }, - }; + } + }; config.associations.client = window_class; config.associations.layout = layout; config_list.push(config.clone()); }; } - if config_list.len() > 0 && !config_list.iter().any(|x| x.associations == Associations::default()) { + if config_list.len() > 0 + && !config_list + .iter() + .any(|x| x.associations == Associations::default()) + { config_list.push(Config::new_empty(device.1.name().unwrap().replace("/", ""))); } let event_device = device.0.as_path().to_str().unwrap().to_string(); if config_list.len() != 0 { - let stream = Arc::new(Mutex::new(get_event_stream(Path::new(&event_device), config_list.clone()))); - let reader = EventReader::new(config_list.clone(), stream, modifiers.clone(), modifier_was_activated.clone(), environment.clone()); + let stream = Arc::new(Mutex::new(get_event_stream( + Path::new(&event_device), + config_list.clone(), + ))); + let reader = EventReader::new( + config_list.clone(), + stream, + modifiers.clone(), + modifier_was_activated.clone(), + environment.clone(), + ); tasks.push(tokio::spawn(start_reader(reader))); devices_found += 1 } @@ -133,24 +163,38 @@ pub async fn start_reader(reader: EventReader) { fn set_environment() -> Environment { match env::var("DBUS_SESSION_BUS_ADDRESS") { Ok(_) => { - let command = Command::new("sh").arg("-c").arg("systemctl --user show-environment").output().unwrap(); - let vars = std::str::from_utf8(command.stdout.as_slice()).unwrap().split("\n").collect::>(); + let command = Command::new("sh") + .arg("-c") + .arg("systemctl --user show-environment") + .output() + .unwrap(); + let vars = std::str::from_utf8(command.stdout.as_slice()) + .unwrap() + .split("\n") + .collect::>(); for var in vars { if let Some((variable, value)) = var.split_once("=") { - if let Err(env::VarError::NotPresent) = env::var(variable) { - env::set_var(variable, value); + if let Err(env::VarError::NotPresent) = env::var(variable) { + env::set_var(variable, value); } } } - }, + } Err(_) => { let uid = Command::new("sh").arg("-c").arg("id -u").output().unwrap(); let uid_number = std::str::from_utf8(uid.stdout.as_slice()).unwrap().trim(); if uid_number != "0" { let bus_address = format!("unix:path=/run/user/{}/bus", uid_number); env::set_var("DBUS_SESSION_BUS_ADDRESS", bus_address); - let command = Command::new("sh").arg("-c").arg("systemctl --user show-environment").output().unwrap(); - let vars = std::str::from_utf8(command.stdout.as_slice()).unwrap().split("\n").collect::>(); + let command = Command::new("sh") + .arg("-c") + .arg("systemctl --user show-environment") + .output() + .unwrap(); + let vars = std::str::from_utf8(command.stdout.as_slice()) + .unwrap() + .split("\n") + .collect::>(); for var in vars { if let Some((variable, value)) = var.split_once("=") { if let Err(env::VarError::NotPresent) = env::var(variable) { @@ -166,21 +210,33 @@ fn set_environment() -> Environment { println!("Warning: unable to inherit user environment.\n\ Launch Makima with 'sudo -E makima' or make sure that your systemd unit is running with the 'User=' parameter.\n"); } - }, + } }; - if let (Err(env::VarError::NotPresent), Ok(_)) = (env::var("XDG_SESSION_TYPE"), env::var("WAYLAND_DISPLAY")) { + if let (Err(env::VarError::NotPresent), Ok(_)) = + (env::var("XDG_SESSION_TYPE"), env::var("WAYLAND_DISPLAY")) + { env::set_var("XDG_SESSION_TYPE", "wayland") } - let supported_compositors = vec!["Hyprland", "sway", "KDE"].into_iter().map(|str| String::from(str)).collect::>(); + let supported_compositors = vec!["Hyprland", "sway", "KDE"] + .into_iter() + .map(|str| String::from(str)) + .collect::>(); let (x11, wayland) = (String::from("x11"), String::from("wayland")); - let server: Server = match (env::var("XDG_SESSION_TYPE"), env::var("XDG_CURRENT_DESKTOP")) { - (Ok(session), Ok(desktop)) if session == wayland && supported_compositors.contains(&desktop) => { + let server: Server = match ( + env::var("XDG_SESSION_TYPE"), + env::var("XDG_CURRENT_DESKTOP"), + ) { + (Ok(session), Ok(desktop)) + if session == wayland && supported_compositors.contains(&desktop) => + { let server = 'a: { if desktop == String::from("KDE") { if let Err(_) = Command::new("kdotool").output() { - println!("Running on KDE but kdotool doesn't seem to be installed.\n\ - Won't be able to change bindings according to the active window.\n"); + println!( + "Running on KDE but kdotool doesn't seem to be installed.\n\ + Won't be able to change bindings according to the active window.\n" + ); break 'a Server::Unsupported; } } @@ -188,28 +244,28 @@ fn set_environment() -> Environment { Server::Connected(desktop) }; server - }, + } (Ok(session), Ok(desktop)) if session == wayland => { println!("Warning: unsupported compositor: {}, won't be able to change bindings according to the active window.\n\ Currently supported desktops: Hyprland, Sway, Plasma/KWin, X11.\n", desktop); Server::Unsupported - }, + } (Ok(session), _) if session == x11 => { println!("Running on X11, per application bindings enabled."); Server::Connected(session) - }, + } (Ok(session), Err(_)) if session == wayland => { println!("Warning: unable to retrieve the current desktop based on XDG_CURRENT_DESKTOP env var.\n\ Won't be able to change bindings according to the active window.\n"); Server::Unsupported - }, + } (Err(_), _) => { println!("Warning: unable to retrieve the session type based on XDG_SESSION_TYPE or WAYLAND_DISPLAY env vars.\n\ Is your Wayland compositor or X server running?\n\ Exiting Makima."); std::process::exit(0); - }, - _ => Server::Failed + } + _ => Server::Failed, }; Environment { @@ -221,16 +277,26 @@ fn set_environment() -> Environment { pub fn get_event_stream(path: &Path, config: Vec) -> EventStream { let mut device: Device = Device::open(path).expect("Couldn't open device path."); - match config.iter().find(|&x| x.associations == Associations::default()).unwrap().settings.get("GRAB_DEVICE") { + match config + .iter() + .find(|&x| x.associations == Associations::default()) + .unwrap() + .settings + .get("GRAB_DEVICE") + { Some(value) => { if value == &true.to_string() { - device.grab().expect("Unable to grab device. Is another instance of Makima running?") + device + .grab() + .expect("Unable to grab device. Is another instance of Makima running?") } } - None => device.grab().expect("Unable to grab device. Is another instance of Makima running?") + None => device + .grab() + .expect("Unable to grab device. Is another instance of Makima running?"), } let stream: EventStream = device.into_event_stream().unwrap(); - return stream + return stream; } pub fn is_mapped(udev_device: &tokio_udev::Device, config_files: &Vec) -> bool { @@ -239,15 +305,17 @@ pub fn is_mapped(udev_device: &tokio_udev::Device, config_files: &Vec) - let evdev_devices: evdev::EnumerateDevices = evdev::enumerate(); for evdev_device in evdev_devices { for config in config_files { - if config.name.contains(&evdev_device.1.name().unwrap().to_string()) - && devnode.to_path_buf() == evdev_device.0 { - return true + if config + .name + .contains(&evdev_device.1.name().unwrap().to_string()) + && devnode.to_path_buf() == evdev_device.0 + { + return true; } } } } - _ => return false + _ => return false, } - return false + return false; } - diff --git a/src/virtual_devices.rs b/src/virtual_devices.rs index 46b76f5..d835f77 100644 --- a/src/virtual_devices.rs +++ b/src/virtual_devices.rs @@ -1,5 +1,7 @@ -use evdev::{Key, uinput::{VirtualDevice, VirtualDeviceBuilder}}; - +use evdev::{ + uinput::{VirtualDevice, VirtualDeviceBuilder}, + Key, +}; pub struct VirtualDevices { pub keys: VirtualDevice, @@ -9,9 +11,13 @@ pub struct VirtualDevices { impl VirtualDevices { pub fn new() -> Self { let mut key_capabilities = evdev::AttributeSet::new(); - for i in 1..334 {key_capabilities.insert(Key(i));}; + for i in 1..334 { + key_capabilities.insert(Key(i)); + } let mut axis_capabilities = evdev::AttributeSet::new(); - for i in 0..13 {axis_capabilities.insert(evdev::RelativeAxisType(i));}; + for i in 0..13 { + axis_capabilities.insert(evdev::RelativeAxisType(i)); + } let keys_builder = VirtualDeviceBuilder::new() .expect("Unable to create virtual device through uinput. Take a look at the Troubleshooting section for more info.") .name("Makima Virtual Keyboard/Mouse") @@ -28,4 +34,3 @@ impl VirtualDevices { } } } -