1use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
16use std::fmt::Write as _;
17
18use smithay::input::keyboard::xkb;
19
20pub const OVERLAY_FIRST_KEYCODE: u32 = 0x120;
23pub const OVERLAY_LAST_KEYCODE: u32 = 0x2ff;
26pub const OVERLAY_CAPACITY: usize = (OVERLAY_LAST_KEYCODE - OVERLAY_FIRST_KEYCODE + 1) as usize;
28
29pub const TEXT_SPARE_KEYCODES: [u32; 14] = [
37 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 97, 132,
38];
39
40const VENDOR_KEYSYMS: std::ops::RangeInclusive<u32> = 0x1008_0000..=0x1008_ffff;
42
43const MAX_LEVELS: u32 = 4;
46
47pub struct KeymapPolicy {
49 base_text: String,
50 base_map: HashMap<u32, (u32, u32)>,
52 slots: Vec<Option<u32>>,
54 by_sym: HashMap<u32, usize>,
56 lru: VecDeque<usize>,
58 overlay_codes: Vec<u32>,
61 preferred: usize,
64 base_bound: HashSet<u32>,
68 manual_overlay: BTreeMap<u32, u32>,
73}
74
75pub fn keysym_for_char(c: char) -> u32 {
81 let c = if c == '\n' { '\r' } else { c };
82 xkb::utf32_to_keysym(c as u32).raw()
83}
84
85pub fn compile_keymap(text: &str) -> Option<xkb::Keymap> {
87 let ctx = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
88 xkb::Keymap::new_from_string(
89 &ctx,
90 text.to_string(),
91 xkb::KEYMAP_FORMAT_TEXT_V1,
92 xkb::KEYMAP_COMPILE_NO_FLAGS,
93 )
94}
95
96pub fn compile_rmlvo(
99 rules: &str,
100 model: &str,
101 layout: &str,
102 variant: &str,
103 options: &str,
104) -> Option<String> {
105 let ctx = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
106 let options = (!options.is_empty()).then(|| options.to_string());
107 let keymap = xkb::Keymap::new_from_names(
108 &ctx,
109 rules,
110 model,
111 layout,
112 variant,
113 options,
114 xkb::KEYMAP_COMPILE_NO_FLAGS,
115 )?;
116 Some(keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1))
117}
118
119pub fn level0_syms(keymap: &xkb::Keymap) -> HashMap<u32, u32> {
122 let mut out = HashMap::new();
123 let lo = keymap.min_keycode().raw();
124 let hi = keymap.max_keycode().raw();
125 for kc in lo..=hi {
126 let syms = keymap.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0);
127 if syms.len() == 1 {
128 let sym = syms[0].raw();
129 if sym != 0 {
130 out.insert(kc, sym);
131 }
132 }
133 }
134 out
135}
136
137impl KeymapPolicy {
138 pub fn empty() -> Self {
140 Self::with_overlay_codes((OVERLAY_FIRST_KEYCODE..=OVERLAY_LAST_KEYCODE).collect())
141 }
142
143 pub fn seat() -> Self {
146 let mut policy = Self::with_overlay_codes(
147 TEXT_SPARE_KEYCODES
148 .into_iter()
149 .chain(OVERLAY_FIRST_KEYCODE..=OVERLAY_LAST_KEYCODE)
150 .collect(),
151 );
152 policy.preferred = TEXT_SPARE_KEYCODES.len();
153 policy
154 }
155
156 pub fn with_overlay_codes(overlay_codes: Vec<u32>) -> Self {
161 Self {
162 base_text: String::new(),
163 base_map: HashMap::new(),
164 slots: Vec::new(),
165 by_sym: HashMap::new(),
166 lru: VecDeque::new(),
167 overlay_codes,
168 preferred: 0,
169 base_bound: HashSet::new(),
170 manual_overlay: BTreeMap::new(),
171 }
172 }
173
174 pub fn rebuild_base(&mut self, base_text: String) -> bool {
180 let Some(keymap) = compile_keymap(&base_text) else {
181 return false;
182 };
183 self.base_map.clear();
184 self.base_bound.clear();
185 {
186 let lo = keymap.min_keycode().raw();
187 let hi = keymap.max_keycode().raw();
188 for level in 0..MAX_LEVELS {
191 for kc in lo..=hi {
192 let code = xkb::Keycode::new(kc);
193 if keymap.num_levels_for_key(code, 0) <= level {
194 continue;
195 }
196 for sym in keymap.key_get_syms_by_level(code, 0, level) {
197 let raw = sym.raw();
198 if raw != 0 {
199 self.base_map.entry(raw).or_insert((kc, level));
200 if TEXT_SPARE_KEYCODES.contains(&kc) && !VENDOR_KEYSYMS.contains(&raw) {
201 self.base_bound.insert(kc);
202 }
203 }
204 }
205 }
206 }
207 }
208 self.base_text = base_text;
209 true
210 }
211
212 pub fn has_base(&self) -> bool {
214 !self.base_text.is_empty()
215 }
216
217 pub fn resolve(&self, keysym: u32) -> Option<(u32, u32)> {
219 if let Some(&hit) = self.base_map.get(&keysym)
220 && self.slot_at(hit.0).is_none()
221 {
222 return Some(hit);
223 }
224 self.by_sym
225 .get(&keysym)
226 .map(|&slot| (self.overlay_codes[slot], 0))
227 }
228
229 fn slot_at(&self, keycode: u32) -> Option<usize> {
231 let slot = self.overlay_codes.iter().position(|&kc| kc == keycode)?;
232 self.slots.get(slot)?.map(|_| slot)
233 }
234
235 fn unshadow(
238 &mut self,
239 keysyms: &[u32],
240 pressed: &HashSet<u32>,
241 plain_only: bool,
242 changed: &mut bool,
243 ) -> HashSet<u32> {
244 let mut reserved = HashSet::new();
245 for sym in keysyms {
246 let Some(&(kc, level)) = self.base_map.get(sym) else {
247 continue;
248 };
249 if (plain_only && level != 0) || !reserved.insert(kc) || pressed.contains(&kc) {
250 continue;
251 }
252 if let Some(slot) = self.slot_at(kc)
253 && let Some(old) = self.slots[slot].take()
254 {
255 self.by_sym.remove(&old);
256 self.lru.retain(|&s| s != slot);
257 *changed = true;
258 }
259 }
260 reserved
261 }
262
263 pub fn resolves_plain(&self, keysym: u32) -> bool {
266 matches!(self.resolve(keysym), Some((_, 0)))
267 }
268
269 pub fn bind_many(
274 &mut self,
275 keysyms: &[u32],
276 pressed: &HashSet<u32>,
277 ) -> (Vec<(u32, u32)>, bool) {
278 let mut out = Vec::with_capacity(keysyms.len());
279 let mut changed = false;
280 let mut reserved = self.unshadow(keysyms, pressed, false, &mut changed);
281 for &sym in keysyms {
282 let hit = self.bind_one(sym, pressed, &reserved, false, &mut changed);
283 reserved.insert(hit.0);
285 out.push(hit);
286 }
287 (out, changed)
288 }
289
290 pub fn bind_many_plain(&mut self, keysyms: &[u32], pressed: &HashSet<u32>) -> (Vec<u32>, bool) {
294 let mut out = Vec::with_capacity(keysyms.len());
295 let mut changed = false;
296 let mut reserved = self.unshadow(keysyms, pressed, true, &mut changed);
297 for &sym in keysyms {
298 let kc = self.bind_one(sym, pressed, &reserved, true, &mut changed).0;
299 reserved.insert(kc);
300 out.push(kc);
301 }
302 (out, changed)
303 }
304
305 fn bind_one(
306 &mut self,
307 sym: u32,
308 pressed: &HashSet<u32>,
309 reserved: &HashSet<u32>,
310 plain_only: bool,
311 changed: &mut bool,
312 ) -> (u32, u32) {
313 if sym == 0 {
314 return (0, 0);
315 }
316 if let Some(&(kc, level)) = self.base_map.get(&sym)
317 && (!plain_only || level == 0)
318 && self.slot_at(kc).is_none()
319 {
320 return (kc, level);
321 }
322 if let Some(&slot) = self.by_sym.get(&sym) {
323 if let Some(at) = self.lru.iter().position(|&s| s == slot) {
324 self.lru.remove(at);
325 }
326 self.lru.push_back(slot);
327 return (self.overlay_codes[slot], 0);
328 }
329 let free_in = |me: &Self, range: std::ops::Range<usize>| {
330 range.into_iter().find(|&i| {
331 me.slots.get(i).is_none_or(Option::is_none)
332 && me.takes(me.overlay_codes[i], reserved)
333 })
334 };
335 let preferred = self.preferred.min(self.overlay_codes.len());
336 let slot = free_in(self, 0..preferred)
337 .or_else(|| self.recycle_slot(pressed, reserved, preferred))
338 .or_else(|| free_in(self, preferred..self.overlay_codes.len()))
339 .or_else(|| self.recycle_slot(pressed, reserved, usize::MAX));
340 let Some(slot) = slot else {
341 return (0, 0);
342 };
343 if slot >= self.slots.len() {
344 self.slots.resize(slot + 1, None);
345 }
346 if let Some(old) = self.slots[slot].replace(sym) {
347 self.by_sym.remove(&old);
348 }
349 self.by_sym.insert(sym, slot);
350 self.lru.push_back(slot);
351 *changed = true;
352 (self.overlay_codes[slot], 0)
353 }
354
355 fn recycle_slot(
359 &mut self,
360 pressed: &HashSet<u32>,
361 reserved: &HashSet<u32>,
362 below: usize,
363 ) -> Option<usize> {
364 let at = self.lru.iter().position(|&slot| {
365 let kc = self.overlay_codes[slot];
366 slot < below && !pressed.contains(&kc) && self.takes(kc, reserved)
367 })?;
368 self.lru.remove(at)
369 }
370
371 fn takes(&self, keycode: u32, reserved: &HashSet<u32>) -> bool {
374 !reserved.contains(&keycode)
375 && !self.base_bound.contains(&keycode)
376 && !self.manual_overlay.contains_key(&keycode)
377 }
378
379 pub fn set_manual_overlay(&mut self, binds: &[(u32, u32)]) {
386 self.manual_overlay = binds.iter().copied().collect();
387 for slot in 0..self.slots.len() {
388 if self.manual_overlay.contains_key(&self.overlay_codes[slot])
389 && let Some(old) = self.slots[slot].take()
390 {
391 self.by_sym.remove(&old);
392 self.lru.retain(|&s| s != slot);
393 }
394 }
395 }
396
397 pub fn keymap_text(&self) -> String {
402 let occupied: Vec<(usize, u32)> = self
403 .slots
404 .iter()
405 .enumerate()
406 .filter_map(|(i, s)| s.map(|sym| (i, sym)))
407 .collect();
408 if occupied.is_empty() && self.manual_overlay.is_empty() {
409 return self.base_text.clone();
410 }
411 let base = &self.base_text;
412 let Some(max_at) = base.find("maximum = ") else {
413 return self.base_text.clone();
414 };
415 let num_at = max_at + "maximum = ".len();
416 let Some(num_len) = base[num_at..].find(';') else {
417 return self.base_text.clone();
418 };
419 let old_max: u32 = base[num_at..num_at + num_len].trim().parse().unwrap_or(255);
420 let slot_max = occupied
421 .iter()
422 .map(|&(i, _)| self.overlay_codes[i])
423 .max()
424 .unwrap_or(0);
425 let manual_max = self.manual_overlay.keys().copied().max().unwrap_or(0);
426 let need_max = slot_max.max(manual_max);
427 let mut text =
428 String::with_capacity(base.len() + (occupied.len() + self.manual_overlay.len()) * 48);
429 text.push_str(&base[..num_at]);
430 text.push_str(&old_max.max(need_max).to_string());
431 let rest = &base[num_at + num_len..];
432 let Some(kc_end) = rest.find("};") else {
434 return self.base_text.clone();
435 };
436 text.push_str(&rest[..kc_end]);
437 for &(i, _) in &occupied {
438 let _ = writeln!(text, "\t<P{:03}> = {};", i, self.overlay_codes[i]);
439 }
440 for &kc in self.manual_overlay.keys() {
441 let _ = writeln!(text, "\t<X{kc:03}> = {kc};");
442 }
443 let rest = &rest[kc_end..];
444 let Some(close_at) = rest
445 .find("xkb_symbols")
446 .and_then(|sym_at| Self::section_close(rest, sym_at))
447 else {
448 return self.base_text.clone();
449 };
450 text.push_str(&rest[..close_at]);
451 for &(i, sym) in &occupied {
452 let _ = writeln!(text, "\tkey <P{:03}> {{ [ {:#x} ] }};", i, sym);
453 }
454 for (&kc, &sym) in &self.manual_overlay {
455 let _ = writeln!(text, "\tkey <X{kc:03}> {{ [ {sym:#x} ] }};");
456 }
457 text.push_str(&rest[close_at..]);
458 text
459 }
460
461 fn section_close(text: &str, from: usize) -> Option<usize> {
463 let open = from + text[from..].find('{')?;
464 let mut depth = 0usize;
465 for (i, ch) in text[open..].char_indices() {
466 match ch {
467 '{' => depth += 1,
468 '}' => {
469 depth -= 1;
470 if depth == 0 {
471 return Some(open + i);
472 }
473 }
474 _ => {}
475 }
476 }
477 None
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
488
489 fn us_base() -> String {
490 compile_rmlvo("", "", "us", "", "").expect("us keymap")
491 }
492
493 fn policy() -> KeymapPolicy {
494 let mut p = KeymapPolicy::empty();
495 p.rebuild_base(us_base());
496 p
497 }
498
499 #[test]
500 fn base_keysyms_resolve_without_overlay() {
501 let mut p = policy();
502 let (out, changed) = p.bind_many(&[0x61, 0x41], &HashSet::new());
504 assert!(!changed);
505 assert_eq!(out[0].1, 0);
506 assert_eq!(out[1].0, out[0].0);
507 assert_eq!(out[1].1, 1);
508 }
509
510 #[test]
511 fn batch_bind_is_one_swap_and_compiles() {
512 let mut p = policy();
513 let syms: Vec<u32> = (0..30).map(|i| 0x1004E00 + i).collect();
514 let (out, changed) = p.bind_many(&syms, &HashSet::new());
515 assert!(changed);
516 let (_, changed_again) = p.bind_many(&syms, &HashSet::new());
517 assert!(!changed_again, "re-binding bound keysyms must not swap");
518 let text = p.keymap_text();
519 let km = compile_keymap(&text).expect("overlay keymap compiles");
520 for (i, &(kc, level)) in out.iter().enumerate() {
521 assert_eq!(level, 0);
522 let got = km.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0);
523 assert_eq!(got.len(), 1, "keycode {kc} has one sym");
524 assert_eq!(got[0].raw(), syms[i]);
525 }
526 }
527
528 #[test]
529 fn pressed_keycode_is_never_recycled() {
530 let mut p = policy();
531 let syms: Vec<u32> = (0..OVERLAY_CAPACITY as u32)
532 .map(|i| 0x1005000 + i)
533 .collect();
534 let (out, _) = p.bind_many(&syms, &HashSet::new());
535 let held_kc = out[0].0;
536 let held_sym = syms[0];
537 let pressed: HashSet<u32> = [held_kc].into_iter().collect();
538 let extra: Vec<u32> = (0..8).map(|i| 0x1006000 + i).collect();
540 let (extra_out, changed) = p.bind_many(&extra, &pressed);
541 assert!(changed);
542 for &(kc, _) in &extra_out {
543 assert_ne!(kc, held_kc, "held keycode must not be rebound");
544 }
545 assert_eq!(p.resolve(held_sym), Some((held_kc, 0)));
546 }
547
548 #[test]
552 fn the_seat_binds_text_on_the_text_spares_first() {
553 let mut p = KeymapPolicy::seat();
554 p.rebuild_base(us_base());
555 let free: Vec<u32> = TEXT_SPARE_KEYCODES
556 .into_iter()
557 .filter(|kc| !p.base_bound.contains(kc))
558 .collect();
559 assert!(
560 free.len() >= 10,
561 "the US base leaves most text spares free: {free:?}"
562 );
563 let syms: Vec<u32> = (0..free.len() as u32 + 1).map(|i| 0x100AC00 + i).collect();
564 let (out, changed) = p.bind_many(&syms, &HashSet::new());
565 assert!(changed);
566 let codes: Vec<u32> = out.iter().map(|&(kc, _)| kc).collect();
567 assert_eq!(codes[..free.len()], free[..], "the text spares, in order");
568 assert_eq!(codes[free.len()], OVERLAY_FIRST_KEYCODE, "then above 255");
569 let km = compile_keymap(&p.keymap_text()).expect("seat keymap compiles");
570 for (&kc, &sym) in codes.iter().zip(&syms) {
571 let got = km.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0);
572 assert_eq!(
573 got.iter().map(|s| s.raw()).collect::<Vec<_>>(),
574 vec![sym],
575 "keycode {kc}"
576 );
577 }
578 }
579
580 #[test]
583 fn the_seat_gives_way_to_selkies_on_a_text_spare() {
584 let mut p = KeymapPolicy::seat();
585 p.rebuild_base(us_base());
586 let (out, _) = p.bind_many(&[0x100AC00], &HashSet::new());
587 let first = out[0].0;
588 assert_eq!(first, TEXT_SPARE_KEYCODES[0]);
589 p.set_manual_overlay(&[(first, 0x100D55C)]);
590 assert_eq!(p.resolve(0x100AC00), None, "the slot gave its keycode up");
591 let (out, changed) = p.bind_many(&[0x100AC00, 0x100AC01], &HashSet::new());
592 assert!(changed);
593 assert!(out.iter().all(|&(kc, _)| kc != first), "{out:?}");
594 let km = compile_keymap(&p.keymap_text()).expect("shared keymap compiles");
595 let at = |kc: u32| km.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0)[0].raw();
596 assert_eq!(at(first), 0x100D55C);
597 assert_eq!(at(out[0].0), 0x100AC00);
598 }
599
600 #[test]
603 fn the_seat_recycles_a_text_spare_before_going_above_255() {
604 let mut p = KeymapPolicy::seat();
605 p.rebuild_base(us_base());
606 let spares = TEXT_SPARE_KEYCODES
607 .into_iter()
608 .filter(|kc| !p.base_bound.contains(kc))
609 .count();
610 let first: Vec<u32> = (0..spares as u32).map(|i| 0x100AC00 + i).collect();
611 let (out, _) = p.bind_many(&first, &HashSet::new());
612 let (next, _) = p.bind_many(&[0x100B000, 0x100B001], &HashSet::new());
613 assert_eq!(next[0].0, out[0].0, "the oldest text spare");
614 assert_eq!(next[1].0, out[1].0, "then the next oldest");
615 let batch: Vec<u32> = (0..spares as u32 + 1).map(|i| 0x100C000 + i).collect();
616 let (out, _) = p.bind_many(&batch, &HashSet::new());
617 let codes: HashSet<u32> = out.iter().map(|&(kc, _)| kc).collect();
618 assert_eq!(
619 codes.len(),
620 batch.len(),
621 "every keysym of a batch keeps its own code: {out:?}"
622 );
623 }
624
625 #[test]
626 fn character_key_overlay_wins_its_keys_and_gives_them_back() {
627 fn syms_at(km: &xkb::Keymap, kc: u32) -> Vec<u32> {
632 km.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0)
633 .iter()
634 .map(|s| s.raw())
635 .collect()
636 }
637 let mut p = KeymapPolicy::with_overlay_codes(vec![10, 11, 38]);
638 p.rebuild_base(us_base());
639 let (out, changed) = p.bind_many_plain(&[0x1004E2D, 0x61], &HashSet::new());
640 assert!(changed);
641 assert_eq!(out, vec![10, 38], "the overlay skips the key 'a' types on");
642 let km = compile_keymap(&p.keymap_text()).expect("overlay keymap compiles");
643 assert_eq!(syms_at(&km, 10), vec![0x1004E2D]);
644 assert_eq!(syms_at(&km, 38), vec![0x61]);
645 let (out, changed) = p.bind_many_plain(&[0x31, 0x1004E2D], &HashSet::new());
646 assert!(changed);
647 assert_eq!(
648 out,
649 vec![10, 11],
650 "'1' takes its key back and the overlay moves on"
651 );
652 let km = compile_keymap(&p.keymap_text()).expect("given-back keymap compiles");
653 assert_eq!(syms_at(&km, 10), vec![0x31]);
654 assert_eq!(syms_at(&km, 11), vec![0x1004E2D]);
655 assert_eq!(p.resolve(0x31), Some((10, 0)));
656 }
657
658 #[test]
659 fn manual_overlay_survives_policy_rebind_and_layout_swap() {
660 let mut p = policy();
663 let manual = [(220u32, 0x0101_F600u32), (221u32, 0x0101_F601u32)];
665 p.set_manual_overlay(&manual);
666 let (_out, changed) = p.bind_many(&[0x1004E00, 0x1004E01], &HashSet::new());
668 assert!(changed);
669 let km = compile_keymap(&p.keymap_text()).expect("merged keymap compiles");
670 for &(kc, sym) in &manual {
671 let got = km.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0);
672 assert_eq!(got.len(), 1, "manual keycode {kc} has one sym");
673 assert_eq!(got[0].raw(), sym, "manual keycode {kc} keeps its keysym");
674 }
675 let de = compile_rmlvo("", "", "de", "", "").expect("de keymap");
677 assert!(p.rebuild_base(de));
678 let km = compile_keymap(&p.keymap_text()).expect("post-swap keymap compiles");
679 for &(kc, sym) in &manual {
680 let got = km.key_get_syms_by_level(xkb::Keycode::new(kc), 0, 0);
681 assert_eq!(got.first().map(|s| s.raw()), Some(sym));
682 }
683 p.set_manual_overlay(&[]);
685 let km = compile_keymap(&p.keymap_text()).expect("cleared keymap compiles");
686 let got = km.key_get_syms_by_level(xkb::Keycode::new(220), 0, 0);
687 assert!(got.iter().all(|s| s.raw() != manual[0].1));
688 }
689
690 #[test]
691 fn rebuild_base_keeps_overlay_assignments() {
692 let mut p = policy();
693 let (out, _) = p.bind_many(&[0x1004E2D], &HashSet::new());
694 let de = compile_rmlvo("", "", "de", "", "").expect("de keymap");
695 p.rebuild_base(de);
696 assert_eq!(p.resolve(0x1004E2D), Some((out[0].0, 0)));
697 let (u_out, changed) = p.bind_many(&[0xFC], &HashSet::new());
699 assert!(!changed);
700 assert_eq!(u_out[0].1, 0);
701 assert!(compile_keymap(&p.keymap_text()).is_some());
702 }
703
704 #[test]
709 fn typed_characters_map_to_the_keysyms_a_keymap_names() {
710 assert_eq!(keysym_for_char('a'), 0x61);
711 assert_eq!(keysym_for_char('~'), 0x7E);
712 assert_eq!(keysym_for_char('\u{FC}'), 0xFC);
713 assert_eq!(keysym_for_char('\n'), 0xFF0D);
714 assert_eq!(keysym_for_char('\r'), 0xFF0D);
715 assert_eq!(keysym_for_char('\t'), 0xFF09);
716 assert_eq!(keysym_for_char('\u{1B}'), 0xFF1B);
717 assert_eq!(keysym_for_char('\u{4E2D}'), 0x0100_4E2D);
718 assert_eq!(keysym_for_char('\u{1F600}'), 0x0101_F600);
719 }
720}