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