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