From 3d07d3ceac3f1233f58498e7725c34c867450016 Mon Sep 17 00:00:00 2001 From: Enrico Stemmer Date: Mon, 5 Jan 2026 02:02:17 +0100 Subject: [PATCH] feat: allow custom key for switch mode fix: update hyprland plugin chore: fix packaging for nix Signed-off-by: Enrico Stemmer --- .../config-edit-lib/src/components/changes.rs | 30 ++++++-- .../src/components/shortcut_dialog.rs | 4 +- .../config-edit-lib/src/components/switch.rs | 4 +- .../config-edit-lib/src/components/windows.rs | 2 +- .../src/components/windows_overview.rs | 4 +- crates/config-edit-lib/src/structs.rs | 4 +- crates/config-edit-lib/src/util.rs | 52 +++++-------- crates/config-lib/src/lib.rs | 1 - crates/config-lib/src/migrate/m1t2/convert.rs | 1 + crates/config-lib/src/migrate/m2t3/convert.rs | 1 + crates/config-lib/src/structs.rs | 4 + crates/exec-lib/src/plugin.rs | 8 +- .../plugin/.idea/workspace.xml | 16 +++- crates/hyprland-plugin/plugin/Makefile | 7 +- crates/hyprland-plugin/plugin/src/defs-test.h | 1 + crates/hyprland-plugin/plugin/src/defs.h | 2 + crates/hyprland-plugin/plugin/src/globals.h | 1 + crates/hyprland-plugin/plugin/src/handlers.h | 2 +- crates/hyprland-plugin/plugin/src/init.cpp | 1 + .../hyprland-plugin/plugin/src/key-press.cpp | 42 ++++++---- .../plugin/src/keyboard-focus.cpp | 2 +- .../plugin/src/layer-change.cpp | 2 +- .../plugin/src/mouse-button.cpp | 4 +- crates/hyprland-plugin/src/configure.rs | 8 +- crates/hyprland-plugin/src/test.rs | 1 + crates/windows-lib/src/switch/create.rs | 7 +- flake.lock | 76 +++++++++---------- flake.nix | 4 + nix/build.nix | 12 +++ nix/module.nix | 3 +- src/keybinds.rs | 2 +- 31 files changed, 183 insertions(+), 125 deletions(-) diff --git a/crates/config-edit-lib/src/components/changes.rs b/crates/config-edit-lib/src/components/changes.rs index 1cc0d96..7f4e08f 100644 --- a/crates/config-edit-lib/src/components/changes.rs +++ b/crates/config-edit-lib/src/components/changes.rs @@ -1,6 +1,5 @@ use crate::flags_csv; use crate::structs::{Config, Plugins}; -use crate::util::key_to_name; use relm4::adw::ActionRow; use relm4::adw::gtk::SelectionMode; use relm4::adw::prelude::*; @@ -165,13 +164,8 @@ pub fn generate_items(changes: >k::ListBox, config: &Config, prev_config: &Con changes, "Changed overview key", format!( - "{} ({}) -> {} ({})", - prev_config.windows.overview.key, - key_to_name(&prev_config.windows.overview.key) - .unwrap_or_else(|| String::from("---")), - config.windows.overview.key, - key_to_name(&config.windows.overview.key) - .unwrap_or_else(|| String::from("---")), + "{} -> {}", + prev_config.windows.overview.key, config.windows.overview.key, ), ); } @@ -298,6 +292,16 @@ pub fn generate_items(changes: >k::ListBox, config: &Config, prev_config: &Con add_info(changes, "Enabled Switch view"); } + if prev_config.windows.switch.key != config.windows.switch.key { + add_info_subtitle( + changes, + "Changed switch key", + format!( + "{} -> {}", + prev_config.windows.switch.key, config.windows.switch.key + ), + ); + } if prev_config.windows.switch.modifier != config.windows.switch.modifier { add_info_subtitle( changes, @@ -362,6 +366,16 @@ pub fn generate_items(changes: >k::ListBox, config: &Config, prev_config: &Con add_info(changes, "Enabled Switch 2 view"); } + if prev_config.windows.switch_2.key != config.windows.switch_2.key { + add_info_subtitle( + changes, + "Changed switch 2 key", + format!( + "{} -> {}", + prev_config.windows.switch_2.key, config.windows.switch_2.key + ), + ); + } if prev_config.windows.switch_2.modifier != config.windows.switch_2.modifier { add_info_subtitle( changes, diff --git a/crates/config-edit-lib/src/components/shortcut_dialog.rs b/crates/config-edit-lib/src/components/shortcut_dialog.rs index 2919e62..cc81f18 100644 --- a/crates/config-edit-lib/src/components/shortcut_dialog.rs +++ b/crates/config-edit-lib/src/components/shortcut_dialog.rs @@ -93,9 +93,9 @@ impl SimpleComponent for KeyboardShortcut { let entry_2 = entry.clone(); let window = dialog.widgets().gtk_window_12.clone(); let send = sender.clone(); - key_controller.connect_key_pressed(move |_, val, id, state| { + key_controller.connect_key_pressed(move |_, val, _, state| { debug!("Raw key event - val: {}, state: {:?}", val, state); - match handle_key(val, state, id) { + match handle_key(val, state) { Some((key, r#mod, label)) => { entry.set_text(&label); send.input(KeyboardShortcutInput::UpdateKey(key)); diff --git a/crates/config-edit-lib/src/components/switch.rs b/crates/config-edit-lib/src/components/switch.rs index 5f758db..5357cc7 100644 --- a/crates/config-edit-lib/src/components/switch.rs +++ b/crates/config-edit-lib/src/components/switch.rs @@ -70,9 +70,9 @@ impl SimpleComponent for Switch { #[watch] set_sensitive: model.config.enabled, }, - _adw::ShortcutLabel::new(&mod_key_to_accelerator(model.config.modifier, &model.config.key).unwrap_or_default()) { + _adw::ShortcutLabel::new(&mod_key_to_accelerator(model.config.modifier, &model.config.key)) { #[watch] - set_accelerator: &mod_key_to_accelerator(model.config.modifier, &model.config.key).unwrap_or_default(), + set_accelerator: &mod_key_to_accelerator(model.config.modifier, &model.config.key), #[watch] set_css_classes: if model.config.enabled { if mod_key_to_accelerator(model.config.modifier, &model.config.key) == mod_key_to_accelerator(model.prev_config.modifier, &model.prev_config.key) diff --git a/crates/config-edit-lib/src/components/windows.rs b/crates/config-edit-lib/src/components/windows.rs index 4c7aae5..09476e5 100644 --- a/crates/config-edit-lib/src/components/windows.rs +++ b/crates/config-edit-lib/src/components/windows.rs @@ -141,7 +141,7 @@ impl SimpleComponent for Windows { let switch_2 = Switch::builder() .launch(SwitchInit { config: init.config.switch_2.clone(), - name: "Switch 2", + name: "Switch 2 (TODO)", }) .forward(sender.output_sender(), WindowsOutput::Switch2); diff --git a/crates/config-edit-lib/src/components/windows_overview.rs b/crates/config-edit-lib/src/components/windows_overview.rs index 159e951..7f379b1 100644 --- a/crates/config-edit-lib/src/components/windows_overview.rs +++ b/crates/config-edit-lib/src/components/windows_overview.rs @@ -68,9 +68,9 @@ impl SimpleComponent for WindowsOverview { #[watch] set_sensitive: model.config.enabled, }, - _adw::ShortcutLabel::new(&mod_key_to_accelerator(model.config.modifier, &model.config.key).unwrap_or_default()) { + _adw::ShortcutLabel::new(&mod_key_to_accelerator(model.config.modifier, &model.config.key)) { #[watch] - set_accelerator: &mod_key_to_accelerator(model.config.modifier, &model.config.key).unwrap_or_default(), + set_accelerator: &mod_key_to_accelerator(model.config.modifier, &model.config.key), #[watch] set_css_classes: if model.config.enabled { if mod_key_to_accelerator(model.config.modifier, &model.config.key) == mod_key_to_accelerator(model.prev_config.modifier, &model.prev_config.key) diff --git a/crates/config-edit-lib/src/structs.rs b/crates/config-edit-lib/src/structs.rs index fb854f5..fc0d2d8 100644 --- a/crates/config-edit-lib/src/structs.rs +++ b/crates/config-edit-lib/src/structs.rs @@ -248,7 +248,7 @@ impl From for Option { key: Box::from(value.key), filter_by: vec, switch_workspaces: value.switch_workspaces, - // TODO exclude_special_workspaces + exclude_special_workspaces: Box::from(value.exclude_special_workspaces), }) } else { None @@ -295,7 +295,7 @@ impl From for Option { modifier: value.modifier.into(), filter_by: vec, hide_filtered: false, - // TODO exclude_special_workspaces + exclude_special_workspaces: Box::from(value.exclude_special_workspaces), }) } else { None diff --git a/crates/config-edit-lib/src/util.rs b/crates/config-edit-lib/src/util.rs index cdc3bae..1700480 100644 --- a/crates/config-edit-lib/src/util.rs +++ b/crates/config-edit-lib/src/util.rs @@ -1,6 +1,6 @@ use crate::structs::ConfigModifier; -use relm4::gtk::gdk::{Cursor, Display, Key, ModifierType}; -use relm4::gtk::prelude::{Cast, DisplayExtManual, EditableExt, WidgetExt}; +use relm4::gtk::gdk::{Cursor, Key, ModifierType}; +use relm4::gtk::prelude::{Cast, EditableExt, WidgetExt}; use relm4::{adw, gtk}; // use relm4::tokio::time::sleep; use tracing::{instrument, warn}; @@ -87,11 +87,7 @@ impl SelectRow for gtk::ListBox { } } -pub fn handle_key( - val: Key, - state: ModifierType, - id: u32, -) -> Option<(String, ConfigModifier, String)> { +pub fn handle_key(val: Key, state: ModifierType) -> Option<(String, ConfigModifier, String)> { let key_name = val.name()?; let modifier = match val { Key::Alt_L | Key::Alt_R => ConfigModifier::Alt, @@ -112,7 +108,7 @@ pub fn handle_key( format!("{modifier} + {key_name}") }; - Some((format!("code:{id}"), modifier, label)) + Some((key_name.to_string(), modifier, label)) } pub fn default_config() -> config_lib::Config { @@ -123,39 +119,31 @@ pub fn default_config() -> config_lib::Config { } #[instrument(level = "trace", ret(level = "trace"))] -pub fn mod_key_to_accelerator(modifier: ConfigModifier, key: &str) -> Option { - let key = key_to_name(key)?; +pub fn mod_key_to_accelerator(modifier: ConfigModifier, key: &str) -> String { + // correct some keys that can sometimes have wrong capitalization + let key = match &*key.to_lowercase() { + "super_l" => "Super_L", + "super_r" => "Super_R", + "alt_l" => "Alt_L", + "alt_r" => "Alt_R", + "control_l" => "Control_L", + "control_r" => "Control_R", + _ => key, + }; + if modifier == ConfigModifier::None { - Some(key) + key.to_string() } else { - Some(format!("<{modifier}>{key}")) + format!("<{modifier}>{key}") } } #[instrument(level = "trace", ret(level = "trace"))] pub fn mod_key_to_string(modifier: ConfigModifier, key: &str) -> String { if modifier == ConfigModifier::None { - key_to_name(key).unwrap_or_else(|| "---".to_string()) + key.to_string() } else { - format!( - "{} + {}", - modifier, - key_to_name(key).unwrap_or_else(|| "---".to_string()) - ) - } -} - -pub fn key_to_name(key: &str) -> Option { - // key is keycode - if key.starts_with("code:") { - let key_id = key.split(':').nth(1)?; - let code = key_id.parse::().ok()?; - let display = &Display::default()?; - let data = display.map_keycode(code)?; - let (_, key) = data.iter().find(|(m, _k)| m.level() == 0)?; - Some(key.name()?.to_string()) - } else { - Some(key.to_string()) + format!("{modifier} + {key}") } } diff --git a/crates/config-lib/src/lib.rs b/crates/config-lib/src/lib.rs index c99c86a..e64644c 100644 --- a/crates/config-lib/src/lib.rs +++ b/crates/config-lib/src/lib.rs @@ -14,5 +14,4 @@ pub use modifier::*; pub use save::write_config; pub use structs::*; -// TODO inc pub const CURRENT_CONFIG_VERSION: u16 = 3; diff --git a/crates/config-lib/src/migrate/m1t2/convert.rs b/crates/config-lib/src/migrate/m1t2/convert.rs index d3d3006..22a7cbf 100644 --- a/crates/config-lib/src/migrate/m1t2/convert.rs +++ b/crates/config-lib/src/migrate/m1t2/convert.rs @@ -42,6 +42,7 @@ impl From for crate::Switch { modifier: value.modifier.into(), key: "tab".into(), switch_workspaces: value.show_workspaces, + exclude_special_workspaces: "".into(), } } } diff --git a/crates/config-lib/src/migrate/m2t3/convert.rs b/crates/config-lib/src/migrate/m2t3/convert.rs index ebb7012..156fcfd 100644 --- a/crates/config-lib/src/migrate/m2t3/convert.rs +++ b/crates/config-lib/src/migrate/m2t3/convert.rs @@ -29,6 +29,7 @@ impl From for crate::Overview { filter_by: value.filter_by, hide_filtered: value.hide_filtered, launcher: value.launcher.into(), + exclude_special_workspaces: "".into(), } } } diff --git a/crates/config-lib/src/structs.rs b/crates/config-lib/src/structs.rs index 7d02e34..959f637 100644 --- a/crates/config-lib/src/structs.rs +++ b/crates/config-lib/src/structs.rs @@ -45,6 +45,8 @@ pub struct Overview { pub filter_by: Vec, #[default = false] pub hide_filtered: bool, + #[default = ""] + pub exclude_special_workspaces: Box, } #[derive(SmartDefault, Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] @@ -188,6 +190,8 @@ pub struct Switch { pub filter_by: Vec, #[default = false] pub switch_workspaces: bool, + #[default = ""] + pub exclude_special_workspaces: Box, } #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] diff --git a/crates/exec-lib/src/plugin.rs b/crates/exec-lib/src/plugin.rs index 2b98426..c4bb187 100644 --- a/crates/exec-lib/src/plugin.rs +++ b/crates/exec-lib/src/plugin.rs @@ -13,7 +13,7 @@ use tracing::{debug, debug_span, info, trace}; static PLUGIN_COULD_BE_BUILD: OnceLock = OnceLock::new(); pub fn load_plugin( - switch: Option, + switch: Option<(Modifier, Box)>, overview: Option<(Modifier, Box)>, ) -> anyhow::Result<()> { let _span = debug_span!("load_plugin").entered(); @@ -23,7 +23,10 @@ pub fn load_plugin( } let config = PluginConfig { - xkb_key_switch_mod: switch.map(|s| Box::from(mod_to_xkb_key(s))), + xkb_key_switch_mod: switch + .as_ref() + .map(|(r#mod, _)| Box::from(mod_to_xkb_key(*r#mod))), + xkb_key_switch_key: switch.map(|(_, key)| key), xkb_key_overview_mod: overview .as_ref() .map(|(r#mod, _)| Box::from(r#mod.to_string())), @@ -91,7 +94,6 @@ pub const fn mod_to_xkb_key(r#mod: Modifier) -> &'static str { Modifier::Alt => "XKB_KEY_Alt", Modifier::Ctrl => "XKB_KEY_Control", Modifier::Super => "XKB_KEY_Super", - // TODO Modifier::None => "XKB_KEY_NoSymbol", } } diff --git a/crates/hyprland-plugin/plugin/.idea/workspace.xml b/crates/hyprland-plugin/plugin/.idea/workspace.xml index df3de28..f38dce6 100644 --- a/crates/hyprland-plugin/plugin/.idea/workspace.xml +++ b/crates/hyprland-plugin/plugin/.idea/workspace.xml @@ -32,7 +32,19 @@ - + + + + + + + + + + + + +