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 mouse_drag(x: u32, y: u32) -> Self {
103 Event::Mouse(MouseEvent {
104 kind: MouseKind::Drag(MouseButton::Left),
105 x,
106 y,
107 modifiers: KeyModifiers::NONE,
108 pixel_x: None,
109 pixel_y: None,
110 })
111 }
112
113 pub fn mouse_up(x: u32, y: u32) -> Self {
115 Event::Mouse(MouseEvent {
116 kind: MouseKind::Up(MouseButton::Left),
117 x,
118 y,
119 modifiers: KeyModifiers::NONE,
120 pixel_x: None,
121 pixel_y: None,
122 })
123 }
124
125 pub fn scroll_up(x: u32, y: u32) -> Self {
127 Event::Mouse(MouseEvent {
128 kind: MouseKind::ScrollUp,
129 x,
130 y,
131 modifiers: KeyModifiers::NONE,
132 pixel_x: None,
133 pixel_y: None,
134 })
135 }
136
137 pub fn scroll_down(x: u32, y: u32) -> Self {
139 Event::Mouse(MouseEvent {
140 kind: MouseKind::ScrollDown,
141 x,
142 y,
143 modifiers: KeyModifiers::NONE,
144 pixel_x: None,
145 pixel_y: None,
146 })
147 }
148
149 pub fn key_release(c: char) -> Self {
151 Event::Key(KeyEvent {
152 code: KeyCode::Char(c),
153 modifiers: KeyModifiers::NONE,
154 kind: KeyEventKind::Release,
155 })
156 }
157
158 pub fn paste(text: impl Into<String>) -> Self {
160 Event::Paste(text.into())
161 }
162
163 pub fn as_key(&self) -> Option<&KeyEvent> {
165 match self {
166 Event::Key(k) => Some(k),
167 _ => None,
168 }
169 }
170
171 pub fn as_mouse(&self) -> Option<&MouseEvent> {
173 match self {
174 Event::Mouse(m) => Some(m),
175 _ => None,
176 }
177 }
178
179 pub fn as_resize(&self) -> Option<(u32, u32)> {
181 match self {
182 Event::Resize(w, h) => Some((*w, *h)),
183 _ => None,
184 }
185 }
186
187 pub fn as_paste(&self) -> Option<&str> {
189 match self {
190 Event::Paste(s) => Some(s),
191 _ => None,
192 }
193 }
194
195 pub fn is_key(&self) -> bool {
197 matches!(self, Event::Key(_))
198 }
199
200 pub fn is_mouse(&self) -> bool {
202 matches!(self, Event::Mouse(_))
203 }
204}
205
206#[non_exhaustive]
208#[derive(Debug, Clone, PartialEq, Eq)]
209pub struct KeyEvent {
210 pub code: KeyCode,
212 pub modifiers: KeyModifiers,
214 pub kind: KeyEventKind,
216}
217
218impl KeyEvent {
219 pub fn is_char(&self, c: char) -> bool {
221 self.code == KeyCode::Char(c)
222 && self.modifiers == KeyModifiers::NONE
223 && self.kind == KeyEventKind::Press
224 }
225
226 pub fn is_ctrl_char(&self, c: char) -> bool {
228 self.code == KeyCode::Char(c)
229 && self.modifiers == KeyModifiers::CONTROL
230 && self.kind == KeyEventKind::Press
231 }
232
233 pub fn is_code(&self, code: KeyCode) -> bool {
235 self.code == code
236 && self.modifiers == KeyModifiers::NONE
237 && self.kind == KeyEventKind::Press
238 }
239}
240
241#[non_exhaustive]
243#[derive(Debug, Clone, Copy, PartialEq, Eq)]
244pub enum KeyEventKind {
245 Press,
247 Release,
249 Repeat,
251}
252
253#[non_exhaustive]
259#[derive(Debug, Clone, PartialEq, Eq)]
260pub enum KeyCode {
261 Char(char),
263 Enter,
265 Backspace,
267 Tab,
269 BackTab,
271 Esc,
273 Up,
275 Down,
277 Left,
279 Right,
281 Home,
283 End,
285 PageUp,
287 PageDown,
289 Delete,
291 Insert,
293 Null,
295 CapsLock,
297 ScrollLock,
299 NumLock,
301 PrintScreen,
303 Pause,
305 Menu,
307 KeypadBegin,
309 F(u8),
311}
312
313#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
318pub struct KeyModifiers(pub u8);
319
320impl KeyModifiers {
321 pub const NONE: Self = Self(0);
323 pub const SHIFT: Self = Self(1 << 0);
325 pub const CONTROL: Self = Self(1 << 1);
327 pub const ALT: Self = Self(1 << 2);
329 pub const SUPER: Self = Self(1 << 3);
331 pub const HYPER: Self = Self(1 << 4);
333 pub const META: Self = Self(1 << 5);
335
336 #[inline]
338 pub fn contains(self, other: Self) -> bool {
339 (self.0 & other.0) == other.0
340 }
341}
342
343#[non_exhaustive]
353#[derive(Debug, Clone, PartialEq, Eq)]
354pub struct MouseEvent {
355 pub kind: MouseKind,
357 pub x: u32,
359 pub y: u32,
361 pub modifiers: KeyModifiers,
363 pub pixel_x: Option<u16>,
368 pub pixel_y: Option<u16>,
373}
374
375impl MouseEvent {
376 pub fn new(
378 kind: MouseKind,
379 x: u32,
380 y: u32,
381 modifiers: KeyModifiers,
382 pixel_x: Option<u16>,
383 pixel_y: Option<u16>,
384 ) -> Self {
385 Self {
386 kind,
387 x,
388 y,
389 modifiers,
390 pixel_x,
391 pixel_y,
392 }
393 }
394
395 pub fn is_scroll(&self) -> bool {
397 matches!(
398 self.kind,
399 MouseKind::ScrollUp
400 | MouseKind::ScrollDown
401 | MouseKind::ScrollLeft
402 | MouseKind::ScrollRight
403 )
404 }
405}
406
407#[non_exhaustive]
409#[derive(Debug, Clone, PartialEq, Eq)]
410pub enum MouseKind {
411 Down(MouseButton),
413 Up(MouseButton),
415 Drag(MouseButton),
417 ScrollUp,
419 ScrollDown,
421 ScrollLeft,
423 ScrollRight,
425 Moved,
427}
428
429#[non_exhaustive]
431#[derive(Debug, Clone, Copy, PartialEq, Eq)]
432pub enum MouseButton {
433 Left,
435 Right,
437 Middle,
439}
440
441#[cfg(feature = "crossterm")]
442fn convert_modifiers(modifiers: crossterm_event::KeyModifiers) -> KeyModifiers {
443 let mut out = KeyModifiers::NONE;
444 if modifiers.contains(crossterm_event::KeyModifiers::SHIFT) {
445 out.0 |= KeyModifiers::SHIFT.0;
446 }
447 if modifiers.contains(crossterm_event::KeyModifiers::CONTROL) {
448 out.0 |= KeyModifiers::CONTROL.0;
449 }
450 if modifiers.contains(crossterm_event::KeyModifiers::ALT) {
451 out.0 |= KeyModifiers::ALT.0;
452 }
453 if modifiers.contains(crossterm_event::KeyModifiers::SUPER) {
454 out.0 |= KeyModifiers::SUPER.0;
455 }
456 if modifiers.contains(crossterm_event::KeyModifiers::HYPER) {
457 out.0 |= KeyModifiers::HYPER.0;
458 }
459 if modifiers.contains(crossterm_event::KeyModifiers::META) {
460 out.0 |= KeyModifiers::META.0;
461 }
462 out
463}
464
465#[cfg(feature = "crossterm")]
466fn convert_button(button: crossterm_event::MouseButton) -> MouseButton {
467 match button {
468 crossterm_event::MouseButton::Left => MouseButton::Left,
469 crossterm_event::MouseButton::Right => MouseButton::Right,
470 crossterm_event::MouseButton::Middle => MouseButton::Middle,
471 }
472}
473
474#[cfg(feature = "crossterm")]
479pub(crate) fn from_crossterm(raw: crossterm_event::Event) -> Option<Event> {
480 match raw {
481 crossterm_event::Event::Key(k) => {
482 let code = match k.code {
483 crossterm_event::KeyCode::Char(c) => KeyCode::Char(c),
484 crossterm_event::KeyCode::Enter => KeyCode::Enter,
485 crossterm_event::KeyCode::Backspace => KeyCode::Backspace,
486 crossterm_event::KeyCode::Tab => KeyCode::Tab,
487 crossterm_event::KeyCode::BackTab => KeyCode::BackTab,
488 crossterm_event::KeyCode::Esc => KeyCode::Esc,
489 crossterm_event::KeyCode::Up => KeyCode::Up,
490 crossterm_event::KeyCode::Down => KeyCode::Down,
491 crossterm_event::KeyCode::Left => KeyCode::Left,
492 crossterm_event::KeyCode::Right => KeyCode::Right,
493 crossterm_event::KeyCode::Home => KeyCode::Home,
494 crossterm_event::KeyCode::End => KeyCode::End,
495 crossterm_event::KeyCode::PageUp => KeyCode::PageUp,
496 crossterm_event::KeyCode::PageDown => KeyCode::PageDown,
497 crossterm_event::KeyCode::Delete => KeyCode::Delete,
498 crossterm_event::KeyCode::Insert => KeyCode::Insert,
499 crossterm_event::KeyCode::Null => KeyCode::Null,
500 crossterm_event::KeyCode::CapsLock => KeyCode::CapsLock,
501 crossterm_event::KeyCode::ScrollLock => KeyCode::ScrollLock,
502 crossterm_event::KeyCode::NumLock => KeyCode::NumLock,
503 crossterm_event::KeyCode::PrintScreen => KeyCode::PrintScreen,
504 crossterm_event::KeyCode::Pause => KeyCode::Pause,
505 crossterm_event::KeyCode::Menu => KeyCode::Menu,
506 crossterm_event::KeyCode::KeypadBegin => KeyCode::KeypadBegin,
507 crossterm_event::KeyCode::F(n) => KeyCode::F(n),
508 _ => return None,
509 };
510 let modifiers = convert_modifiers(k.modifiers);
511 let kind = match k.kind {
512 crossterm_event::KeyEventKind::Press => KeyEventKind::Press,
513 crossterm_event::KeyEventKind::Repeat => KeyEventKind::Repeat,
514 crossterm_event::KeyEventKind::Release => KeyEventKind::Release,
515 };
516 Some(Event::Key(KeyEvent {
517 code,
518 modifiers,
519 kind,
520 }))
521 }
522 crossterm_event::Event::Mouse(m) => {
523 let kind = match m.kind {
524 crossterm_event::MouseEventKind::Down(btn) => MouseKind::Down(convert_button(btn)),
525 crossterm_event::MouseEventKind::Up(btn) => MouseKind::Up(convert_button(btn)),
526 crossterm_event::MouseEventKind::Drag(btn) => MouseKind::Drag(convert_button(btn)),
527 crossterm_event::MouseEventKind::Moved => MouseKind::Moved,
528 crossterm_event::MouseEventKind::ScrollUp => MouseKind::ScrollUp,
529 crossterm_event::MouseEventKind::ScrollDown => MouseKind::ScrollDown,
530 crossterm_event::MouseEventKind::ScrollLeft => MouseKind::ScrollLeft,
531 crossterm_event::MouseEventKind::ScrollRight => MouseKind::ScrollRight,
532 };
533
534 Some(Event::Mouse(MouseEvent {
535 kind,
536 x: m.column as u32,
537 y: m.row as u32,
538 modifiers: convert_modifiers(m.modifiers),
539 pixel_x: None,
540 pixel_y: None,
541 }))
542 }
543 crossterm_event::Event::Resize(cols, rows) => Some(Event::Resize(cols as u32, rows as u32)),
544 crossterm_event::Event::Paste(mut s) => {
545 const MAX_PASTE_BYTES: usize = 1 << 20;
550 if s.len() > MAX_PASTE_BYTES {
551 let mut end = MAX_PASTE_BYTES;
552 while end > 0 && !s.is_char_boundary(end) {
553 end -= 1;
554 }
555 s.truncate(end);
556 s.push('…');
557 }
558 Some(Event::Paste(s))
559 }
560 crossterm_event::Event::FocusGained => Some(Event::FocusGained),
561 crossterm_event::Event::FocusLost => Some(Event::FocusLost),
562 }
563}
564
565#[cfg(test)]
566mod event_constructor_tests {
567 use super::*;
568
569 #[test]
570 fn test_key_char() {
571 let e = Event::key_char('q');
572 if let Event::Key(k) = e {
573 assert!(matches!(k.code, KeyCode::Char('q')));
574 assert_eq!(k.modifiers, KeyModifiers::NONE);
575 assert!(matches!(k.kind, KeyEventKind::Press));
576 } else {
577 panic!("Expected Key event");
578 }
579 }
580
581 #[test]
582 fn test_key() {
583 let e = Event::key(KeyCode::Enter);
584 if let Event::Key(k) = e {
585 assert!(matches!(k.code, KeyCode::Enter));
586 assert_eq!(k.modifiers, KeyModifiers::NONE);
587 assert!(matches!(k.kind, KeyEventKind::Press));
588 } else {
589 panic!("Expected Key event");
590 }
591 }
592
593 #[test]
594 fn test_key_ctrl() {
595 let e = Event::key_ctrl('s');
596 if let Event::Key(k) = e {
597 assert!(matches!(k.code, KeyCode::Char('s')));
598 assert_eq!(k.modifiers, KeyModifiers::CONTROL);
599 assert!(matches!(k.kind, KeyEventKind::Press));
600 } else {
601 panic!("Expected Key event");
602 }
603 }
604
605 #[test]
606 fn test_key_mod() {
607 let modifiers = KeyModifiers(KeyModifiers::SHIFT.0 | KeyModifiers::ALT.0);
608 let e = Event::key_mod(KeyCode::Tab, modifiers);
609 if let Event::Key(k) = e {
610 assert!(matches!(k.code, KeyCode::Tab));
611 assert_eq!(k.modifiers, modifiers);
612 assert!(matches!(k.kind, KeyEventKind::Press));
613 } else {
614 panic!("Expected Key event");
615 }
616 }
617
618 #[test]
619 fn test_resize() {
620 let e = Event::resize(80, 24);
621 assert!(matches!(e, Event::Resize(80, 24)));
622 }
623
624 #[test]
625 fn test_mouse_click() {
626 let e = Event::mouse_click(10, 5);
627 if let Event::Mouse(m) = e {
628 assert!(matches!(m.kind, MouseKind::Down(MouseButton::Left)));
629 assert_eq!(m.x, 10);
630 assert_eq!(m.y, 5);
631 assert_eq!(m.modifiers, KeyModifiers::NONE);
632 } else {
633 panic!("Expected Mouse event");
634 }
635 }
636
637 #[test]
638 fn test_mouse_move() {
639 let e = Event::mouse_move(10, 5);
640 if let Event::Mouse(m) = e {
641 assert!(matches!(m.kind, MouseKind::Moved));
642 assert_eq!(m.x, 10);
643 assert_eq!(m.y, 5);
644 assert_eq!(m.modifiers, KeyModifiers::NONE);
645 } else {
646 panic!("Expected Mouse event");
647 }
648 }
649
650 #[test]
651 fn test_paste() {
652 let e = Event::paste("hello");
653 assert!(matches!(e, Event::Paste(s) if s == "hello"));
654 }
655}