winit/platform_impl/linux/common/xkb/
mod.rs
1use std::ops::Deref;
2use std::os::raw::c_char;
3use std::ptr::{self, NonNull};
4use std::sync::atomic::{AtomicBool, Ordering};
5
6use crate::utils::Lazy;
7use smol_str::SmolStr;
8#[cfg(wayland_platform)]
9use std::os::unix::io::OwnedFd;
10use tracing::warn;
11use xkbcommon_dl::{
12 self as xkb, xkb_compose_status, xkb_context, xkb_context_flags, xkbcommon_compose_handle,
13 xkbcommon_handle, XkbCommon, XkbCommonCompose,
14};
15#[cfg(x11_platform)]
16use {x11_dl::xlib_xcb::xcb_connection_t, xkbcommon_dl::x11::xkbcommon_x11_handle};
17
18use crate::event::{ElementState, KeyEvent};
19use crate::keyboard::{Key, KeyLocation};
20use crate::platform_impl::KeyEventExtra;
21
22mod compose;
23mod keymap;
24mod state;
25
26use compose::{ComposeStatus, XkbComposeState, XkbComposeTable};
27use keymap::XkbKeymap;
28
29#[cfg(x11_platform)]
30pub use keymap::raw_keycode_to_physicalkey;
31pub use keymap::{physicalkey_to_scancode, scancode_to_physicalkey};
32pub use state::XkbState;
33
34static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false);
36
37static XKBH: Lazy<&'static XkbCommon> = Lazy::new(xkbcommon_handle);
38static XKBCH: Lazy<&'static XkbCommonCompose> = Lazy::new(xkbcommon_compose_handle);
39#[cfg(feature = "x11")]
40static XKBXH: Lazy<&'static xkb::x11::XkbCommonX11> = Lazy::new(xkbcommon_x11_handle);
41
42#[inline(always)]
43pub fn reset_dead_keys() {
44 RESET_DEAD_KEYS.store(true, Ordering::SeqCst);
45}
46
47#[derive(Debug)]
48pub enum Error {
49 XKBNotFound,
51}
52
53#[derive(Debug)]
54pub struct Context {
55 #[cfg(x11_platform)]
57 pub core_keyboard_id: i32,
58 state: Option<XkbState>,
59 keymap: Option<XkbKeymap>,
60 compose_state1: Option<XkbComposeState>,
61 compose_state2: Option<XkbComposeState>,
62 _compose_table: Option<XkbComposeTable>,
63 context: XkbContext,
64 scratch_buffer: Vec<u8>,
65}
66
67impl Context {
68 pub fn new() -> Result<Self, Error> {
69 if xkb::xkbcommon_option().is_none() {
70 return Err(Error::XKBNotFound);
71 }
72
73 let context = XkbContext::new()?;
74 let mut compose_table = XkbComposeTable::new(&context);
75 let mut compose_state1 = compose_table.as_ref().and_then(|table| table.new_state());
76 let mut compose_state2 = compose_table.as_ref().and_then(|table| table.new_state());
77
78 if compose_table.is_none() || compose_state1.is_none() || compose_state2.is_none() {
80 compose_state2 = None;
81 compose_state1 = None;
82 compose_table = None;
83 }
84
85 Ok(Self {
86 state: None,
87 keymap: None,
88 compose_state1,
89 compose_state2,
90 #[cfg(x11_platform)]
91 core_keyboard_id: 0,
92 _compose_table: compose_table,
93 context,
94 scratch_buffer: Vec::with_capacity(8),
95 })
96 }
97
98 #[cfg(feature = "x11")]
99 pub fn from_x11_xkb(xcb: *mut xcb_connection_t) -> Result<Self, Error> {
100 let result = unsafe {
101 (XKBXH.xkb_x11_setup_xkb_extension)(
102 xcb,
103 1,
104 2,
105 xkbcommon_dl::x11::xkb_x11_setup_xkb_extension_flags::XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
106 ptr::null_mut(),
107 ptr::null_mut(),
108 ptr::null_mut(),
109 ptr::null_mut(),
110 )
111 };
112
113 if result != 1 {
114 return Err(Error::XKBNotFound);
115 }
116
117 let mut this = Self::new()?;
118 this.core_keyboard_id = unsafe { (XKBXH.xkb_x11_get_core_keyboard_device_id)(xcb) };
119 this.set_keymap_from_x11(xcb);
120 Ok(this)
121 }
122
123 pub fn state_mut(&mut self) -> Option<&mut XkbState> {
124 self.state.as_mut()
125 }
126
127 pub fn keymap_mut(&mut self) -> Option<&mut XkbKeymap> {
128 self.keymap.as_mut()
129 }
130
131 #[cfg(wayland_platform)]
132 pub fn set_keymap_from_fd(&mut self, fd: OwnedFd, size: usize) {
133 let keymap = XkbKeymap::from_fd(&self.context, fd, size);
134 let state = keymap.as_ref().and_then(XkbState::new_wayland);
135 if keymap.is_none() || state.is_none() {
136 warn!("failed to update xkb keymap");
137 }
138 self.state = state;
139 self.keymap = keymap;
140 }
141
142 #[cfg(x11_platform)]
143 pub fn set_keymap_from_x11(&mut self, xcb: *mut xcb_connection_t) {
144 let keymap = XkbKeymap::from_x11_keymap(&self.context, xcb, self.core_keyboard_id);
145 let state = keymap.as_ref().and_then(|keymap| XkbState::new_x11(xcb, keymap));
146 if keymap.is_none() || state.is_none() {
147 warn!("failed to update xkb keymap");
148 }
149 self.state = state;
150 self.keymap = keymap;
151 }
152
153 pub fn key_context(&mut self) -> Option<KeyContext<'_>> {
155 let state = self.state.as_mut()?;
156 let keymap = self.keymap.as_mut()?;
157 let compose_state1 = self.compose_state1.as_mut();
158 let compose_state2 = self.compose_state2.as_mut();
159 let scratch_buffer = &mut self.scratch_buffer;
160 Some(KeyContext { state, keymap, compose_state1, compose_state2, scratch_buffer })
161 }
162
163 #[cfg(x11_platform)]
167 pub fn key_context_with_state<'a>(
168 &'a mut self,
169 state: &'a mut XkbState,
170 ) -> Option<KeyContext<'a>> {
171 let keymap = self.keymap.as_mut()?;
172 let compose_state1 = self.compose_state1.as_mut();
173 let compose_state2 = self.compose_state2.as_mut();
174 let scratch_buffer = &mut self.scratch_buffer;
175 Some(KeyContext { state, keymap, compose_state1, compose_state2, scratch_buffer })
176 }
177}
178
179pub struct KeyContext<'a> {
180 pub state: &'a mut XkbState,
181 pub keymap: &'a mut XkbKeymap,
182 compose_state1: Option<&'a mut XkbComposeState>,
183 compose_state2: Option<&'a mut XkbComposeState>,
184 scratch_buffer: &'a mut Vec<u8>,
185}
186
187impl KeyContext<'_> {
188 pub fn process_key_event(
189 &mut self,
190 keycode: u32,
191 state: ElementState,
192 repeat: bool,
193 ) -> KeyEvent {
194 let mut event =
195 KeyEventResults::new(self, keycode, !repeat && state == ElementState::Pressed);
196 let physical_key = keymap::raw_keycode_to_physicalkey(keycode);
197 let (logical_key, location) = event.key();
198 let text = event.text();
199 let (key_without_modifiers, _) = event.key_without_modifiers();
200 let text_with_all_modifiers = event.text_with_all_modifiers();
201
202 let platform_specific = KeyEventExtra { text_with_all_modifiers, key_without_modifiers };
203
204 KeyEvent { physical_key, logical_key, text, location, state, repeat, platform_specific }
205 }
206
207 fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option<SmolStr> {
208 self.scratch_buffer.clear();
209 self.scratch_buffer.reserve(8);
210 loop {
211 let bytes_written = unsafe {
212 (XKBH.xkb_keysym_to_utf8)(
213 keysym,
214 self.scratch_buffer.as_mut_ptr().cast(),
215 self.scratch_buffer.capacity(),
216 )
217 };
218 if bytes_written == 0 {
219 return None;
220 } else if bytes_written == -1 {
221 self.scratch_buffer.reserve(8);
222 } else {
223 unsafe { self.scratch_buffer.set_len(bytes_written.try_into().unwrap()) };
224 break;
225 }
226 }
227
228 self.scratch_buffer.pop();
230 byte_slice_to_smol_str(self.scratch_buffer)
231 }
232}
233
234struct KeyEventResults<'a, 'b> {
235 context: &'a mut KeyContext<'b>,
236 keycode: u32,
237 keysym: u32,
238 compose: ComposeStatus,
239}
240
241impl<'a, 'b> KeyEventResults<'a, 'b> {
242 fn new(context: &'a mut KeyContext<'b>, keycode: u32, compose: bool) -> Self {
243 let keysym = context.state.get_one_sym_raw(keycode);
244
245 let compose = if let Some(state) = context.compose_state1.as_mut().filter(|_| compose) {
246 if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) {
247 state.reset();
248 context.compose_state2.as_mut().unwrap().reset();
249 }
250 state.feed(keysym)
251 } else {
252 ComposeStatus::None
253 };
254
255 KeyEventResults { context, keycode, keysym, compose }
256 }
257
258 pub fn key(&mut self) -> (Key, KeyLocation) {
259 let (key, location) = match self.keysym_to_key(self.keysym) {
260 Ok(known) => return known,
261 Err(undefined) => undefined,
262 };
263
264 if let ComposeStatus::Accepted(xkb_compose_status::XKB_COMPOSE_COMPOSING) = self.compose {
265 let compose_state = self.context.compose_state2.as_mut().unwrap();
266 compose_state.feed(self.keysym);
272 if matches!(compose_state.feed(self.keysym), ComposeStatus::Accepted(_)) {
273 let text = compose_state.get_string(self.context.scratch_buffer);
276 let key = Key::Dead(text.and_then(|s| s.chars().next()));
277 (key, location)
278 } else {
279 (key, location)
280 }
281 } else {
282 let key = self
283 .composed_text()
284 .unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym))
285 .map(Key::Character)
286 .unwrap_or(key);
287 (key, location)
288 }
289 }
290
291 pub fn key_without_modifiers(&mut self) -> (Key, KeyLocation) {
292 let layout = self.context.state.layout(self.keycode);
295 let keysym = self.context.keymap.first_keysym_by_level(layout, self.keycode);
296
297 match self.keysym_to_key(keysym) {
298 Ok((key, location)) => (key, location),
299 Err((key, location)) => {
300 let key =
301 self.context.keysym_to_utf8_raw(keysym).map(Key::Character).unwrap_or(key);
302 (key, location)
303 },
304 }
305 }
306
307 fn keysym_to_key(&self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> {
308 let location = keymap::keysym_location(keysym);
309 let key = keymap::keysym_to_key(keysym);
310 if matches!(key, Key::Unidentified(_)) {
311 Err((key, location))
312 } else {
313 Ok((key, location))
314 }
315 }
316
317 pub fn text(&mut self) -> Option<SmolStr> {
318 self.composed_text().unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym))
319 }
320
321 pub fn text_with_all_modifiers(&mut self) -> Option<SmolStr> {
325 match self.composed_text() {
326 Ok(text) => text,
327 Err(_) => self.context.state.get_utf8_raw(self.keycode, self.context.scratch_buffer),
328 }
329 }
330
331 fn composed_text(&mut self) -> Result<Option<SmolStr>, ()> {
332 match self.compose {
333 ComposeStatus::Accepted(status) => match status {
334 xkb_compose_status::XKB_COMPOSE_COMPOSED => {
335 let state = self.context.compose_state1.as_mut().unwrap();
336 Ok(state.get_string(self.context.scratch_buffer))
337 },
338 xkb_compose_status::XKB_COMPOSE_COMPOSING
339 | xkb_compose_status::XKB_COMPOSE_CANCELLED => Ok(None),
340 xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()),
341 },
342 _ => Err(()),
343 }
344 }
345}
346
347#[derive(Debug)]
348pub struct XkbContext {
349 context: NonNull<xkb_context>,
350}
351
352impl XkbContext {
353 pub fn new() -> Result<Self, Error> {
354 let context = unsafe { (XKBH.xkb_context_new)(xkb_context_flags::XKB_CONTEXT_NO_FLAGS) };
355
356 let context = match NonNull::new(context) {
357 Some(context) => context,
358 None => return Err(Error::XKBNotFound),
359 };
360
361 Ok(Self { context })
362 }
363}
364
365impl Drop for XkbContext {
366 fn drop(&mut self) {
367 unsafe {
368 (XKBH.xkb_context_unref)(self.context.as_ptr());
369 }
370 }
371}
372
373impl Deref for XkbContext {
374 type Target = NonNull<xkb_context>;
375
376 fn deref(&self) -> &Self::Target {
377 &self.context
378 }
379}
380
381fn make_string_with<F>(scratch_buffer: &mut Vec<u8>, mut f: F) -> Option<SmolStr>
384where
385 F: FnMut(*mut c_char, usize) -> i32,
386{
387 let size = f(ptr::null_mut(), 0);
388 if size == 0 {
389 return None;
390 }
391 let size = usize::try_from(size).unwrap();
392 scratch_buffer.clear();
393 scratch_buffer.reserve(size + 1);
395 unsafe {
396 let written = f(scratch_buffer.as_mut_ptr().cast(), scratch_buffer.capacity());
397 if usize::try_from(written).unwrap() != size {
398 return None;
400 }
401 scratch_buffer.set_len(size);
402 };
403
404 byte_slice_to_smol_str(scratch_buffer)
405}
406
407#[track_caller]
409fn byte_slice_to_smol_str(bytes: &[u8]) -> Option<SmolStr> {
410 std::str::from_utf8(bytes)
411 .map(SmolStr::new)
412 .map_err(|e| {
413 tracing::warn!("UTF-8 received from libxkbcommon ({:?}) was invalid: {e}", bytes)
414 })
415 .ok()
416}