1 | //! The keyboard input handling. |
2 | |
3 | use std::sync::Mutex; |
4 | use std::time::Duration; |
5 | |
6 | use calloop::timer::{TimeoutAction, Timer}; |
7 | use calloop::{LoopHandle, RegistrationToken}; |
8 | use tracing::warn; |
9 | |
10 | use sctk::reexports::client::protocol::wl_keyboard::{ |
11 | Event as WlKeyboardEvent, KeyState as WlKeyState, KeymapFormat as WlKeymapFormat, WlKeyboard, |
12 | }; |
13 | use sctk::reexports::client::protocol::wl_seat::WlSeat; |
14 | use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, WEnum}; |
15 | |
16 | use crate::event::{ElementState, WindowEvent}; |
17 | use crate::keyboard::ModifiersState; |
18 | |
19 | use crate::platform_impl::common::xkb::Context; |
20 | use crate::platform_impl::wayland::event_loop::sink::EventSink; |
21 | use crate::platform_impl::wayland::state::WinitState; |
22 | use crate::platform_impl::wayland::{self, DeviceId, WindowId}; |
23 | |
24 | impl 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)] |
275 | pub 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 | |
295 | impl 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 | |
308 | impl 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: RegistrationToken) = 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)] |
322 | pub 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 | |
336 | impl 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)] |
347 | pub 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 | |
355 | impl KeyboardData { |
356 | pub fn new(seat: WlSeat) -> Self { |
357 | Self { window_id: Default::default(), seat } |
358 | } |
359 | } |
360 | |
361 | fn 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: WindowId = match *data.window_id.lock().unwrap() { |
370 | Some(window_id: WindowId) => window_id, |
371 | None => return, |
372 | }; |
373 | |
374 | let device_id: DeviceId = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId)); |
375 | if let Some(mut key_context: KeyContext<'_>) = keyboard_state.xkb_context.key_context() { |
376 | let event: KeyEvent = key_context.process_key_event(keycode, state, repeat); |
377 | let event: WindowEvent = WindowEvent::KeyboardInput { device_id, event, is_synthetic: false }; |
378 | event_sink.push_window_event(event, window_id); |
379 | } |
380 | } |
381 | |