Skip to content

Commit d8ed538

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

File tree

11 files changed

+160
-24
lines changed

11 files changed

+160
-24
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/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: 96 additions & 9 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::{KeyEvent, KeyState};
4+
use crate::input::WeakSeat;
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,6 +281,7 @@ impl<D: SeatHandler + 'static> KbdInternal<D> {
266281
led_mapping,
267282
led_state,
268283
grab: GrabStatus::None,
284+
key_repeat_timer: None,
269285
})
270286
}
271287

@@ -602,10 +618,10 @@ pub trait KeyboardGrab<D: SeatHandler>: Downcast {
602618
data: &mut D,
603619
handle: &mut KeyboardInnerHandle<'_, D>,
604620
keycode: Keycode,
605-
state: KeyState,
621+
event: KeyEvent,
606622
modifiers: Option<ModifiersState>,
607623
serial: Serial,
608-
time: u32,
624+
time_ms: u32,
609625
);
610626

611627
/// A focus change was requested.
@@ -1016,10 +1032,74 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
10161032
keycode: Keycode,
10171033
state: KeyState,
10181034
serial: Serial,
1019-
time: u32,
1035+
time_ms: u32,
10201036
mods_changed: bool,
10211037
) {
10221038
let mut guard = self.arc.internal.lock().unwrap();
1039+
if let Some(token) = guard.key_repeat_timer.take() {
1040+
// Releasing a key press obviously stops the repeat.
1041+
// But also, pressing another key stops the repeat of the previous key and starts it for the newly pressed key.
1042+
// TODO: this may had odd consequences when a modifier is pressed as the second key. But is that worth worrying about?
1043+
data.clear_timeout(token);
1044+
};
1045+
match state {
1046+
KeyState::Pressed => {
1047+
let seat = self.get_seat(data);
1048+
let delay = guard.repeat_delay;
1049+
let rate = guard.repeat_rate;
1050+
let mut time_ms = time_ms;
1051+
1052+
fn issue_repeat<D: SeatHandler + 'static>(data: &mut D, time_ms: u32, kbd: &Arc<KbdRc<D>>, mods_changed: bool, weak_seat: &WeakSeat<D>, keycode: Keycode, serial: Serial) {
1053+
let mut guard = kbd.internal.lock().unwrap();
1054+
let modifiers = mods_changed.then_some(guard.mods_state);
1055+
let seat = weak_seat.upgrade().unwrap();
1056+
guard.with_grab(data, &seat, |data, handle, grab| {
1057+
grab.input(data, handle, keycode, KeyEvent::Repeated, modifiers, serial, time_ms);
1058+
});
1059+
if guard.focus.is_some() {
1060+
trace!("Input forwarded to client");
1061+
} else {
1062+
trace!("No client currently focused");
1063+
}
1064+
}
1065+
1066+
// This closure-in-closure business is somewhat ugly.
1067+
// 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.
1068+
// 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.
1069+
let kbd = self.arc.clone();
1070+
let weak_seat = seat.downgrade();
1071+
guard.key_repeat_timer = Some(data.set_timeout(
1072+
Duration::from_millis(delay as _),
1073+
move |data| {
1074+
time_ms += delay as u32;
1075+
issue_repeat(data, time_ms, &kbd, mods_changed, &weak_seat, keycode, serial);
1076+
let mut guard = kbd.internal.lock().unwrap();
1077+
1078+
match guard.key_repeat_timer {
1079+
// Can the repeat timer fire again if this handler is stalled before the cancel?
1080+
Some(token) => data.clear_timeout(token),
1081+
// This is a bug currently where all repeat cancelling is handled on key press/release. This might not be a bug in the future when other events can cancel key repeat.
1082+
None => warn!("Key starts repeating but there is no delay timer. This might be a bug."),
1083+
};
1084+
1085+
// This implementation doesn't take into account changes to the repeat rate after repeating begins.
1086+
let kbd = kbd.clone();
1087+
let weak_seat = weak_seat.clone();
1088+
guard.key_repeat_timer = Some(data.set_timeout(
1089+
Duration::from_millis(rate as _),
1090+
move |data| {
1091+
time_ms += rate as u32;
1092+
issue_repeat(data, time_ms, &kbd, mods_changed, &weak_seat, keycode, serial);
1093+
},
1094+
));
1095+
},
1096+
));
1097+
},
1098+
KeyState::Released => {
1099+
// Nothing to do; timer is released for both in the common path.
1100+
}
1101+
}
1102+
10231103
match state {
10241104
KeyState::Pressed => {
10251105
guard.forwarded_pressed_keys.insert(keycode);
@@ -1028,12 +1108,14 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
10281108
guard.forwarded_pressed_keys.remove(&keycode);
10291109
}
10301110
};
1111+
1112+
let event = state.into();
10311113

