1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
//! Requesting srdwm's own session lock. Split out of the original single
//! `manager.rs` - see `super` (`mod.rs`) for `WindowManager`'s field
//! definitions; everything here is plain `impl WindowManager` methods.
use super::*;
impl WindowManager {
/// Queues a request for the backend to enter its own lock UI - the
/// only caller today is the IPC `"lock"` dispatch, the compositor-
/// agnostic side of `srd dispatch lock`. Core cannot lock the screen
/// itself (that's real rendering/input-routing, backend-owned); see
/// `lock_requested`'s own doc comment for why this has to cross the
/// core/backend boundary as a queued request rather than a direct call.
///
/// Idempotent by construction (a plain `bool`, not a counter): asking
/// to lock twice before the backend's next poll drains it is exactly
/// as locked as asking once.
pub fn request_lock(&mut self) {
self.lock_requested = true;
}
/// Takes the current lock request, if any, leaving none pending. The
/// backend calls this once per poll, same as `drain_output_position_
/// requests`.
pub fn drain_lock_request(&mut self) -> bool {
std::mem::take(&mut self.lock_requested)
}
/// Queues a request for the config layer to re-read `init.lua` and fire
/// the `srd.on("refresh", ...)` handler.
///
/// Same core/backend split as `request_lock` above, for the same
/// reason: core owns no Lua state, so it cannot reload a config or run
/// a handler itself. The desktop menu's own "Refresh" row is the
/// caller.
///
/// Asked for as "does refresh refresh configs in a function list in the
/// config ie refresh os, etc, ags/aegis/polybar/waybar". Refresh used
/// to re-scan the desktop icon grid and nothing else, so there was no
/// way to make it reload anything the user actually cared about. What
/// "refresh" *means* beyond srdwm's own config is deliberately the
/// config's decision, not a hardcoded list of other people's tools --
/// this compositor has no business knowing whether the user runs
/// waybar or AGS.
pub fn request_refresh(&mut self) {
self.refresh_requested = true;
}
/// Takes the current refresh request, if any. Drained once per poll,
/// same as `drain_lock_request`.
pub fn drain_refresh_request(&mut self) -> bool {
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.
///
/// Last write wins, so setting the same key twice leaves one entry and
/// the replay applies each key exactly once.
pub fn record_live_setting(&mut self, key: &str, value_json: String) {
self.live_settings.insert(key.to_string(), value_json);
}
/// Drops the recorded live override for `key`, so the next replay does
/// not reapply it. Used when a reloaded config states that key itself:
/// the file is then the more deliberate statement of intent, and has to
/// win, or a value changed at runtime could never be corrected by
/// editing the config again.
pub fn forget_live_setting(&mut self, key: &str) {
self.live_settings.remove(key);
}
/// Every live setting recorded so far, for replay after a config
/// reload. Cloned rather than borrowed: the replay mutates the same
/// `WindowManager` this came from.
pub fn live_settings(&self) -> Vec<(String, String)> {
self.live_settings.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn drain_lock_request_is_true_once_then_false() {
let mut wm = WindowManager::new();
assert!(!wm.drain_lock_request(), "nothing requested yet");
wm.request_lock();
assert!(wm.drain_lock_request(), "must report the pending request");
assert!(!wm.drain_lock_request(), "must not report the same request twice");
}
#[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");
wm.request_refresh();
assert!(wm.drain_refresh_request(), "must report the pending request");
assert!(!wm.drain_refresh_request(), "must not report the same request twice");
}
#[test]
fn a_refresh_request_is_independent_of_a_lock_request() {
let mut wm = WindowManager::new();
wm.request_refresh();
assert!(!wm.drain_lock_request(), "refresh must not look like a lock");
assert!(wm.drain_refresh_request());
}
#[test]
fn requesting_lock_twice_before_a_drain_is_still_just_one_pending_request() {
let mut wm = WindowManager::new();
wm.request_lock();
wm.request_lock();
assert!(wm.drain_lock_request());
assert!(!wm.drain_lock_request());
}
}
|