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
|
use std::ops::RangeInclusive;
use crate::Result;
use xcb::{
x::{GetKeyboardMapping, GetKeyboardMappingReply, GetModifierMapping, ModMask},
Connection,
};
use xkeysym::{KeyCode, Keysym, RawKeyCode};
/// Cached information about our keyboard layout.
pub struct KeyboardInfo {
/// The range of keycodes used
keycodes: RangeInclusive<RawKeyCode>,
/// The `ModMask` corresponding to `NumLock`.
/// This varies sometimes, and we need to know to ignore it.
numlock_mask: ModMask,
/// The mapping from keycodes to (multiple) key symbols
mapping: GetKeyboardMappingReply,
}
impl KeyboardInfo {
/// Query information about the keyboard layout from the given connection.
pub fn new_with(conn: &Connection) -> Result<Self> {
let min_keycode = conn.get_setup().min_keycode();
let max_keycode = conn.get_setup().max_keycode();
let mapping = conn.wait_for_reply(conn.send_request(&GetKeyboardMapping {
first_keycode: min_keycode,
count: max_keycode - min_keycode + 1,
}))?;
let mut this = Self {
keycodes: RawKeyCode::from(min_keycode)..=RawKeyCode::from(max_keycode),
numlock_mask: ModMask::empty(),
mapping,
};
let Some(numlock_keycode) = this.keysym_to_keycode(Keysym::Num_Lock) else {
// No numlock button, so no modmask for numlock
return Ok(this);
};
let mod_map = conn.wait_for_reply(conn.send_request(&GetModifierMapping {}))?;
let keypermod = mod_map.keycodes().len() / 8;
for i in 0..8 {
for j in 0..keypermod {
if RawKeyCode::from(mod_map.keycodes()[i * keypermod + j]) == numlock_keycode.raw()
{
this.numlock_mask =
ModMask::from_bits(1 << i).expect("x11 returned unrecognised modifier");
}
}
}
Ok(this)
}
/// Get the modifier mask being used for numlock
pub const fn numlock_mask(&self) -> ModMask {
self.numlock_mask
}
/// Iterate over all keycodes and their bound keysyms.
/// This is likely to contain duplicate pairs.
pub fn iter_keycodes_keysyms(&self) -> impl Iterator<Item = (KeyCode, Keysym)> + '_ {
(0..self.mapping.keysyms_per_keycode())
.flat_map(|shift| self.keycodes.clone().map(move |keycode| (shift, keycode)))
.filter_map(|(shift, keycode)| -> Option<_> {
Some((
keycode.into(),
self.keycode_to_keysym(keycode.into(), shift)?,
))
})
}
/// Lookup the first keycode which has the given keysym in any column
pub(crate) fn keysym_to_keycode(&self, keysym: Keysym) -> Option<KeyCode> {
for shift in 0..self.mapping.keysyms_per_keycode() {
for keycode in self.keycodes.clone() {
if self.mapping.keysyms()[(keycode as usize - *self.keycodes.start() as usize)
* self.mapping.keysyms_per_keycode() as usize
+ shift as usize]
== keysym.raw()
{
return Some(keycode.into());
}
}
}
None
}
/// Lookup the keysym in the given column for the given keycode
pub fn keycode_to_keysym(&self, keycode: KeyCode, col: u8) -> Option<Keysym> {
xkeysym::keysym(
keycode,
col,
(*self.keycodes.start()).into(),
self.mapping.keysyms_per_keycode(),
self.mapping.keysyms(),
)
}
}
|