smithay_client_toolkit/seat/keyboard/mod.rs
1use std::{
2 convert::TryInto,
3 env,
4 fmt::Debug,
5 marker::PhantomData,
6 num::NonZeroU32,
7 sync::{
8 atomic::{AtomicBool, Ordering},
9 Arc, Mutex,
10 },
11 time::Duration,
12};
13
14#[doc(inline)]
15pub use xkeysym::{KeyCode, Keysym};
16
17#[cfg(feature = "calloop")]
18use calloop::timer::{TimeoutAction, Timer};
19use wayland_client::{
20 protocol::{wl_keyboard, wl_seat, wl_surface},
21 Connection, Dispatch, Proxy, QueueHandle, WEnum,
22};
23
24use xkbcommon::xkb;
25
26#[cfg(feature = "calloop")]
27use repeat::{RepeatData, RepeatedKey};
28
29use super::{Capability, SeatError, SeatHandler, SeatState};
30
31#[cfg(feature = "calloop")]
32pub mod repeat;
33
34/// Error when creating a keyboard.
35#[must_use]
36#[derive(Debug, thiserror::Error)]
37pub enum KeyboardError {
38 /// Seat error.
39 #[error(transparent)]
40 Seat(#[from] SeatError),
41
42 /// The specified keymap (RMLVO) is not valid.
43 #[error("invalid keymap was specified")]
44 InvalidKeymap,
45}
46
47impl SeatState {
48 /// Creates a keyboard from a seat.
49 ///
50 /// This keyboard implementation uses libxkbcommon for the keymap.
51 ///
52 /// Typically the compositor will provide a keymap, but you may specify your own keymap using the `rmlvo`
53 /// field.
54 ///
55 /// This keyboard only sends key repeats if they are issued by the compositor.
56 /// See wl_keyboard version 10.
57 ///
58 /// ## Errors
59 ///
60 /// This will return [`SeatError::UnsupportedCapability`] if the seat does not support a keyboard.
61 pub fn get_keyboard<D, T: 'static>(
62 &mut self,
63 qh: &QueueHandle<D>,
64 seat: &wl_seat::WlSeat,
65 rmlvo: Option<RMLVO>,
66 ) -> Result<wl_keyboard::WlKeyboard, KeyboardError>
67 where
68 D: Dispatch<wl_keyboard::WlKeyboard, KeyboardData<T>>
69 + SeatHandler
70 + KeyboardHandler
71 + 'static,
72 {
73 let udata = match rmlvo {
74 Some(rmlvo) => KeyboardData::from_rmlvo(seat.clone(), rmlvo)?,
75 None => KeyboardData::new(seat.clone()),
76 };
77
78 self.get_keyboard_with_data(qh, seat, udata)
79 }
80
81 /// Creates a keyboard from a seat.
82 ///
83 /// This keyboard implementation uses libxkbcommon for the keymap.
84 ///
85 /// Typically the compositor will provide a keymap, but you may specify your own keymap using the `rmlvo`
86 /// field.
87 ///
88 /// ## Errors
89 ///
90 /// This will return [`SeatError::UnsupportedCapability`] if the seat does not support a keyboard.
91 pub fn get_keyboard_with_data<D, U>(
92 &mut self,
93 qh: &QueueHandle<D>,
94 seat: &wl_seat::WlSeat,
95 udata: U,
96 ) -> Result<wl_keyboard::WlKeyboard, KeyboardError>
97 where
98 D: Dispatch<wl_keyboard::WlKeyboard, U> + SeatHandler + KeyboardHandler + 'static,
99 U: KeyboardDataExt + 'static,
100 {
101 let inner =
102 self.seats.iter().find(|inner| &inner.seat == seat).ok_or(SeatError::DeadObject)?;
103
104 if !inner.data.has_keyboard.load(Ordering::SeqCst) {
105 return Err(SeatError::UnsupportedCapability(Capability::Keyboard).into());
106 }
107
108 Ok(seat.get_keyboard(qh, udata))
109 }
110}
111
112/// Wrapper around a libxkbcommon keymap
113#[allow(missing_debug_implementations)]
114pub struct Keymap<'a>(&'a xkb::Keymap);
115
116impl Keymap<'_> {
117 /// Get keymap as string in text format. The keymap should always be valid.
118 pub fn as_string(&self) -> String {
119 self.0.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1)
120 }
121}
122
123/// Handler trait for keyboard input.
124///
125/// The functions defined in this trait are called as keyboard events are received from the compositor.
126pub trait KeyboardHandler: Sized {
127 /// The keyboard has entered a surface.
128 ///
129 /// When called, you may assume the specified surface has keyboard focus.
130 ///
131 /// When a keyboard enters a surface, the `raw` and `keysym` fields indicate which keys are currently
132 /// pressed.
133 #[allow(clippy::too_many_arguments)]
134 fn enter(
135 &mut self,
136 conn: &Connection,
137 qh: &QueueHandle<Self>,
138 keyboard: &wl_keyboard::WlKeyboard,
139 surface: &wl_surface::WlSurface,
140 serial: u32,
141 raw: &[u32],
142 keysyms: &[Keysym],
143 );
144
145 /// The keyboard has left a surface.
146 ///
147 /// When called, keyboard focus leaves the specified surface.
148 ///
149 /// All currently held down keys are released when this event occurs.
150 fn leave(
151 &mut self,
152 conn: &Connection,
153 qh: &QueueHandle<Self>,
154 keyboard: &wl_keyboard::WlKeyboard,
155 surface: &wl_surface::WlSurface,
156 serial: u32,
157 );
158
159 /// A key has been pressed on the keyboard.
160 ///
161 /// The key will repeat if there is no other press event afterwards or the key is released.
162 fn press_key(
163 &mut self,
164 conn: &Connection,
165 qh: &QueueHandle<Self>,
166 keyboard: &wl_keyboard::WlKeyboard,
167 serial: u32,
168 event: KeyEvent,
169 );
170
171 /// A key has been previously pressed and is now repeating.
172 ///
173 /// This is only called on supporting compositors.
174 fn repeat_key(
175 &mut self,
176 conn: &Connection,
177 qh: &QueueHandle<Self>,
178 keyboard: &wl_keyboard::WlKeyboard,
179 serial: u32,
180 event: KeyEvent,
181 );
182
183 /// A key has been released.
184 ///
185 /// This stops the key from being repeated if the key is the last key which was pressed.
186 fn release_key(
187 &mut self,
188 conn: &Connection,
189 qh: &QueueHandle<Self>,
190 keyboard: &wl_keyboard::WlKeyboard,
191 serial: u32,
192 event: KeyEvent,
193 );
194
195 /// Keyboard modifiers have been updated.
196 ///
197 /// This happens when one of the modifier keys, such as "Shift", "Control" or "Alt" is pressed or
198 /// released.
199 #[allow(clippy::too_many_arguments)]
200 fn update_modifiers(
201 &mut self,
202 conn: &Connection,
203 qh: &QueueHandle<Self>,
204 keyboard: &wl_keyboard::WlKeyboard,
205 serial: u32,
206 modifiers: Modifiers,
207 raw_modifiers: RawModifiers,
208 layout: u32,
209 );
210
211 /// The keyboard has updated the rate and delay between repeating key inputs.
212 ///
213 /// This function does nothing by default but is provided if a repeat mechanism outside of calloop is\
214 /// used.
215 fn update_repeat_info(
216 &mut self,
217 _conn: &Connection,
218 _qh: &QueueHandle<Self>,
219 _keyboard: &wl_keyboard::WlKeyboard,
220 _info: RepeatInfo,
221 ) {
222 }
223
224 /// Keyboard keymap has been updated.
225 ///
226 /// `keymap.as_string()` can be used get the keymap as a string. It cannot be exposed directly
227 /// as an `xkbcommon::xkb::Keymap` due to the fact xkbcommon uses non-thread-safe reference
228 /// counting. But can be used to create an independent `Keymap`.
229 ///
230 /// This is called after the default handler for keymap changes and does nothing by default.
231 fn update_keymap(
232 &mut self,
233 _conn: &Connection,
234 _qh: &QueueHandle<Self>,
235 _keyboard: &wl_keyboard::WlKeyboard,
236 _keymap: Keymap<'_>,
237 ) {
238 }
239}
240
241/// The rate at which a pressed key is repeated.
242#[derive(Debug, Clone, Copy)]
243pub enum RepeatInfo {
244 /// Keys will be repeated at the specified rate and delay.
245 Repeat {
246 /// The number of repetitions per second that should occur.
247 rate: NonZeroU32,
248
249 /// Delay (in milliseconds) between a key press and the start of repetition.
250 delay: u32,
251 },
252
253 /// Keys should not be repeated.
254 Disable,
255}
256
257/// Data associated with a key press or release event.
258#[derive(Debug, Clone)]
259pub struct KeyEvent {
260 /// Time at which the keypress occurred.
261 pub time: u32,
262
263 /// The raw value of the key.
264 pub raw_code: u32,
265
266 /// The interpreted symbol of the key.
267 ///
268 /// This corresponds to one of the assoiated values on the [`Keysym`] type.
269 pub keysym: Keysym,
270
271 /// UTF-8 interpretation of the entered text.
272 ///
273 /// This will always be [`None`] on release events.
274 pub utf8: Option<String>,
275}
276
277/// State of keyboard modifiers, in raw form sent by compositor.
278#[derive(Debug, Clone, Copy, Default)]
279pub struct RawModifiers {
280 pub depressed: u32,
281 pub latched: u32,
282 pub locked: u32,
283}
284
285/// The state of keyboard modifiers
286///
287/// Each field of this indicates whether a specified modifier is active.
288///
289/// Depending on the modifier, the modifier key may currently be pressed or toggled.
290#[derive(Debug, Clone, Copy, Default)]
291pub struct Modifiers {
292 /// The "control" key
293 pub ctrl: bool,
294
295 /// The "alt" key
296 pub alt: bool,
297
298 /// The "shift" key
299 pub shift: bool,
300
301 /// The "Caps lock" key
302 pub caps_lock: bool,
303
304 /// The "logo" key
305 ///
306 /// Also known as the "windows" or "super" key on a keyboard.
307 #[doc(alias = "windows")]
308 #[doc(alias = "super")]
309 pub logo: bool,
310
311 /// The "Num lock" key
312 pub num_lock: bool,
313}
314
315/// The RMLVO description of a keymap
316///
317/// All fields are optional, and the system default
318/// will be used if set to `None`.
319#[derive(Debug)]
320#[allow(clippy::upper_case_acronyms)]
321pub struct RMLVO {
322 /// The rules file to use
323 pub rules: Option<String>,
324
325 /// The keyboard model by which to interpret keycodes and LEDs
326 pub model: Option<String>,
327
328 /// A comma separated list of layouts (languages) to include in the keymap
329 pub layout: Option<String>,
330
331 /// A comma separated list of variants, one per layout, which may modify or
332 /// augment the respective layout in various ways
333 pub variant: Option<String>,
334
335 /// A comma separated list of options, through which the user specifies
336 /// non-layout related preferences, like which key combinations are
337 /// used for switching layouts, or which key is the Compose key.
338 pub options: Option<String>,
339}
340
341pub struct KeyboardData<T> {
342 seat: wl_seat::WlSeat,
343 first_event: AtomicBool,
344 xkb_context: Mutex<xkb::Context>,
345 /// If the user manually specified the RMLVO to use.
346 user_specified_rmlvo: bool,
347 xkb_state: Mutex<Option<xkb::State>>,
348 xkb_compose: Mutex<Option<xkb::compose::State>>,
349 #[cfg(feature = "calloop")]
350 repeat_data: Arc<Mutex<Option<RepeatData<T>>>>,
351 focus: Mutex<Option<wl_surface::WlSurface>>,
352 _phantom_data: PhantomData<T>,
353}
354
355impl<T> Debug for KeyboardData<T> {
356 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
357 f.debug_struct("KeyboardData").finish_non_exhaustive()
358 }
359}
360
361#[macro_export]
362macro_rules! delegate_keyboard {
363 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
364 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
365 [
366 $crate::reexports::client::protocol::wl_keyboard::WlKeyboard: $crate::seat::keyboard::KeyboardData<$ty>
367 ] => $crate::seat::SeatState
368 );
369 };
370 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty, keyboard: [$($udata:ty),* $(,)?]) => {
371 $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
372 [
373 $(
374 $crate::reexports::client::protocol::wl_keyboard::WlKeyboard: $udata,
375 )*
376 ] => $crate::seat::SeatState
377 );
378 };
379}
380
381// SAFETY: The state does not share state with any other rust types.
382unsafe impl<T> Send for KeyboardData<T> {}
383// SAFETY: The state is guarded by a mutex since libxkbcommon has no internal synchronization.
384unsafe impl<T> Sync for KeyboardData<T> {}
385
386impl<T> KeyboardData<T> {
387 pub fn new(seat: wl_seat::WlSeat) -> Self {
388 let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
389 let udata = KeyboardData {
390 seat,
391 first_event: AtomicBool::new(false),
392 xkb_context: Mutex::new(xkb_context),
393 xkb_state: Mutex::new(None),
394 user_specified_rmlvo: false,
395 xkb_compose: Mutex::new(None),
396 #[cfg(feature = "calloop")]
397 repeat_data: Arc::new(Mutex::new(None)),
398 focus: Mutex::new(None),
399 _phantom_data: PhantomData,
400 };
401
402 udata.init_compose();
403
404 udata
405 }
406
407 pub fn seat(&self) -> &wl_seat::WlSeat {
408 &self.seat
409 }
410
411 pub fn from_rmlvo(seat: wl_seat::WlSeat, rmlvo: RMLVO) -> Result<Self, KeyboardError> {
412 let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
413 let keymap = xkb::Keymap::new_from_names(
414 &xkb_context,
415 &rmlvo.rules.unwrap_or_default(),
416 &rmlvo.model.unwrap_or_default(),
417 &rmlvo.layout.unwrap_or_default(),
418 &rmlvo.variant.unwrap_or_default(),
419 rmlvo.options,
420 xkb::COMPILE_NO_FLAGS,
421 );
422
423 if keymap.is_none() {
424 return Err(KeyboardError::InvalidKeymap);
425 }
426
427 let xkb_state = Some(xkb::State::new(&keymap.unwrap()));
428
429 let udata = KeyboardData {
430 seat,
431 first_event: AtomicBool::new(false),
432 xkb_context: Mutex::new(xkb_context),
433 xkb_state: Mutex::new(xkb_state),
434 user_specified_rmlvo: true,
435 xkb_compose: Mutex::new(None),
436 #[cfg(feature = "calloop")]
437 repeat_data: Arc::new(Mutex::new(None)),
438 focus: Mutex::new(None),
439 _phantom_data: PhantomData,
440 };
441
442 udata.init_compose();
443
444 Ok(udata)
445 }
446
447 fn init_compose(&self) {
448 let xkb_context = self.xkb_context.lock().unwrap();
449
450 if let Some(locale) = env::var_os("LC_ALL")
451 .and_then(|v| if v.is_empty() { None } else { Some(v) })
452 .or_else(|| env::var_os("LC_CTYPE"))
453 .and_then(|v| if v.is_empty() { None } else { Some(v) })
454 .or_else(|| env::var_os("LANG"))
455 .and_then(|v| if v.is_empty() { None } else { Some(v) })
456 .unwrap_or_else(|| "C".into())
457 .to_str()
458 {
459 // TODO: Pending new release of xkbcommon to use new_from_locale with OsStr
460 if let Ok(table) = xkb::compose::Table::new_from_locale(
461 &xkb_context,
462 locale.as_ref(),
463 xkb::compose::COMPILE_NO_FLAGS,
464 ) {
465 let compose_state =
466 xkb::compose::State::new(&table, xkb::compose::COMPILE_NO_FLAGS);
467 *self.xkb_compose.lock().unwrap() = Some(compose_state);
468 }
469 }
470 }
471
472 fn update_modifiers(&self) -> Modifiers {
473 let guard = self.xkb_state.lock().unwrap();
474 let state = guard.as_ref().unwrap();
475
476 Modifiers {
477 ctrl: state.mod_name_is_active(xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE),
478 alt: state.mod_name_is_active(xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE),
479 shift: state.mod_name_is_active(xkb::MOD_NAME_SHIFT, xkb::STATE_MODS_EFFECTIVE),
480 caps_lock: state.mod_name_is_active(xkb::MOD_NAME_CAPS, xkb::STATE_MODS_EFFECTIVE),
481 logo: state.mod_name_is_active(xkb::MOD_NAME_LOGO, xkb::STATE_MODS_EFFECTIVE),
482 num_lock: state.mod_name_is_active(xkb::MOD_NAME_NUM, xkb::STATE_MODS_EFFECTIVE),
483 }
484 }
485}
486
487pub trait KeyboardDataExt: Send + Sync {
488 type State: 'static;
489 fn keyboard_data(&self) -> &KeyboardData<Self::State>;
490 fn keyboard_data_mut(&mut self) -> &mut KeyboardData<Self::State>;
491}
492
493impl<T: 'static> KeyboardDataExt for KeyboardData<T> {
494 /// The type of the user defined state
495 type State = T;
496 fn keyboard_data(&self) -> &KeyboardData<T> {
497 self
498 }
499
500 fn keyboard_data_mut(&mut self) -> &mut KeyboardData<T> {
501 self
502 }
503}
504
505impl<D, U> Dispatch<wl_keyboard::WlKeyboard, U, D> for SeatState
506where
507 D: Dispatch<wl_keyboard::WlKeyboard, U> + KeyboardHandler,
508 U: KeyboardDataExt,
509{
510 fn event(
511 data: &mut D,
512 keyboard: &wl_keyboard::WlKeyboard,
513 event: wl_keyboard::Event,
514 udata: &U,
515 conn: &Connection,
516 qh: &QueueHandle<D>,
517 ) {
518 let udata = udata.keyboard_data();
519
520 // The compositor has no way to tell clients if the seat is not version 4 or above.
521 // In this case, send a synthetic repeat info event using the default repeat values used by the X
522 // server.
523 if keyboard.version() < 4 && udata.first_event.load(Ordering::SeqCst) {
524 udata.first_event.store(true, Ordering::SeqCst);
525
526 data.update_repeat_info(
527 conn,
528 qh,
529 keyboard,
530 RepeatInfo::Repeat { rate: NonZeroU32::new(200).unwrap(), delay: 200 },
531 );
532 }
533
534 match event {
535 wl_keyboard::Event::Keymap { format, fd, size } => {
536 match format {
537 WEnum::Value(format) => match format {
538 wl_keyboard::KeymapFormat::NoKeymap => {
539 log::warn!(target: "sctk", "non-xkb compatible keymap");
540 }
541
542 wl_keyboard::KeymapFormat::XkbV1 => {
543 if udata.user_specified_rmlvo {
544 // state is locked, ignore keymap updates
545 return;
546 }
547
548 let context = udata.xkb_context.lock().unwrap();
549
550 // 0.5.0-beta.0 does not mark this function as unsafe but upstream rightly makes
551 // this function unsafe.
552 //
553 // Version 7 of wl_keyboard requires the file descriptor to be mapped using
554 // MAP_PRIVATE. xkbcommon-rs does mmap the file descriptor properly.
555 //
556 // SAFETY:
557 // - wayland-client guarantees we have received a valid file descriptor.
558 #[allow(unused_unsafe)] // Upstream release will change this
559 match unsafe {
560 xkb::Keymap::new_from_fd(
561 &context,
562 fd,
563 size as usize,
564 xkb::KEYMAP_FORMAT_TEXT_V1,
565 xkb::COMPILE_NO_FLAGS,
566 )
567 } {
568 Ok(Some(keymap)) => {
569 let state = xkb::State::new(&keymap);
570 {
571 let mut state_guard = udata.xkb_state.lock().unwrap();
572 *state_guard = Some(state);
573 }
574 data.update_keymap(conn, qh, keyboard, Keymap(&keymap));
575 }
576
577 Ok(None) => {
578 log::error!(target: "sctk", "invalid keymap");
579 }
580
581 Err(err) => {
582 log::error!(target: "sctk", "{}", err);
583 }
584 }
585 }
586
587 _ => unreachable!(),
588 },
589
590 WEnum::Unknown(value) => {
591 log::warn!(target: "sctk", "unknown keymap format 0x{:x}", value)
592 }
593 }
594 }
595
596 wl_keyboard::Event::Enter { serial, surface, keys } => {
597 let state_guard = udata.xkb_state.lock().unwrap();
598
599 if let Some(guard) = state_guard.as_ref() {
600 // Keysyms are encoded as an array of u32
601 let raw = keys
602 .chunks_exact(4)
603 .flat_map(TryInto::<[u8; 4]>::try_into)
604 .map(u32::from_le_bytes)
605 .collect::<Vec<_>>();
606
607 let keysyms = raw
608 .iter()
609 .copied()
610 // We must add 8 to the keycode for any functions we pass the raw keycode into per
611 // wl_keyboard protocol.
612 .map(|raw| guard.key_get_one_sym(KeyCode::new(raw + 8)))
613 .collect::<Vec<_>>();
614
615 // Drop guard before calling user code.
616 drop(state_guard);
617
618 data.enter(
619 conn,
620 qh,
621 keyboard,
622 &surface,
623 serial,
624 &raw,
625 bytemuck::cast_slice(&keysyms),
626 );
627 }
628
629 *udata.focus.lock().unwrap() = Some(surface);
630 }
631
632 wl_keyboard::Event::Leave { serial, surface } => {
633 // We can send this event without any other checks in the protocol will guarantee a leave is
634 // sent before entering a new surface.
635 #[cfg(feature = "calloop")]
636 {
637 if let Some(repeat_data) = udata.repeat_data.lock().unwrap().as_mut() {
638 repeat_data.current_repeat.take();
639 }
640 }
641
642 data.leave(conn, qh, keyboard, &surface, serial);
643
644 *udata.focus.lock().unwrap() = None;
645 }
646
647 wl_keyboard::Event::Key { serial, time, key, state } => match state {
648 WEnum::Value(state) => {
649 let state_guard = udata.xkb_state.lock().unwrap();
650
651 if let Some(guard) = state_guard.as_ref() {
652 // We must add 8 to the keycode for any functions we pass the raw keycode into per
653 // wl_keyboard protocol.
654 let keycode = KeyCode::new(key + 8);
655 let keysym = guard.key_get_one_sym(keycode);
656 let utf8 = if state == wl_keyboard::KeyState::Pressed {
657 let mut compose = udata.xkb_compose.lock().unwrap();
658
659 match compose.as_mut() {
660 Some(compose) => match compose.feed(keysym) {
661 xkb::FeedResult::Ignored => None,
662 xkb::FeedResult::Accepted => match compose.status() {
663 xkb::Status::Composed => compose.utf8(),
664 xkb::Status::Nothing => Some(guard.key_get_utf8(keycode)),
665 _ => None,
666 },
667 },
668
669 // No compose
670 None => Some(guard.key_get_utf8(keycode)),
671 }
672 } else {
673 None
674 };
675
676 // Drop guard before calling user code.
677 drop(state_guard);
678
679 let event = KeyEvent { time, raw_code: key, keysym, utf8 };
680
681 match state {
682 wl_keyboard::KeyState::Released => {
683 #[cfg(feature = "calloop")]
684 {
685 if let Some(repeat_data) =
686 udata.repeat_data.lock().unwrap().as_mut()
687 {
688 if Some(event.raw_code)
689 == repeat_data
690 .current_repeat
691 .as_ref()
692 .map(|r| r.key.raw_code)
693 {
694 repeat_data.current_repeat = None;
695 }
696 }
697 }
698 data.release_key(conn, qh, keyboard, serial, event);
699 }
700
701 wl_keyboard::KeyState::Repeated => {
702 data.repeat_key(conn, qh, keyboard, serial, event);
703 }
704
705 wl_keyboard::KeyState::Pressed => {
706 data.press_key(conn, qh, keyboard, serial, event.clone());
707 #[cfg(feature = "calloop")]
708 {
709 if let Some(repeat_data) =
710 udata.repeat_data.lock().unwrap().as_mut()
711 {
712 let loop_handle = &mut repeat_data.loop_handle;
713 let state_guard = udata.xkb_state.lock().unwrap();
714 let key_repeats = state_guard
715 .as_ref()
716 .map(|guard| {
717 guard
718 .get_keymap()
719 .key_repeats(KeyCode::new(event.raw_code + 8))
720 })
721 .unwrap_or_default();
722 if key_repeats {
723 // Cancel the previous timer / repeat.
724 if let Some(token) = repeat_data.repeat_token.take() {
725 loop_handle.remove(token);
726 }
727
728 let surface =
729 match udata.focus.lock().unwrap().as_ref().cloned()
730 {
731 Some(surface) => surface,
732
733 None => {
734 log::warn!(
735 "wl_keyboard::key with no focused surface");
736 return;
737 }
738 };
739
740 // Update the current repeat key.
741 repeat_data.current_repeat.replace(RepeatedKey {
742 key: event.clone(),
743 is_first: true,
744 surface,
745 });
746
747 let (delay, rate) = match repeat_data.repeat_info {
748 RepeatInfo::Disable => return,
749 RepeatInfo::Repeat { delay, rate } => (delay, rate),
750 };
751 let gap = Duration::from_micros(
752 1_000_000 / rate.get() as u64,
753 );
754 let timer = Timer::from_duration(
755 Duration::from_millis(delay as u64),
756 );
757 let repeat_data2 = udata.repeat_data.clone();
758
759 // Start the timer.
760 let kbd = keyboard.clone();
761 if let Ok(token) = loop_handle.insert_source(
762 timer,
763 move |_, _, state| {
764 let mut repeat_data =
765 repeat_data2.lock().unwrap();
766 let repeat_data = match repeat_data.as_mut() {
767 Some(repeat_data) => repeat_data,
768 None => return TimeoutAction::Drop,
769 };
770
771 let callback = &mut repeat_data.callback;
772 let key = &mut repeat_data.current_repeat;
773 if key.is_none() {
774 return TimeoutAction::Drop;
775 }
776 let key = key.as_mut().unwrap();
777 // If surface was closed while focused, no `Leave`
778 // event occurred.
779 if !key.surface.is_alive() {
780 return TimeoutAction::Drop;
781 }
782 key.key.time += if key.is_first {
783 key.is_first = false;
784 delay
785 } else {
786 gap.as_millis() as u32
787 };
788 callback(state, &kbd, key.key.clone());
789 TimeoutAction::ToDuration(gap)
790 },
791 ) {
792 repeat_data.repeat_token = Some(token);
793 }
794 }
795 }
796 }
797 }
798
799 _ => unreachable!(),
800 }
801 };
802 }
803
804 WEnum::Unknown(unknown) => {
805 log::warn!(target: "sctk", "{}: compositor sends invalid key state: {:x}", keyboard.id(), unknown);
806 }
807 },
808
809 wl_keyboard::Event::Modifiers {
810 serial,
811 mods_depressed,
812 mods_latched,
813 mods_locked,
814 group,
815 } => {
816 let mut guard = udata.xkb_state.lock().unwrap();
817
818 let state = match guard.as_mut() {
819 Some(state) => state,
820 None => return,
821 };
822
823 // Apply the new xkb state with the new modifiers.
824 let _ = state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
825
826 // Update the currently repeating key if any.
827 #[cfg(feature = "calloop")]
828 if let Some(repeat_data) = udata.repeat_data.lock().unwrap().as_mut() {
829 if let Some(mut event) = repeat_data.current_repeat.take() {
830 // Apply new modifiers to get new utf8.
831 event.key.utf8 = {
832 let mut compose = udata.xkb_compose.lock().unwrap();
833
834 match compose.as_mut() {
835 Some(compose) => match compose.feed(event.key.keysym) {
836 xkb::FeedResult::Ignored => None,
837 xkb::FeedResult::Accepted => match compose.status() {
838 xkb::Status::Composed => compose.utf8(),
839 xkb::Status::Nothing => Some(
840 state
841 .key_get_utf8(KeyCode::new(event.key.raw_code + 8)),
842 ),
843 _ => None,
844 },
845 },
846
847 // No compose.
848 None => {
849 Some(state.key_get_utf8(KeyCode::new(event.key.raw_code + 8)))
850 }
851 }
852 };
853
854 // Update the stored event.
855 repeat_data.current_repeat = Some(event);
856 }
857 }
858
859 // Drop guard before calling user code.
860 drop(guard);
861
862 let raw_modifiers = RawModifiers {
863 depressed: mods_depressed,
864 latched: mods_latched,
865 locked: mods_locked,
866 };
867
868 // Always issue the modifiers update for the user.
869 let modifiers = udata.update_modifiers();
870 data.update_modifiers(conn, qh, keyboard, serial, modifiers, raw_modifiers, group);
871 }
872
873 wl_keyboard::Event::RepeatInfo { rate, delay } => {
874 let info = if rate != 0 {
875 RepeatInfo::Repeat {
876 rate: NonZeroU32::new(rate as u32).unwrap(),
877 delay: delay as u32,
878 }
879 } else {
880 RepeatInfo::Disable
881 };
882
883 #[cfg(feature = "calloop")]
884 {
885 if let Some(repeat_data) = udata.repeat_data.lock().unwrap().as_mut() {
886 repeat_data.repeat_info = info;
887 }
888 }
889 data.update_repeat_info(conn, qh, keyboard, info);
890 }
891
892 _ => unreachable!(),
893 }
894 }
895}