Skip to main content

libghostty_vt/
key.rs

1//! Encoding key events into terminal escape sequences,
2//!
3//! Supports both legacy encoding as well as Kitty Keyboard Protocol.
4//!
5//! # Basic Usage
6//!
7//!  1. Create an encoder instance with [`Encoder::new`].
8//!  2. Configure encoder options with the various `Encoder::set_*` methods
9//!     or [`Encoder::set_options_from_terminal`] if you have a [`Terminal`].
10//!  3. For each key event:
11//!     *  Create a key event with [`Event::new`] (or reuse an existing one)
12//!     *  Set event properties (action, key, modifiers, etc.)
13//!     *  Encode with [`Encoder::encode_to_vec`] (with a growable `Vec` buffer)
14//!        or [`Encoder::encode`] (with a fixed byte buffer).
15#![allow(clippy::cast_possible_truncation, reason = "bindgen ain't perfect")]
16use std::mem::MaybeUninit;
17
18use crate::{
19    Error,
20    alloc::{Allocator, Object},
21    error::{Result, from_result, from_result_with_len},
22    ffi,
23    terminal::Terminal,
24};
25
26/// Key encoder that converts key events into terminal escape sequences.
27#[derive(Debug)]
28pub struct Encoder<'alloc>(Object<'alloc, ffi::GhosttyKeyEncoder>);
29
30impl<'alloc> Encoder<'alloc> {
31    /// Create a new key encoder instance.
32    pub fn new() -> Result<Self> {
33        // SAFETY: A NULL allocator is always valid
34        unsafe { Self::new_inner(std::ptr::null()) }
35    }
36
37    /// Create a new key encoder instance with a custom allocator.
38    ///
39    /// See the [crate-level documentation](crate#memory-management-and-lifetimes)
40    /// regarding custom memory management and lifetimes.
41    pub fn new_with_alloc<'ctx: 'alloc, Ctx>(alloc: &'alloc Allocator<'ctx, Ctx>) -> Result<Self> {
42        // SAFETY: Borrow checking should forbid invalid allocators
43        unsafe { Self::new_inner(alloc.to_raw()) }
44    }
45
46    unsafe fn new_inner(alloc: *const ffi::GhosttyAllocator) -> Result<Self> {
47        let mut raw: ffi::GhosttyKeyEncoder_ptr = std::ptr::null_mut();
48        let result = unsafe { ffi::ghostty_key_encoder_new(alloc, &raw mut raw) };
49        from_result(result)?;
50        Ok(Self(Object::new(raw)?))
51    }
52
53    unsafe fn setopt(
54        &mut self,
55        option: ffi::GhosttyKeyEncoderOption,
56        value: *const std::ffi::c_void,
57    ) {
58        unsafe { ffi::ghostty_key_encoder_setopt(self.0.as_raw(), option, value) }
59    }
60
61    /// Encode a key event into a terminal escape sequence.
62    ///
63    /// Converts a key event into the appropriate terminal escape sequence
64    /// based on the encoder's current options.
65    ///
66    /// Not all key events produce output. For example, unmodified modifier
67    /// keys typically don't generate escape sequences. Check the returned
68    /// `Vec` to determine if any data was written.
69    pub fn encode_to_vec(&mut self, event: &Event, vec: &mut Vec<u8>) -> Result<()> {
70        let remaining = vec.capacity() - vec.len();
71
72        let written = match self.encode_to_uninit_buf(event, vec.spare_capacity_mut()) {
73            Ok(v) => Ok(v),
74            Err(Error::OutOfSpace { required }) => {
75                // Retry with more capacity
76                vec.reserve(required - remaining);
77                self.encode_to_uninit_buf(event, vec.spare_capacity_mut())
78            }
79            Err(e) => Err(e),
80        };
81
82        // SAFETY: A successful call to `encode_to_uninit_buf` assures us
83        // that a `written` number of bytes have been initialized.
84        unsafe { vec.set_len(vec.len() + written?) };
85        Ok(())
86    }
87
88    /// Encode a key event into a terminal escape sequence.
89    ///
90    /// Converts a key event into the appropriate terminal escape sequence
91    /// based on the encoder's current options. The sequence is written to
92    /// the provided buffer.
93    ///
94    /// Not all key events produce output. For example, unmodified modifier
95    /// keys typically don't generate escape sequences. Check the returned
96    /// `usize` to determine if any data was written.
97    ///
98    /// If the output buffer is too small, this returns
99    /// `Err(Error::OutOfSpace { required })` where `required` is the required
100    /// buffer size. The caller can then allocate a larger buffer and call
101    /// the method again.
102    pub fn encode(&mut self, event: &Event, buf: &mut [u8]) -> Result<usize> {
103        // SAFETY: It is always safe to reinterpret T as a MaybeUninit<T>.
104        self.encode_to_uninit_buf(event, unsafe {
105            std::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len())
106        })
107    }
108
109    fn encode_to_uninit_buf(
110        &mut self,
111        event: &Event,
112        buf: &mut [MaybeUninit<u8>],
113    ) -> Result<usize> {
114        let mut written: usize = 0;
115        let result = unsafe {
116            ffi::ghostty_key_encoder_encode(
117                self.0.as_raw(),
118                event.inner.as_raw(),
119                buf.as_mut_ptr().cast(),
120                buf.len(),
121                &raw mut written,
122            )
123        };
124        from_result_with_len(result, written)
125    }
126
127    /// Set encoder options from a terminal's current state.
128    ///
129    /// Reads the terminal's current modes and flags and applies them to the
130    /// encoder's options. This sets cursor key application mode, keypad mode,
131    /// alt escape prefix, modifyOtherKeys state, and Kitty keyboard protocol
132    /// flags from the terminal state.
133    ///
134    /// Note that the `macos_option_as_alt` option cannot be determined from
135    /// terminal state and is reset to [`OptionAsAlt::False`] by this call.
136    /// Use [`Encoder::set_macos_option_as_alt`] to set it afterward if needed.
137    pub fn set_options_from_terminal(&mut self, terminal: &Terminal<'_, '_>) -> &mut Self {
138        unsafe {
139            ffi::ghostty_key_encoder_setopt_from_terminal(self.0.as_raw(), terminal.inner.as_raw());
140        }
141        self
142    }
143
144    /// Set terminal DEC mode 1: cursor key application mode.
145    pub fn set_cursor_key_application(&mut self, value: bool) -> &mut Self {
146        unsafe {
147            self.setopt(
148                ffi::GhosttyKeyEncoderOption_GHOSTTY_KEY_ENCODER_OPT_CURSOR_KEY_APPLICATION,
149                std::ptr::from_ref(&value).cast(),
150            );
151        }
152        self
153    }
154    /// Set terminal DEC mode 66: keypad key application mode.
155    pub fn set_keypad_key_application(&mut self, value: bool) -> &mut Self {
156        unsafe {
157            self.setopt(
158                ffi::GhosttyKeyEncoderOption_GHOSTTY_KEY_ENCODER_OPT_KEYPAD_KEY_APPLICATION,
159                std::ptr::from_ref(&value).cast(),
160            );
161        }
162        self
163    }
164    /// Set terminal DEC mode 1035: ignore keypad with numlock.
165    pub fn set_ignore_keypad_with_numlock(&mut self, value: bool) -> &mut Self {
166        unsafe {
167            self.setopt(
168                ffi::GhosttyKeyEncoderOption_GHOSTTY_KEY_ENCODER_OPT_IGNORE_KEYPAD_WITH_NUMLOCK,
169                std::ptr::from_ref(&value).cast(),
170            );
171        }
172        self
173    }
174    /// Set terminal DEC mode 1036: alt sends escape prefix.
175    pub fn set_alt_esc_prefix(&mut self, value: bool) -> &mut Self {
176        unsafe {
177            self.setopt(
178                ffi::GhosttyKeyEncoderOption_GHOSTTY_KEY_ENCODER_OPT_ALT_ESC_PREFIX,
179                std::ptr::from_ref(&value).cast(),
180            );
181        }
182        self
183    }
184    /// Set xterm modifyOtherKeys mode 2.
185    pub fn set_modify_other_keys_state_2(&mut self, value: bool) -> &mut Self {
186        unsafe {
187            self.setopt(
188                ffi::GhosttyKeyEncoderOption_GHOSTTY_KEY_ENCODER_OPT_MODIFY_OTHER_KEYS_STATE_2,
189                std::ptr::from_ref(&value).cast(),
190            );
191        }
192        self
193    }
194    /// Set Kitty keyboard protocol flags.
195    pub fn set_kitty_flags(&mut self, value: KittyKeyFlags) -> &mut Self {
196        let value = value.bits();
197        unsafe {
198            self.setopt(
199                ffi::GhosttyKeyEncoderOption_GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS,
200                std::ptr::from_ref(&value).cast(),
201            );
202        }
203        self
204    }
205    /// Set macOS option-as-alt setting.
206    pub fn set_macos_option_as_alt(&mut self, value: OptionAsAlt) -> &mut Self {
207        unsafe {
208            self.setopt(
209                ffi::GhosttyKeyEncoderOption_GHOSTTY_KEY_ENCODER_OPT_MACOS_OPTION_AS_ALT,
210                std::ptr::from_ref(&value).cast(),
211            );
212        }
213        self
214    }
215}
216
217impl Drop for Encoder<'_> {
218    fn drop(&mut self) {
219        unsafe { ffi::ghostty_key_encoder_free(self.0.as_raw()) }
220    }
221}
222
223/// Keyboard input event containing information about the physical key pressed,
224/// modifiers, and generated text.
225#[derive(Debug)]
226pub struct Event<'alloc> {
227    inner: Object<'alloc, ffi::GhosttyKeyEvent>,
228    text: Option<String>,
229}
230
231impl<'alloc> Event<'alloc> {
232    /// Create a new key event instance.
233    pub fn new() -> Result<Self> {
234        // SAFETY: A NULL allocator is always valid
235        unsafe { Self::new_inner(std::ptr::null()) }
236    }
237
238    /// Create a new key event instance with a custom allocator.
239    ///
240    /// See the [crate-level documentation](crate#memory-management-and-lifetimes)
241    /// regarding custom memory management and lifetimes.
242    pub fn new_with_alloc<'ctx: 'alloc, Ctx>(alloc: &'alloc Allocator<'ctx, Ctx>) -> Result<Self> {
243        // SAFETY: Borrow checking should forbid invalid allocators
244        unsafe { Self::new_inner(alloc.to_raw()) }
245    }
246
247    unsafe fn new_inner(alloc: *const ffi::GhosttyAllocator) -> Result<Self> {
248        let mut raw: ffi::GhosttyKeyEvent_ptr = std::ptr::null_mut();
249        let result = unsafe { ffi::ghostty_key_event_new(alloc, &raw mut raw) };
250        from_result(result)?;
251        Ok(Self {
252            inner: Object::new(raw)?,
253            text: None,
254        })
255    }
256
257    /// Set the key action (press, release, repeat).
258    pub fn set_action(&mut self, action: Action) -> &mut Self {
259        unsafe { ffi::ghostty_key_event_set_action(self.inner.as_raw(), action.into()) }
260        self
261    }
262
263    /// Get the key action (press, release, repeat).
264    #[must_use]
265    pub fn action(&self) -> Action {
266        Action::try_from(unsafe { ffi::ghostty_key_event_get_action(self.inner.as_raw()) })
267            .unwrap_or(Action::Press)
268    }
269
270    /// Set the physical key code.
271    pub fn set_key(&mut self, key: Key) -> &mut Self {
272        unsafe { ffi::ghostty_key_event_set_key(self.inner.as_raw(), key.into()) }
273        self
274    }
275
276    /// Get the physical key code.
277    #[must_use]
278    pub fn key(&self) -> Key {
279        Key::try_from(unsafe { ffi::ghostty_key_event_get_key(self.inner.as_raw()) })
280            .unwrap_or(Key::Unidentified)
281    }
282
283    /// Set the modifier keys bitmask.
284    pub fn set_mods(&mut self, mods: Mods) -> &mut Self {
285        unsafe { ffi::ghostty_key_event_set_mods(self.inner.as_raw(), mods.bits()) }
286        self
287    }
288
289    /// Get the modifier keys bitmask.
290    #[must_use]
291    pub fn mods(&self) -> Mods {
292        Mods::from_bits_retain(unsafe { ffi::ghostty_key_event_get_mods(self.inner.as_raw()) })
293    }
294
295    /// Set the consumed modifiers bitmask.
296    pub fn set_consumed_mods(&mut self, mods: Mods) -> &mut Self {
297        unsafe { ffi::ghostty_key_event_set_consumed_mods(self.inner.as_raw(), mods.bits()) }
298        self
299    }
300
301    /// Get the consumed modifiers bitmask.
302    #[must_use]
303    pub fn consumed_mods(&self) -> Mods {
304        Mods::from_bits_retain(unsafe {
305            ffi::ghostty_key_event_get_consumed_mods(self.inner.as_raw())
306        })
307    }
308
309    /// Set whether the key event is part of a composition sequence.
310    pub fn set_composing(&mut self, composing: bool) -> &mut Self {
311        unsafe { ffi::ghostty_key_event_set_composing(self.inner.as_raw(), composing) }
312        self
313    }
314
315    /// Get whether the key event is part of a composition sequence.
316    #[must_use]
317    pub fn is_composing(&self) -> bool {
318        unsafe { ffi::ghostty_key_event_get_composing(self.inner.as_raw()) }
319    }
320
321    /// Set the UTF-8 text generated by the key event.
322    ///
323    /// The event makes an internal copy of the text since the C API
324    /// may reuse it without any rigid lifetime guarantees.
325    pub fn set_utf8<S: Into<String>>(&mut self, text: Option<S>) -> &mut Self {
326        self.text = text.map(Into::into);
327
328        match &self.text {
329            Some(text) => unsafe {
330                ffi::ghostty_key_event_set_utf8(
331                    self.inner.as_raw(),
332                    text.as_ptr().cast(),
333                    text.len(),
334                );
335            },
336            None => unsafe {
337                ffi::ghostty_key_event_set_utf8(self.inner.as_raw(), std::ptr::null(), 0);
338            },
339        }
340        self
341    }
342
343    /// Get the UTF-8 text generated by the key event.
344    pub fn utf8(&mut self) -> Option<&str> {
345        // We actually sidestep the `ghostty_key_event_get_utf8` method to
346        // avoid unclear lifetimes. See `set_utf8`.
347        self.text.as_deref()
348    }
349
350    /// Set the unshifted Unicode codepoint.
351    pub fn set_unshifted_codepoint(&mut self, codepoint: char) -> &mut Self {
352        unsafe {
353            ffi::ghostty_key_event_set_unshifted_codepoint(self.inner.as_raw(), codepoint.into())
354        }
355        self
356    }
357
358    /// Get the unshifted Unicode codepoint.
359    #[must_use]
360    pub fn unshifted_codepoint(&self) -> char {
361        unsafe {
362            char::from_u32_unchecked(ffi::ghostty_key_event_get_unshifted_codepoint(
363                self.inner.as_raw(),
364            ))
365        }
366    }
367}
368
369impl Drop for Event<'_> {
370    fn drop(&mut self) {
371        unsafe { ffi::ghostty_key_event_free(self.inner.as_raw()) }
372    }
373}
374
375#[repr(u32)]
376#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
377#[non_exhaustive]
378#[expect(missing_docs, reason = "self-explanatory")]
379pub enum Key {
380    Unidentified = 0,
381    Backquote = 1,
382    Backslash = 2,
383    BracketLeft = 3,
384    BracketRight = 4,
385    Comma = 5,
386    Digit0 = 6,
387    Digit1 = 7,
388    Digit2 = 8,
389    Digit3 = 9,
390    Digit4 = 10,
391    Digit5 = 11,
392    Digit6 = 12,
393    Digit7 = 13,
394    Digit8 = 14,
395    Digit9 = 15,
396    Equal = 16,
397    IntlBackslash = 17,
398    IntlRo = 18,
399    IntlYen = 19,
400    A = 20,
401    B = 21,
402    C = 22,
403    D = 23,
404    E = 24,
405    F = 25,
406    G = 26,
407    H = 27,
408    I = 28,
409    J = 29,
410    K = 30,
411    L = 31,
412    M = 32,
413    N = 33,
414    O = 34,
415    P = 35,
416    Q = 36,
417    R = 37,
418    S = 38,
419    T = 39,
420    U = 40,
421    V = 41,
422    W = 42,
423    X = 43,
424    Y = 44,
425    Z = 45,
426    Minus = 46,
427    Period = 47,
428    Quote = 48,
429    Semicolon = 49,
430    Slash = 50,
431    AltLeft = 51,
432    AltRight = 52,
433    Backspace = 53,
434    CapsLock = 54,
435    ContextMenu = 55,
436    ControlLeft = 56,
437    ControlRight = 57,
438    Enter = 58,
439    MetaLeft = 59,
440    MetaRight = 60,
441    ShiftLeft = 61,
442    ShiftRight = 62,
443    Space = 63,
444    Tab = 64,
445    Convert = 65,
446    KanaMode = 66,
447    NonConvert = 67,
448    Delete = 68,
449    End = 69,
450    Help = 70,
451    Home = 71,
452    Insert = 72,
453    PageDown = 73,
454    PageUp = 74,
455    ArrowDown = 75,
456    ArrowLeft = 76,
457    ArrowRight = 77,
458    ArrowUp = 78,
459    NumLock = 79,
460    Numpad0 = 80,
461    Numpad1 = 81,
462    Numpad2 = 82,
463    Numpad3 = 83,
464    Numpad4 = 84,
465    Numpad5 = 85,
466    Numpad6 = 86,
467    Numpad7 = 87,
468    Numpad8 = 88,
469    Numpad9 = 89,
470    NumpadAdd = 90,
471    NumpadBackspace = 91,
472    NumpadClear = 92,
473    NumpadClearEntry = 93,
474    NumpadComma = 94,
475    NumpadDecimal = 95,
476    NumpadDivide = 96,
477    NumpadEnter = 97,
478    NumpadEqual = 98,
479    NumpadMemoryAdd = 99,
480    NumpadMemoryClear = 100,
481    NumpadMemoryRecall = 101,
482    NumpadMemoryStore = 102,
483    NumpadMemorySubtract = 103,
484    NumpadMultiply = 104,
485    NumpadParenLeft = 105,
486    NumpadParenRight = 106,
487    NumpadSubtract = 107,
488    NumpadSeparator = 108,
489    NumpadUp = 109,
490    NumpadDown = 110,
491    NumpadRight = 111,
492    NumpadLeft = 112,
493    NumpadBegin = 113,
494    NumpadHome = 114,
495    NumpadEnd = 115,
496    NumpadInsert = 116,
497    NumpadDelete = 117,
498    NumpadPageUp = 118,
499    NumpadPageDown = 119,
500    Escape = 120,
501    F1 = 121,
502    F2 = 122,
503    F3 = 123,
504    F4 = 124,
505    F5 = 125,
506    F6 = 126,
507    F7 = 127,
508    F8 = 128,
509    F9 = 129,
510    F10 = 130,
511    F11 = 131,
512    F12 = 132,
513    F13 = 133,
514    F14 = 134,
515    F15 = 135,
516    F16 = 136,
517    F17 = 137,
518    F18 = 138,
519    F19 = 139,
520    F20 = 140,
521    F21 = 141,
522    F22 = 142,
523    F23 = 143,
524    F24 = 144,
525    F25 = 145,
526    Fn = 146,
527    FnLock = 147,
528    PrintScreen = 148,
529    ScrollLock = 149,
530    Pause = 150,
531    BrowserBack = 151,
532    BrowserFavorites = 152,
533    BrowserForward = 153,
534    BrowserHome = 154,
535    BrowserRefresh = 155,
536    BrowserSearch = 156,
537    BrowserStop = 157,
538    Eject = 158,
539    LaunchApp1 = 159,
540    LaunchApp2 = 160,
541    LaunchMail = 161,
542    MediaPlayPause = 162,
543    MediaSelect = 163,
544    MediaStop = 164,
545    MediaTrackNext = 165,
546    MediaTrackPrevious = 166,
547    Power = 167,
548    Sleep = 168,
549    AudioVolumeDown = 169,
550    AudioVolumeMute = 170,
551    AudioVolumeUp = 171,
552    WakeUp = 172,
553    Copy = 173,
554    Cut = 174,
555    Paste = 175,
556}
557
558/// Key event action type.
559#[repr(u32)]
560#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
561#[non_exhaustive]
562pub enum Action {
563    /// Key was pressed.
564    Press = ffi::GhosttyKeyAction_GHOSTTY_KEY_ACTION_PRESS,
565    /// Key was released.
566    Release = ffi::GhosttyKeyAction_GHOSTTY_KEY_ACTION_RELEASE,
567    /// Key is being repeated (held down).
568    Repeat = ffi::GhosttyKeyAction_GHOSTTY_KEY_ACTION_REPEAT,
569}
570
571/// macOS option key behavior.
572///
573/// Determines whether the "option" key on macOS is treated as "alt" or not.
574/// See the Ghostty `macos-option-as-alt` configuration option for more details.
575#[repr(u32)]
576#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
577pub enum OptionAsAlt {
578    /// Option key is not treated as alt.
579    False = ffi::GhosttyOptionAsAlt_GHOSTTY_OPTION_AS_ALT_FALSE,
580    /// Option key is treated as alt.
581    True = ffi::GhosttyOptionAsAlt_GHOSTTY_OPTION_AS_ALT_TRUE,
582    /// Only left option key is treated as alt.
583    Left = ffi::GhosttyOptionAsAlt_GHOSTTY_OPTION_AS_ALT_LEFT,
584    /// Only right option key is treated as alt.
585    Right = ffi::GhosttyOptionAsAlt_GHOSTTY_OPTION_AS_ALT_RIGHT,
586}
587
588bitflags::bitflags! {
589    /// Keyboard modifier keys bitmask.
590    ///
591    /// A bitmask representing all keyboard modifiers. This tracks which modifier
592    /// keys are pressed and, where supported by the platform, which side (left or
593    /// right) of each modifier is active.
594    ///
595    /// Modifier side bits are only meaningful when the corresponding modifier bit
596    /// is set. Not all platforms support distinguishing between left and right
597    /// modifier keys and Ghostty is built to expect that some platforms may not
598    /// provide this information.
599    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
600    pub struct Mods: u16 {
601        /// Shift key is pressed.
602        const SHIFT = ffi::GHOSTTY_MODS_SHIFT as u16;
603        /// Alt key is pressed.
604        const ALT = ffi::GHOSTTY_MODS_ALT as u16;
605        /// Control key is pressed.
606        const CTRL = ffi::GHOSTTY_MODS_CTRL as u16;
607        /// Super/Command/Windows key is pressed.
608        const SUPER = ffi::GHOSTTY_MODS_SUPER as u16;
609        /// Caps Lock is active.
610        const CAPS_LOCK = ffi::GHOSTTY_MODS_CAPS_LOCK as u16;
611        /// Num Lock is active.
612        const NUM_LOCK = ffi::GHOSTTY_MODS_NUM_LOCK as u16;
613        /// Right Shift is pressed (unset = left, set = right).
614        ///
615        /// Only valid when [`Mods::SHIFT`] is set.
616        const SHIFT_SIDE = ffi::GHOSTTY_MODS_SHIFT_SIDE as u16;
617        /// Right Alt is pressed (unset = left, set = right).
618        ///
619        /// Only valid when [`Mods::ALT`] is set.
620        const ALT_SIDE = ffi::GHOSTTY_MODS_ALT_SIDE as u16;
621        /// Right Control is pressed (unset = left, set = right).
622        ///
623        /// Only valid when [`Mods::CTRL`] is set.
624        const CTRL_SIDE = ffi::GHOSTTY_MODS_CTRL_SIDE as u16;
625        /// Right Super is pressed (unset = left, set = right).
626        ///
627        /// Only valid when [`Mods::SUPER`] is set.
628        const SUPER_SIDE = ffi::GHOSTTY_MODS_SUPER_SIDE as u16;
629    }
630
631    /// Kitty keyboard protocol flags.
632    ///
633    /// Bitflags representing the various modes of the Kitty keyboard protocol.
634    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
635    pub struct KittyKeyFlags: u8 {
636        /// Kitty keyboard protocol disabled (all flags off).
637        const DISABLED = ffi::GHOSTTY_KITTY_KEY_DISABLED as u8;
638        /// Disambiguate escape codes.
639        const DISAMBIGUATE = ffi::GHOSTTY_KITTY_KEY_DISAMBIGUATE as u8;
640        /// Report key press and release events.
641        const REPORT_EVENTS = ffi::GHOSTTY_KITTY_KEY_REPORT_EVENTS as u8;
642        /// Report alternate key codes.
643        const REPORT_ALTERNATES = ffi::GHOSTTY_KITTY_KEY_REPORT_ALTERNATES as u8;
644        /// Report all key events including those normally handled by the terminal.
645        const REPORT_ALL = ffi::GHOSTTY_KITTY_KEY_REPORT_ALL as u8;
646        /// Report associated text with key events
647        const REPORT_ASSOCIATED = ffi::GHOSTTY_KITTY_KEY_REPORT_ASSOCIATED as u8;
648        /// All Kitty keyboard protocol flags enabled
649        const ALL = ffi::GHOSTTY_KITTY_KEY_ALL as u8;
650    }
651}