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