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 log::warn;
9
10use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
11use sctk::reexports::client::protocol::wl_keyboard::{
12 Event as WlKeyboardEvent, KeyState as WlKeyState, KeymapFormat as WlKeymapFormat,
13};
14use sctk::reexports::client::protocol::wl_seat::WlSeat;
15use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, WEnum};
16
17use crate::event::{ElementState, WindowEvent};
18use crate::keyboard::ModifiersState;
19
20use crate::platform_impl::common::xkb::Context;
21use crate::platform_impl::wayland::event_loop::sink::EventSink;
22use crate::platform_impl::wayland::seat::WinitSeatState;
23use crate::platform_impl::wayland::state::WinitState;
24use crate::platform_impl::wayland::{self, DeviceId, WindowId};
25
26impl 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)]
290pub 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
310impl 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
323impl 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)]
337pub 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
351impl 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)]
365pub 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
373impl KeyboardData {
374 pub fn new(seat: WlSeat) -> Self {
375 Self {
376 window_id: Default::default(),
377 seat,
378 }
379 }
380}
381
382fn 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