yay
This commit is contained in:
parent
8a5f321f37
commit
4d58c95c73
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "keydisplay"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.8"
|
||||
dirs = "5.0"
|
||||
inputbot = "0.6.0"
|
||||
thiserror = "2.0.11"
|
38
README.md
38
README.md
@ -1,2 +1,40 @@
|
||||
# keydisplay
|
||||
note: you will need to add yourself to the input group or run as root if you intend to use wayland
|
||||
|
||||
you can do it like this
|
||||
|
||||
`doas usermod -aG input $USER`
|
||||
|
||||
## config.rs
|
||||
- use serde with a config struct
|
||||
|
||||
- impl with defaults and read function
|
||||
|
||||
### read:
|
||||
- get the current users config folder
|
||||
|
||||
- make a folder if the "keydisplay" folder doesnt exist
|
||||
|
||||
- make a keydisplay/config.toml if it doesnt exist
|
||||
|
||||
- give it 600 permissions
|
||||
|
||||
config:
|
||||
- theme = {catppuccin_mocha, grey, night, day}
|
||||
- [listen]
|
||||
- - keys = "z, x"
|
||||
- - mouse = "m1, m2"
|
||||
- [display]
|
||||
- - mouse = "key"
|
||||
|
||||
## listen.rs
|
||||
- use inputbot to listen for inputs https://github.com/obv-mikhail/InputBot
|
||||
|
||||
- keys you can use are here https://docs.rs/inputbot/latest/src/inputbot/public.rs.html#300-352
|
||||
|
||||
## main.rs
|
||||
- read config
|
||||
|
||||
- start listener on keys from config
|
||||
|
||||
- load gui using theme
|
||||
|
95
src/config.rs
Normal file
95
src/config.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ConfigError {
|
||||
#[error("Failed to read config file: {0}")]
|
||||
FileRead(#[from] std::io::Error),
|
||||
#[error("Failed to parse config file: {0}")]
|
||||
Parse(#[from] toml::ser::Error),
|
||||
#[error("Failed to parse config file: {0}")]
|
||||
ParseDeserialise(#[from] toml::de::Error),
|
||||
#[error("Failed to write config file: {0}")]
|
||||
FileWrite(std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
// theme = grey / night / day / catppuccin_mocha
|
||||
pub theme: String,
|
||||
pub listen: ListenConfig,
|
||||
pub display: DisplayConfig,
|
||||
}
|
||||
|
||||
// [listen]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ListenConfig {
|
||||
// keys = ["KeyZ", "KeyX"]
|
||||
pub keys: Vec<String>,
|
||||
// mouse = ["LeftButton", "RightButton"]
|
||||
pub mouse: Vec<String>,
|
||||
}
|
||||
|
||||
// [display]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DisplayConfig {
|
||||
pub mouse: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
// Provide default values
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
theme: "grey".to_string(),
|
||||
listen: ListenConfig {
|
||||
keys: vec!["ZKey".to_string(), "XKey".to_string()],
|
||||
mouse: vec!["LeftButton".to_string(), "RightButton".to_string()],
|
||||
},
|
||||
display: DisplayConfig {
|
||||
mouse: "key".to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Load config from a file, creating it with defaults if it doesn't exist
|
||||
pub fn load_or_create(path: PathBuf) -> Result<Self, ConfigError> {
|
||||
let full_path = path.join("config.toml");
|
||||
|
||||
if !full_path.exists() {
|
||||
println!("Creating default config");
|
||||
let config = Self::default();
|
||||
|
||||
fs::create_dir_all(&path)?;
|
||||
|
||||
config.save(&full_path)?;
|
||||
return Ok(config);
|
||||
}
|
||||
|
||||
println!("Loading default config from {full_path:?}");
|
||||
let content = fs::read_to_string(&full_path)?;
|
||||
let config: Config = toml::from_str(&content)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
// Save config to a file
|
||||
pub fn save(&self, path: &PathBuf) -> Result<(), ConfigError> {
|
||||
let content = toml::to_string_pretty(&self).map_err(|e| ConfigError::Parse(e))?;
|
||||
|
||||
fs::write(path, content).map_err(ConfigError::FileWrite)?;
|
||||
|
||||
// Ensure proper permissions on Unix-like systems
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = fs::metadata(path)
|
||||
.map_err(ConfigError::FileWrite)?
|
||||
.permissions();
|
||||
perms.set_mode(0o600); // Read/write for owner only
|
||||
fs::set_permissions(path, perms).map_err(ConfigError::FileWrite)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
318
src/listen.rs
Normal file
318
src/listen.rs
Normal file
@ -0,0 +1,318 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use inputbot::{KeybdKey, KeybdKey::*, MouseButton, MouseButton::*};
|
||||
|
||||
pub fn listen(keys: Vec<KeybdKey>, m_buttons: Vec<MouseButton>) {
|
||||
for key in keys {
|
||||
key.bind(move || {
|
||||
recieve_key_press(key);
|
||||
while key.is_pressed() {
|
||||
// theres probably a better way to do this
|
||||
}
|
||||
recieve_key_release(key);
|
||||
})
|
||||
}
|
||||
|
||||
for button in m_buttons {
|
||||
button.bind(move || {
|
||||
recieve_mouse_press(button);
|
||||
while button.is_pressed() {}
|
||||
recieve_mouse_release(button);
|
||||
})
|
||||
}
|
||||
|
||||
MiddleButton.bind(|| {
|
||||
println!("aaa");
|
||||
});
|
||||
|
||||
inputbot::handle_input_events();
|
||||
}
|
||||
|
||||
// move me into gui.rs later
|
||||
fn recieve_key_press(key: KeybdKey) {
|
||||
println!("pressed {}", string_from_keybdkey(key));
|
||||
}
|
||||
|
||||
fn recieve_key_release(key: KeybdKey) {
|
||||
println!("released {}", string_from_keybdkey(key));
|
||||
}
|
||||
|
||||
fn recieve_mouse_press(button: MouseButton) {
|
||||
println!("pressed {}", string_from_mousebutton(button));
|
||||
}
|
||||
|
||||
fn recieve_mouse_release(button: MouseButton) {
|
||||
println!("released {}", string_from_mousebutton(button));
|
||||
}
|
||||
|
||||
pub fn keybdkey_from_config(key: &str) -> KeybdKey {
|
||||
match key {
|
||||
"BackspaceKey" => BackspaceKey,
|
||||
"TabKey" => TabKey,
|
||||
"EnterKey" => EnterKey,
|
||||
"EscapeKey" => EscapeKey,
|
||||
"SpaceKey" => EscapeKey,
|
||||
"PageUpKey" => PageUpKey,
|
||||
"PageDownKey" => PageDownKey,
|
||||
"EndKey" => EndKey,
|
||||
"HomeKey" => HomeKey,
|
||||
"LeftKey" => LeftKey,
|
||||
"UpKey" => UpKey,
|
||||
"RightKey" => RightKey,
|
||||
"DownKey" => DownKey,
|
||||
"InsertKey" => InsertKey,
|
||||
"DeleteKey" => DeleteKey,
|
||||
"Numrow0Key" => Numrow0Key,
|
||||
"Numrow1Key" => Numrow1Key,
|
||||
"Numrow2Key" => Numrow2Key,
|
||||
"Numrow3Key" => Numrow3Key,
|
||||
"Numrow4Key" => Numrow4Key,
|
||||
"Numrow5Key" => Numrow5Key,
|
||||
"Numrow6Key" => Numrow6Key,
|
||||
"Numrow7Key" => Numrow7Key,
|
||||
"Numrow8Key" => Numrow8Key,
|
||||
"Numrow9Key" => Numrow9Key,
|
||||
"AKey" => AKey,
|
||||
"BKey" => BKey,
|
||||
"CKey" => CKey,
|
||||
"DKey" => DKey,
|
||||
"EKey" => EKey,
|
||||
"FKey" => FKey,
|
||||
"GKey" => GKey,
|
||||
"HKey" => HKey,
|
||||
"IKey" => IKey,
|
||||
"JKey" => JKey,
|
||||
"KKey" => KKey,
|
||||
"LKey" => LKey,
|
||||
"MKey" => MKey,
|
||||
"NKey" => NKey,
|
||||
"OKey" => OKey,
|
||||
"PKey" => PKey,
|
||||
"QKey" => QKey,
|
||||
"RKey" => RKey,
|
||||
"SKey" => SKey,
|
||||
"TKey" => TKey,
|
||||
"UKey" => UKey,
|
||||
"VKey" => VKey,
|
||||
"WKey" => WKey,
|
||||
"XKey" => XKey,
|
||||
"YKey" => YKey,
|
||||
"ZKey" => ZKey,
|
||||
"LSuper" => LSuper,
|
||||
"RSuper" => RSuper,
|
||||
"Numpad0Key" => Numpad0Key,
|
||||
"Numpad1Key" => Numpad1Key,
|
||||
"Numpad2Key" => Numpad2Key,
|
||||
"Numpad3Key" => Numpad3Key,
|
||||
"Numpad4Key" => Numpad4Key,
|
||||
"Numpad5Key" => Numpad5Key,
|
||||
"Numpad6Key" => Numpad6Key,
|
||||
"Numpad7Key" => Numpad7Key,
|
||||
"Numpad8Key" => Numpad8Key,
|
||||
"Numpad9Key" => Numpad9Key,
|
||||
"F1Key" => F1Key,
|
||||
"F2Key" => F2Key,
|
||||
"F3Key" => F3Key,
|
||||
"F4Key" => F4Key,
|
||||
"F5Key" => F5Key,
|
||||
"F6Key" => F6Key,
|
||||
"F7Key" => F7Key,
|
||||
"F8Key" => F8Key,
|
||||
"F9Key" => F9Key,
|
||||
"F10Key" => F10Key,
|
||||
"F11Key" => F11Key,
|
||||
"F12Key" => F12Key,
|
||||
"F13Key" => F13Key,
|
||||
"F14Key" => F14Key,
|
||||
"F15Key" => F15Key,
|
||||
"F16Key" => F16Key,
|
||||
"F17Key" => F17Key,
|
||||
"F18Key" => F18Key,
|
||||
"F19Key" => F19Key,
|
||||
"F20Key" => F20Key,
|
||||
"F21Key" => F21Key,
|
||||
"F22Key" => F22Key,
|
||||
"F23Key" => F23Key,
|
||||
"F24Key" => F24Key,
|
||||
"NumLockKey" => NumLockKey,
|
||||
"ScrollLockKey" => ScrollLockKey,
|
||||
"CapsLockKey" => CapsLockKey,
|
||||
"LShiftKey" => LShiftKey,
|
||||
"RShiftKey" => RShiftKey,
|
||||
"LControlKey" => LControlKey,
|
||||
"RControlKey" => RControlKey,
|
||||
"LAltKey" => LAltKey,
|
||||
"RAltKey" => RAltKey,
|
||||
|
||||
"BrowserBackKey" => BrowserBackKey,
|
||||
"BrowserForwardKey" => BrowserForwardKey,
|
||||
"BrowserRefreshKey" => BrowserRefreshKey,
|
||||
|
||||
"VolumeMuteKey" => VolumeMuteKey,
|
||||
"VolumeDownKey" => VolumeDownKey,
|
||||
"VolumeUpKey" => VolumeUpKey,
|
||||
|
||||
"MediaNextTrackKey" => MediaNextTrackKey,
|
||||
"MediaPrevTrackKey" => MediaPrevTrackKey,
|
||||
"MediaStopKey" => MediaStopKey,
|
||||
"MediaPlayPauseKey" => MediaPlayPauseKey,
|
||||
|
||||
"BackquoteKey" => BackquoteKey,
|
||||
"SlashKey" => SlashKey,
|
||||
"BackslashKey" => BackslashKey,
|
||||
"CommaKey" => CommaKey,
|
||||
"PeriodKey" => PeriodKey,
|
||||
"MinusKey" => MinusKey,
|
||||
"QuoteKey" => QuoteKey,
|
||||
"SemicolonKey" => SemicolonKey,
|
||||
"LBracketKey" => LBracketKey,
|
||||
"RBracketKey" => RBracketKey,
|
||||
"EqualKey" => EqualKey,
|
||||
_ => panic!("invalid keycode!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn string_from_keybdkey(key: KeybdKey) -> String {
|
||||
match key {
|
||||
BackspaceKey => String::from("🡐"),
|
||||
TabKey => String::from("⇌"),
|
||||
EnterKey => String::from("⮠"),
|
||||
EscapeKey => String::from("ESC"),
|
||||
SpaceKey => String::from(""),
|
||||
PageUpKey => String::from("PGUP"),
|
||||
PageDownKey => String::from("PGDN"),
|
||||
EndKey => String::from("END"),
|
||||
HomeKey => String::from("HOME"),
|
||||
LeftKey => String::from("⯇"),
|
||||
UpKey => String::from("⯅"),
|
||||
RightKey => String::from("⯈"),
|
||||
DownKey => String::from("⯆"),
|
||||
InsertKey => String::from("INS"),
|
||||
DeleteKey => String::from("DEL"),
|
||||
Numrow0Key => String::from("0"),
|
||||
Numrow1Key => String::from("1"),
|
||||
Numrow2Key => String::from("2"),
|
||||
Numrow3Key => String::from("3"),
|
||||
Numrow4Key => String::from("4"),
|
||||
Numrow5Key => String::from("5"),
|
||||
Numrow6Key => String::from("6"),
|
||||
Numrow7Key => String::from("7"),
|
||||
Numrow8Key => String::from("8"),
|
||||
Numrow9Key => String::from("9"),
|
||||
AKey => String::from("A"),
|
||||
BKey => String::from("B"),
|
||||
CKey => String::from("C"),
|
||||
DKey => String::from("D"),
|
||||
EKey => String::from("E"),
|
||||
FKey => String::from("F"),
|
||||
GKey => String::from("G"),
|
||||
HKey => String::from("H"),
|
||||
IKey => String::from("I"),
|
||||
JKey => String::from("J"),
|
||||
KKey => String::from("K"),
|
||||
LKey => String::from("L"),
|
||||
MKey => String::from("M"),
|
||||
NKey => String::from("N"),
|
||||
OKey => String::from("O"),
|
||||
PKey => String::from("P"),
|
||||
QKey => String::from("Q"),
|
||||
RKey => String::from("R"),
|
||||
SKey => String::from("S"),
|
||||
TKey => String::from("T"),
|
||||
UKey => String::from("U"),
|
||||
VKey => String::from("V"),
|
||||
WKey => String::from("W"),
|
||||
XKey => String::from("X"),
|
||||
YKey => String::from("Y"),
|
||||
ZKey => String::from("Z"),
|
||||
LSuper => String::from("SUPER"),
|
||||
RSuper => String::from("SUPER"),
|
||||
Numpad0Key => String::from("0"),
|
||||
Numpad1Key => String::from("1"),
|
||||
Numpad2Key => String::from("2"),
|
||||
Numpad3Key => String::from("3"),
|
||||
Numpad4Key => String::from("4"),
|
||||
Numpad5Key => String::from("5"),
|
||||
Numpad6Key => String::from("6"),
|
||||
Numpad7Key => String::from("7"),
|
||||
Numpad8Key => String::from("8"),
|
||||
Numpad9Key => String::from("9"),
|
||||
F1Key => String::from("F1"),
|
||||
F2Key => String::from("F2"),
|
||||
F3Key => String::from("F3"),
|
||||
F4Key => String::from("F4"),
|
||||
F5Key => String::from("F5"),
|
||||
F6Key => String::from("F6"),
|
||||
F7Key => String::from("F7"),
|
||||
F8Key => String::from("F8"),
|
||||
F9Key => String::from("F9"),
|
||||
F10Key => String::from("F10"),
|
||||
F11Key => String::from("F11"),
|
||||
F12Key => String::from("F12"),
|
||||
F13Key => String::from("F13"),
|
||||
F14Key => String::from("F14"),
|
||||
F15Key => String::from("F15"),
|
||||
F16Key => String::from("F16"),
|
||||
F17Key => String::from("F17"),
|
||||
F18Key => String::from("F18"),
|
||||
F19Key => String::from("F19"),
|
||||
F20Key => String::from("F20"),
|
||||
F21Key => String::from("F21"),
|
||||
F22Key => String::from("F22"),
|
||||
F23Key => String::from("F23"),
|
||||
F24Key => String::from("F24"),
|
||||
NumLockKey => String::from("Num\nLock"),
|
||||
ScrollLockKey => String::from("Scroll\nLock"),
|
||||
CapsLockKey => String::from("CAPS"),
|
||||
LShiftKey => String::from("SHIFT"),
|
||||
RShiftKey => String::from("SHIFT"),
|
||||
LControlKey => String::from("CTRL"),
|
||||
RControlKey => String::from("CTRL"),
|
||||
LAltKey => String::from("ALT"),
|
||||
RAltKey => String::from("ALT"),
|
||||
BrowserBackKey => String::from("🡨"),
|
||||
BrowserForwardKey => String::from("🡪"),
|
||||
BrowserRefreshKey => String::from("⟳"),
|
||||
VolumeMuteKey => String::from("🔊"),
|
||||
VolumeDownKey => String::from("🔉"),
|
||||
VolumeUpKey => String::from("🔈+"),
|
||||
MediaNextTrackKey => String::from("⏭"),
|
||||
MediaPrevTrackKey => String::from("⏮"),
|
||||
MediaStopKey => String::from("⏹ "),
|
||||
MediaPlayPauseKey => String::from("⏵"),
|
||||
BackquoteKey => String::from("`"),
|
||||
SlashKey => String::from("/"),
|
||||
BackslashKey => String::from("\\"),
|
||||
CommaKey => String::from(","),
|
||||
PeriodKey => String::from("."),
|
||||
MinusKey => String::from("-"),
|
||||
QuoteKey => String::from("\""),
|
||||
SemicolonKey => String::from(";"),
|
||||
LBracketKey => String::from("("),
|
||||
RBracketKey => String::from("),"),
|
||||
EqualKey => String::from("="),
|
||||
_ => panic!("invalid keycode!"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mousebutton_from_config(button: &str) -> MouseButton {
|
||||
match button {
|
||||
"LeftButton" => LeftButton,
|
||||
"MiddleButton" => MiddleButton,
|
||||
"RightButton" => RightButton,
|
||||
_ => {
|
||||
panic!("invalid mouse button! (InputBot hasn't implemented it or your config is wrong)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn string_from_mousebutton(button: MouseButton) -> String {
|
||||
match button {
|
||||
LeftButton => String::from("M1"),
|
||||
MiddleButton => String::from("M3"),
|
||||
RightButton => String::from("M2"),
|
||||
_ => {
|
||||
panic!("invalid mouse button! (InputBot hasn't implemented it or your config is wrong)")
|
||||
}
|
||||
}
|
||||
}
|
27
src/main.rs
Normal file
27
src/main.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use inputbot::{get_keybd_key, KeybdKey, KeybdKeyIter, MouseButton};
|
||||
|
||||
mod config;
|
||||
mod listen;
|
||||
|
||||
fn main() -> Result<(), config::ConfigError> {
|
||||
println!("Hello, world!");
|
||||
let config_dir = dirs::config_dir().expect("Config directory not found");
|
||||
let keydisplay_dir = config_dir.join("keydisplay");
|
||||
|
||||
let config = config::Config::load_or_create(keydisplay_dir)?;
|
||||
|
||||
let mut config_to_keys: Vec<KeybdKey> = Vec::new();
|
||||
for key in config.listen.keys {
|
||||
// rust is magic 😍 🦀
|
||||
config_to_keys.push(listen::keybdkey_from_config(&key));
|
||||
}
|
||||
|
||||
let mut config_to_mouse: Vec<MouseButton> = Vec::new();
|
||||
for button in config.listen.mouse {
|
||||
config_to_mouse.push(listen::mousebutton_from_config(&button));
|
||||
}
|
||||
|
||||
listen::listen(config_to_keys, config_to_mouse);
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user