Make key repeat rate on Wayland more precise (2) (#43589)
Ole Jørgen Brønner
created
CLOSES #39042
This is a reopening of #34985+.
_Original descrioption_:
In Wayland, the client implement key repeat themself. In Zed this is
ultimately handled by the gpui crate by inserting a timer source into
the event loop which repeat itself if the key is still held down [1].
But it seems the processing of the repeated key event happen
synchronously inside the timer source handler, meaning the effective
rate become slightly lower (since the repeated timer is scheduled using
the 1/rate as delay).
I measured the event processing time on my laptop and it's typically
around 3ms, but sometimes spiking at 10ms. At low key repeat rates this
is probably not _very_ noticeable. I see the default in Zed is set to a
(measly) 16/s, but I assume most systems will use something closer to
25, which is a 40ms delay. So ~3ms is around 7.5% of the delay. At
higher rate the discrepancy become worse of course.
I can visible notice the spikes, and doing some crude stopwatch
measurements using gedit as a reference I can reproduce around 5-10%
slower rates in Zed.
IMO this is significant enough to warrant improving, especially since
some people can get quite used the repeat rate and might feel something
being "off" in Zed.
~~The suggested fix simply subtract the processing time from the next
delay timer.~~
[1] https://github.com/olejorgenb/zed/blob/32df726f3b7fa83e7399f6629c59e0a3f3fff125/crates/gpui/src/platform/linux/wayland/client.rs#L1355
Release Notes:
- Improved Wayland (Linux) key repeat rate precision
@@ -1419,6 +1419,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
state.repeat.current_keycode = Some(keycode);
let rate = state.repeat.characters_per_second;
+ let repeat_interval = Duration::from_secs(1) / rate;
let id = state.repeat.current_id;
state
.loop_handle
@@ -1428,7 +1429,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
is_held: true,
prefer_character_input: false,
});
- move |_event, _metadata, this| {
+ move |event_timestamp, _metadata, this| {
let mut client = this.get_client();
let mut state = client.borrow_mut();
let is_repeating = id == state.repeat.current_id
@@ -1445,7 +1446,8 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
drop(state);
focused_window.handle_input(input.clone());
- TimeoutAction::ToDuration(Duration::from_secs(1) / rate)
+ // If the new scheduled time is in the past the event will repeat as soon as possible
+ TimeoutAction::ToInstant(event_timestamp + repeat_interval)
}
})
.unwrap();