1#[cfg(feature = "crossterm")]
11use crossterm::event as crossterm_event;
12
13#[non_exhaustive]
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum Event {
21 Key(KeyEvent),
23 Mouse(MouseEvent),
25 Resize(u32, u32),
27 Paste(String),
29 FocusGained,
31 FocusLost,
33}
34
35impl Event {
36 pub fn key_char(c: char) -> Self {
38 Event::Key(KeyEvent {
39 code: KeyCode::Char(c),
40 modifiers: KeyModifiers::NONE,
41 kind: KeyEventKind::Press,
42 })
43 }
44
45 pub fn key(code: KeyCode) -> Self {
47 Event::Key(KeyEvent {
48 code,
49 modifiers: KeyModifiers::NONE,
50 kind: KeyEventKind::Press,
51 })
52 }
53
54 pub fn key_ctrl(c: char) -> Self {
56 Event::Key(KeyEvent {
57 code: KeyCode::Char(c),
58 modifiers: KeyModifiers::CONTROL,
59 kind: KeyEventKind::Press,
60 })
61 }
62
63 pub fn key_mod(code: KeyCode, modifiers: KeyModifiers) -> Self {
65 Event::Key(KeyEvent {
66 code,
67 modifiers,
68 kind: KeyEventKind::Press,
69 })
70 }
71
72 pub fn resize(width: u32, height: u32) -> Self {
74 Event::Resize(width, height)
75 }
76
77 pub fn mouse_click(x: u32, y: u32) -> Self {
79 Event::Mouse(MouseEvent {
80 kind: MouseKind::Down(MouseButton::Left),
81 x,
82 y,
83 modifiers: KeyModifiers::NONE,
84 pixel_x: None,
85 pixel_y: None,
86 })
87 }
88
89 pub fn mouse_move(x: u32, y: u32) -> Self {
91 Event::Mouse(MouseEvent {
92 kind: MouseKind::Moved,
93 x,
94 y,
95 modifiers: KeyModifiers::NONE,
96 pixel_x: None,
97 pixel_y: None,
98 })
99 }
100
101 pub fn scroll_up(x: u32, y: u32) -> Self {
103 Event::Mouse(MouseEvent {
104 kind: MouseKind::ScrollUp,
105 x,
106 y,
107 modifiers: KeyModifiers::NONE,
108 pixel_x: None,
109 pixel_y: None,
110 })
111 }
112
113 pub fn scroll_down(x: u32, y: u32) -> Self {
115 Event::Mouse(MouseEvent {
116 kind: MouseKind::ScrollDown,
117 x,
118 y,
119 modifiers: KeyModifiers::NONE,
120 pixel_x: None,
121 pixel_y: None,
122 })
123 }
124
125 pub fn key_release(c: char) -> Self {
127 Event::Key(KeyEvent {
128 code: KeyCode::Char(c),
129 modifiers: KeyModifiers::NONE,
130 kind: KeyEventKind::Release,
131 })
132 }
133
134 pub fn paste(text: impl Into<String>) -> Self {
136 Event::Paste(text.into())
137 }
138
139 pub fn as_key(&self) -> Option<&KeyEvent> {
141 match self {
142 Event::Key(k) => Some(k),
143 _ => None,
144 }
145 }
146
147 pub fn as_mouse(&self) -> Option<&MouseEvent> {
149 match self {
150 Event::Mouse(m) => Some(m),
151 _ => None,
152 }
153 }
154
155 pub fn as_resize(&self) -> Option<(u32, u32)> {
157 match self {
158 Event::Resize(w, h) => Some((*w, *h)),
159 _ => None,
160 }
161 }
162
163 pub fn as_paste(&self) -> Option<&str> {
165 match self {
166 Event::Paste(s) => Some(s),
167 _ => None,
168 }
169 }
170
171 pub fn is_key(&self) -> bool {
173 matches!(self, Event::Key(_))
174 }
175
176 pub fn is_mouse(&self) -> bool {
178 matches!(self, Event::Mouse(_))
179 }
180}
181
182#[non_exhaustive]
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub struct KeyEvent {
186 pub code: KeyCode,
188 pub modifiers: KeyModifiers,
190 pub kind: KeyEventKind,
192}
193
194impl KeyEvent {
195 pub fn is_char(&self, c: char) -> bool {
197 self.code == KeyCode::Char(c)
198 && self.modifiers == KeyModifiers::NONE
199 && self.kind == KeyEventKind::Press
200 }
201
202 pub fn is_ctrl_char(&self, c: char) -> bool {
204 self.code == KeyCode::Char(c)
205 && self.modifiers == KeyModifiers::CONTROL
206 && self.kind == KeyEventKind::Press
207 }
208
209 pub fn is_code(&self, code: KeyCode) -> bool {
211 self.code == code
212 && self.modifiers == KeyModifiers::NONE
213 && self.kind == KeyEventKind::Press
214 }
215}
216
217#[non_exhaustive]
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220pub enum KeyEventKind {
221 Press,
223 Release,
225 Repeat,
227}
228
229#[non_exhaustive]
235#[derive(Debug, Clone, PartialEq, Eq)]
236pub enum KeyCode {
237 Char(char),
239 Enter,
241 Backspace,
243 Tab,
245 BackTab,
247 Esc,
249 Up,
251 Down,
253 Left,
255 Right,
257 Home,
259 End,
261 PageUp,
263 PageDown,
265 Delete,
267 Insert,
269 Null,
271 CapsLock,
273 ScrollLock,
275 NumLock,
277 PrintScreen,
279 Pause,
281 Menu,
283 KeypadBegin,
285 F(u8),
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
294pub struct KeyModifiers(pub u8);
295
296impl KeyModifiers {
297 pub const NONE: Self = Self(0);
299 pub const SHIFT: Self = Self(1 << 0);
301 pub const CONTROL: Self = Self(1 << 1);
303 pub const ALT: Self = Self(1 << 2);
305 pub const SUPER: Self = Self(1 << 3);
307 pub const HYPER: Self = Self(1 << 4);
309 pub const META: Self = Self(1 << 5);
311
312 #[inline]
314 pub fn contains(self, other: Self) -> bool {
315 (self.0 & other.0) == other.0
316 }
317}
318
319#[non_exhaustive]
327#[derive(Debug, Clone, PartialEq, Eq)]
328pub struct MouseEvent {
329 pub kind: MouseKind,
331 pub x: u32,
333 pub y: u32,
335 pub modifiers: KeyModifiers,
337 pub pixel_x: Option<u16>,
339 pub pixel_y: Option<u16>,
341}
342
343impl MouseEvent {
344 pub fn new(
346 kind: MouseKind,
347 x: u32,
348 y: u32,
349 modifiers: KeyModifiers,
350 pixel_x: Option<u16>,
351 pixel_y: Option<u16>,
352 ) -> Self {
353 Self {
354 kind,
355 x,
356 y,
357 modifiers,
358 pixel_x,
359 pixel_y,
360 }
361 }
362
363 pub fn is_scroll(&self) -> bool {
365 matches!(
366 self.kind,
367 MouseKind::ScrollUp
368 | MouseKind::ScrollDown
369 | MouseKind::ScrollLeft
370 | MouseKind::ScrollRight
371 )
372 }
373}
374
375#[non_exhaustive]
377#[derive(Debug, Clone, PartialEq, Eq)]
378pub enum MouseKind {
379 Down(MouseButton),
381 Up(MouseButton),
383 Drag(MouseButton),
385 ScrollUp,
387 ScrollDown,
389 ScrollLeft,
391 ScrollRight,
393 Moved,
395}
396
397#[non_exhaustive]
399#[derive(Debug, Clone, Copy, PartialEq, Eq)]
400pub enum MouseButton {
401 Left,
403 Right,
405 Middle,
407}
408
409#[cfg(feature = "crossterm")]
410fn convert_modifiers(modifiers: crossterm_event::KeyModifiers) -> KeyModifiers {
411 let mut out = KeyModifiers::NONE;
412 if modifiers.contains(crossterm_event::KeyModifiers::SHIFT) {
413 out.0 |= KeyModifiers::SHIFT.0;
414 }
415 if modifiers.contains(crossterm_event::KeyModifiers::CONTROL) {
416 out.0 |= KeyModifiers::CONTROL.0;
417 }
418 if modifiers.contains(crossterm_event::KeyModifiers::ALT) {
419 out.0 |= KeyModifiers::ALT.0;
420 }
421 if modifiers.contains(crossterm_event::KeyModifiers::SUPER) {
422 out.0 |= KeyModifiers::SUPER.0;
423 }
424 if modifiers.contains(crossterm_event::KeyModifiers::HYPER) {
425 out.0 |= KeyModifiers::HYPER.0;
426 }
427 if modifiers.contains(crossterm_event::KeyModifiers::META) {
428 out.0 |= KeyModifiers::META.0;
429 }
430 out
431}
432
433#[cfg(feature = "crossterm")]
434fn convert_button(button: crossterm_event::MouseButton) -> MouseButton {
435 match button {
436 crossterm_event::MouseButton::Left => MouseButton::Left,
437 crossterm_event::MouseButton::Right => MouseButton::Right,
438 crossterm_event::MouseButton::Middle => MouseButton::Middle,
439 }
440}
441
442#[cfg(feature = "crossterm")]
447pub(crate) fn from_crossterm(raw: crossterm_event::Event) -> Option<Event> {
448 match raw {
449 crossterm_event::Event::Key(k) => {
450 let code = match k.code {
451 crossterm_event::KeyCode::Char(c) => KeyCode::Char(c),
452 crossterm_event::KeyCode::Enter => KeyCode::Enter,
453 crossterm_event::KeyCode::Backspace => KeyCode::Backspace,
454 crossterm_event::KeyCode::Tab => KeyCode::Tab,
455 crossterm_event::KeyCode::BackTab => KeyCode::BackTab,
456 crossterm_event::KeyCode::Esc => KeyCode::Esc,
457 crossterm_event::KeyCode::Up => KeyCode::Up,
458 crossterm_event::KeyCode::Down => KeyCode::Down,
459 crossterm_event::KeyCode::Left => KeyCode::Left,
460 crossterm_event::KeyCode::Right => KeyCode::Right,
461 crossterm_event::KeyCode::Home => KeyCode::Home,
462 crossterm_event::KeyCode::End => KeyCode::End,
463 crossterm_event::KeyCode::PageUp => KeyCode::PageUp,
464 crossterm_event::KeyCode::PageDown => KeyCode::PageDown,
465 crossterm_event::KeyCode::Delete => KeyCode::Delete,
466 crossterm_event::KeyCode::Insert => KeyCode::Insert,
467 crossterm_event::KeyCode::Null => KeyCode::Null,
468 crossterm_event::KeyCode::CapsLock => KeyCode::CapsLock,
469 crossterm_event::KeyCode::ScrollLock => KeyCode::ScrollLock,
470 crossterm_event::KeyCode::NumLock => KeyCode::NumLock,
471 crossterm_event::KeyCode::PrintScreen => KeyCode::PrintScreen,
472 crossterm_event::KeyCode::Pause => KeyCode::Pause,
473 crossterm_event::KeyCode::Menu => KeyCode::Menu,
474 crossterm_event::KeyCode::KeypadBegin => KeyCode::KeypadBegin,
475 crossterm_event::KeyCode::F(n) => KeyCode::F(n),
476 _ => return None,
477 };
478 let modifiers = convert_modifiers(k.modifiers);
479 let kind = match k.kind {
480 crossterm_event::KeyEventKind::Press => KeyEventKind::Press,
481 crossterm_event::KeyEventKind::Repeat => KeyEventKind::Repeat,
482 crossterm_event::KeyEventKind::Release => KeyEventKind::Release,
483 };
484 Some(Event::Key(KeyEvent {
485 code,
486 modifiers,
487 kind,
488 }))
489 }
490 crossterm_event::Event::Mouse(m) => {
491 let kind = match m.kind {
492 crossterm_event::MouseEventKind::Down(btn) => MouseKind::Down(convert_button(btn)),
493 crossterm_event::MouseEventKind::Up(btn) => MouseKind::Up(convert_button(btn)),
494 crossterm_event::MouseEventKind::Drag(btn) => MouseKind::Drag(convert_button(btn)),
495 crossterm_event::MouseEventKind::Moved => MouseKind::Moved,
496 crossterm_event::MouseEventKind::ScrollUp => MouseKind::ScrollUp,
497 crossterm_event::MouseEventKind::ScrollDown => MouseKind::ScrollDown,
498 crossterm_event::MouseEventKind::ScrollLeft => MouseKind::ScrollLeft,
499 crossterm_event::MouseEventKind::ScrollRight => MouseKind::ScrollRight,
500 };
501
502 Some(Event::Mouse(MouseEvent {
503 kind,
504 x: m.column as u32,
505 y: m.row as u32,
506 modifiers: convert_modifiers(m.modifiers),
507 pixel_x: None,
508 pixel_y: None,
509 }))
510 }
511 crossterm_event::Event::Resize(cols, rows) => Some(Event::Resize(cols as u32, rows as u32)),
512 crossterm_event::Event::Paste(s) => Some(Event::Paste(s)),
513 crossterm_event::Event::FocusGained => Some(Event::FocusGained),
514 crossterm_event::Event::FocusLost => Some(Event::FocusLost),
515 }
516}
517
518#[cfg(test)]
519mod event_constructor_tests {
520 use super::*;
521
522 #[test]
523 fn test_key_char() {
524 let e = Event::key_char('q');
525 if let Event::Key(k) = e {
526 assert!(matches!(k.code, KeyCode::Char('q')));
527 assert_eq!(k.modifiers, KeyModifiers::NONE);
528 assert!(matches!(k.kind, KeyEventKind::Press));
529 } else {
530 panic!("Expected Key event");
531 }
532 }
533
534 #[test]
535 fn test_key() {
536 let e = Event::key(KeyCode::Enter);
537 if let Event::Key(k) = e {
538 assert!(matches!(k.code, KeyCode::Enter));
539 assert_eq!(k.modifiers, KeyModifiers::NONE);
540 assert!(matches!(k.kind, KeyEventKind::Press));
541 } else {
542 panic!("Expected Key event");
543 }
544 }
545
546 #[test]
547 fn test_key_ctrl() {
548 let e = Event::key_ctrl('s');
549 if let Event::Key(k) = e {
550 assert!(matches!(k.code, KeyCode::Char('s')));
551 assert_eq!(k.modifiers, KeyModifiers::CONTROL);
552 assert!(matches!(k.kind, KeyEventKind::Press));
553 } else {
554 panic!("Expected Key event");
555 }
556 }
557
558 #[test]
559 fn test_key_mod() {
560 let modifiers = KeyModifiers(KeyModifiers::SHIFT.0 | KeyModifiers::ALT.0);
561 let e = Event::key_mod(KeyCode::Tab, modifiers);
562 if let Event::Key(k) = e {
563 assert!(matches!(k.code, KeyCode::Tab));
564 assert_eq!(k.modifiers, modifiers);
565 assert!(matches!(k.kind, KeyEventKind::Press));
566 } else {
567 panic!("Expected Key event");
568 }
569 }
570
571 #[test]
572 fn test_resize() {
573 let e = Event::resize(80, 24);
574 assert!(matches!(e, Event::Resize(80, 24)));
575 }
576
577 #[test]
578 fn test_mouse_click() {
579 let e = Event::mouse_click(10, 5);
580 if let Event::Mouse(m) = e {
581 assert!(matches!(m.kind, MouseKind::Down(MouseButton::Left)));
582 assert_eq!(m.x, 10);
583 assert_eq!(m.y, 5);
584 assert_eq!(m.modifiers, KeyModifiers::NONE);
585 } else {
586 panic!("Expected Mouse event");
587 }
588 }
589
590 #[test]
591 fn test_mouse_move() {
592 let e = Event::mouse_move(10, 5);
593 if let Event::Mouse(m) = e {
594 assert!(matches!(m.kind, MouseKind::Moved));
595 assert_eq!(m.x, 10);
596 assert_eq!(m.y, 5);
597 assert_eq!(m.modifiers, KeyModifiers::NONE);
598 } else {
599 panic!("Expected Mouse event");
600 }
601 }
602
603 #[test]
604 fn test_paste() {
605 let e = Event::paste("hello");
606 assert!(matches!(e, Event::Paste(s) if s == "hello"));
607 }
608}