about summary refs log tree commit diff stats
path: root/pkgs/by-name/ri/river-mk-keymap/src
diff options
context:
space:
mode:
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(())
+}