1#![allow(non_upper_case_globals)]
9#![deprecated = "the `xkeysym` crate implements this crate without any cruft"]
10#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
11#![no_std]
12
13use breadx::{
14 display::Cookie,
15 prelude::*,
16 protocol::xproto::{GetKeyboardMappingReply, Keycode, Keysym, Setup},
17 Error, Result,
18};
19use keysyms::*;
20
21const NO_SYMBOL: Keysym = 0;
22
23#[path = "automatically_generated.rs"]
24pub mod keysyms;
25
26pub struct KeyboardState {
28 innards: Innards,
29}
30
31enum Innards {
32 Unresolved(Cookie<GetKeyboardMappingReply>),
34 Resolved(GetKeyboardMappingReply),
36}
37
38impl KeyboardState {
39 pub fn new(dpy: &mut impl Display) -> Result<Self> {
41 let min_keycode = dpy.setup().min_keycode;
43 let max_keycode = dpy.setup().max_keycode;
44 let cookie = dpy.get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)?;
45
46 Ok(Self {
47 innards: Innards::Unresolved(cookie),
48 })
49 }
50
51 #[cfg(feature = "async")]
53 pub async fn new_async(dpy: &mut impl AsyncDisplay) -> Result<Self> {
54 let min_keycode = dpy.setup().min_keycode;
55 let max_keycode = dpy.setup().max_keycode;
56 let cookie = dpy
57 .get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)
58 .await?;
59
60 Ok(Self {
61 innards: Innards::Unresolved(cookie),
62 })
63 }
64
65 fn resolve(&mut self, dpy: &mut impl Display) -> Result<&mut GetKeyboardMappingReply> {
67 match self.innards {
68 Innards::Unresolved(ref cookie) => {
69 let reply = dpy.wait_for_reply(*cookie)?;
70 self.innards = Innards::Resolved(reply);
71 match &mut self.innards {
72 Innards::Resolved(reply) => Ok(reply),
73 _ => unreachable!(),
74 }
75 }
76 Innards::Resolved(ref mut reply) => Ok(reply),
77 }
78 }
79
80 #[cfg(feature = "async")]
81 async fn resolve_async(
82 &mut self,
83 dpy: &mut impl AsyncDisplay,
84 ) -> Result<&mut GetKeyboardMappingReply> {
85 match self.innards {
86 Innards::Unresolved(ref cookie) => {
87 let reply = dpy.wait_for_reply(*cookie).await?;
88 self.innards = Innards::Resolved(reply);
89 match &mut self.innards {
90 Innards::Resolved(reply) => Ok(reply),
91 _ => unreachable!(),
92 }
93 }
94 Innards::Resolved(ref mut reply) => Ok(reply),
95 }
96 }
97
98 pub fn refresh(&mut self, dpy: &mut impl Display) -> Result<()> {
100 let min_keycode = dpy.setup().min_keycode;
101 let max_keycode = dpy.setup().max_keycode;
102
103 let cookie = dpy.get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)?;
105
106 self.innards = Innards::Unresolved(cookie);
107 Ok(())
108 }
109
110 #[cfg(feature = "async")]
112 pub async fn refresh_async(&mut self, dpy: &mut impl AsyncDisplay) -> Result<()> {
113 let min_keycode = dpy.setup().min_keycode;
114 let max_keycode = dpy.setup().max_keycode;
115
116 let cookie = dpy
118 .get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)
119 .await?;
120
121 self.innards = Innards::Unresolved(cookie);
122 Ok(())
123 }
124
125 pub fn symbol(
128 &mut self,
129 dpy: &mut impl Display,
130 keycode: Keycode,
131 column: u8,
132 ) -> Result<Keysym> {
133 let reply = self.resolve(dpy)?;
134 get_symbol(dpy.setup(), reply, keycode, column)
135 }
136
137 #[cfg(feature = "async")]
140 pub async fn symbol_async(
141 &mut self,
142 dpy: &mut impl AsyncDisplay,
143 keycode: Keycode,
144 column: u8,
145 ) -> Result<Keysym> {
146 let reply = self.resolve_async(dpy).await?;
147 get_symbol(dpy.setup(), reply, keycode, column)
148 }
149}
150
151fn get_symbol(
152 setup: &Setup,
153 mapping: &GetKeyboardMappingReply,
154 keycode: Keycode,
155 mut column: u8,
156) -> Result<Keysym> {
157 let mut per = mapping.keysyms_per_keycode;
159 if column >= per && column > 3 {
160 return Err(Error::make_msg("Invalid column"));
161 }
162
163 let start = (keycode - setup.min_keycode) as usize * per as usize;
165 let end = start + per as usize;
166 let keysyms = &mapping.keysyms[start..end];
167
168 if column < 4 {
170 if column > 1 {
171 while per > 2 && keysyms[per as usize - 1] == NO_SYMBOL {
172 per -= 1;
173 }
174
175 if per < 3 {
176 column -= 2;
177 }
178 }
179
180 if per <= column | 1 || keysyms[column as usize | 1] == NO_SYMBOL {
181 let (upper, lower) = convert_case(keysyms[column as usize & !1]);
183 if column & 1 == 0 {
184 return Ok(lower);
185 } else {
186 return Ok(upper);
187 }
188 }
189 }
190
191 Ok(keysyms[column as usize])
192}
193
194pub fn is_keypad_key(keysym: Keysym) -> bool {
196 matches!(keysym, KEY_KP_Space..=KEY_KP_Equal)
197}
198
199pub fn is_private_keypad_key(keysym: Keysym) -> bool {
201 matches!(keysym, 0x11000000..=0x1100FFFF)
202}
203
204pub fn is_cursor_key(keysym: Keysym) -> bool {
206 matches!(keysym, KEY_Home..=KEY_Select)
207}
208
209pub fn is_pf_key(keysym: Keysym) -> bool {
211 matches!(keysym, KEY_KP_F1..=KEY_KP_F4)
212}
213
214pub fn is_function_key(keysym: Keysym) -> bool {
216 matches!(keysym, KEY_F1..=KEY_F35)
217}
218
219pub fn is_misc_function_key(keysym: Keysym) -> bool {
221 matches!(keysym, KEY_Select..=KEY_Break)
222}
223
224pub fn is_modifier_key(keysym: Keysym) -> bool {
226 matches!(
227 keysym,
228 KEY_Shift_L..=KEY_Hyper_R
229 | KEY_ISO_Lock..=KEY_ISO_Level5_Lock
230 | KEY_Mode_switch
231 | KEY_Num_Lock
232 )
233}
234
235fn convert_case(keysym: Keysym) -> (Keysym, Keysym) {
237 let (mut upper, mut lower) = (keysym, keysym);
239
240 #[allow(non_upper_case_globals)]
242 match keysym {
243 KEY_A..=KEY_Z => lower += KEY_a - KEY_A,
244 KEY_a..=KEY_z => upper -= KEY_a - KEY_A,
245 KEY_Agrave..=KEY_Odiaeresis => lower += KEY_agrave - KEY_Agrave,
246 KEY_agrave..=KEY_odiaeresis => upper -= KEY_agrave - KEY_Agrave,
247 KEY_Ooblique..=KEY_Thorn => lower += KEY_oslash - KEY_Ooblique,
248 KEY_oslash..=KEY_thorn => upper -= KEY_oslash - KEY_Ooblique,
249 KEY_Aogonek => lower = KEY_aogonek,
250 KEY_aogonek => upper = KEY_Aogonek,
251 KEY_Lstroke..=KEY_Sacute => lower += KEY_lstroke - KEY_Lstroke,
252 KEY_lstroke..=KEY_sacute => upper -= KEY_lstroke - KEY_Lstroke,
253 KEY_Scaron..=KEY_Zacute => lower += KEY_scaron - KEY_Scaron,
254 KEY_scaron..=KEY_zacute => upper -= KEY_scaron - KEY_Scaron,
255 KEY_Zcaron..=KEY_Zabovedot => lower += KEY_zcaron - KEY_Zcaron,
256 KEY_zcaron..=KEY_zabovedot => upper -= KEY_zcaron - KEY_Zcaron,
257 KEY_Racute..=KEY_Tcedilla => lower += KEY_racute - KEY_Racute,
258 KEY_racute..=KEY_tcedilla => upper -= KEY_racute - KEY_Racute,
259 KEY_Hstroke..=KEY_Hcircumflex => lower += KEY_hstroke - KEY_Hstroke,
260 KEY_hstroke..=KEY_hcircumflex => upper -= KEY_hstroke - KEY_Hstroke,
261 KEY_Gbreve..=KEY_Jcircumflex => lower += KEY_gbreve - KEY_Gbreve,
262 KEY_gbreve..=KEY_jcircumflex => upper -= KEY_gbreve - KEY_Gbreve,
263 KEY_Cabovedot..=KEY_Scircumflex => lower += KEY_cabovedot - KEY_Cabovedot,
264 KEY_cabovedot..=KEY_scircumflex => upper -= KEY_cabovedot - KEY_Cabovedot,
265 KEY_Rcedilla..=KEY_Tslash => lower += KEY_rcedilla - KEY_Rcedilla,
266 KEY_rcedilla..=KEY_tslash => upper -= KEY_rcedilla - KEY_Rcedilla,
267 KEY_ENG => lower = KEY_eng,
268 KEY_eng => upper = KEY_ENG,
269 KEY_Amacron..=KEY_Umacron => lower += KEY_amacron - KEY_Amacron,
270 KEY_amacron..=KEY_umacron => upper -= KEY_amacron - KEY_Amacron,
271 KEY_Serbian_DJE..=KEY_Serbian_DZE => lower -= KEY_Serbian_DJE - KEY_Serbian_dje,
272 KEY_Serbian_dje..=KEY_Serbian_dze => upper += KEY_Serbian_DJE - KEY_Serbian_dje,
273 KEY_Cyrillic_YU..=KEY_Cyrillic_HARDSIGN => lower -= KEY_Cyrillic_YU - KEY_Cyrillic_yu,
274 KEY_Cyrillic_yu..=KEY_Cyrillic_hardsign => upper += KEY_Cyrillic_YU - KEY_Cyrillic_yu,
275 KEY_Greek_ALPHAaccent..=KEY_Greek_OMEGAaccent => {
276 lower += KEY_Greek_alphaaccent - KEY_Greek_ALPHAaccent
277 }
278 KEY_Greek_alphaaccent..=KEY_Greek_omegaaccent
279 if !matches!(
280 keysym,
281 KEY_Greek_iotaaccentdieresis | KEY_Greek_upsilonaccentdieresis
282 ) =>
283 {
284 upper -= KEY_Greek_alphaaccent - KEY_Greek_ALPHAaccent
285 }
286 KEY_Greek_ALPHA..=KEY_Greek_OMEGA => lower += KEY_Greek_alpha - KEY_Greek_ALPHA,
287 KEY_Greek_alpha..=KEY_Greek_omega if !matches!(keysym, KEY_Greek_finalsmallsigma) => {
288 upper -= KEY_Greek_alpha - KEY_Greek_ALPHA
289 }
290 KEY_Armenian_AYB..=KEY_Armenian_fe => {
291 lower |= 1;
292 upper &= !1;
293 }
294 _ => {}
295 }
296
297 (upper, lower)
298}