1use std::fmt::Debug;
2use std::format;
3use std::io::{self, Cursor, Seek, Write};
4use std::string::{String, ToString};
5
6use bitflags::bitflags;
7
8use crate::{
9 Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers, MediaKeyCode,
10 ModifierDirection, ModifierKeyCode, MouseButton, MouseEvent, MouseEventKind, ScrollDirection,
11};
12
13bitflags! {
14 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
17 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
18 pub struct KittyFlags: u8 {
19 const DISAMBIGUATE_ESCAPE_CODES = 1<<1;
21 const REPORT_EVENT_TYPES = 1<<2;
23 const REPORT_ALTERNATE_KEYS = 1<<3;
28 const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 1<<4;
30
31 }
32}
33
34#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub enum Encoding {
38 Xterm,
40 Kitty(KittyFlags),
42}
43
44fn unsupported_error<T>(event: T) -> io::Result<usize>
45where
46 T: Debug,
47{
48 Err(io::Error::new(
49 io::ErrorKind::Unsupported,
50 format!("Unsupported event: {event:?}"),
51 ))
52}
53
54impl Event {
55 pub fn encode(&self, buf: &mut [u8], encoding: Encoding) -> io::Result<usize> {
74 match encoding {
75 Encoding::Xterm => self.to_escape_sequence(buf),
76 Encoding::Kitty(flags) => self.to_kitty_escape_sequence(buf, flags),
77 }
78 }
79
80 fn to_escape_sequence(&self, buf: &mut [u8]) -> io::Result<usize> {
81 let mut buf = Cursor::new(buf);
82 match self {
83 Self::FocusGained => {
84 buf.write_all(b"\x1B[I")?;
85 Ok(buf.position() as usize)
86 }
87 Self::FocusLost => {
88 buf.write_all(b"\x1B[O")?;
89 Ok(buf.position() as usize)
90 }
91 Self::Key(key_event) => encode_key_event(key_event, &mut buf),
92 Self::Mouse(mouse_event) => encode_mouse_event(mouse_event, &mut buf),
93 Self::Paste(text) => {
94 buf.write_all(b"\x1B[200~")?;
95 buf.write_all(text.as_bytes())?;
96 buf.write_all(b"\x1B[201~")?;
97 Ok(buf.position() as usize)
98 }
99 Self::Resize { .. } => unsupported_error("Resize"),
100 }
101 }
102
103 fn to_kitty_escape_sequence(&self, buf: &mut [u8], flags: KittyFlags) -> io::Result<usize> {
104 match self {
105 Self::Key(key_event) => self.encode_kitty_key_event(buf, key_event, flags),
106 _ => self.to_escape_sequence(buf),
107 }
108 }
109
110 fn encode_kitty_key_event(
111 &self,
112 buf: &mut [u8],
113 key_event: &KeyEvent,
114 flags: KittyFlags,
115 ) -> io::Result<usize> {
116 if !flags.intersects(
117 KittyFlags::DISAMBIGUATE_ESCAPE_CODES | KittyFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
118 ) {
119 return self.to_escape_sequence(buf);
120 }
121
122 if !flags.intersects(KittyFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES)
125 && key_event.kind == KeyEventKind::Press
126 && !key_event.modifiers.intersects(
127 KeyModifiers::CTRL
128 | KeyModifiers::ALT
129 | KeyModifiers::SUPER
130 | KeyModifiers::HYPER
131 | KeyModifiers::META,
132 )
133 && matches!(key_event.code, KeyCode::Char(_))
134 {
135 return self.to_escape_sequence(buf);
136 }
137
138 let key_event = key_event.normalize_case();
139 let mut buf = Cursor::new(buf);
140 buf.write_all(b"\x1B[")?;
141 let mut trailing_char = b'u';
142 let is_keypad = key_event.state.intersects(KeyEventState::KEYPAD);
143
144 if !is_keypad
146 && matches!(
147 key_event.code,
148 KeyCode::Home
149 | KeyCode::End
150 | KeyCode::Delete
151 | KeyCode::Insert
152 | KeyCode::Left
153 | KeyCode::Right
154 | KeyCode::Up
155 | KeyCode::Down
156 | KeyCode::F(1..=12)
157 )
158 {
159 if key_event.kind == KeyEventKind::Press && !matches!(key_event.code, KeyCode::F(1..=4))
160 {
161 buf.set_position(0);
162 let pos = self.to_escape_sequence(buf.get_mut())?;
163 return Ok(pos);
164 }
165 write_keycode_suffix(key_event.code, key_event.modifiers, false, &mut buf)?;
166 let pos = buf.position();
167 trailing_char = buf.get_ref()[pos as usize - 1];
170 let mut add_placeholder = pos == 3;
172
173 if matches!(key_event.code, KeyCode::F(1..=4)) {
174 buf.set_position(pos - 2);
177 if !key_event.modifiers.is_empty() || key_event.kind != KeyEventKind::Press {
178 add_placeholder = true;
181 }
182 } else {
183 buf.set_position(pos - 1);
184 }
185
186 if add_placeholder {
187 buf.write_all(b"1")?;
188 }
189 } else {
190 write_kitty_encoding(key_event, flags, &mut buf)?;
191 }
192 write_kitty_modifiers(key_event, flags, trailing_char, &mut buf)?;
193 Ok(buf.position() as usize)
194 }
195}
196
197fn encode_key_event(key_event: &KeyEvent, buf: &mut Cursor<&mut [u8]>) -> io::Result<usize> {
198 let key_event = key_event.normalize_case();
199 if key_event.kind != KeyEventKind::Press {
200 return unsupported_error(key_event.kind);
201 }
202
203 let is_shift = key_event.modifiers.intersects(KeyModifiers::SHIFT);
204 let is_ctrl = key_event.modifiers.intersects(KeyModifiers::CTRL);
205 let is_alt = key_event.modifiers.intersects(KeyModifiers::ALT);
206
207 if is_alt {
208 match key_event.code {
209 KeyCode::Char(_)
210 | KeyCode::Esc
211 | KeyCode::Backspace
212 | KeyCode::Enter
213 | KeyCode::Tab => {
214 buf.write_all(b"\x1B")?;
215 }
216 _ => {}
217 }
218 }
219 match key_event.code {
220 KeyCode::F(1..=4) => {
221 buf.write_all(b"\x1B")?;
222 write_keycode_suffix(key_event.code, key_event.modifiers, true, buf)?;
223 }
224 KeyCode::Left
225 | KeyCode::Right
226 | KeyCode::Up
227 | KeyCode::Down
228 | KeyCode::Home
229 | KeyCode::End
230 | KeyCode::PageUp
231 | KeyCode::PageDown
232 | KeyCode::Delete
233 | KeyCode::Insert
234 | KeyCode::F(_) => {
235 buf.write_all(b"\x1B[")?;
236 write_keycode_suffix(key_event.code, key_event.modifiers, true, buf)?;
237 }
238 KeyCode::Tab if is_shift => {
239 buf.write_all(b"\x1B[")?;
240 write_keycode_suffix(key_event.code, key_event.modifiers, true, buf)?;
241 }
242 KeyCode::Char(' ') if is_ctrl => {
243 buf.write_all(b"\x00")?;
244 }
245 KeyCode::Char(c @ '4'..='7') if is_ctrl => {
246 buf.write_all(&[c as u8 - b'4' + b'\x1C'])?;
247 }
248 key_code => {
249 let handled = write_keycode_suffix(key_event.code, key_event.modifiers, true, buf)?;
250 if !handled {
251 return unsupported_error(key_code);
252 }
253 }
254 }
255
256 if !key_event.modifiers.is_empty() {
257 write_modifier_prefix(key_event.code, buf)?;
258 }
259
260 if is_ctrl {
261 match key_event.code {
262 KeyCode::Char(' ' | '4'..='7') if is_ctrl => {}
263 KeyCode::Char(c) => {
264 let pos = buf.position() as usize;
265 let base = (c as u8) + 0x1;
266 if base < b'a' {
267 return unsupported_error(key_event);
268 }
269 buf.get_mut()[pos - 1] = base - b'a';
270 }
271 KeyCode::Backspace => {
272 let pos = buf.position() as usize;
273 buf.get_mut()[pos - 1] = b'\x08';
274 }
275 _ => {}
276 }
277 }
278 match key_event.code {
279 KeyCode::Left
280 | KeyCode::Right
281 | KeyCode::Up
282 | KeyCode::Down
283 | KeyCode::Home
284 | KeyCode::End
285 | KeyCode::PageUp
286 | KeyCode::PageDown
287 | KeyCode::Delete
288 | KeyCode::Insert
289 | KeyCode::F(1..=4) => {
290 buf.get_mut()[4] += key_event.modifiers.bits();
291 }
292 KeyCode::F(_) => {
293 buf.get_mut()[5] += key_event.modifiers.bits();
294 }
295 _ => {}
296 }
297 Ok(buf.position() as usize)
298}
299
300fn encode_mouse_event(mouse_event: &MouseEvent, buf: &mut Cursor<&mut [u8]>) -> io::Result<usize> {
301 let mut base = match mouse_event.kind {
302 MouseEventKind::Moved => 35,
303 MouseEventKind::Down(MouseButton::Left | MouseButton::Unknown)
304 | MouseEventKind::Up(MouseButton::Left | MouseButton::Unknown) => 0,
305 MouseEventKind::Down(MouseButton::Middle) | MouseEventKind::Up(MouseButton::Middle) => 1,
306 MouseEventKind::Down(MouseButton::Right) | MouseEventKind::Up(MouseButton::Right) => 2,
307 MouseEventKind::Drag(MouseButton::Left | MouseButton::Unknown) => 32,
308 MouseEventKind::Drag(MouseButton::Middle) => 33,
309 MouseEventKind::Drag(MouseButton::Right) => 34,
310 MouseEventKind::Scroll(ScrollDirection::Down) => 65,
311 MouseEventKind::Scroll(ScrollDirection::Up) => 64,
312 MouseEventKind::Scroll(ScrollDirection::Left) => 66,
313 MouseEventKind::Scroll(ScrollDirection::Right) => 67,
314 };
315 if mouse_event.modifiers.intersects(KeyModifiers::SHIFT) {
316 base += 4;
317 }
318 if mouse_event.modifiers.intersects(KeyModifiers::ALT) {
319 base += 8;
320 }
321 if mouse_event.modifiers.intersects(KeyModifiers::CTRL) {
322 base += 16;
323 }
324 buf.write_all(b"\x1B[<")?;
325 buf.write_all(base.to_string().as_bytes())?;
326 buf.write_all(b";")?;
327 buf.write_all((mouse_event.column + 1).to_string().as_bytes())?;
328 buf.write_all(b";")?;
329 buf.write_all((mouse_event.row + 1).to_string().as_bytes())?;
330
331 if matches!(mouse_event.kind, MouseEventKind::Up(_)) {
332 buf.write_all(b"m")?;
333 } else {
334 buf.write_all(b"M")?;
335 }
336
337 Ok(buf.position() as usize)
338}
339
340fn write_kitty_modifiers(
341 key_event: KeyEvent,
342 flags: KittyFlags,
343 trailing_char: u8,
344 buf: &mut Cursor<&mut [u8]>,
345) -> io::Result<()> {
346 let report_event_types = flags.intersects(KittyFlags::REPORT_EVENT_TYPES);
347 let extra_modifiers = key_event
348 .state
349 .intersection(KeyEventState::CAPS_LOCK | KeyEventState::NUM_LOCK);
350
351 if !key_event.modifiers.is_empty()
352 || !extra_modifiers.is_empty()
353 || (key_event.kind != KeyEventKind::Press && report_event_types)
354 {
355 buf.write_all(b";")?;
356 let modifier_sum = key_event.modifiers.bits() + (extra_modifiers.bits() << 5) + 1;
357 buf.write_all(&modifier_sum.to_string().into_bytes())?;
358 }
359 if report_event_types {
360 match key_event.kind {
361 KeyEventKind::Repeat => {
362 buf.write_all(b":2")?;
363 }
364 KeyEventKind::Release => {
365 buf.write_all(b":3")?;
366 }
367 KeyEventKind::Press => {}
368 };
369 }
370 buf.write_all(&[trailing_char])?;
371 Ok(())
372}
373
374fn write_kitty_encoding(
375 key_event: KeyEvent,
376 flags: KittyFlags,
377 buf: &mut Cursor<&mut [u8]>,
378) -> io::Result<()> {
379 let is_keypad = key_event.state.intersects(KeyEventState::KEYPAD);
380 match key_event.code {
381 KeyCode::CapsLock => {
382 buf.write_all(b"57358")?;
383 }
384 KeyCode::ScrollLock => {
385 buf.write_all(b"57359")?;
386 }
387 KeyCode::NumLock => {
388 buf.write_all(b"57360")?;
389 }
390 KeyCode::PrintScreen => {
391 buf.write_all(b"57361")?;
392 }
393 KeyCode::Pause => {
394 buf.write_all(b"57362")?;
395 }
396 KeyCode::Menu => {
397 buf.write_all(b"57363")?;
398 }
399 KeyCode::F(val @ 13..=35) => {
400 buf.write_all(&(57376 + (val as u16 - 13)).to_string().into_bytes())?;
401 }
402 KeyCode::F(36..) => return unsupported_error(key_event).map(|_| ()),
403 KeyCode::Media(MediaKeyCode::Play) => {
404 buf.write_all(b"57428")?;
405 }
406 KeyCode::Media(MediaKeyCode::Pause) => {
407 buf.write_all(b"57429")?;
408 }
409 KeyCode::Media(MediaKeyCode::PlayPause) => {
410 buf.write_all(b"57430")?;
411 }
412 KeyCode::Media(MediaKeyCode::Reverse) => {
413 buf.write_all(b"57431")?;
414 }
415 KeyCode::Media(MediaKeyCode::Stop) => {
416 buf.write_all(b"57432")?;
417 }
418 KeyCode::Media(MediaKeyCode::FastForward) => {
419 buf.write_all(b"57433")?;
420 }
421 KeyCode::Media(MediaKeyCode::Rewind) => {
422 buf.write_all(b"57434")?;
423 }
424 KeyCode::Media(MediaKeyCode::TrackNext) => {
425 buf.write_all(b"57435")?;
426 }
427 KeyCode::Media(MediaKeyCode::TrackPrevious) => {
428 buf.write_all(b"57436")?;
429 }
430 KeyCode::Media(MediaKeyCode::Record) => {
431 buf.write_all(b"57437")?;
432 }
433 KeyCode::Media(MediaKeyCode::LowerVolume) => {
434 buf.write_all(b"57438")?;
435 }
436 KeyCode::Media(MediaKeyCode::RaiseVolume) => {
437 buf.write_all(b"57439")?;
438 }
439 KeyCode::Media(MediaKeyCode::MuteVolume) => {
440 buf.write_all(b"57440")?;
441 }
442 KeyCode::Modifier(ModifierKeyCode::Shift, ModifierDirection::Left) => {
443 buf.write_all(b"57441")?;
444 }
445 KeyCode::Modifier(ModifierKeyCode::Control, ModifierDirection::Left) => {
446 buf.write_all(b"57442")?;
447 }
448 KeyCode::Modifier(ModifierKeyCode::Alt, ModifierDirection::Left) => {
449 buf.write_all(b"57443")?;
450 }
451 KeyCode::Modifier(ModifierKeyCode::Super, ModifierDirection::Left) => {
452 buf.write_all(b"57444")?;
453 }
454 KeyCode::Modifier(ModifierKeyCode::Hyper, ModifierDirection::Left) => {
455 buf.write_all(b"57445")?;
456 }
457 KeyCode::Modifier(ModifierKeyCode::Meta, ModifierDirection::Left) => {
458 buf.write_all(b"57446")?;
459 }
460 KeyCode::Modifier(ModifierKeyCode::Shift, ModifierDirection::Right) => {
461 buf.write_all(b"57447")?;
462 }
463 KeyCode::Modifier(ModifierKeyCode::Control, ModifierDirection::Right) => {
464 buf.write_all(b"57448")?;
465 }
466 KeyCode::Modifier(ModifierKeyCode::Alt, ModifierDirection::Right) => {
467 buf.write_all(b"57449")?;
468 }
469 KeyCode::Modifier(ModifierKeyCode::Super, ModifierDirection::Right) => {
470 buf.write_all(b"57450")?;
471 }
472 KeyCode::Modifier(ModifierKeyCode::Hyper, ModifierDirection::Right) => {
473 buf.write_all(b"57451")?;
474 }
475 KeyCode::Modifier(ModifierKeyCode::Meta, ModifierDirection::Right) => {
476 buf.write_all(b"57452")?;
477 }
478 KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift, ModifierDirection::Unknown) => {
479 buf.write_all(b"57453")?;
480 }
481 KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift, ModifierDirection::Unknown) => {
482 buf.write_all(b"57454")?;
483 }
484 KeyCode::Char(val @ '0'..='9') if is_keypad => {
485 buf.write_all(&(57399 + (val as u16 - 48)).to_string().into_bytes())?;
486 }
487 KeyCode::Char('.') if is_keypad => {
488 buf.write_all(b"57409")?;
489 }
490 KeyCode::Char('/') if is_keypad => {
491 buf.write_all(b"57410")?;
492 }
493 KeyCode::Char('*') if is_keypad => {
494 buf.write_all(b"57411")?;
495 }
496 KeyCode::Char('-') if is_keypad => {
497 buf.write_all(b"57412")?;
498 }
499 KeyCode::Char('+') if is_keypad => {
500 buf.write_all(b"57413")?;
501 }
502 KeyCode::Enter if is_keypad => {
503 buf.write_all(b"57414")?;
504 }
505 KeyCode::Char('=') if is_keypad => {
506 buf.write_all(b"57415")?;
507 }
508 KeyCode::Char(',') if is_keypad => {
509 buf.write_all(b"57416")?;
510 }
511 KeyCode::Left if is_keypad => {
512 buf.write_all(b"57417")?;
513 }
514 KeyCode::Right if is_keypad => {
515 buf.write_all(b"57418")?;
516 }
517 KeyCode::Up if is_keypad => {
518 buf.write_all(b"57419")?;
519 }
520 KeyCode::Down if is_keypad => {
521 buf.write_all(b"57420")?;
522 }
523 KeyCode::PageUp if is_keypad => {
524 buf.write_all(b"57421")?;
525 }
526 KeyCode::PageDown if is_keypad => {
527 buf.write_all(b"57422")?;
528 }
529 KeyCode::Home if is_keypad => {
530 buf.write_all(b"57423")?;
531 }
532 KeyCode::End if is_keypad => {
533 buf.write_all(b"57424")?;
534 }
535 KeyCode::Insert if is_keypad => {
536 buf.write_all(b"57425")?;
537 }
538 KeyCode::Delete if is_keypad => {
539 buf.write_all(b"57426")?;
540 }
541 KeyCode::KeypadBegin if is_keypad => {
542 buf.write_all(b"57427")?;
543 }
544 KeyCode::Char(c) => {
545 let c = c.to_ascii_lowercase();
547 convert_suffix_code(KeyCode::Char(c), key_event.modifiers, buf)?;
548 if flags.intersects(KittyFlags::REPORT_ALTERNATE_KEYS)
549 && key_event.modifiers.intersects(KeyModifiers::SHIFT)
550 {
551 let upper = c.to_ascii_uppercase();
554 if upper != c {
555 buf.write_all(b":")?;
556 buf.write_all(&(upper as u8).to_string().into_bytes())?;
557 }
558 }
559 }
560 KeyCode::Esc | KeyCode::Enter | KeyCode::Tab | KeyCode::Backspace => {
561 convert_suffix_code(key_event.code, key_event.modifiers, buf)?;
562 }
563 key_code => {
564 write_keycode_suffix(key_code, key_event.modifiers, false, buf)?;
565 }
566 }
567 Ok(())
568}
569
570fn write_keycode_suffix(
571 key_code: KeyCode,
572 modifiers: KeyModifiers,
573 special_back_tab: bool,
574 buf: &mut Cursor<&mut [u8]>,
575) -> io::Result<bool> {
576 match key_code {
577 KeyCode::Backspace => buf.write_all(b"\x7F"),
578 KeyCode::Enter => buf.write_all(b"\r"),
579 KeyCode::Left => buf.write_all(b"D"),
580 KeyCode::Right => buf.write_all(b"C"),
581 KeyCode::Up => buf.write_all(b"A"),
582 KeyCode::Down => buf.write_all(b"B"),
583 KeyCode::Home => buf.write_all(b"H"),
584 KeyCode::End => buf.write_all(b"F"),
585 KeyCode::PageUp => buf.write_all(b"5~"),
586 KeyCode::PageDown => buf.write_all(b"6~"),
587 KeyCode::Tab if modifiers.intersects(KeyModifiers::SHIFT) && special_back_tab => {
588 buf.write_all(b"Z")
589 }
590 KeyCode::Tab => buf.write_all(b"\t"),
591 KeyCode::Delete => buf.write_all(b"3~"),
592 KeyCode::Insert => buf.write_all(b"2~"),
593 KeyCode::F(1) => buf.write_all(b"OP"),
594 KeyCode::F(2) => buf.write_all(b"OQ"),
595 KeyCode::F(3) => buf.write_all(b"OR"),
596 KeyCode::F(4) => buf.write_all(b"OS"),
597 KeyCode::F(5) => buf.write_all(b"15~"),
598 KeyCode::F(6) => buf.write_all(b"17~"),
599 KeyCode::F(7) => buf.write_all(b"18~"),
600 KeyCode::F(8) => buf.write_all(b"19~"),
601 KeyCode::F(9) => buf.write_all(b"20~"),
602 KeyCode::F(10) => buf.write_all(b"21~"),
603 KeyCode::F(11) => buf.write_all(b"23~"),
604 KeyCode::F(12) => buf.write_all(b"24~"),
605 KeyCode::Char(c) => {
606 let pos = buf.position() as usize;
607 let len = c.encode_utf8(&mut buf.get_mut()[pos..]).len();
608 buf.seek_relative(len as i64)
609 }
610 KeyCode::Esc => buf.write_all(b"\x1B"),
611 _ => return Ok(false),
612 }?;
613 Ok(true)
614}
615
616fn write_modifier_prefix(key_code: KeyCode, buf: &mut Cursor<&mut [u8]>) -> io::Result<()> {
617 let pos = buf.position() as usize;
618 let last = buf.get_mut()[pos - 1];
619 match key_code {
620 KeyCode::Left
621 | KeyCode::Right
622 | KeyCode::Up
623 | KeyCode::Down
624 | KeyCode::Home
625 | KeyCode::End => {
626 buf.seek_relative(-1)?;
627 buf.write_all(b"1;1")?;
628 buf.write_all(&[last])?;
629 }
630 KeyCode::F(1..=4) => {
631 buf.seek_relative(-2)?;
632 buf.write_all(b"[1;1")?;
633 buf.write_all(&[last])?;
634 }
635 KeyCode::PageUp | KeyCode::PageDown | KeyCode::Delete | KeyCode::Insert | KeyCode::F(_) => {
636 buf.seek_relative(-1)?;
637 buf.write_all(b";1")?;
638 buf.write_all(&[last])?;
639 }
640 _ => {}
641 }
642 Ok(())
643}
644
645fn convert_suffix_code(
646 key_code: KeyCode,
647 modifiers: KeyModifiers,
648 buf: &mut Cursor<&mut [u8]>,
649) -> io::Result<()> {
650 let old_pos = buf.position() as usize;
651 write_keycode_suffix(key_code, modifiers, false, buf)?;
652 let new_pos = buf.position() as usize;
653 let suffix_bytes = buf.get_ref()[old_pos..new_pos]
654 .iter()
655 .map(|b| b.to_string())
656 .collect::<String>()
657 .into_bytes();
658 buf.seek_relative(old_pos as i64 - new_pos as i64)?;
659 buf.write_all(&suffix_bytes)?;
660 Ok(())
661}