ltk/input/repeat/
key.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

use std::time::Duration;

use crate::app::App;
use crate::event_loop::{ AppData, SurfaceFocus };
use crate::event_loop::repeat::KeyRepeatState;
use crate::platform::Platform;

use super::super::event::KeyInput;

/// Built-in fallback for the initial key-repeat delay when the
/// compositor does not advertise one and the app does not override
/// [`crate::App::key_repeat_delay`].
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis( 500 );

/// Built-in fallback for the inter-repeat interval when the compositor
/// does not advertise a rate. ~30 Hz, matching the GNOME / KDE default
/// for "fast" without straying into uncomfortably-twitchy territory.
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis( 33 );

impl<A: App, P: Platform> AppData<A, P>
{
	/// Compute the effective initial repeat delay — app override wins,
	/// then compositor info, then a built-in default.
	pub( crate ) fn effective_repeat_delay( &self ) -> Duration
	{
		if let Some( d ) = self.app.key_repeat_delay() { return d; }
		if self.compositor_repeat_delay > 0
		{
			Duration::from_millis( self.compositor_repeat_delay as u64 )
		} else {
			DEFAULT_REPEAT_DELAY
		}
	}

	/// Compute the effective inter-repeat interval. Same precedence as
	/// [`Self::effective_repeat_delay`]: app override → compositor →
	/// built-in default. Returns `None` when repeat is disabled by the
	/// active source (compositor `RepeatInfo::Disable` with no app
	/// override, or an app override of `Some(Duration::ZERO)`).
	pub( crate ) fn effective_repeat_interval( &self ) -> Option<Duration>
	{
		if let Some( d ) = self.app.key_repeat_interval()
		{
			if d.is_zero() { return None; }
			return Some( d );
		}
		if self.compositor_repeat_rate == 0
		{
			// Compositor explicitly disabled repeat, app did not
			// override — fall back to a built-in default rather than
			// disabling, because most environments where the
			// compositor reports 0 also fail to send the event in
			// the first place. The default keeps the feel close to
			// what people expect from a desktop toolkit.
			return Some( DEFAULT_REPEAT_INTERVAL );
		}
		let ms = ( 1000 / self.compositor_repeat_rate ).max( 1 );
		Some( Duration::from_millis( ms as u64 ) )
	}

	/// Schedule a key-repeat timer for the given event. No-op when the
	/// app's [`crate::App::key_repeats`] gate returns `false` for this
	/// keysym, when repeat is disabled, or when the timer
	/// insertion fails (in which case held keys simply do not repeat).
	pub( crate ) fn start_key_repeat( &mut self, focus: SurfaceFocus, event: KeyInput )
	{
		if !self.app.key_repeats( event.keysym ) { return; }
		let interval = match self.effective_repeat_interval()
		{
			Some( i ) => i,
			None      => return,
		};
		let delay = self.effective_repeat_delay();

		let event_for_timer = event.clone();
		let token = self.sources.insert_timer( delay, move |data: &mut AppData<A, P>|
		{
			data.dispatch_key( focus, event_for_timer.clone() );
			Some( interval )
		} );
		if let Ok( token ) = token { self.key_repeat = Some( KeyRepeatState { event, token } ); }
	}

	/// Cancel any active key-repeat timer.
	pub( crate ) fn stop_key_repeat( &mut self )
	{
		if let Some( state ) = self.key_repeat.take()
		{
			self.sources.remove( state.token );
		}
	}
}