diff options
Diffstat (limited to 'crates/core/src')
| -rw-r--r-- | crates/core/src/lib.rs | 1 | ||||
| -rw-r--r-- | crates/core/src/manager/lock.rs | 34 | ||||
| -rw-r--r-- | crates/core/src/manager/mod.rs | 29 |
3 files changed, 63 insertions, 1 deletions
diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index e9d31a5..9a2c3bc 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -19,6 +19,7 @@ pub use layout::{Layout, MasterStackLayout, NoOpLayout, TilingConfig}; pub use lock_config::LockConfig; pub use manager::{CaptureRequest, ColorFilter, Direction, WindowManager}; pub use monitor::{Monitor, MonitorId}; +pub use manager::KeyBinding; pub use placement::{centered_in, PlacementConfig, SmartPlacement, SnapZoneKind, MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, SNAP_FLYOUT_EDGE}; pub use regex::Regex; pub use rules::{WindowMatch, WindowRule, WindowRuleActions}; diff --git a/crates/core/src/manager/lock.rs b/crates/core/src/manager/lock.rs index 15c0a9f..ecea5fe 100644 --- a/crates/core/src/manager/lock.rs +++ b/crates/core/src/manager/lock.rs @@ -52,6 +52,18 @@ impl WindowManager { std::mem::take(&mut self.refresh_requested) } + /// Records the combos the backend was given at startup, so + /// `KeyBinding::grabbed` can be answered. Called once, from `main.rs`, + /// right where that same list is handed to the platform. + pub fn set_grabbed_keys(&mut self, combos: &[String]) { + self.grabbed_keys = combos.iter().cloned().collect(); + } + + /// Whether `combo` is one the backend actually grabs. + pub fn is_grabbed(&self, combo: &str) -> bool { + self.grabbed_keys.contains(combo) + } + /// Records that `key` was set live to `value_json`. See /// `live_settings`' own doc comment. /// @@ -83,6 +95,28 @@ mod tests { } #[test] + fn only_the_combos_handed_to_the_backend_count_as_grabbed() { + // The whole point of the flag: a binding added after startup is + // registered by the config engine but never grabbed, so pressing it + // goes to the focused client instead. + let mut wm = WindowManager::new(); + assert!(!wm.is_grabbed("Mod4+Return"), "nothing grabbed before startup records a set"); + wm.set_grabbed_keys(&["Mod4+Return".to_string(), "Ctrl+Mod4+l".to_string()]); + assert!(wm.is_grabbed("Mod4+Return")); + assert!(wm.is_grabbed("Ctrl+Mod4+l")); + assert!(!wm.is_grabbed("Ctrl+Shift+Mod4+F9"), "added since startup: bound, not grabbed"); + } + + #[test] + fn recording_the_grabbed_set_again_replaces_it_rather_than_accumulating() { + let mut wm = WindowManager::new(); + wm.set_grabbed_keys(&["Mod4+a".to_string()]); + wm.set_grabbed_keys(&["Mod4+b".to_string()]); + assert!(!wm.is_grabbed("Mod4+a"), "the previous set must not linger"); + assert!(wm.is_grabbed("Mod4+b")); + } + + #[test] fn drain_refresh_request_is_true_once_then_false() { let mut wm = WindowManager::new(); assert!(!wm.drain_refresh_request(), "nothing requested yet"); diff --git a/crates/core/src/manager/mod.rs b/crates/core/src/manager/mod.rs index a225220..64cf5c3 100644 --- a/crates/core/src/manager/mod.rs +++ b/crates/core/src/manager/mod.rs @@ -85,6 +85,29 @@ struct ResizeState { /// state and layout policy. Backends (X11, Wayland, ...) drive this via /// `add_window`/`remove_window`/input events, and apply the `Rect`s it /// computes back onto real surfaces. +/// One key binding, as reported by `srd keybindings`. +#[derive(Debug, Clone)] +pub struct KeyBinding { + /// Canonical form (`"Ctrl+Mod4+l"`), which is what a real keypress is + /// matched against - not necessarily how it was written in the config. + pub combo: String, + /// `srd.bind`'s optional third argument, empty when it gave none. + pub description: String, + /// Whether the compositor actually intercepts this key. + /// + /// `main.rs` hands the backend one combo list, once, before connecting: + /// the X11 backend turns it into `XGrabKey` calls and the Wayland one + /// into its intercept set. A reload re-registers the *actions* but + /// cannot re-register the grabs, so a combination added to the config + /// since startup is bound as far as the config engine is concerned and + /// still goes straight to the focused client when pressed. + /// + /// Reported here so a launcher or cheat-sheet can say so rather than + /// listing a shortcut that silently does nothing - which is exactly + /// the failure this field exists to make visible. + pub grabbed: bool, +} + pub struct WindowManager { windows: HashMap<WindowId, Window>, order: Vec<WindowId>, @@ -449,7 +472,10 @@ pub struct WindowManager { /// Asked for as whether "our bindings show in ags's launcher": they /// could not, because nothing published them anywhere a client could /// read. - pub keybindings: Vec<(String, String)>, + pub keybindings: Vec<KeyBinding>, + /// The exact combo list handed to the backend at startup, which is the + /// only set it ever grabs - see `KeyBinding::grabbed`. + grabbed_keys: std::collections::HashSet<String>, /// Set by `request_refresh`, drained by the main loop. Same /// cross-boundary queued-request shape as `lock_requested`. refresh_requested: bool, @@ -640,6 +666,7 @@ impl WindowManager { theme: ThemeConfig::default(), lock: LockConfig::default(), keybindings: Vec::new(), + grabbed_keys: std::collections::HashSet::new(), maximize_covers_dock: true, refresh_requested: false, live_settings: std::collections::BTreeMap::new(), |