aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/ri/river-mk-keymap/src
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-02-02 18:14:33 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-02-02 18:25:39 +0100
commit34b8b4c52e4afa8b854e6c3d37780ce5faf74c05 (patch)
tree31079c091c83c7d02d1e9a4f7a9a34eb9db7f91e /pkgs/by-name/ri/river-mk-keymap/src
parentfeat(lib): Init `baseLib` (diff)
downloadnixos-config-34b8b4c52e4afa8b854e6c3d37780ce5faf74c05.zip
refactor(modules/river): Migrate to `by-name`
This includes a near rewrite `river-mk-keymap` (previously, `river_init_lesser`.)
Diffstat (limited to 'pkgs/by-name/ri/river-mk-keymap/src')
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/cli.rs11
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/key_map/commands.rs109
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs79
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/main.rs34
4 files changed, 233 insertions, 0 deletions
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/cli.rs b/pkgs/by-name/ri/river-mk-keymap/src/cli.rs
new file mode 100644
index 00000000..55b87e1a
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/cli.rs
@@ -0,0 +1,11 @@
+use std::path::PathBuf;
+
+use clap::Parser;
+
+#[derive(Parser, Debug)]
+#[command(author, version, about, long_about = None)]
+/// A tool to manage your key mappings for the river window manager
+pub(super) struct Args {
+ /// Path to mappings JSON file
+ pub path: PathBuf,
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/key_map/commands.rs b/pkgs/by-name/ri/river-mk-keymap/src/key_map/commands.rs
new file mode 100644
index 00000000..a4ac0ebd
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/key_map/commands.rs
@@ -0,0 +1,109 @@
+use std::process::Command;
+
+use keymaps::key_repr::{KeyValue, MediaKeyCode, MouseKeyValue};
+
+use super::{KeyMap, MapMode};
+
+impl KeyMap {
+ #[must_use]
+ pub fn to_commands(self) -> Vec<Command> {
+ self.0
+ .iter()
+ .flat_map(|(key, value)| {
+ let key = key.last().expect("Will exist");
+ let mods = {
+ let modifiers = key.modifiers();
+ let mut output = vec![];
+
+ if modifiers.alt() {
+ output.push("Alt");
+ }
+ if modifiers.ctrl() {
+ output.push("Control");
+ }
+ if modifiers.meta() {
+ output.push("Super");
+ }
+ if modifiers.shift() {
+ output.push("Shift");
+ }
+ if output.is_empty() {
+ "None".to_owned()
+ } else {
+ output.join("+")
+ }
+ };
+ let key_value = match key.value() {
+ KeyValue::Backspace => "BackSpace".to_owned(),
+ KeyValue::Enter => "Enter".to_owned(),
+ KeyValue::Left => "Left".to_owned(),
+ KeyValue::Right => "Right".to_owned(),
+ KeyValue::Up => "Up".to_owned(),
+ KeyValue::Down => "Down".to_owned(),
+ KeyValue::Home => "Home".to_owned(),
+ KeyValue::End => "End".to_owned(),
+ KeyValue::PageUp => "Page_Up".to_owned(),
+ KeyValue::PageDown => "Page_Down".to_owned(),
+ KeyValue::Tab => "Tab".to_owned(),
+ KeyValue::BackTab => "BackTab".to_owned(),
+ KeyValue::Delete => "Delete".to_owned(),
+ KeyValue::Insert => "Insert".to_owned(),
+ KeyValue::F(num) => format!("F{num}"),
+ KeyValue::Char(a) => a.to_string(),
+ KeyValue::Null => "Null".to_owned(),
+ KeyValue::Esc => "Esc".to_owned(),
+ KeyValue::CapsLock => "CapsLock".to_owned(),
+ KeyValue::ScrollLock => "ScrollLock".to_owned(),
+ KeyValue::NumLock => "NumLock".to_owned(),
+ KeyValue::PrintScreen => "Print".to_owned(),
+ KeyValue::Pause => "Pause".to_owned(),
+ KeyValue::Menu => "Menu".to_owned(),
+ KeyValue::KeypadBegin => "KeypadBegin".to_owned(),
+ KeyValue::Media(media_key_code) => match media_key_code {
+ MediaKeyCode::Play => "XF86AudioPlay".to_owned(),
+ MediaKeyCode::Pause => "XF86AudioPause".to_owned(),
+ MediaKeyCode::PlayPause => "XF86AudioPlayPause".to_owned(),
+ MediaKeyCode::Reverse => "XF86AudioReverse".to_owned(),
+ MediaKeyCode::Stop => "XF86AudioStop".to_owned(),
+ MediaKeyCode::FastForward => "XF86AudioFastForward".to_owned(),
+ MediaKeyCode::Rewind => "XF86AudioRewind".to_owned(),
+ MediaKeyCode::TrackNext => "XF86AudioTrackNext".to_owned(),
+ MediaKeyCode::TrackPrevious => "XF86AudioTrackPrevious".to_owned(),
+ MediaKeyCode::Record => "XF86AudioRecord".to_owned(),
+ MediaKeyCode::LowerVolume => "XF86AudioLowerVolume".to_owned(),
+ MediaKeyCode::RaiseVolume => "XF86AudioRaiseVolume".to_owned(),
+ MediaKeyCode::MuteVolume => "XF86AudioMuteVolume".to_owned(),
+ },
+ KeyValue::MouseKey(mouse_key_value) => match mouse_key_value {
+ MouseKeyValue::Left => "BTN_LEFT".to_owned(),
+ MouseKeyValue::Right => "BTN_RIGHT".to_owned(),
+ MouseKeyValue::Middle => "BTN_MIDDLE".to_owned(),
+ },
+ _ => todo!(),
+ };
+
+ value
+ .modes
+ .iter()
+ .map(|mode| {
+ let mut riverctl = Command::new("riverctl");
+ riverctl.args([value.map_mode.as_command(), mode, &mods, &key_value]);
+
+ riverctl.args(value.command.iter().map(String::as_str));
+ riverctl
+ })
+ .collect::<Vec<_>>()
+ })
+ .collect()
+ }
+}
+
+impl MapMode {
+ pub(crate) fn as_command(self) -> &'static str {
+ match self {
+ MapMode::Map => "map",
+ MapMode::MapMouse => "map-pointer",
+ MapMode::Unmap => "unmap",
+ }
+ }
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs b/pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs
new file mode 100644
index 00000000..84a16c9d
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs
@@ -0,0 +1,79 @@
+use std::{collections::HashMap, fmt::Display, ops::Deref, str::FromStr};
+
+use anyhow::Context;
+use keymaps::{key_repr::Key, map_tree::MapTrie};
+use serde::{Deserialize, Serialize};
+
+pub mod commands;
+
+#[derive(Deserialize, Serialize, Debug)]
+#[allow(clippy::module_name_repetitions)]
+pub struct RawKeyMap(HashMap<Key, KeyConfig>);
+
+#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, PartialOrd)]
+/// What values to use for: `riverctl <map_mode> <mode> <mods> <key> <command..>`
+pub struct KeyConfig {
+ command: Vec<String>,
+
+ #[serde(default = "default_mode")]
+ modes: Vec<String>,
+
+ #[serde(default = "MapMode::default")]
+ map_mode: MapMode,
+}
+
+impl FromStr for KeyMap {
+ type Err = anyhow::Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let raw: RawKeyMap =
+ serde_json::from_str(s).context("Failed to parse the keymap config file as json.")?;
+ let mut out = MapTrie::<KeyConfig>::new();
+ for (key, value) in raw.0 {
+ out.insert(&[key], value.clone())
+ .with_context(|| format!("Failed to insert mapping {key} -> {value}"))?;
+ }
+
+ Ok(Self(out))
+ }
+}
+impl Display for KeyConfig {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(self.command.join(" ").as_str())
+ }
+}
+
+fn default_mode() -> Vec<String> {
+ vec!["normal".to_owned()]
+}
+
+#[derive(Copy, Deserialize, Serialize, Debug, Clone, Default, PartialEq, PartialOrd)]
+enum MapMode {
+ #[default]
+ Map,
+ MapMouse,
+ Unmap,
+}
+
+impl Display for MapMode {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ <Self as std::fmt::Debug>::fmt(self, f)
+ }
+}
+
+#[derive(Debug)]
+pub struct KeyMap(MapTrie<KeyConfig>);
+
+impl Display for KeyMap {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl Deref for KeyMap {
+ type Target = MapTrie<KeyConfig>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/main.rs b/pkgs/by-name/ri/river-mk-keymap/src/main.rs
new file mode 100644
index 00000000..5cb99f74
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/main.rs
@@ -0,0 +1,34 @@
+use std::fs;
+
+use anyhow::Context;
+use clap::Parser;
+
+mod cli;
+pub mod key_map;
+
+use crate::{cli::Args, key_map::KeyMap};
+
+fn main() -> Result<(), anyhow::Error> {
+ let args = Args::parse();
+ let keymap_file = fs::read_to_string(&args.path)
+ .with_context(|| format!("Failed to open keymap file at: '{}'.", args.path.display()))?;
+
+ let keymap: KeyMap = keymap_file
+ .parse()
+ .with_context(|| format!("Failed to parse keymap file at: {}", args.path.display()))?;
+
+ // println!("{keymap}");
+ // println!("Commands:");
+ for mut command in keymap.to_commands() {
+ // println!("Executing {command:?}");
+ let status = command
+ .status()
+ .with_context(|| format!("Failed to run command: '{command:?}'"))?;
+
+ if !status.success() {
+ eprintln!("Command ('{command:?}') returned with non zero exit code: {status}");
+ }
+ }
+
+ Ok(())
+}