Wrap compositor status and active client into their own enum, split environment setup and task spawning
This commit is contained in:
parent
0e89f5aa19
commit
6092acc177
1 changed files with 90 additions and 69 deletions
|
@ -2,20 +2,35 @@ use std::{collections::HashMap, sync::Arc, path::Path, process::Command, env};
|
|||
use tokio::sync::Mutex;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_stream::StreamExt;
|
||||
use evdev::{Device, EventStream, Key};
|
||||
use evdev::{Device, EventStream};
|
||||
use crate::Config;
|
||||
use crate::config::Event;
|
||||
use crate::event_reader::EventReader;
|
||||
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Clone)]
|
||||
pub enum Client {
|
||||
Default,
|
||||
Class(String),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Server {
|
||||
Connected(String),
|
||||
Unsupported,
|
||||
Failed,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Environment {
|
||||
pub user: Result<String, env::VarError>,
|
||||
pub sudo_user: Result<String, env::VarError>,
|
||||
pub session_address: Result<String, env::VarError>,
|
||||
pub server: Server,
|
||||
}
|
||||
|
||||
pub async fn start_monitoring_udev(config_files: Vec<Config>, mut tasks: Vec<JoinHandle<()>>) {
|
||||
launch_tasks(&config_files, &mut tasks);
|
||||
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()
|
||||
|
@ -28,67 +43,14 @@ pub async fn start_monitoring_udev(config_files: Vec<Config>, mut tasks: Vec<Joi
|
|||
task.abort();
|
||||
}
|
||||
tasks.clear();
|
||||
launch_tasks(&config_files, &mut tasks)
|
||||
launch_tasks(&config_files, &mut tasks, environment.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn launch_tasks(config_files: &Vec<Config>, tasks: &mut Vec<JoinHandle<()>>) {
|
||||
let modifiers: Arc<Mutex<Vec<Key>>> = Arc::new(Mutex::new(Default::default()));
|
||||
pub fn launch_tasks(config_files: &Vec<Config>, tasks: &mut Vec<JoinHandle<()>>, environment: Environment) {
|
||||
let modifiers: Arc<Mutex<Vec<Event>>> = Arc::new(Mutex::new(Default::default()));
|
||||
let modifier_was_activated: Arc<Mutex<bool>> = Arc::new(Mutex::new(true));
|
||||
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::<Vec<&str>>();
|
||||
for var in vars {
|
||||
if var != "" && !var.contains("DBUS_SESSION_BUS_ADDRESS") {
|
||||
let split_var = var.split("=").collect::<Vec<&str>>();
|
||||
env::set_var(split_var[0], split_var[1]);
|
||||
}
|
||||
}
|
||||
true
|
||||
},
|
||||
Err(_) => {
|
||||
println!("Warning: unable to inherit user environment.\n\
|
||||
Launch Makima with 'sudo -E makima' or add the DBUS_SESSION_BUS_ADDRESS env var to your systemd unit if you're running it through systemd.\n");
|
||||
false
|
||||
},
|
||||
};
|
||||
let environment = Environment {
|
||||
user: env::var("USER"),
|
||||
sudo_user: env::var("SUDO_USER"),
|
||||
session_address: env::var("DBUS_SESSION_BUS_ADDRESS"),
|
||||
};
|
||||
let mut session_var = "WAYLAND_DISPLAY";
|
||||
if let Err(env::VarError::NotPresent) = env::var(session_var) {
|
||||
session_var = "XDG_SESSION_TYPE";
|
||||
}
|
||||
let current_desktop: Option<String> = match (env::var(session_var), env::var("XDG_CURRENT_DESKTOP")) {
|
||||
(Ok(session), Ok(desktop)) if session.contains("wayland") && vec!["Hyprland".to_string(), "sway".to_string()].contains(&desktop) => {
|
||||
println!("Running on {}, active window tracking enabled.", desktop);
|
||||
Option::Some(desktop)
|
||||
},
|
||||
(Ok(session), Ok(desktop)) if session.contains("wayland") => {
|
||||
println!("Warning: unsupported compositor: {}, won't be able to change bindings according to active window.\n\
|
||||
Currently supported desktops: Hyprland, Sway, X11.\n", desktop);
|
||||
Option::None
|
||||
},
|
||||
(Ok(session), _) if session == "x11".to_string() => {
|
||||
println!("Running on X11, active window tracking enabled.");
|
||||
Option::Some("x11".to_string())
|
||||
},
|
||||
(Ok(session), Err(_)) if session.contains("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");
|
||||
Option::None
|
||||
},
|
||||
(Err(_), _) => {
|
||||
println!("Warning: unable to retrieve the session type based on XDG_SESSION_TYPE or WAYLAND_DISPLAY env vars.\n\
|
||||
Won't be able to change bindings according to the active window.\n");
|
||||
Option::None
|
||||
},
|
||||
_ => Option::None
|
||||
};
|
||||
let user_has_access = match Command::new("groups").output() {
|
||||
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");
|
||||
|
@ -104,33 +66,33 @@ pub fn launch_tasks(config_files: &Vec<Config>, tasks: &mut Vec<JoinHandle<()>>)
|
|||
false
|
||||
},
|
||||
Err(_) => {
|
||||
println!("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;
|
||||
for device in devices {
|
||||
let mut config_map: HashMap<String, Config> = HashMap::new();
|
||||
let mut config_map: HashMap<Client, Config> = HashMap::new();
|
||||
for config in config_files {
|
||||
let split_config_name = config.name.split("::").collect::<Vec<&str>>();
|
||||
let associated_device_name = split_config_name[0];
|
||||
if associated_device_name == device.1.name().unwrap().replace("/", "") {
|
||||
let window_class = if split_config_name.len() == 1 {
|
||||
String::from("default")
|
||||
Client::Default
|
||||
} else {
|
||||
split_config_name[1].to_string()
|
||||
Client::Class(split_config_name[1].to_string())
|
||||
};
|
||||
config_map.insert(window_class, config.clone());
|
||||
};
|
||||
}
|
||||
if config_map.len() > 0 && !config_map.contains_key(&"default".to_string()) {
|
||||
config_map.insert("default".to_string(), Config::new_empty(device.1.name().unwrap().replace("/", "")));
|
||||
if config_map.len() > 0 && !config_map.contains_key(&Client::Default) {
|
||||
config_map.insert(Client::Default, Config::new_empty(device.1.name().unwrap().replace("/", "")));
|
||||
}
|
||||
let event_device = device.0.as_path().to_str().unwrap().to_string();
|
||||
if !config_map.is_empty() {
|
||||
let stream = Arc::new(Mutex::new(get_event_stream(Path::new(&event_device), config_map.clone())));
|
||||
let reader = EventReader::new(config_map.clone(), stream, modifiers.clone(), modifier_was_activated.clone(), environment.clone(), current_desktop.clone());
|
||||
let reader = EventReader::new(config_map.clone(), stream, modifiers.clone(), modifier_was_activated.clone(), environment.clone());
|
||||
tasks.push(tokio::spawn(start_reader(reader)));
|
||||
devices_found += 1
|
||||
}
|
||||
|
@ -138,7 +100,7 @@ pub fn launch_tasks(config_files: &Vec<Config>, tasks: &mut Vec<JoinHandle<()>>)
|
|||
if devices_found == 0 && !user_has_access {
|
||||
println!("No matching devices found.\nNote: make sure that your user has access to event devices.\n");
|
||||
} else if devices_found == 0 && user_has_access {
|
||||
println!("No matching devices found.\nNote: double-check that your device and its respective config file have the same name, as reported by `evtest`.\n");
|
||||
println!("No matching devices found.\nNote: double-check that your device and its associated config file have the same name, as reported by 'evtest'.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,9 +108,68 @@ pub async fn start_reader(reader: EventReader) {
|
|||
reader.start().await;
|
||||
}
|
||||
|
||||
pub fn get_event_stream(path: &Path, config: HashMap<String, Config>) -> EventStream {
|
||||
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::<Vec<&str>>();
|
||||
for var in vars {
|
||||
if let Some((variable, value)) = var.split_once("=") {
|
||||
env::set_var(variable, value);
|
||||
}
|
||||
}
|
||||
true
|
||||
},
|
||||
Err(_) => {
|
||||
println!("Warning: unable to inherit user environment.\n\
|
||||
Launch Makima with 'sudo -E makima' or add the DBUS_SESSION_BUS_ADDRESS env var to your systemd unit if you're running it through systemd.\n");
|
||||
false
|
||||
},
|
||||
};
|
||||
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"].into_iter().map(|str| String::from(str)).collect::<Vec<String>>();
|
||||
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) => {
|
||||
println!("Running on {}, per application bindings enabled.", desktop);
|
||||
Server::Connected(desktop)
|
||||
},
|
||||
(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, 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
|
||||
};
|
||||
|
||||
Environment {
|
||||
user: env::var("USER"),
|
||||
sudo_user: env::var("SUDO_USER"),
|
||||
server,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_event_stream(path: &Path, config: HashMap<Client, Config>) -> EventStream {
|
||||
let mut device: Device = Device::open(path).expect("Couldn't open device path.");
|
||||
match config.get("default").unwrap().settings.get("GRAB_DEVICE") {
|
||||
match config.get(&Client::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?")
|
||||
|
|
Loading…
Add table
Reference in a new issue