Skip to main content

drmkit_input/
repeat.rs

1// SPDX-FileCopyrightText: (c) 2026 Joel Winarske
2// SPDX-License-Identifier: MIT
3
4//! Deciding what auto-repeat should do, with no timer and no keymap in sight.
5
6use std::time::Duration;
7
8use crate::event::KeyEvent;
9
10/// How many synthesized events one dispatch will produce.
11///
12/// A loop that stalled comes back to a timerfd holding every expiration it
13/// missed. Emitting all of them hands the consumer a burst of keystrokes the
14/// user did not type, seconds after they stopped typing; stopping at a
15/// handful keeps the key alive without the flood.
16const MAX_BURST: u64 = 8;
17
18/// When a held key starts repeating, and how fast.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[allow(
21    clippy::exhaustive_structs,
22    reason = "built by callers with `..Default::default()`, which a new field does not break"
23)]
24pub struct RepeatConfig {
25    /// How long a key must be held before the first repeat.
26    pub delay: Duration,
27    /// The gap between repeats after that.
28    pub interval: Duration,
29}
30
31impl Default for RepeatConfig {
32    /// The reference's default (`key_repeater.hpp`): 600 ms, then 25 Hz.
33    fn default() -> Self {
34        Self {
35            delay: Duration::from_millis(600),
36            interval: Duration::from_millis(40),
37        }
38    }
39}
40
41/// What the timer should do about a key event.
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43#[non_exhaustive]
44pub enum Timer {
45    /// Start the delay, then repeat at the interval.
46    Arm,
47    /// Stop.
48    Disarm,
49    /// Whatever it was doing, keep doing.
50    Unchanged,
51}
52
53/// Which key is repeating, and what each event does to that.
54///
55/// Split out from the timer so it can be reasoned about, and tested, without
56/// one. libinput deliberately does not auto-repeat -- it reports what the
57/// hardware did, and the repeat rate is a user preference the compositor
58/// owns -- so this is the piece that has to exist somewhere.
59#[derive(Debug, Clone)]
60pub struct Repeat {
61    config: RepeatConfig,
62    held: Option<u32>,
63}
64
65impl Repeat {
66    /// Start with nothing held.
67    #[must_use]
68    pub const fn new(config: RepeatConfig) -> Self {
69        Self { config, held: None }
70    }
71
72    /// The delay and interval this was built with.
73    #[must_use]
74    pub const fn config(&self) -> RepeatConfig {
75        self.config
76    }
77
78    /// The key currently repeating.
79    #[must_use]
80    pub const fn held_key(&self) -> Option<u32> {
81        self.held
82    }
83
84    /// Feed a real key event.
85    ///
86    /// `repeats` is the keymap's answer for this key --
87    /// [`Keyboard::should_repeat`](crate::Keyboard::should_repeat). It is
88    /// passed in rather than looked up so this stays usable without a keymap,
89    /// and so a caller with its own policy can impose it.
90    ///
91    /// Synthesized events are ignored: feeding this its own output back would
92    /// re-arm the delay on every tick and the key would never repeat.
93    pub fn on_key(&mut self, event: &KeyEvent, repeats: bool) -> Timer {
94        if event.repeat {
95            return Timer::Unchanged;
96        }
97
98        if event.pressed {
99            if !repeats {
100                // A modifier, or a lock. It does not disturb a key already
101                // repeating -- holding `a` and pressing Shift should go on
102                // repeating, in upper case from the next tick.
103                return Timer::Unchanged;
104            }
105            self.held = Some(event.key);
106            return Timer::Arm;
107        }
108
109        // A release only ends the repeat if it is the key that was repeating.
110        // Letting go of something else -- a modifier, a key pressed and
111        // released while this one is held -- leaves it running.
112        if self.held == Some(event.key) {
113            self.held = None;
114            Timer::Disarm
115        } else {
116            Timer::Unchanged
117        }
118    }
119
120    /// Stop repeating.
121    ///
122    /// For a session pause, or any point where the held-key state has stopped
123    /// being trustworthy.
124    pub fn cancel(&mut self) -> Timer {
125        self.held = None;
126        Timer::Disarm
127    }
128
129    /// Turn a batch of timer expirations into events to deliver.
130    ///
131    /// Returns how many were dropped to the burst cap, which is worth saying
132    /// out loud rather than swallowing: a nonzero answer means the loop is not
133    /// keeping up.
134    ///
135    /// The events carry only the key. A caller that wants `sym` and `utf8`
136    /// runs them through [`Keyboard::resolve`](crate::Keyboard::resolve),
137    /// which reads against the modifiers held *now* -- so pressing Shift
138    /// partway through a held key changes what the next repeat types.
139    pub fn synthesize(&self, expirations: u64, time_ms: u32, out: &mut Vec<KeyEvent>) -> u64 {
140        let Some(key) = self.held else {
141            return 0;
142        };
143        let emit = expirations.min(MAX_BURST);
144        out.reserve(usize::try_from(emit).unwrap_or(usize::MAX));
145        for _ in 0..emit {
146            out.push(KeyEvent {
147                time_ms,
148                key,
149                pressed: true,
150                repeat: true,
151                sym: 0,
152                utf8: String::new(),
153            });
154        }
155        expirations - emit
156    }
157}