Skip to main content

smithay_client_toolkit/seat/keyboard/
repeat.rs

1use std::sync::atomic::Ordering;
2
3use calloop::{LoopHandle, RegistrationToken};
4use wayland_client::{
5    protocol::{
6        wl_keyboard::{self, WlKeyboard},
7        wl_seat, wl_surface,
8    },
9    Dispatch, QueueHandle,
10};
11
12use super::{
13    Capability, KeyEvent, KeyboardData, KeyboardError, KeyboardHandler, RepeatInfo, SeatError,
14    RMLVO,
15};
16use crate::seat::SeatState;
17
18pub(crate) struct RepeatedKey {
19    pub(crate) key: KeyEvent,
20    /// Whether this is the first event of the repeat sequence.
21    pub(crate) is_first: bool,
22    pub(crate) surface: wl_surface::WlSurface,
23}
24
25pub type RepeatCallback<T> = Box<dyn FnMut(&mut T, &WlKeyboard, KeyEvent) + 'static>;
26
27pub(crate) struct RepeatData<T> {
28    pub(crate) current_repeat: Option<RepeatedKey>,
29    pub(crate) repeat_info: RepeatInfo,
30    pub(crate) loop_handle: LoopHandle<'static, T>,
31    pub(crate) callback: RepeatCallback<T>,
32    pub(crate) repeat_token: Option<RegistrationToken>,
33}
34
35impl<T> Drop for RepeatData<T> {
36    fn drop(&mut self) {
37        if let Some(token) = self.repeat_token.take() {
38            self.loop_handle.remove(token);
39        }
40    }
41}
42
43impl SeatState {
44    /// Creates a keyboard from a seat.
45    ///
46    /// This function returns an [`EventSource`] that indicates when a key press is going to repeat.
47    ///
48    /// This keyboard implementation uses libxkbcommon for the keymap.
49    ///
50    /// Typically the compositor will provide a keymap, but you may specify your own keymap using the `rmlvo`
51    /// field.
52    ///
53    /// ## Errors
54    ///
55    /// This will return [`SeatError::UnsupportedCapability`] if the seat does not support a keyboard.
56    ///
57    /// [`EventSource`]: calloop::EventSource
58    pub fn get_keyboard_with_repeat<D>(
59        &mut self,
60        qh: &QueueHandle<D>,
61        seat: &wl_seat::WlSeat,
62        rmlvo: Option<RMLVO>,
63        loop_handle: LoopHandle<'static, D>,
64        callback: RepeatCallback<D>,
65    ) -> Result<wl_keyboard::WlKeyboard, KeyboardError>
66    where
67        D: Dispatch<wl_keyboard::WlKeyboard, KeyboardData<D, ()>> + KeyboardHandler + 'static,
68    {
69        let udata = match rmlvo {
70            Some(rmlvo) => KeyboardData::from_rmlvo(seat.clone(), rmlvo, ())?,
71            None => KeyboardData::new(seat.clone(), ()),
72        };
73
74        let inner =
75            self.seats.iter().find(|inner| &inner.seat == seat).ok_or(SeatError::DeadObject)?;
76
77        if !inner.data.has_keyboard.load(Ordering::SeqCst) {
78            return Err(SeatError::UnsupportedCapability(Capability::Keyboard).into());
79        }
80
81        udata.repeat_data.lock().unwrap().replace(RepeatData {
82            current_repeat: None,
83            repeat_info: RepeatInfo::Disable,
84            loop_handle: loop_handle.clone(),
85            callback,
86            repeat_token: None,
87        });
88        udata.init_compose();
89
90        Ok(seat.get_keyboard(qh, udata))
91    }
92
93    /// Creates a keyboard from a seat.
94    ///
95    /// This function returns an [`EventSource`] that indicates when a key press is going to repeat.
96    ///
97    /// This keyboard implementation uses libxkbcommon for the keymap.
98    ///
99    /// Typically the compositor will provide a keymap, but you may specify your own keymap using the `rmlvo`
100    /// field.
101    ///
102    /// ## Errors
103    ///
104    /// This will return [`SeatError::UnsupportedCapability`] if the seat does not support a keyboard.
105    ///
106    /// [`EventSource`]: calloop::EventSource
107    pub fn get_keyboard_with_repeat_with_data<D, U>(
108        &mut self,
109        qh: &QueueHandle<D>,
110        seat: &wl_seat::WlSeat,
111        udata: U,
112        loop_handle: LoopHandle<'static, D>,
113        callback: RepeatCallback<D>,
114    ) -> Result<wl_keyboard::WlKeyboard, KeyboardError>
115    where
116        D: Dispatch<wl_keyboard::WlKeyboard, KeyboardData<D, U>> + KeyboardHandler + 'static,
117        U: Send + Sync + 'static,
118    {
119        let inner =
120            self.seats.iter().find(|inner| &inner.seat == seat).ok_or(SeatError::DeadObject)?;
121
122        if !inner.data.has_keyboard.load(Ordering::SeqCst) {
123            return Err(SeatError::UnsupportedCapability(Capability::Keyboard).into());
124        }
125
126        let udata = KeyboardData::new(seat.clone(), udata);
127
128        udata.repeat_data.lock().unwrap().replace(RepeatData {
129            current_repeat: None,
130            repeat_info: RepeatInfo::Disable,
131            loop_handle: loop_handle.clone(),
132            callback,
133            repeat_token: None,
134        });
135        udata.init_compose();
136
137        Ok(seat.get_keyboard(qh, udata))
138    }
139}