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]
351#[derive(Debug, Clone, PartialEq, Eq)]
352pub struct MouseEvent {
353 pub kind: MouseKind,
355 pub x: u32,
357 pub y: u32,
359 pub modifiers: KeyModifiers,
361 pub pixel_x: Option<u16>,
363 pub pixel_y: Option<u16>,
365}
366
367impl MouseEvent {
368 pub fn new(
370 kind: MouseKind,
371 x: u32,
372 y: u32,
373 modifiers: KeyModifiers,
374 pixel_x: Option<u16>,
375 pixel_y: Option<u16>,
376 ) -> Self {
377 Self {
378 kind,
379 x,
380 y,
381 modifiers,
382 pixel_x,
383 pixel_y,
384 }
385 }
386
387 pub fn is_scroll(&self) -> bool {
389 matches!(
390 self.kind,
391 MouseKind::ScrollUp
392 | MouseKind::ScrollDown
393 | MouseKind::ScrollLeft
394 | MouseKind::ScrollRight
395 )
396 }
397}
398
399#[non_exhaustive]
401#[derive(Debug, Clone, PartialEq, Eq)]
402pub enum MouseKind {
403 Down(MouseButton),
405 Up(MouseButton),
407 Drag(MouseButton),
409 ScrollUp,
411 ScrollDown,
413 ScrollLeft,
415 ScrollRight,
417 Moved,
419}
420
421#[non_exhaustive]
423#[derive(Debug, Clone, Copy, PartialEq, Eq)]
424pub enum MouseButton {
425 Left,
427 Right,
429 Middle,
431}
432
433#[cfg(feature = "crossterm")]
434fn convert_modifiers(modifiers: crossterm_event::KeyModifiers) -> KeyModifiers {
435 let mut out = KeyModifiers::NONE;
436 if modifiers.contains(crossterm_event::KeyModifiers::SHIFT) {
437 out.0 |= KeyModifiers::SHIFT.0;
438 }
439 if modifiers.contains(crossterm_event::KeyModifiers::CONTROL) {
440 out.0 |= KeyModifiers::CONTROL.0;
441 }
442 if modifiers.contains(crossterm_event::KeyModifiers::ALT) {
443 out.0 |= KeyModifiers::ALT.0;
444 }
445 if modifiers.contains(crossterm_event::KeyModifiers::SUPER) {
446 out.0 |= KeyModifiers::SUPER.0;
447 }
448 if modifiers.contains(crossterm_event::KeyModifiers::HYPER) {
449 out.0 |= KeyModifiers::HYPER.0;
450 }
451 if modifiers.contains(crossterm_event::KeyModifiers::META) {
452 out.0 |= KeyModifiers::META.0;
453 }
454 out
455}
456
457#[cfg(feature = "crossterm")]
458fn convert_button(button: crossterm_event::MouseButton) -> MouseButton {
459 match button {
460 crossterm_event::MouseButton::Left => MouseButton::Left,
461 crossterm_event::MouseButton::Right => MouseButton::Right,
462 crossterm_event::MouseButton::Middle => MouseButton::Middle,
463 }
464}
465
466#[cfg(feature = "crossterm")]
471pub(crate) fn from_crossterm(raw: crossterm_event::Event) -> Option<Event> {
472 match raw {
473 crossterm_event::Event::Key(k) => {
474 let code = match k.code {
475 crossterm_event::KeyCode::Char(c) => KeyCode::Char(c),
476 crossterm_event::KeyCode::Enter => KeyCode::Enter,
477 crossterm_event::KeyCode::Backspace => KeyCode::Backspace,
478 crossterm_event::KeyCode::Tab => KeyCode::Tab,
479 crossterm_event::KeyCode::BackTab => KeyCode::BackTab,
480 crossterm_event::KeyCode::Esc => KeyCode::Esc,
481 crossterm_event::KeyCode::Up => KeyCode::Up,
482 crossterm_event::KeyCode::Down => KeyCode::Down,
483 crossterm_event::KeyCode::Left => KeyCode::Left,
484 crossterm_event::KeyCode::Right => KeyCode::Right,
485 crossterm_event::KeyCode::Home => KeyCode::Home,
486 crossterm_event::KeyCode::End => KeyCode::End,
487 crossterm_event::KeyCode::PageUp => KeyCode::PageUp,
488 crossterm_event::KeyCode::PageDown => KeyCode::PageDown,
489 crossterm_event::KeyCode::Delete => KeyCode::Delete,
490 crossterm_event::KeyCode::Insert => KeyCode::Insert,
491 crossterm_event::KeyCode::Null => KeyCode::Null,
492 crossterm_event::KeyCode::CapsLock => KeyCode::CapsLock,
493 crossterm_event::KeyCode::ScrollLock => KeyCode::ScrollLock,
494 crossterm_event::KeyCode::NumLock => KeyCode::NumLock,
495 crossterm_event::KeyCode::PrintScreen => KeyCode::PrintScreen,
496 crossterm_event::KeyCode::Pause => KeyCode::Pause,
497 crossterm_event::KeyCode::Menu => KeyCode::Menu,
498 crossterm_event::KeyCode::KeypadBegin => KeyCode::KeypadBegin,
499 crossterm_event::KeyCode::F(n) => KeyCode::F(n),
500 _ => return None,
501 };
502 let modifiers = convert_modifiers(k.modifiers);
503 let kind = match k.kind {
504 crossterm_event::KeyEventKind::Press => KeyEventKind::Press,
505 crossterm_event::KeyEventKind::Repeat => KeyEventKind::Repeat,
506 crossterm_event::KeyEventKind::Release => KeyEventKind::Release,
507 };
508 Some(Event::Key(KeyEvent {
509 code,
510 modifiers,
511 kind,
512 }))
513 }
514 crossterm_event::Event::Mouse(m) => {
515 let kind = match m.kind {
516 crossterm_event::MouseEventKind::Down(btn) => MouseKind::Down(convert_button(btn)),
517 crossterm_event::MouseEventKind::Up(btn) => MouseKind::Up(convert_button(btn)),
518 crossterm_event::MouseEventKind::Drag(btn) => MouseKind::Drag(convert_button(btn)),
519 crossterm_event::MouseEventKind::Moved => MouseKind::Moved,
520 crossterm_event::MouseEventKind::ScrollUp => MouseKind::ScrollUp,
521 crossterm_event::MouseEventKind::ScrollDown => MouseKind::ScrollDown,
522 crossterm_event::MouseEventKind::ScrollLeft => MouseKind::ScrollLeft,
523 crossterm_event::MouseEventKind::ScrollRight => MouseKind::ScrollRight,
524 };
525
526 Some(Event::Mouse(MouseEvent {
527 kind,
528 x: m.column as u32,
529 y: m.row as u32,
530 modifiers: convert_modifiers(m.modifiers),
531 pixel_x: None,
532 pixel_y: None,
533 }))
534 }
535 crossterm_event::Event::Resize(cols, rows) => Some(Event::Resize(cols as u32, rows as u32)),
536 crossterm_event::Event::Paste(mut s) => {
537 const MAX_PASTE_BYTES: usize = 1 << 20;
542 if s.len() > MAX_PASTE_BYTES {
543 let mut end = MAX_PASTE_BYTES;
544 while end > 0 && !s.is_char_boundary(end) {
545 end -= 1;
546 }
547 s.truncate(end);
548 s.push('…');
549 }
550 Some(Event::Paste(s))
551 }
552 crossterm_event::Event::FocusGained => Some(Event::FocusGained),
553 crossterm_event::Event::FocusLost => Some(Event::FocusLost),
554 }
555}
556
557#[cfg(test)]
558mod event_constructor_tests {
559 use super::*;
560
561 #[test]
562 fn test_key_char() {
563 let e = Event::key_char('q');
564 if let Event::Key(k) = e {
565 assert!(matches!(k.code, KeyCode::Char('q')));
566 assert_eq!(k.modifiers, KeyModifiers::NONE);
567 assert!(matches!(k.kind, KeyEventKind::Press));
568 } else {
569 panic!("Expected Key event");
570 }
571 }
572
573 #[test]
574 fn test_key() {
575 let e = Event::key(KeyCode::Enter);
576 if let Event::Key(k) = e {
577 assert!(matches!(k.code, KeyCode::Enter));
578 assert_eq!(k.modifiers, KeyModifiers::NONE);
579 assert!(matches!(k.kind, KeyEventKind::Press));
580 } else {
581 panic!("Expected Key event");
582 }
583 }
584
585 #[test]
586 fn test_key_ctrl() {
587 let e = Event::key_ctrl('s');
588 if let Event::Key(k) = e {
589 assert!(matches!(k.code, KeyCode::Char('s')));
590 assert_eq!(k.modifiers, KeyModifiers::CONTROL);
591 assert!(matches!(k.kind, KeyEventKind::Press));
592 } else {
593 panic!("Expected Key event");
594 }
595 }
596
597 #[test]
598 fn test_key_mod() {
599 let modifiers = KeyModifiers(KeyModifiers::SHIFT.0 | KeyModifiers::ALT.0);
600 let e = Event::key_mod(KeyCode::Tab, modifiers);
601 if let Event::Key(k) = e {
602 assert!(matches!(k.code, KeyCode::Tab));
603 assert_eq!(k.modifiers, modifiers);
604 assert!(matches!(k.kind, KeyEventKind::Press));
605 } else {
606 panic!("Expected Key event");
607 }
608 }
609
610 #[test]
611 fn test_resize() {
612 let e = Event::resize(80, 24);
613 assert!(matches!(e, Event::Resize(80, 24)));
614 }
615
616 #[test]
617 fn test_mouse_click() {
618 let e = Event::mouse_click(10, 5);
619 if let Event::Mouse(m) = e {
620 assert!(matches!(m.kind, MouseKind::Down(MouseButton::Left)));
621 assert_eq!(m.x, 10);
622 assert_eq!(m.y, 5);
623 assert_eq!(m.modifiers, KeyModifiers::NONE);
624 } else {
625 panic!("Expected Mouse event");
626 }
627 }
628
629 #[test]
630 fn test_mouse_move() {
631 let e = Event::mouse_move(10, 5);
632 if let Event::Mouse(m) = e {
633 assert!(matches!(m.kind, MouseKind::Moved));
634 assert_eq!(m.x, 10);
635 assert_eq!(m.y, 5);
636 assert_eq!(m.modifiers, KeyModifiers::NONE);
637 } else {
638 panic!("Expected Mouse event");
639 }
640 }
641
642 #[test]
643 fn test_paste() {
644 let e = Event::paste("hello");
645 assert!(matches!(e, Event::Paste(s) if s == "hello"));
646 }
647}