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