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 .and_then(|v| if v.is_empty() { None } else { Some(v) })
461 .or_else(|| env::var_os("LC_CTYPE"))
462 .and_then(|v| if v.is_empty() { None } else { Some(v) })
463 .or_else(|| env::var_os("LANG"))
464 .and_then(|v| if v.is_empty() { None } else { Some(v) })
465 .unwrap_or_else(|| "C".into())
466 .to_str()
467 {
468 // TODO: Pending new release of xkbcommon to use new_from_locale with OsStr
469 if let Ok(table) = xkb::compose::Table::new_from_locale(
470 &xkb_context,
471 locale.as_ref(),
472 xkb::compose::COMPILE_NO_FLAGS,
473 ) {
474 let compose_state =
475 xkb::compose::State::new(&table, xkb::compose::COMPILE_NO_FLAGS);
476 *self.xkb_compose.lock().unwrap() = Some(compose_state);
477 }
478 }
479 }
480
481 fn update_modifiers(&self) -> Modifiers {
482 let guard = self.xkb_state.lock().unwrap();
483 let state = guard.as_ref().unwrap();
484
485 Modifiers {
486 ctrl: state.mod_name_is_active(xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE),
487 alt: state.mod_name_is_active(xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE),
488 shift: state.mod_name_is_active(xkb::MOD_NAME_SHIFT, xkb::STATE_MODS_EFFECTIVE),
489 caps_lock: state.mod_name_is_active(xkb::MOD_NAME_CAPS, xkb::STATE_MODS_EFFECTIVE),
490 logo: state.mod_name_is_active(xkb::MOD_NAME_LOGO, xkb::STATE_MODS_EFFECTIVE),
491 num_lock: state.mod_name_is_active(xkb::MOD_NAME_NUM, xkb::STATE_MODS_EFFECTIVE),
492 }
493 }
494}
495
496impl<D, U> Dispatch2<wl_keyboard::WlKeyboard, D> for KeyboardData<D, U>
497where
498 D: KeyboardHandler + 'static,
499{
500 fn event(
501 &self,
502 data: &mut D,
503 keyboard: &wl_keyboard::WlKeyboard,
504 event: wl_keyboard::Event,
505 conn: &Connection,
506 qh: &QueueHandle<D>,
507 ) {
508 // The compositor has no way to tell clients if the seat is not version 4 or above.
509 // In this case, send a synthetic repeat info event using the default repeat values used by the X
510 // server.
511 if keyboard.version() < 4 && self.first_event.load(Ordering::SeqCst) {
512 self.first_event.store(true, Ordering::SeqCst);
513
514 data.update_repeat_info(
515 conn,
516 qh,
517 keyboard,
518 RepeatInfo::Repeat { rate: NonZeroU32::new(200).unwrap(), delay: 200 },
519 );
520 }
521
522 match event {
523 wl_keyboard::Event::Keymap { format, fd, size } => {
524 match format {
525 WEnum::Value(format) => match format {
526 wl_keyboard::KeymapFormat::NoKeymap => {
527 log::warn!(target: "sctk", "non-xkb compatible keymap");
528 }
529
530 wl_keyboard::KeymapFormat::XkbV1 => {
531 if self.user_specified_rmlvo {
532 // state is locked, ignore keymap updates
533 return;
534 }
535
536 let context = self.xkb_context.lock().unwrap();
537
538 // 0.5.0-beta.0 does not mark this function as unsafe but upstream rightly makes
539 // this function unsafe.
540 //
541 // Version 7 of wl_keyboard requires the file descriptor to be mapped using
542 // MAP_PRIVATE. xkbcommon-rs does mmap the file descriptor properly.
543 //
544 // SAFETY:
545 // - wayland-client guarantees we have received a valid file descriptor.
546 #[allow(unused_unsafe)] // Upstream release will change this
547 match unsafe {
548 xkb::Keymap::new_from_fd(
549 &context,
550 fd,
551 size as usize,
552 xkb::KEYMAP_FORMAT_TEXT_V1,
553 xkb::COMPILE_NO_FLAGS,
554 )
555 } {
556 Ok(Some(keymap)) => {
557 let state = xkb::State::new(&keymap);
558 {
559 let mut state_guard = self.xkb_state.lock().unwrap();
560 *state_guard = Some(state);
561 }
562 data.update_keymap(conn, qh, keyboard, Keymap(&keymap));
563 }
564
565 Ok(None) => {
566 log::error!(target: "sctk", "invalid keymap");
567 }
568
569 Err(err) => {
570 log::error!(target: "sctk", "{}", err);
571 }
572 }
573 }
574
575 _ => unreachable!(),
576 },
577
578 WEnum::Unknown(value) => {
579 log::warn!(target: "sctk", "unknown keymap format 0x{:x}", value)
580 }
581 }
582 }
583
584 wl_keyboard::Event::Enter { serial, surface, keys } => {
585 let state_guard = self.xkb_state.lock().unwrap();
586
587 if let Some(guard) = state_guard.as_ref() {
588 // Keysyms are encoded as an array of u32
589 let raw = keys
590 .chunks_exact(4)
591 .flat_map(TryInto::<[u8; 4]>::try_into)
592 .map(u32::from_le_bytes)
593 .collect::<Vec<_>>();
594
595 let keysyms = raw
596 .iter()
597 .copied()
598 // We must add 8 to the keycode for any functions we pass the raw keycode into per
599 // wl_keyboard protocol.
600 .map(|raw| guard.key_get_one_sym(KeyCode::new(raw + 8)))
601 .collect::<Vec<_>>();
602
603 // Drop guard before calling user code.
604 drop(state_guard);
605
606 data.enter(
607 conn,
608 qh,
609 keyboard,
610 &surface,
611 serial,
612 &raw,
613 bytemuck::cast_slice(&keysyms),
614 );
615 }
616
617 *self.focus.lock().unwrap() = Some(surface);
618 }
619
620 wl_keyboard::Event::Leave { serial, surface } => {
621 // We can send this event without any other checks in the protocol will guarantee a leave is
622 // sent before entering a new surface.
623 #[cfg(feature = "calloop")]
624 {
625 if let Some(repeat_data) = self.repeat_data.lock().unwrap().as_mut() {
626 repeat_data.current_repeat.take();
627 }
628 }
629
630 data.leave(conn, qh, keyboard, &surface, serial);
631
632 *self.focus.lock().unwrap() = None;
633 }
634
635 wl_keyboard::Event::Key { serial, time, key, state } => match state {
636 WEnum::Value(state) => {
637 let state_guard = self.xkb_state.lock().unwrap();
638
639 if let Some(guard) = state_guard.as_ref() {
640 // We must add 8 to the keycode for any functions we pass the raw keycode into per
641 // wl_keyboard protocol.
642 let keycode = KeyCode::new(key + 8);
643 let keysym = guard.key_get_one_sym(keycode);
644 let utf8 = if state == wl_keyboard::KeyState::Pressed {
645 let mut compose = self.xkb_compose.lock().unwrap();
646
647 match compose.as_mut() {
648 Some(compose) => match compose.feed(keysym) {
649 xkb::FeedResult::Ignored => None,
650 xkb::FeedResult::Accepted => match compose.status() {
651 xkb::Status::Composed => compose.utf8(),
652 xkb::Status::Nothing => Some(guard.key_get_utf8(keycode)),
653 _ => None,
654 },
655 },
656
657 // No compose
658 None => Some(guard.key_get_utf8(keycode)),
659 }
660 } else {
661 None
662 };
663
664 // Drop guard before calling user code.
665 drop(state_guard);
666
667 let event = KeyEvent { time, raw_code: key, keysym, utf8 };
668
669 match state {
670 wl_keyboard::KeyState::Released => {
671 #[cfg(feature = "calloop")]
672 {
673 if let Some(repeat_data) =
674 self.repeat_data.lock().unwrap().as_mut()
675 {
676 if Some(event.raw_code)
677 == repeat_data
678 .current_repeat
679 .as_ref()
680 .map(|r| r.key.raw_code)
681 {
682 repeat_data.current_repeat = None;
683 }
684 }
685 }
686 data.release_key(conn, qh, keyboard, serial, event);
687 }
688
689 wl_keyboard::KeyState::Repeated => {
690 data.repeat_key(conn, qh, keyboard, serial, event);
691 }
692
693 wl_keyboard::KeyState::Pressed => {
694 data.press_key(conn, qh, keyboard, serial, event.clone());
695 #[cfg(feature = "calloop")]
696 {
697 if let Some(repeat_data) =
698 self.repeat_data.lock().unwrap().as_mut()
699 {
700 let loop_handle = &mut repeat_data.loop_handle;
701 let state_guard = self.xkb_state.lock().unwrap();
702 let key_repeats = state_guard
703 .as_ref()
704 .map(|guard| {
705 guard
706 .get_keymap()
707 .key_repeats(KeyCode::new(event.raw_code + 8))
708 })
709 .unwrap_or_default();
710 if key_repeats {
711 // Cancel the previous timer / repeat.
712 if let Some(token) = repeat_data.repeat_token.take() {
713 loop_handle.remove(token);
714 }
715
716 let surface = match self
717 .focus
718 .lock()
719 .unwrap()
720 .as_ref()
721 .cloned()
722 {
723 Some(surface) => surface,
724
725 None => {
726 log::warn!(
727 "wl_keyboard::key with no focused surface"
728 );
729 return;
730 }
731 };
732
733 // Update the current repeat key.
734 repeat_data.current_repeat.replace(RepeatedKey {
735 key: event.clone(),
736 is_first: true,
737 surface,
738 });
739
740 let (delay, rate) = match repeat_data.repeat_info {
741 RepeatInfo::Disable => return,
742 RepeatInfo::Repeat { delay, rate } => (delay, rate),
743 };
744 let gap = Duration::from_micros(
745 1_000_000 / rate.get() as u64,
746 );
747 let timer = Timer::from_duration(
748 Duration::from_millis(delay as u64),
749 );
750 let repeat_data2 = self.repeat_data.clone();
751
752 // Start the timer.
753 let kbd = keyboard.clone();
754 if let Ok(token) = loop_handle.insert_source(
755 timer,
756 move |_, _, state| {
757 let mut repeat_data =
758 repeat_data2.lock().unwrap();
759 let repeat_data = match repeat_data.as_mut() {
760 Some(repeat_data) => repeat_data,
761 None => return TimeoutAction::Drop,
762 };
763
764 let callback = &mut repeat_data.callback;
765 let key = &mut repeat_data.current_repeat;
766 if key.is_none() {
767 return TimeoutAction::Drop;
768 }
769 let key = key.as_mut().unwrap();
770 // If surface was closed while focused, no `Leave`
771 // event occurred.
772 if !key.surface.is_alive() {
773 return TimeoutAction::Drop;
774 }
775 key.key.time += if key.is_first {
776 key.is_first = false;
777 delay
778 } else {
779 gap.as_millis() as u32
780 };
781 callback(state, &kbd, key.key.clone());
782 TimeoutAction::ToDuration(gap)
783 },
784 ) {
785 repeat_data.repeat_token = Some(token);
786 }
787 }
788 }
789 }
790 }
791
792 _ => unreachable!(),
793 }
794 };
795 }
796
797 WEnum::Unknown(unknown) => {
798 log::warn!(target: "sctk", "{}: compositor sends invalid key state: {:x}", keyboard.id(), unknown);
799 }
800 },
801
802 wl_keyboard::Event::Modifiers {
803 serial,
804 mods_depressed,
805 mods_latched,
806 mods_locked,
807 group,
808 } => {
809 let mut guard = self.xkb_state.lock().unwrap();
810
811 let state = match guard.as_mut() {
812 Some(state) => state,
813 None => return,
814 };
815
816 // Apply the new xkb state with the new modifiers.
817 let _ = state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
818
819 // Update the currently repeating key if any.
820 #[cfg(feature = "calloop")]
821 if let Some(repeat_data) = self.repeat_data.lock().unwrap().as_mut() {
822 if let Some(mut event) = repeat_data.current_repeat.take() {
823 // Apply new modifiers to get new utf8.
824 event.key.utf8 = {
825 let mut compose = self.xkb_compose.lock().unwrap();
826
827 match compose.as_mut() {
828 Some(compose) => match compose.feed(event.key.keysym) {
829 xkb::FeedResult::Ignored => None,
830 xkb::FeedResult::Accepted => match compose.status() {
831 xkb::Status::Composed => compose.utf8(),
832 xkb::Status::Nothing => Some(
833 state
834 .key_get_utf8(KeyCode::new(event.key.raw_code + 8)),
835 ),
836 _ => None,
837 },
838 },
839
840 // No compose.
841 None => {
842 Some(state.key_get_utf8(KeyCode::new(event.key.raw_code + 8)))
843 }
844 }
845 };
846
847 // Update the stored event.
848 repeat_data.current_repeat = Some(event);
849 }
850 }
851
852 // Drop guard before calling user code.
853 drop(guard);
854
855 let raw_modifiers = RawModifiers {
856 depressed: mods_depressed,
857 latched: mods_latched,
858 locked: mods_locked,
859 };
860
861 // Always issue the modifiers update for the user.
862 let modifiers = self.update_modifiers();
863 data.update_modifiers(conn, qh, keyboard, serial, modifiers, raw_modifiers, group);
864 }
865
866 wl_keyboard::Event::RepeatInfo { rate, delay } => {
867 let info = if rate != 0 {
868 RepeatInfo::Repeat {
869 rate: NonZeroU32::new(rate as u32).unwrap(),
870 delay: delay as u32,
871 }
872 } else {
873 RepeatInfo::Disable
874 };
875
876 #[cfg(feature = "calloop")]
877 {
878 if let Some(repeat_data) = self.repeat_data.lock().unwrap().as_mut() {
879 repeat_data.repeat_info = info;
880 }
881 }
882 data.update_repeat_info(conn, qh, keyboard, info);
883 }
884
885 _ => unreachable!(),
886 }
887 }
888}