1use crate::event::{KeyEvent, MouseEvent};
7use smallvec::SmallVec;
8use std::mem;
9
10const INLINE_SIZE: usize = 24;
12
13#[derive(Debug, Clone)]
15pub enum OptimizedEvent<M> {
16 Key(KeyEvent),
18 Mouse(MouseEvent),
20 Resize {
22 width: u16,
24 height: u16,
26 },
27 Tick,
29 User(InlineMessage<M>),
31 Quit,
33 Focus,
35 Blur,
37 Suspend,
39 Resume,
41 Paste(std::borrow::Cow<'static, str>),
43 #[doc(hidden)]
45 ExecProcess,
46}
47
48#[derive(Debug, Clone)]
50pub enum InlineMessage<M> {
51 Inline(M),
53 Boxed(Box<M>),
55}
56
57impl<M> InlineMessage<M> {
58 #[inline(always)]
60 pub fn new(msg: M) -> Self {
61 if mem::size_of::<M>() <= INLINE_SIZE {
62 Self::Inline(msg)
63 } else {
64 Self::Boxed(Box::new(msg))
65 }
66 }
67
68 #[inline(always)]
70 pub fn get(&self) -> &M {
71 match self {
72 Self::Inline(msg) => msg,
73 Self::Boxed(msg) => msg.as_ref(),
74 }
75 }
76
77 #[inline(always)]
79 pub fn into_inner(self) -> M {
80 match self {
81 Self::Inline(msg) => msg,
82 Self::Boxed(msg) => *msg,
83 }
84 }
85
86 #[inline(always)]
88 pub fn is_inline(&self) -> bool {
89 matches!(self, Self::Inline(_))
90 }
91}
92
93impl<M> OptimizedEvent<M> {
94 #[inline(always)]
96 pub fn user(msg: M) -> Self {
97 Self::User(InlineMessage::new(msg))
98 }
99
100 #[inline(always)]
102 pub const fn is_key(&self) -> bool {
103 matches!(self, Self::Key(_))
104 }
105
106 #[inline(always)]
108 pub fn is_key_press(&self, key: crate::event::Key) -> bool {
109 matches!(self, Self::Key(k) if k.key == key)
110 }
111
112 #[inline(always)]
114 pub const fn as_key(&self) -> Option<&KeyEvent> {
115 match self {
116 Self::Key(k) => Some(k),
117 _ => None,
118 }
119 }
120
121 #[inline(always)]
123 pub const fn is_mouse(&self) -> bool {
124 matches!(self, Self::Mouse(_))
125 }
126
127 #[inline(always)]
129 pub const fn as_mouse(&self) -> Option<&MouseEvent> {
130 match self {
131 Self::Mouse(m) => Some(m),
132 _ => None,
133 }
134 }
135
136 #[inline(always)]
138 pub const fn is_user(&self) -> bool {
139 matches!(self, Self::User(_))
140 }
141
142 #[inline(always)]
144 pub fn as_user(&self) -> Option<&M> {
145 match self {
146 Self::User(msg) => Some(msg.get()),
147 _ => None,
148 }
149 }
150
151 #[inline(always)]
153 pub fn into_user(self) -> Option<M> {
154 match self {
155 Self::User(msg) => Some(msg.into_inner()),
156 _ => None,
157 }
158 }
159
160 #[inline(always)]
162 pub const fn is_quit(&self) -> bool {
163 matches!(self, Self::Quit)
164 }
165
166 #[inline(always)]
168 pub const fn is_tick(&self) -> bool {
169 matches!(self, Self::Tick)
170 }
171
172 #[inline(always)]
174 pub const fn is_resize(&self) -> bool {
175 matches!(self, Self::Resize { .. })
176 }
177
178 #[inline(always)]
180 pub const fn as_resize(&self) -> Option<(u16, u16)> {
181 match self {
182 Self::Resize { width, height } => Some((*width, *height)),
183 _ => None,
184 }
185 }
186}
187
188#[derive(Debug, Clone)]
190pub struct EventBatch<M> {
191 events: SmallVec<[OptimizedEvent<M>; 8]>,
193}
194
195impl<M> EventBatch<M> {
196 #[inline]
198 pub fn new() -> Self {
199 Self {
200 events: SmallVec::new(),
201 }
202 }
203
204 #[inline]
206 pub fn with_capacity(capacity: usize) -> Self {
207 Self {
208 events: SmallVec::with_capacity(capacity),
209 }
210 }
211
212 #[inline]
214 pub fn push(&mut self, event: OptimizedEvent<M>) {
215 self.events.push(event);
216 }
217
218 #[inline]
220 pub fn len(&self) -> usize {
221 self.events.len()
222 }
223
224 #[inline]
226 pub fn is_empty(&self) -> bool {
227 self.events.is_empty()
228 }
229
230 #[inline]
232 pub fn iter(&self) -> std::slice::Iter<'_, OptimizedEvent<M>> {
233 self.events.iter()
234 }
235
236 #[inline]
238 pub fn drain(&mut self) -> smallvec::Drain<'_, [OptimizedEvent<M>; 8]> {
239 self.events.drain(..)
240 }
241
242 #[inline]
244 pub fn clear(&mut self) {
245 self.events.clear();
246 }
247
248 #[inline]
250 pub fn is_inline(&self) -> bool {
251 !self.events.spilled()
252 }
253}
254
255impl<M> Default for EventBatch<M> {
256 #[inline]
257 fn default() -> Self {
258 Self::new()
259 }
260}
261
262impl<M> IntoIterator for EventBatch<M> {
263 type Item = OptimizedEvent<M>;
264 type IntoIter = smallvec::IntoIter<[OptimizedEvent<M>; 8]>;
265
266 #[inline]
267 fn into_iter(self) -> Self::IntoIter {
268 self.events.into_iter()
269 }
270}
271
272pub struct EventCoalescer<M> {
274 buffer: SmallVec<[OptimizedEvent<M>; 16]>,
276 last_resize: Option<(u16, u16)>,
278}
279
280impl<M> EventCoalescer<M> {
281 #[inline]
283 pub fn new() -> Self {
284 Self {
285 buffer: SmallVec::new(),
286 last_resize: None,
287 }
288 }
289
290 pub fn push(&mut self, event: OptimizedEvent<M>) {
292 match event {
293 OptimizedEvent::Resize { width, height } => {
295 self.last_resize = Some((width, height));
296 }
297 OptimizedEvent::Tick => {
299 if !self.buffer.iter().any(|e| e.is_tick()) {
300 self.buffer.push(event);
301 }
302 }
303 _ => {
305 self.buffer.push(event);
306 }
307 }
308 }
309
310 pub fn finish(&mut self) -> EventBatch<M> {
312 if let Some((width, height)) = self.last_resize.take() {
314 self.buffer.push(OptimizedEvent::Resize { width, height });
315 }
316
317 let mut batch = EventBatch::with_capacity(self.buffer.len());
318 batch.events.extend(self.buffer.drain(..));
319 batch
320 }
321
322 #[inline]
324 pub fn clear(&mut self) {
325 self.buffer.clear();
326 self.last_resize = None;
327 }
328}
329
330impl<M> Default for EventCoalescer<M> {
331 #[inline]
332 fn default() -> Self {
333 Self::new()
334 }
335}
336
337impl<M> From<crate::event::Event<M>> for OptimizedEvent<M> {
339 #[inline]
340 fn from(event: crate::event::Event<M>) -> Self {
341 match event {
342 crate::event::Event::Key(k) => Self::Key(k),
343 crate::event::Event::Mouse(m) => Self::Mouse(m),
344 crate::event::Event::Resize { width, height } => Self::Resize { width, height },
345 crate::event::Event::Tick => Self::Tick,
346 crate::event::Event::User(m) => Self::user(m),
347 crate::event::Event::Quit => Self::Quit,
348 crate::event::Event::Focus => Self::Focus,
349 crate::event::Event::Blur => Self::Blur,
350 crate::event::Event::Suspend => Self::Suspend,
351 crate::event::Event::Resume => Self::Resume,
352 crate::event::Event::Paste(text) => Self::Paste(text.into()),
353 crate::event::Event::ExecProcess => Self::ExecProcess,
354 }
355 }
356}
357
358impl<M> From<OptimizedEvent<M>> for crate::event::Event<M> {
360 #[inline]
361 fn from(event: OptimizedEvent<M>) -> Self {
362 match event {
363 OptimizedEvent::Key(k) => Self::Key(k),
364 OptimizedEvent::Mouse(m) => Self::Mouse(m),
365 OptimizedEvent::Resize { width, height } => Self::Resize { width, height },
366 OptimizedEvent::Tick => Self::Tick,
367 OptimizedEvent::User(m) => Self::User(m.into_inner()),
368 OptimizedEvent::Quit => Self::Quit,
369 OptimizedEvent::Focus => Self::Focus,
370 OptimizedEvent::Blur => Self::Blur,
371 OptimizedEvent::Suspend => Self::Suspend,
372 OptimizedEvent::Resume => Self::Resume,
373 OptimizedEvent::Paste(text) => Self::Paste(text.into_owned()),
374 OptimizedEvent::ExecProcess => Self::ExecProcess,
375 }
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 #[test]
384 fn test_inline_message_small() {
385 let msg = 42u32;
386 let inline_msg = InlineMessage::new(msg);
387 assert!(inline_msg.is_inline());
388 assert_eq!(*inline_msg.get(), 42);
389 }
390
391 #[test]
392 fn test_inline_message_large() {
393 #[derive(Debug, PartialEq, Clone)]
395 struct LargeStruct([u8; 32]); let msg = LargeStruct([0u8; 32]);
398 let inline_msg = InlineMessage::new(msg.clone());
399 assert!(!inline_msg.is_inline());
401 assert_eq!(*inline_msg.get(), msg);
402 }
403
404 #[test]
405 fn test_event_coalescing() {
406 let mut coalescer: EventCoalescer<()> = EventCoalescer::new();
407
408 coalescer.push(OptimizedEvent::Resize {
410 width: 80,
411 height: 24,
412 });
413 coalescer.push(OptimizedEvent::Resize {
414 width: 100,
415 height: 30,
416 });
417 coalescer.push(OptimizedEvent::Resize {
418 width: 120,
419 height: 40,
420 });
421
422 coalescer.push(OptimizedEvent::Tick);
424 coalescer.push(OptimizedEvent::Tick);
425
426 let batch = coalescer.finish();
427
428 assert_eq!(batch.len(), 2);
430
431 let events: Vec<_> = batch.into_iter().collect();
432 assert!(events.iter().any(|e| matches!(e, OptimizedEvent::Tick)));
433 assert!(events.iter().any(|e| matches!(
434 e,
435 OptimizedEvent::Resize {
436 width: 120,
437 height: 40
438 }
439 )));
440 }
441
442 #[test]
443 fn test_event_batch_inline_storage() {
444 let mut batch = EventBatch::new();
445
446 batch.push(OptimizedEvent::Tick);
448 batch.push(OptimizedEvent::user(42u32));
449 batch.push(OptimizedEvent::Quit);
450
451 assert!(batch.is_inline()); assert_eq!(batch.len(), 3);
453 }
454
455 #[test]
456 fn test_zero_cost_event_checks() {
457 let event = OptimizedEvent::user(String::from("test"));
458
459 assert!(event.is_user());
461 assert!(!event.is_key());
462 assert!(!event.is_mouse());
463 assert_eq!(event.as_user().unwrap(), "test");
464 }
465}