1use std::cell::RefCell;
5use std::collections::VecDeque;
6use std::time::Duration;
7
8pub const SCROLL_PIXELS_PER_LINE: f32 = 50.0;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum PointerPhase {
12 Down,
13 Move,
14 Up,
15 Cancel,
16}
17
18#[derive(Debug)]
19pub enum InputMessage {
20 Resize {
22 width: u32,
23 height: u32,
24 dpr: f32,
25 },
26
27 Viewport {
29 width: f32,
30 height: f32,
31 },
32
33 MouseMove {
35 x: f32,
36 y: f32,
37 buttons: u8,
38 dx: f32,
39 dy: f32,
40 time: Duration,
41 },
42
43 MouseButton {
45 x: f32,
46 y: f32,
47 button: u8,
48 pressed: bool,
49 time: Duration,
50 },
51
52 MouseWheel {
54 dx: f32,
55 dy: f32,
56 },
57
58 Key {
60 code: String,
61 key: String,
62 pressed: bool,
63 repeat: bool,
64 ctrl: bool,
65 shift: bool,
66 alt: bool,
67 meta: bool,
68 },
69
70 Focus(bool),
71
72 Visibility(bool),
73
74 Start,
75
76 PointerLockChanged {
78 locked: bool,
79 released: bool,
80 },
81
82 Pointer {
84 id: u64,
85 x: f32,
86 y: f32,
87 phase: PointerPhase,
88 time: Duration,
89 },
90
91 Host {
93 topic: String,
94 values: Vec<f32>,
95 },
96}
97
98pub const MESSAGE_QUEUE_CAPACITY: usize = 256;
99
100thread_local! {
101 static MESSAGE_QUEUE: RefCell<VecDeque<InputMessage>> = const { RefCell::new(VecDeque::new()) };
102}
103
104fn control_kind(msg: &InputMessage) -> Option<usize> {
105 match msg {
106 InputMessage::Resize { .. } => Some(0),
107 InputMessage::Visibility(_) => Some(1),
108 InputMessage::Start => Some(2),
109 InputMessage::PointerLockChanged { .. } => Some(3),
110 InputMessage::Focus(_) => Some(4),
111 InputMessage::Viewport { .. } => Some(5),
112 _ => None,
113 }
114}
115
116fn coalesced_move(q: &VecDeque<InputMessage>, msg: &InputMessage) -> Option<usize> {
117 let InputMessage::Pointer {
118 id,
119 phase: PointerPhase::Move,
120 ..
121 } = msg
122 else {
123 return None;
124 };
125 q.iter()
126 .enumerate()
127 .rev()
128 .map_while(|(index, queued)| match queued {
129 InputMessage::Pointer {
130 id: queued_id,
131 phase: PointerPhase::Move,
132 ..
133 } => Some((index, queued_id == id)),
134 _ => None,
135 })
136 .find(|(_, same_pointer)| *same_pointer)
137 .map(|(index, _)| index)
138}
139
140pub fn enqueue(msg: InputMessage) {
141 MESSAGE_QUEUE.with(|q| {
142 let mut q = q.borrow_mut();
143 let msg = match msg {
144 InputMessage::Host { topic, values } if !values.is_empty() => {
145 let earlier = q
146 .iter_mut()
147 .rev()
148 .filter_map(|queued| match queued {
149 InputMessage::Host { topic, values } => Some((topic, values)),
150 _ => None,
151 })
152 .take_while(|(_, queued_values)| !queued_values.is_empty())
153 .find(|(queued_topic, _)| **queued_topic == topic)
154 .map(|(_, queued_values)| queued_values);
155 if let Some(earlier) = earlier {
156 *earlier = values;
157 return;
158 }
159 InputMessage::Host { topic, values }
160 }
161 msg => msg,
162 };
163 if let Some(index) = coalesced_move(&q, &msg) {
164 q.remove(index);
165 }
166 if q.len() >= MESSAGE_QUEUE_CAPACITY {
167 let mut latest = [None; 6];
168 for (index, queued) in q.iter().enumerate() {
169 if let Some(kind) = control_kind(queued) {
170 latest[kind] = Some(index);
171 }
172 }
173 let mut index = 0;
174 q.retain(|queued| {
175 let keep = control_kind(queued).is_some_and(|kind| latest[kind] == Some(index));
176 index += 1;
177 keep
178 });
179 q.push_front(InputMessage::Focus(false));
180 tracing::warn!("input queue overflow: released held input and discarded stale events");
181 }
182 q.push_back(msg);
183 });
184}
185
186pub fn drain_messages_into(batch: &mut VecDeque<InputMessage>) {
187 batch.clear();
188 MESSAGE_QUEUE.with(|q| std::mem::swap(&mut *q.borrow_mut(), batch));
189}