Skip to content

Commit 57a863f

Browse files
author
dcz
committed
Implement compositor-side key repeating
1 parent f1e34c9 commit 57a863f

File tree

12 files changed

+474
-271
lines changed

12 files changed

+474
-271
lines changed

anvil/src/focus.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ use std::borrow::Cow;
33
#[cfg(feature = "xwayland")]
44
use smithay::xwayland::X11Surface;
55
pub use smithay::{
6-
backend::input::KeyState,
76
desktop::{LayerSurface, PopupKind},
87
input::{
98
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
109
pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget, RelativeMotionEvent},
1110
Seat,
1211
},
13-
reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource},
12+
reexports::wayland_server::{
13+
backend::ObjectId,
14+
protocol::{
15+
wl_keyboard::KeyState,
16+
wl_surface::WlSurface,
17+
},
18+
Resource,
19+
},
1420
utils::{IsAlive, Serial},
1521
wayland::seat::WaylandFocus,
1622
};

anvil/src/input_handler.rs

Lines changed: 283 additions & 232 deletions
Large diffs are not rendered by default.

anvil/src/state.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use smithay::{
3535
},
3636
output::Output,
3737
reexports::{
38-
calloop::{generic::Generic, Interest, LoopHandle, Mode, PostAction},
38+
calloop::{self, generic::Generic, Interest, LoopHandle, Mode, PostAction, RegistrationToken},
3939
wayland_protocols::xdg::decoration::{
4040
self as xdg_decoration, zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode,
4141
},
@@ -301,6 +301,27 @@ impl<BackendData: Backend> SeatHandler for AnvilState<BackendData> {
301301
fn led_state_changed(&mut self, _seat: &Seat<Self>, led_state: LedState) {
302302
self.backend_data.update_led_state(led_state)
303303
}
304+
305+
fn set_timeout(
306+
&mut self,
307+
duration: std::time::Duration,
308+
mut callback: impl FnMut(&mut Self) + 'static,
309+
) -> RegistrationToken {
310+
self.handle.insert_source(
311+
calloop::timer::Timer::from_duration(duration),
312+
move |_, _, state| {
313+
callback(state);
314+
calloop::timer::TimeoutAction::ToDuration(duration)
315+
},
316+
).unwrap()
317+
}
318+
319+
fn clear_timeout(
320+
&mut self,
321+
token: RegistrationToken,
322+
) {
323+
self.handle.remove(token);
324+
}
304325
}
305326
delegate_seat!(@<BackendData: Backend + 'static> AnvilState<BackendData>);
306327

src/backend/input/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ pub enum KeyState {
9696
Pressed,
9797
}
9898

99+
pub use wayland_server::protocol::wl_keyboard::KeyState as KeyEvent;
100+
99101
/// Trait for keyboard event
100102
pub trait KeyboardKeyEvent<B: InputBackend>: Event<B> {
101103
/// Returns the numerical button code of the keyboard button.

src/desktop/wayland/popup/grab.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
use wayland_server::{protocol::wl_surface::WlSurface, Resource};
77

88
use crate::{
9-
backend::input::{ButtonState, KeyState, Keycode},
9+
backend::input::{ButtonState, KeyEvent, Keycode},
1010
input::{
1111
keyboard::{
1212
GrabStartData as KeyboardGrabStartData, KeyboardGrab, KeyboardHandle, KeyboardInnerHandle,
@@ -434,7 +434,7 @@ where
434434
data: &mut D,
435435
handle: &mut KeyboardInnerHandle<'_, D>,
436436
keycode: Keycode,
437-
state: KeyState,
437+
state: KeyEvent,
438438
modifiers: Option<ModifiersState>,
439439
serial: Serial,
440440
time: u32,

src/input/keyboard/mod.rs

Lines changed: 127 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
//! Keyboard-related types for smithay's input abstraction
22
3-
use crate::backend::input::KeyState;
3+
use crate::backend::input::{Event, InputBackend, KeyEvent, KeyState, KeyboardKeyEvent};
4+
use crate::reexports::calloop::LoopHandle;
45
use crate::utils::{IsAlive, Serial, SERIAL_COUNTER};
6+
use calloop::RegistrationToken;
57
use downcast_rs::{impl_downcast, Downcast};
68
use std::collections::HashSet;
9+
use std::time::Duration;
710
#[cfg(feature = "wayland_frontend")]
811
use std::sync::RwLock;
912
use std::{
@@ -12,7 +15,7 @@ use std::{
1215
sync::{Arc, Mutex},
1316
};
1417
use thiserror::Error;
15-
use tracing::{debug, error, info, info_span, instrument, trace};
18+
use tracing::{debug, error, info, info_span, instrument, trace, warn};
1619

1720
use xkbcommon::xkb::ffi::XKB_STATE_LAYOUT_EFFECTIVE;
1821
pub use xkbcommon::xkb::{self, keysyms, Keycode, Keysym};
@@ -47,7 +50,7 @@ where
4750
seat: &Seat<D>,
4851
data: &mut D,
4952
key: KeysymHandle<'_>,
50-
state: KeyState,
53+
state: KeyEvent,
5154
serial: Serial,
5255
time: u32,
5356
);
@@ -215,6 +218,17 @@ pub(crate) struct KbdInternal<D: SeatHandler> {
215218
led_mapping: LedMapping,
216219
pub(crate) led_state: LedState,
217220
grab: GrabStatus<dyn KeyboardGrab<D>>,
221+
/// Warning: the token cannot unregister itself when the object is dropped, because it can't hold a reference to the event loop. This object is Send, but the event loop is not.
222+
pub(crate) key_repeat_timer: Option<RegistrationToken>,
223+
}
224+
225+
#[cfg(feature = "wayland_frontend")]
226+
impl<D: SeatHandler> Drop for KbdInternal<D> {
227+
fn drop(&mut self) {
228+
if self.key_repeat_timer.is_some() {
229+
error!("A keyboard was dropped without unregistering a repeat handler. This is a bug in smithay or in the compositor.");
230+
}
231+
}
218232
}
219233

220234
// focus_hook does not implement debug, so we have to impl Debug manually
@@ -229,6 +243,7 @@ impl<D: SeatHandler> fmt::Debug for KbdInternal<D> {
229243
.field("xkb", &self.xkb)
230244
.field("repeat_rate", &self.repeat_rate)
231245
.field("repeat_delay", &self.repeat_delay)
246+
.field("key_repeat_timer", &self.key_repeat_timer)
232247
.finish()
233248
}
234249
}
@@ -266,21 +281,25 @@ impl<D: SeatHandler + 'static> KbdInternal<D> {
266281
led_mapping,
267282
led_state,
268283
grab: GrabStatus::None,
284+
key_repeat_timer: None,
269285
})
270286
}
271287

272288
// returns whether the modifiers or led state has changed
273-
fn key_input(&mut self, keycode: Keycode, state: KeyState) -> (bool, bool) {
289+
fn key_input(&mut self, keycode: Keycode, state: KeyEvent) -> (bool, bool) {
274290
// track pressed keys as xkbcommon does not seem to expose it :(
275291
let direction = match state {
276-
KeyState::Pressed => {
292+
KeyEvent::Pressed => {
277293
self.pressed_keys.insert(keycode);
278294
xkb::KeyDirection::Down
279295
}
280-
KeyState::Released => {
296+
KeyEvent::Released => {
281297
self.pressed_keys.remove(&keycode);
282298
xkb::KeyDirection::Up
283299
}
300+
KeyEvent::Repeated | _ => {
301+
return (false, false);
302+
}
284303
};
285304

286305
// update state
@@ -602,10 +621,10 @@ pub trait KeyboardGrab<D: SeatHandler>: Downcast {
602621
data: &mut D,
603622
handle: &mut KeyboardInnerHandle<'_, D>,
604623
keycode: Keycode,
605-
state: KeyState,
624+
event: KeyEvent,
606625
modifiers: Option<ModifiersState>,
607626
serial: Serial,
608-
time: u32,
627+
time_ms: u32,
609628
);
610629

611630
/// A focus change was requested.
@@ -931,6 +950,91 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
931950
}
932951
}
933952

953+
/// Processes the keyboard event, starting or stopping key repeat.
954+
/// If this method is used, it must receive all keyboard events.
955+
/// The `on_event` argument will be called with all events: the ones received directly and the generated repeats.
956+
pub fn key_register_repeat<B: InputBackend>(
957+
&self,
958+
data: &mut D,
959+
get_handle: impl Fn(&D) -> &LoopHandle<'static, D> + 'static,
960+
event: B::KeyboardKeyEvent,
961+
// event, timeout, code.
962+
// This is Clone because there are two closures in here...
963+
on_event: impl Fn(&mut D, KeyEvent, u32, Keycode) + Clone + 'static,
964+
) {
965+
let time_ms = event.time_msec();
966+
let keycode = event.key_code();
967+
let state = event.state();
968+
969+
// Forward initial hardware event as logical event
970+
on_event(data, state.into(), time_ms, keycode);
971+
972+
// Unregister preexisting repeating
973+
// Releasing a key press obviously stops the repeat.
974+
// But also, pressing another key stops the repeat of the previous key and starts it for the newly pressed key.
975+
// TODO: this may had odd consequences when a modifier is pressed as the second key. But is that worth worrying about?
976+
self.key_stop_repeat(data, &get_handle);
977+
978+
// Register repeating
979+
match event.state() {
980+
KeyState::Pressed => {
981+
let mut guard = self.arc.internal.lock().unwrap();
982+
let delay = guard.repeat_delay;
983+
let rate = guard.repeat_rate;
984+
let mut time_ms = time_ms;
985+
986+
// This closure-in-closure business is somewhat ugly.
987+
// The reason is that there are two timers needed: first, the delay timer, and after the delay, the repeat timer. Both of them receive different tokens for cancelling, so we have to swap the token after the delay.
988+
// The only comparable alternative I can think of is to wrap the key_repeat_timer in an Mutex<Arc<>> and change the token when delay turns into repeat. But locks are worse than nesting.
989+
let kbd = self.arc.clone();
990+
let duration = Duration::from_millis(delay as _);
991+
let handle = get_handle(data);
992+
guard.key_repeat_timer = Some(handle.insert_source(
993+
calloop::timer::Timer::from_duration(duration),
994+
move |_, _, data| {
995+
time_ms += delay as u32;
996+
on_event(data, KeyEvent::Repeated, time_ms, keycode);
997+
let mut guard = kbd.internal.lock().unwrap();
998+
999+
match guard.key_repeat_timer {
1000+
Some(token) => data.clear_timeout(token),
1001+
None => debug!("Key starts repeating but there is no delay timer. Was repeat already cancelled?"),
1002+
};
1003+
1004+
// This implementation doesn't take into account changes to the repeat rate after repeating begins.
1005+
let handle = get_handle(data);
1006+
let on_event = on_event.clone();
1007+
let duration = Duration::from_millis(rate as _);
1008+
guard.key_repeat_timer = Some(handle.insert_source(
1009+
calloop::timer::Timer::from_duration(duration),
1010+
move |_, _, data| {
1011+
time_ms += rate as u32;
1012+
on_event(data, KeyEvent::Repeated, time_ms, keycode);
1013+
calloop::timer::TimeoutAction::ToDuration(duration)
1014+
},
1015+
).unwrap());
1016+
calloop::timer::TimeoutAction::Drop
1017+
}
1018+
).unwrap());
1019+
},
1020+
KeyState::Released => {
1021+
// Nothing to do; timer is released for both in the common path.
1022+
}
1023+
}
1024+
}
1025+
1026+
/// Cancels any ongoing key repeat
1027+
pub fn key_stop_repeat(&self,
1028+
data: &mut D,
1029+
get_handle: impl Fn(&D) -> &LoopHandle<'static, D>,
1030+
) {
1031+
let mut guard = self.arc.internal.lock().unwrap();
1032+
if let Some(token) = guard.key_repeat_timer.take() {
1033+
let handle = get_handle(data);
1034+
handle.remove(token);
1035+
};
1036+
}
1037+
9341038
/// Handle a keystroke
9351039
///
9361040
/// All keystrokes from the input backend should be fed _in order_ to this method of the
@@ -949,7 +1053,7 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
9491053
&self,
9501054
data: &mut D,
9511055
keycode: Keycode,
952-
state: KeyState,
1056+
state: KeyEvent,
9531057
serial: Serial,
9541058
time: u32,
9551059
filter: F,
@@ -979,13 +1083,13 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
9791083
&self,
9801084
data: &mut D,
9811085
keycode: Keycode,
982-
state: KeyState,
1086+
state: KeyEvent,
9831087
filter: F,
9841088
) -> (T, bool)
9851089
where
9861090
F: FnOnce(&mut D, &ModifiersState, KeysymHandle<'_>) -> T,
9871091
{
988-
trace!("Handling keystroke");
1092+
trace!("Handling key event");
9891093

9901094
let mut guard = self.arc.internal.lock().unwrap();
9911095
let (mods_changed, leds_changed) = guard.key_input(keycode, state);
@@ -1014,26 +1118,20 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
10141118
&self,
10151119
data: &mut D,
10161120
keycode: Keycode,
1017-
state: KeyState,
1121+
state: KeyEvent,
10181122
serial: Serial,
1019-
time: u32,
1123+
time_ms: u32,
10201124
mods_changed: bool,
10211125
) {
10221126
let mut guard = self.arc.internal.lock().unwrap();
1023-
match state {
1024-
KeyState::Pressed => {
1025-
guard.forwarded_pressed_keys.insert(keycode);
1026-
}
1027-
KeyState::Released => {
1028-
guard.forwarded_pressed_keys.remove(&keycode);
1029-
}
1030-
};
1127+
1128+
let event = state.into();
10311129

10321130
// forward to client if no keybinding is triggered
10331131
let seat = self.get_seat(data);
10341132
let modifiers = mods_changed.then_some(guard.mods_state);
10351133
guard.with_grab(data, &seat, |data, handle, grab| {
1036-
grab.input(data, handle, keycode, state, modifiers, serial, time);
1134+
grab.input(data, handle, keycode, event, modifiers, serial, time_ms);
10371135
});
10381136
if guard.focus.is_some() {
10391137
trace!("Input forwarded to client");
@@ -1151,6 +1249,11 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
11511249
continue;
11521250
};
11531251
if kbd.version() >= 4 {
1252+
let rate = if kbd.version() >= 10 {
1253+
0 // Enables compositor-side key repeat. See wl_keyboard key event
1254+
} else {
1255+
rate
1256+
};
11541257
kbd.repeat_info(rate, delay);
11551258
}
11561259
}
@@ -1266,7 +1369,7 @@ impl<D: SeatHandler + 'static> KeyboardInnerHandle<'_, D> {
12661369
&mut self,
12671370
data: &mut D,
12681371
keycode: Keycode,
1269-
key_state: KeyState,
1372+
key_state: KeyEvent,
12701373
modifiers: Option<ModifiersState>,
12711374
serial: Serial,
12721375
time: u32,
@@ -1377,7 +1480,7 @@ impl<D: SeatHandler + 'static> KeyboardGrab<D> for DefaultGrab {
13771480
data: &mut D,
13781481
handle: &mut KeyboardInnerHandle<'_, D>,
13791482
keycode: Keycode,
1380-
state: KeyState,
1483+
state: KeyEvent,
13811484
modifiers: Option<ModifiersState>,
13821485
serial: Serial,
13831486
time: u32,

src/input/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
//! These methods return handles that can be cloned and sent across thread, so you can keep one around
118118
//! in your event-handling code to forward inputs to your clients.
119119
//!
120-
120+
use calloop::RegistrationToken;
121121
use std::{
122122
fmt,
123123
hash::Hash,
@@ -161,6 +161,19 @@ pub trait SeatHandler: Sized {
161161

162162
/// Callback that will be notified whenever the keyboard led state changes.
163163
fn led_state_changed(&mut self, _seat: &Seat<Self>, _led_state: LedState) {}
164+
165+
/// Register a timeout for key repeat.
166+
fn set_timeout(
167+
&mut self,
168+
duration: std::time::Duration,
169+
callback: impl FnMut(&mut Self) + 'static,
170+
) -> RegistrationToken;
171+
172+
/// Cleare a nonrepeating timeout for key repeat.
173+
fn clear_timeout(
174+
&mut self,
175+
token: RegistrationToken,
176+
);
164177
}
165178
/// Delegate type for all [Seat] globals.
166179
///

0 commit comments

Comments
 (0)