1
0
Fork 0
forked from wry/wry

seat: implement key repeat

This commit is contained in:
Julian Orth 2025-10-16 21:18:26 +02:00
parent 0e1be7544f
commit 4b4f05d153
10 changed files with 153 additions and 19 deletions

View file

@ -315,6 +315,7 @@ pub enum BackendEvent {
pub enum KeyState {
Released,
Pressed,
Repeated,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]

View file

@ -59,6 +59,7 @@ impl EiKeyboard {
state: match state {
KeyState::Released => 0,
KeyState::Pressed => 1,
KeyState::Repeated => return,
},
});
}

View file

@ -188,6 +188,12 @@ pub struct WlSeatGlobal {
>,
data_control_devices: CopyHashMap<DataControlDeviceId, Rc<dyn DynDataControlDevice>>,
repeat_rate: Cell<(i32, i32)>,
key_repeater: Cell<Option<SpawnedFuture<()>>>,
repeat_key: Cell<Option<Keycode>>,
repeat_key_state: CloneCell<Option<Rc<RefCell<KbvmState>>>>,
repeat_key_version: NumCell<u64>,
repeat_key_start_ns: Cell<u64>,
have_repeat_key: AsyncEvent,
seat_kb_map: CloneCell<Rc<KbvmMap>>,
seat_kb_state: CloneCell<Rc<RefCell<KbvmState>>>,
latest_kb_state: CloneCell<Rc<dyn DynKeyboardState>>,
@ -280,6 +286,12 @@ impl WlSeatGlobal {
data_devices: RefCell::new(Default::default()),
primary_selection_devices: RefCell::new(Default::default()),
repeat_rate: Cell::new((25, 250)),
key_repeater: Default::default(),
repeat_key: Default::default(),
repeat_key_state: Default::default(),
repeat_key_version: Default::default(),
repeat_key_start_ns: Default::default(),
have_repeat_key: Default::default(),
seat_kb_map: CloneCell::new(state.default_keymap.clone()),
seat_kb_state: CloneCell::new(seat_kb_state.clone()),
latest_kb_state: CloneCell::new(seat_kb_state.clone()),
@ -336,6 +348,7 @@ impl WlSeatGlobal {
slf.pointer_cursor.set_owner(slf.clone());
slf.modifiers_listener
.attach(&seat_kb_state.borrow().kb_state.leds_changed);
slf.create_repeat_handler();
let seat = slf.clone();
let future = state.eng.spawn("seat handler", async move {
loop {
@ -684,8 +697,9 @@ impl WlSeatGlobal {
self.repeat_rate.get()
}
pub fn set_rate(&self, rate: i32, delay: i32) {
pub fn set_rate(self: &Rc<Self>, rate: i32, delay: i32) {
self.repeat_rate.set((rate, delay));
self.create_repeat_handler();
let bindings = self.bindings.borrow_mut();
for client in bindings.values() {
for seat in client.values() {
@ -1252,6 +1266,8 @@ impl WlSeatGlobal {
self.tablet_clear();
self.ei_seats.clear();
self.marks.clear();
self.key_repeater.take();
self.repeat_key_state.take();
}
pub fn id(&self) -> SeatId {
@ -1482,7 +1498,7 @@ impl Global for WlSeatGlobal {
}
fn version(&self) -> u32 {
9
10
}
}

View file

@ -902,6 +902,7 @@ impl WlSeatGlobal {
match key_state {
KeyState::Released => pk.remove(&kc.to_evdev()),
KeyState::Pressed => pk.insert(kc.to_evdev()),
KeyState::Repeated => unreachable!(),
}
};
if key_state == KeyState::Pressed
@ -922,6 +923,7 @@ impl WlSeatGlobal {
continue;
}
shortcuts.clear();
let repeats;
{
let mut mods = kbvm_state.kb_state.mods.mods.0 & !(CAPS.0 | NUM.0);
if key_state == KeyState::Released {
@ -933,6 +935,7 @@ impl WlSeatGlobal {
ModifierMask::default(),
kc,
);
repeats = keysyms.repeats();
let mut revert_pointer_to_default = false;
for props in keysyms {
let sym = props.keysym().0;
@ -982,28 +985,44 @@ impl WlSeatGlobal {
}
}
self.send_components(&mut components_changed, &kbvm_state);
let mut forward_to_node = true;
if let Some(g) = self.input_method_grab.get() {
forward_to_node =
g.on_key(time_usec, kc.to_evdev(), key_state, &kbvm_state.kb_state);
}
if forward_to_node {
self.keyboard_node.get().node_on_key(
self,
time_usec,
kc.to_evdev(),
key_state,
&kbvm_state.kb_state,
)
}
self.send_key(time_usec, kc, key_state, &kbvm_state.kb_state);
self.for_each_ei_seat(|ei_seat| {
ei_seat.handle_key(time_usec, kc.to_evdev(), key_state, &kbvm_state.kb_state);
});
update_pressed_keys(&mut kbvm_state);
match key_state {
KeyState::Released => {
if self.repeat_key.get() == Some(kc) {
self.clear_repeat_key();
}
}
KeyState::Pressed => {
if repeats {
self.set_repeat_key(kc, kbvm_state_rc);
}
}
KeyState::Repeated => {}
}
}
self.send_components(&mut components_changed, &kbvm_state);
}
fn send_key(&self, time_usec: u64, kc: Keycode, key_state: KeyState, kb_state: &KeyboardState) {
let mut forward_to_node = true;
if let Some(g) = self.input_method_grab.get() {
forward_to_node = g.on_key(time_usec, kc.to_evdev(), key_state, kb_state);
}
if forward_to_node {
self.keyboard_node.get().node_on_key(
self,
time_usec,
kc.to_evdev(),
key_state,
kb_state,
)
}
}
pub fn create_mark_interactive(&self) {
self.mark_mode.set(Some(MarkMode::Mark));
}
@ -1306,6 +1325,82 @@ impl WlSeatGlobal {
}
self.changes.set(0);
}
pub(super) fn set_repeat_key(&self, key: Keycode, state: &Rc<RefCell<KbvmState>>) {
self.repeat_key_version.fetch_add(1);
self.repeat_key_start_ns.set(self.state.now_nsec());
self.repeat_key_state.set(Some(state.clone()));
let old = self.repeat_key.replace(Some(key));
if old.is_none() {
self.have_repeat_key.trigger();
}
}
pub(super) fn clear_repeat_key(&self) {
self.repeat_key_version.fetch_add(1);
self.repeat_key_state.take();
self.repeat_key.take();
}
pub(super) fn create_repeat_handler(self: &Rc<Self>) {
self.clear_repeat_key();
let (rate, delay_ms) = self.repeat_rate.get();
self.key_repeater.take();
if rate == 0 {
return;
}
let delay_first_repeat_ns = delay_ms as u64 * 1_000_000;
let delay_subsequent_repeat_ns = 1_000_000_000 / rate as u64;
let slf = self.clone();
let handle = self.state.eng.spawn("key repeat", async move {
'outer: loop {
let Some(key) = slf.repeat_key.get() else {
slf.have_repeat_key.triggered().await;
continue;
};
let mut base_ns = slf.repeat_key_start_ns.get();
let kbvm_state = slf.repeat_key_state.get().unwrap();
let version = slf.repeat_key_version.get();
macro_rules! check_version {
() => {
if slf.repeat_key_version.get() != version {
continue 'outer;
}
};
}
let target_ns = base_ns + delay_first_repeat_ns;
slf.state.ring.timeout(target_ns).await.unwrap();
check_version!();
let send_key = |now_ns: u64| {
slf.send_key(
now_ns / 1_000,
key,
KeyState::Repeated,
&kbvm_state.borrow().kb_state,
);
};
send_key(target_ns);
base_ns = target_ns;
let mut now_ns = slf.state.now_nsec();
loop {
let max_sleep_ns = now_ns + delay_first_repeat_ns;
let target_ns = base_ns + delay_subsequent_repeat_ns;
slf.state
.ring
.timeout(target_ns.min(max_sleep_ns))
.await
.unwrap();
check_version!();
now_ns = slf.state.now_nsec();
if now_ns >= target_ns {
send_key(target_ns);
base_ns = target_ns;
}
}
}
});
self.key_repeater.set(Some(handle));
}
}
// Button callbacks

View file

@ -168,7 +168,7 @@ impl UnicodeInput {
impl InputMethodKeyboardGrab for SimpleIm {
fn on_key(&self, _time_usec: u64, key: u32, state: KeyState, kb_state: &KeyboardState) -> bool {
if state != KeyState::Pressed {
if state == KeyState::Released {
return true;
}
let Some(con) = self.con.get() else {

View file

@ -69,6 +69,7 @@ impl ZwpInputMethodKeyboardGrabV2 {
state: match state {
KeyState::Released => wl_keyboard::RELEASED,
KeyState::Pressed => wl_keyboard::PRESSED,
KeyState::Repeated => return,
},
})
}

View file

@ -18,6 +18,7 @@ use {
};
pub const REPEAT_INFO_SINCE: Version = Version(4);
pub const REPEATED_SINCE: Version = Version(10);
#[expect(dead_code)]
const NO_KEYMAP: u32 = 0;
@ -25,6 +26,7 @@ pub const XKB_V1: u32 = 1;
pub const RELEASED: u32 = 0;
pub const PRESSED: u32 = 1;
pub const REPEATED: u32 = 2;
pub struct WlKeyboard {
id: WlKeyboardId,
@ -132,6 +134,9 @@ impl WlKeyboard {
}
fn send_key(&self, serial: u64, time: u32, key: u32, state: KeyState) {
if state == KeyState::Repeated && self.seat.version < REPEATED_SINCE {
return;
}
{
let pk = &mut self.pressed_keys.borrow_mut();
match state {
@ -145,6 +150,11 @@ impl WlKeyboard {
return;
}
}
KeyState::Repeated => {
if !pk.contains(&key) {
return;
}
}
}
}
self.seat.client.event(Key {
@ -155,6 +165,7 @@ impl WlKeyboard {
state: match state {
KeyState::Released => RELEASED,
KeyState::Pressed => PRESSED,
KeyState::Repeated => REPEATED,
},
})
}
@ -178,7 +189,11 @@ impl WlKeyboard {
})
}
pub fn send_repeat_info(self: &Rc<Self>, rate: i32, delay: i32) {
pub fn send_repeat_info(self: &Rc<Self>, mut rate: i32, mut delay: i32) {
if self.seat.version >= REPEATED_SINCE {
rate = 0;
delay = 0;
}
self.seat.client.event(RepeatInfo {
self_id: self.id,
rate,

View file

@ -46,6 +46,7 @@ impl TestVirtualKeyboard {
let state = match state {
KeyState::Released => wl_keyboard::RELEASED,
KeyState::Pressed => wl_keyboard::PRESSED,
KeyState::Repeated => wl_keyboard::REPEATED,
};
self.tran.send(Key {
self_id: self.id,

View file

@ -213,6 +213,9 @@ impl PhysicalKeyboardState {
return;
}
}
KeyState::Repeated => {
return;
}
}
let state = &mut *self.state.borrow_mut();
state.map.state_machine.handle_key(
@ -222,6 +225,7 @@ impl PhysicalKeyboardState {
match key_state {
KeyState::Released => Direction::Up,
KeyState::Pressed => Direction::Down,
KeyState::Repeated => unreachable!(),
},
);
self.events.append(&mut inner.event_stash);