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}