diff --git a/config.ron b/config.ron index 2a34d462..451dcecd 100644 --- a/config.ron +++ b/config.ron @@ -75,6 +75,7 @@ (modifiers: [Super], key: "a"): Spawn("busctl --user call com.system76.CosmicAppLibrary /com/system76/CosmicAppLibrary com.system76.CosmicAppLibrary Toggle"), (modifiers: [Super], key: "w"): Spawn("busctl --user call com.system76.CosmicWorkspaces /com/system76/CosmicWorkspaces com.system76.CosmicWorkspaces Toggle"), (modifiers: [Super], key: "slash"): Spawn("busctl --user call com.system76.CosmicLauncher /com/system76/CosmicLauncher com.system76.CosmicLauncher Toggle"), + (modifiers: [Super]): Spawn("busctl --user call com.system76.CosmicLauncher /com/system76/CosmicLauncher com.system76.CosmicLauncher Toggle"), (modifiers: [], key: "XF86AudioRaiseVolume"): Spawn("amixer sset Master 5%+"), (modifiers: [], key: "XF86AudioLowerVolume"): Spawn("amixer sset Master 5%-"), @@ -86,4 +87,4 @@ workspace_amount: Dynamic, workspace_layout: Vertical, tiling_enabled: false, -) +) \ No newline at end of file diff --git a/src/config/key_bindings.rs b/src/config/key_bindings.rs index 4ad89f6a..985d4b8f 100644 --- a/src/config/key_bindings.rs +++ b/src/config/key_bindings.rs @@ -89,12 +89,12 @@ pub struct KeyPattern { #[serde(deserialize_with = "deserialize_KeyModifiers")] pub modifiers: KeyModifiers, /// The actual key, that was pressed - #[serde(deserialize_with = "deserialize_Keysym")] - pub key: u32, + #[serde(deserialize_with = "deserialize_Keysym", default)] + pub key: Option, } impl KeyPattern { - pub fn new(modifiers: impl Into, key: u32) -> KeyPattern { + pub fn new(modifiers: impl Into, key: Option) -> KeyPattern { KeyPattern { modifiers: modifiers.into(), key, @@ -117,7 +117,12 @@ impl ToString for KeyPattern { if self.modifiers.shift { result += "Shift+"; } - result += &keysym_get_name(self.key); + + if let Some(key) = self.key { + result += &keysym_get_name(key); + } else { + result.remove(result.len() - 1); + } result } } @@ -176,7 +181,7 @@ fn insert_binding( for key in keys { let pattern = KeyPattern { modifiers: modifiers.clone(), - key, + key: Some(key), }; if !key_bindings.contains_key(&pattern) { key_bindings.insert(pattern, action.clone()); diff --git a/src/config/types.rs b/src/config/types.rs index 349dc216..03fa4eb7 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -56,7 +56,7 @@ where } #[allow(non_snake_case)] -pub fn deserialize_Keysym<'de, D>(deserializer: D) -> Result +pub fn deserialize_Keysym<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { @@ -76,9 +76,9 @@ where name, xkb::keysym_get_name(x) ); - Ok(x) + Ok(Some(x)) } }, - x => Ok(x), + x => Ok(Some(x)), } } diff --git a/src/input/mod.rs b/src/input/mod.rs index fd4db964..b9210e8d 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -67,6 +67,8 @@ pub struct SeatId(pub usize); pub struct ActiveOutput(pub RefCell); #[derive(Default)] pub struct SupressedKeys(RefCell)>>); +#[derive(Default, Debug)] +pub struct ModifiersShortcutQueue(RefCell>); #[derive(Default)] pub struct Devices(RefCell>>); @@ -107,6 +109,28 @@ impl SupressedKeys { } } +impl ModifiersShortcutQueue { + pub fn set(&self, binding: KeyPattern) { + let mut set = self.0.borrow_mut(); + *set = Some(binding); + } + + pub fn take(&self, binding: &KeyPattern) -> bool { + let mut set = self.0.borrow_mut(); + if set.is_some() && set.as_ref().unwrap() == binding { + *set = None; + true + } else { + false + } + } + + pub fn clear(&self) { + let mut set = self.0.borrow_mut(); + *set = None; + } +} + impl Devices { fn add_device(&self, device: &D) -> Vec { let id = device.id(); @@ -152,6 +176,7 @@ pub fn add_seat( userdata.insert_if_missing(SeatId::default); userdata.insert_if_missing(Devices::default); userdata.insert_if_missing(SupressedKeys::default); + userdata.insert_if_missing(ModifiersShortcutQueue::default); userdata.insert_if_missing(SeatMoveGrabState::default); userdata.insert_if_missing(CursorState::default); userdata.insert_if_missing(|| ActiveOutput(RefCell::new(output.clone()))); @@ -283,7 +308,7 @@ impl State { || (action_pattern.modifiers.alt && !modifiers.alt) || (action_pattern.modifiers.logo && !modifiers.logo) || (action_pattern.modifiers.shift && !modifiers.shift) - || (handle.raw_syms().contains(&action_pattern.key) && state == KeyState::Released) + || (action_pattern.key.is_some() && handle.raw_syms().contains(&action_pattern.key.unwrap()) && state == KeyState::Released) { data.common.shell.set_overview_mode(None, data.common.event_loop_handle.clone()); @@ -344,8 +369,8 @@ impl State { if let (ResizeMode::Started(action_pattern, _, _), _) = data.common.shell.resize_mode() { - if state == KeyState::Released - && handle.raw_syms().contains(&action_pattern.key) + if action_pattern.key.is_some() && state == KeyState::Released + && handle.raw_syms().contains(&action_pattern.key.unwrap()) { data.common.shell.set_resize_mode(None, &data.common.config, data.common.event_loop_handle.clone()); } else if action_pattern.modifiers != *modifiers { @@ -390,7 +415,7 @@ impl State { let action = Action::_ResizingInternal(direction, edge, state); let key_pattern = KeyPattern { modifiers: modifiers.clone().into(), - key: handle.raw_code(), + key: Some(handle.raw_code()), }; if state == KeyState::Released { @@ -412,7 +437,7 @@ impl State { }).ok() } else { None }; - userdata + userdata .get::() .unwrap() .add(&handle, token); @@ -472,14 +497,30 @@ impl State { } // handle the rest of the global shortcuts + let mut can_clear_modifiers_shortcut = true; if !shortcuts_inhibited { + let modifiers_queue = userdata.get::().unwrap(); for (binding, action) in data.common.config.static_conf.key_bindings.iter() { - if state == KeyState::Pressed - && binding.modifiers == *modifiers - && handle.raw_syms().contains(&binding.key) + let modifiers_bypass = binding.key.is_none() + && state == KeyState::Released + && binding.modifiers != *modifiers + && modifiers_queue.take(binding); + + if !modifiers_bypass && binding.key.is_none() && state == KeyState::Pressed && binding.modifiers == *modifiers { + modifiers_queue.set(binding.clone()); + can_clear_modifiers_shortcut = false; + } + + if ( + binding.key.is_some() + && state == KeyState::Pressed + && handle.raw_syms().contains(&binding.key.unwrap()) + && binding.modifiers == *modifiers + ) || modifiers_bypass { + modifiers_queue.clear(); userdata .get::() .unwrap() @@ -492,6 +533,10 @@ impl State { } } + if can_clear_modifiers_shortcut { + userdata.get::().unwrap().clear(); + } + // keys are passed through to apps FilterResult::Forward }, diff --git a/src/shell/layout/tiling/grabs/swap.rs b/src/shell/layout/tiling/grabs/swap.rs index 4272d86d..162fe8fc 100644 --- a/src/shell/layout/tiling/grabs/swap.rs +++ b/src/shell/layout/tiling/grabs/swap.rs @@ -62,7 +62,7 @@ impl KeyboardGrab for SwapWindowGrab { (pattern.key, *direction) }) .collect::>(); - let Some(direction) = syms.iter().find_map(|sym| focus_bindings.iter().find_map(|(key, direction)| (sym == key).then_some(*direction))) else { return }; + let Some(direction) = syms.iter().find_map(|sym| focus_bindings.iter().find_map(|(key, direction)| (key.is_some() && sym == key.as_ref().unwrap()).then_some(*direction))) else { return }; data.handle_action( Action::Focus(direction), @@ -71,7 +71,7 @@ impl KeyboardGrab for SwapWindowGrab { time, KeyPattern { modifiers: modifiers.map(Into::into).unwrap_or_default(), - key: keycode, + key: Some(keycode), }, None, );