10321114
// forward to client if no keybinding is triggered
10331115
let seat = self.get_seat(data);
10341116
let modifiers = mods_changed.then_some(guard.mods_state);
10351117
guard.with_grab(data, &seat, |data, handle, grab| {
1036-
grab.input(data, handle, keycode, state, modifiers, serial, time);
1118+
grab.input(data, handle, keycode, event, modifiers, serial, time_ms);
10371119
});
10381120
if guard.focus.is_some() {
10391121
trace!("Input forwarded to client");
@@ -1151,6 +1233,11 @@ impl<D: SeatHandler + 'static> KeyboardHandle<D> {
11511233
continue;
11521234
};
11531235
if kbd.version() >= 4 {
1236+
let rate = if kbd.version() >= 10 {
1237+
0 // Enables compositor-side key repeat. See wl_keyboard key event
1238+
} else {
1239+
rate
1240+
};
11541241
kbd.repeat_info(rate, delay);
11551242
}
11561243
}
@@ -1266,7 +1353,7 @@ impl<D: SeatHandler + 'static> KeyboardInnerHandle<'_, D> {
12661353
&mut self,
12671354
data: &mut D,
12681355
keycode: Keycode,
1269-
key_state: KeyState,
1356+
key_state: KeyEvent,
12701357
modifiers: Option<ModifiersState>,
12711358
serial: Serial,
12721359
time: u32,
@@ -1377,7 +1464,7 @@ impl<D: SeatHandler + 'static> KeyboardGrab<D> for DefaultGrab {
13771464
data: &mut D,
13781465
handle: &mut KeyboardInnerHandle<'_, D>,
13791466
keycode: Keycode,
1380-
state: KeyState,
1467+
state: KeyEvent,
13811468
modifiers: Option<ModifiersState>,
13821469
serial: Serial,
13831470
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
///

src/wayland/input_method/input_method_keyboard_grab.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::input::{
1818
};
1919
use crate::wayland::text_input::TextInputHandle;
2020
use crate::{
21-
backend::input::{KeyState, Keycode},
21+
backend::input::{KeyEvent, Keycode},
2222
utils::Serial,
2323
};
2424

@@ -45,7 +45,7 @@ where
4545
_data: &mut D,
4646
_handle: &mut KeyboardInnerHandle<'_, D>,
4747
keycode: Keycode,
48-
key_state: KeyState,
48+
key_state: KeyEvent,
4949
modifiers: Option<ModifiersState>,
5050
serial: Serial,
5151
time: u32,

src/wayland/seat/keyboard.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,12 @@ where
6868

6969
let guard = self.arc.internal.lock().unwrap();
7070
if kbd.version() >= 4 {
71-
kbd.repeat_info(guard.repeat_rate, guard.repeat_delay);
71+
let rate = if kbd.version() >= 10 {
72+
0 // Enables compositor-side key repeat. See wl_keyboard key event
73+
} else {
74+
guard.repeat_rate
75+
};
76+
kbd.repeat_info(rate, guard.repeat_delay);
7277
}
7378
if let Some((focused, serial)) = guard.focus.as_ref() {
7479
if focused.same_client_as(&kbd.id()) {
@@ -211,7 +216,7 @@ impl<D: SeatHandler + 'static> KeyboardTarget<D> for WlSurface {
211216
seat: &Seat<D>,
212217
_data: &mut D,
213218
key: KeysymHandle<'_>,
214-
state: KeyState,
219+
state: WlKeyState,
215220
serial: Serial,
216221
time: u32,
217222
) {

src/wayland/seat/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ impl<D: SeatHandler + 'static> SeatState<D> {
173173
{
174174
let Seat { arc } = self.new_seat(name);
175175

176-
let global_id = display.create_global::<D, _, _>(9, SeatGlobalData { arc: arc.clone() });
176+
let global_id = display.create_global::<D, _, _>(10, SeatGlobalData { arc: arc.clone() });
177177
arc.inner.lock().unwrap().global = Some(global_id);
178178

179179
Seat { arc }

src/wayland/xwayland_keyboard_grab.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use wayland_server::{
4747
};
4848

4949
use crate::{
50-
backend::input::{KeyState, Keycode},
50+
backend::input::{KeyEvent, Keycode},
5151
input::{
5252
keyboard::{self, KeyboardGrab, KeyboardInnerHandle},
5353
Seat, SeatHandler,
@@ -109,7 +109,7 @@ impl<D: XWaylandKeyboardGrabHandler + 'static> KeyboardGrab<D> for XWaylandKeybo
109109
data: &mut D,
110110
handle: &mut KeyboardInnerHandle<'_, D>,
111111
keycode: Keycode,
112-
state: KeyState,
112+
state: KeyEvent,
113113
modifiers: Option<keyboard::ModifiersState>,
114114
serial: Serial,
115115
time: u32,

0 commit comments

Comments
 (0)