winit/platform_impl/linux/wayland/seat/keyboard/
mod.rs

1//! The keyboard input handling.
2
3use std::sync::Mutex;
4use std::time::Duration;
5
6use calloop::timer::{TimeoutAction, Timer};
7use calloop::{LoopHandle, RegistrationToken};
8use tracing::warn;
9
10use sctk::reexports::client::protocol::wl_keyboard::{
11    Event as WlKeyboardEvent, KeyState as WlKeyState, KeymapFormat as WlKeymapFormat, WlKeyboard,
12};
13use sctk::reexports::client::protocol::wl_seat::WlSeat;
14use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, WEnum};
15
16use crate::event::{ElementState, WindowEvent};
17use crate::keyboard::ModifiersState;
18
19use crate::platform_impl::common::xkb::Context;
20use crate::platform_impl::wayland::event_loop::sink::EventSink;
21use crate::platform_impl::wayland::state::WinitState;
22use crate::platform_impl::wayland::{self, DeviceId, WindowId};
23
24impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
25    fn event(
26        state: &mut WinitState,
27        wl_keyboard: &WlKeyboard,
28        event: <WlKeyboard as Proxy>::Event,
29        data: &KeyboardData,
30        _: &Connection,
31        _: &QueueHandle<WinitState>,
32    ) {
33        let seat_state = match state.seats.get_mut(&data.seat.id()) {
34            Some(seat_state) => seat_state,
35            None => {
36                warn!("Received keyboard event {event:?} without seat");
37                return;
38            },
39        };
40        let keyboard_state = match seat_state.keyboard_state.as_mut() {
41            Some(keyboard_state) => keyboard_state,
42            None => {
43                warn!("Received keyboard event {event:?} without keyboard");
44                return;
45            },
46        };
47
48        match event {
49            WlKeyboardEvent::Keymap { format, fd, size } => match format {
50                WEnum::Value(format) => match format {
51                    WlKeymapFormat::NoKeymap => {
52                        warn!("non-xkb compatible keymap")
53                    },
54                    WlKeymapFormat::XkbV1 => {
55                        let context = &mut keyboard_state.xkb_context;
56                        context.set_keymap_from_fd(fd, size as usize);
57                    },
58                    _ => unreachable!(),
59                },
60                WEnum::Unknown(value) => {
61                    warn!("unknown keymap format 0x{:x}", value)
62                },
63            },
64            WlKeyboardEvent::Enter { surface, .. } => {
65                let window_id = wayland::make_wid(&surface);
66
67                // Mark the window as focused.
68                let was_unfocused = match state.windows.get_mut().get(&window_id) {
69                    Some(window) => {
70                        let mut window = window.lock().unwrap();
71                        let was_unfocused = !window.has_focus();
72                        window.add_seat_focus(data.seat.id());
73                        was_unfocused
74                    },
75                    None => return,
76                };
77
78                // Drop the repeat, if there were any.
79                keyboard_state.current_repeat = None;
80                if let Some(token) = keyboard_state.repeat_token.take() {
81                    keyboard_state.loop_handle.remove(token);
82                }
83
84                *data.window_id.lock().unwrap() = Some(window_id);
85
86                // The keyboard focus is considered as general focus.
87                if was_unfocused {
88                    state.events_sink.push_window_event(WindowEvent::Focused(true), window_id);
89                }
90
91                // HACK: this is just for GNOME not fixing their ordering issue of modifiers.
92                if std::mem::take(&mut seat_state.modifiers_pending) {
93                    state.events_sink.push_window_event(
94                        WindowEvent::ModifiersChanged(seat_state.modifiers.into()),
95                        window_id,
96                    );
97                }
98            },
99            WlKeyboardEvent::Leave { surface, .. } => {
100                let window_id = wayland::make_wid(&surface);
101
102                // NOTE: we should drop the repeat regardless whethere it was for the present
103                // window of for the window which just went gone.
104                keyboard_state.current_repeat = None;
105                if let Some(token) = keyboard_state.repeat_token.take() {
106                    keyboard_state.loop_handle.remove(token);
107                }
108
109                // NOTE: The check whether the window exists is essential as we might get a
110                // nil surface, regardless of what protocol says.
111                let focused = match state.windows.get_mut().get(&window_id) {
112                    Some(window) => {
113                        let mut window = window.lock().unwrap();
114                        window.remove_seat_focus(&data.seat.id());
115                        window.has_focus()
116                    },
117                    None => return,
118                };
119
120                // We don't need to update it above, because the next `Enter` will overwrite
121                // anyway.
122                *data.window_id.lock().unwrap() = None;
123
124                if !focused {
125                    // Notify that no modifiers are being pressed.
126                    state.events_sink.push_window_event(
127                        WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
128                        window_id,
129                    );
130
131                    state.events_sink.push_window_event(WindowEvent::Focused(false), window_id);
132                }
133            },
134            WlKeyboardEvent::Key { key, state: WEnum::Value(WlKeyState::Pressed), .. } => {
135                let key = key + 8;
136
137                key_input(
138                    keyboard_state,
139                    &mut state.events_sink,
140                    data,
141                    key,
142                    ElementState::Pressed,
143                    false,
144                );
145
146                let delay = match keyboard_state.repeat_info {
147                    RepeatInfo::Repeat { delay, .. } => delay,
148                    RepeatInfo::Disable => return,
149                };
150
151                if !keyboard_state.xkb_context.keymap_mut().unwrap().key_repeats(key) {
152                    return;
153                }
154
155                keyboard_state.current_repeat = Some(key);
156
157                // NOTE terminate ongoing timer and start a new timer.
158
159                if let Some(token) = keyboard_state.repeat_token.take() {
160                    keyboard_state.loop_handle.remove(token);
161                }
162
163                let timer = Timer::from_duration(delay);
164                let wl_keyboard = wl_keyboard.clone();
165                keyboard_state.repeat_token = keyboard_state
166                    .loop_handle
167                    .insert_source(timer, move |_, _, state| {
168                        // Required to handle the wakeups from the repeat sources.
169                        state.dispatched_events = true;
170
171                        let data = wl_keyboard.data::<KeyboardData>().unwrap();
172                        let seat_state = match state.seats.get_mut(&data.seat.id()) {
173                            Some(seat_state) => seat_state,
174                            None => return TimeoutAction::Drop,
175                        };
176
177                        let keyboard_state = match seat_state.keyboard_state.as_mut() {
178                            Some(keyboard_state) => keyboard_state,
179                            None => return TimeoutAction::Drop,
180                        };
181
182                        // NOTE: The removed on event source is batched, but key change to `None`
183                        // is instant.
184                        let repeat_keycode = match keyboard_state.current_repeat {
185                            Some(repeat_keycode) => repeat_keycode,
186                            None => return TimeoutAction::Drop,
187                        };
188
189                        key_input(
190                            keyboard_state,
191                            &mut state.events_sink,
192                            data,
193                            repeat_keycode,
194                            ElementState::Pressed,
195                            true,
196                        );
197
198                        // NOTE: the gap could change dynamically while repeat is going.
199                        match keyboard_state.repeat_info {
200                            RepeatInfo::Repeat { gap, .. } => TimeoutAction::ToDuration(gap),
201                            RepeatInfo::Disable => TimeoutAction::Drop,
202                        }
203                    })
204                    .ok();
205            },
206            WlKeyboardEvent::Key { key, state: WEnum::Value(WlKeyState::Released), .. } => {
207                let key = key + 8;
208
209                key_input(
210                    keyboard_state,
211                    &mut state.events_sink,
212                    data,
213                    key,
214                    ElementState::Released,
215                    false,
216                );
217
218                if keyboard_state.repeat_info != RepeatInfo::Disable
219                    && keyboard_state.xkb_context.keymap_mut().unwrap().key_repeats(key)
220                    && Some(key) == keyboard_state.current_repeat
221                {
222                    keyboard_state.current_repeat = None;
223                    if let Some(token) = keyboard_state.repeat_token.take() {
224                        keyboard_state.loop_handle.remove(token);
225                    }
226                }
227            },
228            WlKeyboardEvent::Modifiers {
229                mods_depressed, mods_latched, mods_locked, group, ..
230            } => {
231                let xkb_context = &mut keyboard_state.xkb_context;
232                let xkb_state = match xkb_context.state_mut() {
233                    Some(state) => state,
234                    None => return,
235                };
236
237                xkb_state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group);
238                seat_state.modifiers = xkb_state.modifiers().into();
239
240                // HACK: part of the workaround from `WlKeyboardEvent::Enter`.
241                let window_id = match *data.window_id.lock().unwrap() {
242                    Some(window_id) => window_id,
243                    None => {
244                        seat_state.modifiers_pending = true;
245                        return;
246                    },
247                };
248
249                state.events_sink.push_window_event(
250                    WindowEvent::ModifiersChanged(seat_state.modifiers.into()),
251                    window_id,
252                );
253            },
254            WlKeyboardEvent::RepeatInfo { rate, delay } => {
255                keyboard_state.repeat_info = if rate == 0 {
256                    // Stop the repeat once we get a disable event.
257                    keyboard_state.current_repeat = None;
258                    if let Some(repeat_token) = keyboard_state.repeat_token.take() {
259                        keyboard_state.loop_handle.remove(repeat_token);
260                    }
261                    RepeatInfo::Disable
262                } else {
263                    let gap = Duration::from_micros(1_000_000 / rate as u64);
264                    let delay = Duration::from_millis(delay as u64);
265                    RepeatInfo::Repeat { gap, delay }
266                };
267            },
268            _ => unreachable!(),
269        }
270    }
271}
272
273/// The state of the keyboard on the current seat.
274#[derive(Debug)]
275pub struct KeyboardState {
276    /// The underlying WlKeyboard.
277    pub keyboard: WlKeyboard,
278
279    /// Loop handle to handle key repeat.
280    pub loop_handle: LoopHandle<'static, WinitState>,
281
282    /// The state of the keyboard.
283    pub xkb_context: Context,
284
285    /// The information about the repeat rate obtained from the compositor.
286    pub repeat_info: RepeatInfo,
287
288    /// The token of the current handle inside the calloop's event loop.
289    pub repeat_token: Option<RegistrationToken>,
290
291    /// The current repeat raw key.
292    pub current_repeat: Option<u32>,
293}
294
295impl KeyboardState {
296    pub fn new(keyboard: WlKeyboard, loop_handle: LoopHandle<'static, WinitState>) -> Self {
297        Self {
298            keyboard,
299            loop_handle,
300            xkb_context: Context::new().unwrap(),
301            repeat_info: RepeatInfo::default(),
302            repeat_token: None,
303            current_repeat: None,
304        }
305    }
306}
307
308impl Drop for KeyboardState {
309    fn drop(&mut self) {
310        if self.keyboard.version() >= 3 {
311            self.keyboard.release();
312        }
313
314        if let Some(token) = self.repeat_token.take() {
315            self.loop_handle.remove(token);
316        }
317    }
318}
319
320/// The rate at which a pressed key is repeated.
321#[derive(Debug, Clone, Copy, PartialEq, Eq)]
322pub enum RepeatInfo {
323    /// Keys will be repeated at the specified rate and delay.
324    Repeat {
325        /// The time between the key repeats.
326        gap: Duration,
327
328        /// Delay (in milliseconds) between a key press and the start of repetition.
329        delay: Duration,
330    },
331
332    /// Keys should not be repeated.
333    Disable,
334}
335
336impl Default for RepeatInfo {
337    /// The default repeat rate is 25 keys per second with the delay of 200ms.
338    ///
339    /// The values are picked based on the default in various compositors and Xorg.
340    fn default() -> Self {
341        Self::Repeat { gap: Duration::from_millis(40), delay: Duration::from_millis(200) }
342    }
343}
344
345/// Keyboard user data.
346#[derive(Debug)]
347pub struct KeyboardData {
348    /// The currently focused window surface. Could be `None` on bugged compositors, like mutter.
349    window_id: Mutex<Option<WindowId>>,
350
351    /// The seat used to create this keyboard.
352    seat: WlSeat,
353}
354
355impl KeyboardData {
356    pub fn new(seat: WlSeat) -> Self {
357        Self { window_id: Default::default(), seat }
358    }
359}
360
361fn key_input(
362    keyboard_state: &mut KeyboardState,
363    event_sink: &mut EventSink,
364    data: &KeyboardData,
365    keycode: u32,
366    state: ElementState,
367    repeat: bool,
368) {
369    let window_id = match *data.window_id.lock().unwrap() {
370        Some(window_id) => window_id,
371        None => return,
372    };
373
374    let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
375    if let Some(mut key_context) = keyboard_state.xkb_context.key_context() {
376        let event = key_context.process_key_event(keycode, state, repeat);
377        let event = WindowEvent::KeyboardInput { device_id, event, is_synthetic: false };
378        event_sink.push_window_event(event, window_id);
379    }
380}