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