You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.7 KiB
121 lines
3.7 KiB
use clap::{arg, App};
|
|
use evdev::Device;
|
|
use evdev::Key;
|
|
use glob::glob;
|
|
use nix::unistd;
|
|
use std::env;
|
|
use std::path::Path;
|
|
use std::process::exit;
|
|
|
|
pub fn main() {
|
|
/* Clap builder for flag handling */
|
|
let args = App::new("swhkd")
|
|
.version(env!("CARGO_PKG_VERSION"))
|
|
.author("Shinyzenith <aakashsensharma@gmail.com>")
|
|
.about("Simple Wayland HotKey Daemon")
|
|
.arg(
|
|
arg!(-c --config <CONFIG_FILE_PATH>)
|
|
.required(false)
|
|
.help("Set a custom config file path"),
|
|
)
|
|
.arg(arg!(-d - -debug).required(false).help("Enable debug mode"))
|
|
.get_matches();
|
|
|
|
/* Set log level to debug if flag is present */
|
|
if args.is_present("debug") {
|
|
env::set_var("RUST_LOG", "swhkd=trace");
|
|
}
|
|
|
|
env_logger::init();
|
|
log::trace!("Logger initialized.");
|
|
|
|
if permission_check() == false {
|
|
exit(1);
|
|
}
|
|
|
|
/* Get appropriate config file path */
|
|
let config_file_path: std::path::PathBuf;
|
|
if args.is_present("config") {
|
|
config_file_path = Path::new(args.value_of("config").unwrap()).to_path_buf();
|
|
if config_file_path.exists() == false {
|
|
log::error!("{:#?} path doesn't exist", config_file_path);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
match env::var("XDG_CONFIG_HOME") {
|
|
Ok(val) => {
|
|
config_file_path = Path::new(&val).join("swhkd/swhkdrc");
|
|
log::debug!("XDG_CONFIG_HOME exists: {:#?}", val);
|
|
}
|
|
Err(_) => {
|
|
log::error!("XDG_CONFIG_HOME has not been set.");
|
|
match env::var("HOME") {
|
|
Ok(val) => {
|
|
config_file_path = Path::new(&val).join(".config/swhkd/swhkdrc");
|
|
}
|
|
Err(_) => {
|
|
log::error!("HOME env var has not been set");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
log::debug!("Using config file path: {:#?}", config_file_path);
|
|
|
|
log::trace!("Attempting to find all keyboard file descriptors.");
|
|
for entry in glob("/dev/input/event*").expect("Failed to read /dev/input/event*.") {
|
|
match entry {
|
|
Ok(path) => {
|
|
check_keyboard(path.into_os_string().into_string().unwrap());
|
|
}
|
|
Err(error) => log::error!("Unexpected error occured: {}", error),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn permission_check() -> bool {
|
|
if unistd::Uid::current().is_root() {
|
|
log::error!("Refusing to run swhkd as root.");
|
|
return false;
|
|
}
|
|
|
|
log::trace!("Checking if invoking user is in input group.");
|
|
let groups = unistd::getgroups();
|
|
for (_, groups) in groups.iter().enumerate() {
|
|
for group in groups {
|
|
let group = unistd::Group::from_gid(*group);
|
|
if group.unwrap().unwrap().name == "input" {
|
|
log::debug!("Invoking user is in input group.");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
log::error!("Invoking user is NOT in input group.");
|
|
return false;
|
|
}
|
|
|
|
pub fn check_keyboard(input_path: String) -> bool {
|
|
/* Try to open the device fd. */
|
|
let device = Device::open(&input_path);
|
|
match &device {
|
|
Ok(_) => (),
|
|
Err(error) => {
|
|
log::error!("Failed to open device {}: {}", input_path, error);
|
|
}
|
|
}
|
|
|
|
/* Check for the presence of enter key. */
|
|
if device
|
|
.unwrap()
|
|
.supported_keys()
|
|
.map_or(false, |keys| keys.contains(Key::KEY_ENTER))
|
|
{
|
|
log::debug!("{} is a keyboard.", input_path);
|
|
return true;
|
|
} else {
|
|
log::debug!("{} is not a keyboard.", input_path);
|
|
return false;
|
|
}
|
|
}
|