1use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
16
17use smithay::input::keyboard::xkb;
18
19pub const OVERLAY_FIRST_KEYCODE: u32 = 0x120;
22pub const OVERLAY_LAST_KEYCODE: u32 = 0x2ff;
25pub const OVERLAY_CAPACITY: usize = (OVERLAY_LAST_KEYCODE - OVERLAY_FIRST_KEYCODE + 1) as usize;
27
28const MAX_LEVELS: u32 = 4;
31
32pub struct KeymapPolicy {
34 base_text: String,
35 base_map: HashMap<u32, (u32, u32)>,
37 slots: Vec<Option<u32>>,
39 by_sym: HashMap<u32, usize>,
41 lru: VecDeque<usize>,
43 overlay_first: u32,
45 overlay_capacity: usize,
47 manual_overlay: BTreeMap<u32, u32>,
52}
53
54pub fn keysym_for_char(c: char) -> u32 {
60 let c = if c == '\n' { '\r' } else { c };
61 xkb::utf32_to_keysym(c as u32).raw()
62}
63
64pub fn compile_keymap(text: &str) -> Option<xkb::Keymap> {
66 let ctx = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
67 xkb::Keymap::new_from_string(
68 &ctx,
69 text.to_string(),
70 xkb::KEYMAP_FORMAT_TEXT_V1,
71 xkb::KEYMAP_COMPILE_NO_FLAGS,
72 )
73}
74
75pub fn compile_rmlvo(
78 rules: &str,
79 model: &str,
80 layout: &str,
81 variant: &str,
82 options: &str,
83) -> Option<String> {
84 let ctx = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
85 let options = (!options.is_empty()).then(|| options.to_string());
86 let keymap = xkb::Keymap::new_from_names(
87 &ctx,
88 rules,
89 model,
90 layout,
91 variant,
92 options,
93 xkb::KEYMAP_COMPILE_NO_FLAGS,
94 )?;
95 Some(keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1))
96}
97
98pub fn level0_syms(keymap: &xkb::Keymap) -> HashMap<u32, u32> {
101 let mut out = HashMap::new();
102 let lo = keymap.min_keycode().raw();
103 let hi = keymap.max_keycode().raw();
104 for kc in lo..=hi {
105 let syms = keymap.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0);
106 if syms.len() == 1 {
107 let sym = syms[0].raw();
108 if sym != 0 {
109 out.insert(kc, sym);
110 }
111 }
112 }
113 out
114}
115
116impl KeymapPolicy {
117 pub fn empty() -> Self {
119 Self::with_overlay_range(OVERLAY_FIRST_KEYCODE, OVERLAY_LAST_KEYCODE)
120 }
121
122 pub fn with_overlay_range(first: u32, last: u32) -> Self {
127 Self {
128 base_text: String::new(),
129 base_map: HashMap::new(),
130 slots: Vec::new(),
131 by_sym: HashMap::new(),
132 lru: VecDeque::new(),
133 overlay_first: first,
134 overlay_capacity: (last - first + 1) as usize,
135 manual_overlay: BTreeMap::new(),
136 }
137 }
138
139 pub fn rebuild_base(&mut self, base_text: String) -> bool {
145 let Some(keymap) = compile_keymap(&base_text) else {
146 return false;
147 };
148 self.base_map.clear();
149 {
150 let lo = keymap.min_keycode().raw();
151 let hi = keymap.max_keycode().raw();
152 for level in 0..MAX_LEVELS {
155 for kc in lo..=hi {
156 let code = xkb::Keycode::new(kc);
157 if keymap.num_levels_for_key(code, 0) <= level {
158 continue;
159 }
160 for sym in keymap.key_get_syms_by_level(code, 0, level) {
161 let raw = sym.raw();
162 if raw != 0 {
163 self.base_map.entry(raw).or_insert((kc, level));
164 }
165 }
166 }
167 }
168 }
169 self.base_text = base_text;
170 true
171 }
172
173 pub fn has_base(&self) -> bool {
175 !self.base_text.is_empty()
176 }
177
178 pub fn resolve(&self, keysym: u32) -> Option<(u32, u32)> {
180 if let Some(&hit) = self.base_map.get(&keysym) {
181 return Some(hit);
182 }
183 self.by_sym
184 .get(&keysym)
185 .map(|&slot| (self.overlay_first + slot as u32, 0))
186 }
187
188 pub fn resolves_plain(&self, keysym: u32) -> bool {
191 matches!(self.resolve(keysym), Some((_, 0)))
192 }
193
194 pub fn bind_many(
199 &mut self,
200 keysyms: &[u32],
201 pressed: &HashSet<u32>,
202 ) -> (Vec<(u32, u32)>, bool) {
203 let mut out = Vec::with_capacity(keysyms.len());
204 let mut changed = false;
205 for &sym in keysyms {
206 out.push(self.bind_one(sym, pressed, false, &mut changed));
207 }
208 (out, changed)
209 }
210
211 pub fn bind_many_plain(&mut self, keysyms: &[u32], pressed: &HashSet<u32>) -> (Vec<u32>, bool) {
215 let mut out = Vec::with_capacity(keysyms.len());
216 let mut changed = false;
217 for &sym in keysyms {
218 out.push(self.bind_one(sym, pressed, true, &mut changed).0);
219 }
220 (out, changed)
221 }
222
223 fn bind_one(
224 &mut self,
225 sym: u32,
226 pressed: &HashSet<u32>,
227 plain_only: bool,
228 changed: &mut bool,
229 ) -> (u32, u32) {
230 if sym == 0 {
231 return (0, 0);
232 }
233 if let Some(&(kc, level)) = self.base_map.get(&sym)
234 && (!plain_only || level == 0) {
235 return (kc, level);
236 }
237 if let Some(&slot) = self.by_sym.get(&sym) {
238 if let Some(at) = self.lru.iter().position(|&s| s == slot) {
239 self.lru.remove(at);
240 }
241 self.lru.push_back(slot);
242 return (self.overlay_first + slot as u32, 0);
243 }
244 let slot = if self.slots.len() < self.overlay_capacity {
245 self.slots.push(None);
246 self.slots.len() - 1
247 } else {
248 match self.recycle_slot(pressed) {
249 Some(s) => s,
250 None => return (0, 0),
251 }
252 };
253 if let Some(old) = self.slots[slot].replace(sym) {
254 self.by_sym.remove(&old);
255 }
256 self.by_sym.insert(sym, slot);
257 self.lru.push_back(slot);
258 *changed = true;
259 (self.overlay_first + slot as u32, 0)
260 }
261
262 fn recycle_slot(&mut self, pressed: &HashSet<u32>) -> Option<usize> {
265 let at = self
266 .lru
267 .iter()
268 .position(|&slot| !pressed.contains(&(self.overlay_first + slot as u32)))?;
269 self.lru.remove(at)
270 }
271
272 pub fn set_manual_overlay(&mut self, binds: &[(u32, u32)]) {
277 self.manual_overlay = binds.iter().copied().collect();
278 }
279
280 pub fn keymap_text(&self) -> String {
285 let occupied: Vec<(usize, u32)> = self
286 .slots
287 .iter()
288 .enumerate()
289 .filter_map(|(i, s)| s.map(|sym| (i, sym)))
290 .collect();
291 if occupied.is_empty() && self.manual_overlay.is_empty() {
292 return self.base_text.clone();
293 }
294 let base = &self.base_text;
295 let Some(max_at) = base.find("maximum = ") else {
296 return self.base_text.clone();
297 };
298 let num_at = max_at + "maximum = ".len();
299 let Some(num_len) = base[num_at..].find(';') else {
300 return self.base_text.clone();
301 };
302 let old_max: u32 = base[num_at..num_at + num_len].trim().parse().unwrap_or(255);
303 let slot_max = self.overlay_first + occupied.last().map(|&(i, _)| i as u32).unwrap_or(0);
304 let manual_max = self.manual_overlay.keys().copied().max().unwrap_or(0);
305 let need_max = slot_max.max(manual_max);
306 let mut text =
307 String::with_capacity(base.len() + (occupied.len() + self.manual_overlay.len()) * 48);
308 text.push_str(&base[..num_at]);
309 text.push_str(&old_max.max(need_max).to_string());
310 let rest = &base[num_at + num_len..];
311 let Some(kc_end) = rest.find("};") else {
313 return self.base_text.clone();
314 };
315 text.push_str(&rest[..kc_end]);
316 for &(i, _) in &occupied {
317 text.push_str(&format!("\t<P{:03}> = {};\n", i, self.overlay_first + i as u32));
318 }
319 for &kc in self.manual_overlay.keys() {
320 text.push_str(&format!("\t<X{kc:03}> = {kc};\n"));
321 }
322 let rest = &rest[kc_end..];
323 let Some(close_at) = rest
324 .find("xkb_symbols")
325 .and_then(|sym_at| Self::section_close(rest, sym_at))
326 else {
327 return self.base_text.clone();
328 };
329 text.push_str(&rest[..close_at]);
330 for &(i, sym) in &occupied {
331 text.push_str(&format!("\tkey <P{:03}> {{ [ {:#x} ] }};\n", i, sym));
332 }
333 for (&kc, &sym) in &self.manual_overlay {
334 text.push_str(&format!("\tkey <X{kc:03}> {{ [ {sym:#x} ] }};\n"));
335 }
336 text.push_str(&rest[close_at..]);
337 text
338 }
339
340 fn section_close(text: &str, from: usize) -> Option<usize> {
342 let open = from + text[from..].find('{')?;
343 let mut depth = 0usize;
344 for (i, ch) in text[open..].char_indices() {
345 match ch {
346 '{' => depth += 1,
347 '}' => {
348 depth -= 1;
349 if depth == 0 {
350 return Some(open + i);
351 }
352 }
353 _ => {}
354 }
355 }
356 None
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
367
368 fn us_base() -> String {
369 compile_rmlvo("", "", "us", "", "").expect("us keymap")
370 }
371
372 fn policy() -> KeymapPolicy {
373 let mut p = KeymapPolicy::empty();
374 p.rebuild_base(us_base());
375 p
376 }
377
378 #[test]
379 fn base_keysyms_resolve_without_overlay() {
380 let mut p = policy();
381 let (out, changed) = p.bind_many(&[0x61, 0x41], &HashSet::new());
383 assert!(!changed);
384 assert_eq!(out[0].1, 0);
385 assert_eq!(out[1].0, out[0].0);
386 assert_eq!(out[1].1, 1);
387 }
388
389 #[test]
390 fn batch_bind_is_one_swap_and_compiles() {
391 let mut p = policy();
392 let syms: Vec<u32> = (0..30).map(|i| 0x1004E00 + i).collect();
393 let (out, changed) = p.bind_many(&syms, &HashSet::new());
394 assert!(changed);
395 let (_, changed_again) = p.bind_many(&syms, &HashSet::new());
396 assert!(!changed_again, "re-binding bound keysyms must not swap");
397 let text = p.keymap_text();
398 let km = compile_keymap(&text).expect("overlay keymap compiles");
399 for (i, &(kc, level)) in out.iter().enumerate() {
400 assert_eq!(level, 0);
401 let got = km.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0);
402 assert_eq!(got.len(), 1, "keycode {kc} has one sym");
403 assert_eq!(got[0].raw(), syms[i]);
404 }
405 }
406
407 #[test]
408 fn pressed_keycode_is_never_recycled() {
409 let mut p = policy();
410 let syms: Vec<u32> = (0..OVERLAY_CAPACITY as u32).map(|i| 0x1005000 + i).collect();
411 let (out, _) = p.bind_many(&syms, &HashSet::new());
412 let held_kc = out[0].0;
413 let held_sym = syms[0];
414 let pressed: HashSet<u32> = [held_kc].into_iter().collect();
415 let extra: Vec<u32> = (0..8).map(|i| 0x1006000 + i).collect();
417 let (extra_out, changed) = p.bind_many(&extra, &pressed);
418 assert!(changed);
419 for &(kc, _) in &extra_out {
420 assert_ne!(kc, held_kc, "held keycode must not be rebound");
421 }
422 assert_eq!(p.resolve(held_sym), Some((held_kc, 0)));
423 }
424
425 #[test]
426 fn sub256_overlay_range_overrides_base_keycode_names() {
427 let mut p = KeymapPolicy::with_overlay_range(150, 255);
431 p.rebuild_base(us_base());
432 let (out, changed) = p.bind_many_plain(&[0x1004E2D, 0x61], &HashSet::new());
433 assert!(changed);
434 assert_eq!(out[0], 150);
435 let km = compile_keymap(&p.keymap_text()).expect("sub-256 overlay keymap compiles");
436 let got = km.key_get_syms_by_level(xkb::Keycode::new(out[0]), 0, 0);
437 assert_eq!(got.len(), 1);
438 assert_eq!(got[0].raw(), 0x1004E2D);
439 assert!(out[1] < 150);
441 assert_eq!(out[1], p.resolve(0x61).unwrap().0);
442 }
443
444 #[test]
445 fn manual_overlay_survives_policy_rebind_and_layout_swap() {
446 let mut p = policy();
449 let manual = [(220u32, 0x0101_F600u32), (221u32, 0x0101_F601u32)];
451 p.set_manual_overlay(&manual);
452 let (_out, changed) = p.bind_many(&[0x1004E00, 0x1004E01], &HashSet::new());
454 assert!(changed);
455 let km = compile_keymap(&p.keymap_text()).expect("merged keymap compiles");
456 for &(kc, sym) in &manual {
457 let got = km.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0);
458 assert_eq!(got.len(), 1, "manual keycode {kc} has one sym");
459 assert_eq!(got[0].raw(), sym, "manual keycode {kc} keeps its keysym");
460 }
461 let de = compile_rmlvo("", "", "de", "", "").expect("de keymap");
463 assert!(p.rebuild_base(de));
464 let km = compile_keymap(&p.keymap_text()).expect("post-swap keymap compiles");
465 for &(kc, sym) in &manual {
466 let got = km.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0);
467 assert_eq!(got.first().map(|s| s.raw()), Some(sym));
468 }
469 p.set_manual_overlay(&[]);
471 let km = compile_keymap(&p.keymap_text()).expect("cleared keymap compiles");
472 let got = km.key_get_syms_by_level(xkb::Keycode::new(220), 0, 0);
473 assert!(got.iter().all(|s| s.raw() != manual[0].1));
474 }
475
476 #[test]
477 fn rebuild_base_keeps_overlay_assignments() {
478 let mut p = policy();
479 let (out, _) = p.bind_many(&[0x1004E2D], &HashSet::new());
480 let de = compile_rmlvo("", "", "de", "", "").expect("de keymap");
481 p.rebuild_base(de);
482 assert_eq!(p.resolve(0x1004E2D), Some((out[0].0, 0)));
483 let (u_out, changed) = p.bind_many(&[0xFC], &HashSet::new());
485 assert!(!changed);
486 assert_eq!(u_out[0].1, 0);
487 assert!(compile_keymap(&p.keymap_text()).is_some());
488 }
489}