1use std::mem::MaybeUninit;
16
17use crate::{
18 Error,
19 alloc::{Allocator, Object},
20 error::{Result, from_result, from_result_with_len},
21 ffi::{self, KeyEncoderOption as Opt},
22 terminal::Terminal,
23};
24
25#[derive(Debug)]
27pub struct Encoder<'alloc>(Object<'alloc, ffi::KeyEncoderImpl>);
28
29impl<'alloc> Encoder<'alloc> {
30 pub fn new() -> Result<Self> {
32 unsafe { Self::new_inner(std::ptr::null()) }
34 }
35
36 pub fn new_with_alloc<'ctx: 'alloc>(alloc: &'alloc Allocator<'ctx>) -> Result<Self> {
41 unsafe { Self::new_inner(alloc.to_raw()) }
43 }
44
45 unsafe fn new_inner(alloc: *const ffi::Allocator) -> Result<Self> {
46 let mut raw: ffi::KeyEncoder = std::ptr::null_mut();
47 let result = unsafe { ffi::ghostty_key_encoder_new(alloc, &raw mut raw) };
48 from_result(result)?;
49 Ok(Self(Object::new(raw)?))
50 }
51
52 unsafe fn setopt(
53 &mut self,
54 option: ffi::KeyEncoderOption::Type,
55 value: *const std::ffi::c_void,
56 ) {
57 unsafe { ffi::ghostty_key_encoder_setopt(self.0.as_raw(), option, value) }
58 }
59
60 pub fn encode_to_vec(&mut self, event: &Event, vec: &mut Vec<u8>) -> Result<()> {
69 let remaining = vec.capacity() - vec.len();
70
71 let written = match self.encode_to_uninit_buf(event, vec.spare_capacity_mut()) {
72 Ok(v) => Ok(v),
73 Err(Error::OutOfSpace { required }) => {
74 vec.reserve(required - remaining);
76 self.encode_to_uninit_buf(event, vec.spare_capacity_mut())
77 }
78 Err(e) => Err(e),
79 };
80
81 unsafe { vec.set_len(vec.len() + written?) };
84 Ok(())
85 }
86
87 pub fn encode(&mut self, event: &Event, buf: &mut [u8]) -> Result<usize> {
102 self.encode_to_uninit_buf(event, unsafe {
104 std::slice::from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len())
105 })
106 }
107
108 fn encode_to_uninit_buf(
109 &mut self,
110 event: &Event,
111 buf: &mut [MaybeUninit<u8>],
112 ) -> Result<usize> {
113 let mut written: usize = 0;
114 let result = unsafe {
115 ffi::ghostty_key_encoder_encode(
116 self.0.as_raw(),
117 event.inner.as_raw(),
118 buf.as_mut_ptr().cast(),
119 buf.len(),
120 &raw mut written,
121 )
122 };
123 from_result_with_len(result, written)
124 }
125
126 pub fn set_options_from_terminal(&mut self, terminal: &Terminal<'_, '_>) -> &mut Self {
137 unsafe {
138 ffi::ghostty_key_encoder_setopt_from_terminal(self.0.as_raw(), terminal.inner.as_raw());
139 }
140 self
141 }
142
143 pub fn set_cursor_key_application(&mut self, value: bool) -> &mut Self {
145 unsafe {
146 self.setopt(
147 Opt::CURSOR_KEY_APPLICATION,
148 std::ptr::from_ref(&value).cast(),
149 );
150 }
151 self
152 }
153 pub fn set_keypad_key_application(&mut self, value: bool) -> &mut Self {
155 unsafe {
156 self.setopt(
157 Opt::KEYPAD_KEY_APPLICATION,
158 std::ptr::from_ref(&value).cast(),
159 );
160 }
161 self
162 }
163 pub fn set_ignore_keypad_with_numlock(&mut self, value: bool) -> &mut Self {
165 unsafe {
166 self.setopt(
167 Opt::IGNORE_KEYPAD_WITH_NUMLOCK,
168 std::ptr::from_ref(&value).cast(),
169 );
170 }
171 self
172 }
173 pub fn set_alt_esc_prefix(&mut self, value: bool) -> &mut Self {
175 unsafe {
176 self.setopt(Opt::ALT_ESC_PREFIX, std::ptr::from_ref(&value).cast());
177 }
178 self
179 }
180 pub fn set_modify_other_keys_state_2(&mut self, value: bool) -> &mut Self {
182 unsafe {
183 self.setopt(
184 Opt::MODIFY_OTHER_KEYS_STATE_2,
185 std::ptr::from_ref(&value).cast(),
186 );
187 }
188 self
189 }
190 pub fn set_kitty_flags(&mut self, value: KittyKeyFlags) -> &mut Self {
192 let value = value.bits();
193 unsafe {
194 self.setopt(Opt::KITTY_FLAGS, std::ptr::from_ref(&value).cast());
195 }
196 self
197 }
198 pub fn set_macos_option_as_alt(&mut self, value: OptionAsAlt) -> &mut Self {
200 unsafe {
201 self.setopt(Opt::MACOS_OPTION_AS_ALT, std::ptr::from_ref(&value).cast());
202 }
203 self
204 }
205 pub fn set_backarrow_key_mode(&mut self, value: bool) -> &mut Self {
212 unsafe {
213 self.setopt(Opt::BACKARROW_KEY_MODE, std::ptr::from_ref(&value).cast());
214 }
215 self
216 }
217}
218
219impl Drop for Encoder<'_> {
220 fn drop(&mut self) {
221 unsafe { ffi::ghostty_key_encoder_free(self.0.as_raw()) }
222 }
223}
224
225#[derive(Debug)]
228pub struct Event<'alloc> {
229 inner: Object<'alloc, ffi::KeyEventImpl>,
230 text: Option<String>,
231}
232
233impl<'alloc> Event<'alloc> {
234 pub fn new() -> Result<Self> {
236 unsafe { Self::new_inner(std::ptr::null()) }
238 }
239
240 pub fn new_with_alloc<'ctx: 'alloc>(alloc: &'alloc Allocator<'ctx>) -> Result<Self> {
245 unsafe { Self::new_inner(alloc.to_raw()) }
247 }
248
249 unsafe fn new_inner(alloc: *const ffi::Allocator) -> Result<Self> {
250 let mut raw: ffi::KeyEvent = std::ptr::null_mut();
251 let result = unsafe { ffi::ghostty_key_event_new(alloc, &raw mut raw) };
252 from_result(result)?;
253 Ok(Self {
254 inner: Object::new(raw)?,
255 text: None,
256 })
257 }
258
259 pub fn set_action(&mut self, action: Action) -> &mut Self {
261 unsafe { ffi::ghostty_key_event_set_action(self.inner.as_raw(), action.into()) }
262 self
263 }
264
265 #[must_use]
267 pub fn action(&self) -> Action {
268 Action::try_from(unsafe { ffi::ghostty_key_event_get_action(self.inner.as_raw()) })
269 .unwrap_or(Action::Press)
270 }
271
272 pub fn set_key(&mut self, key: Key) -> &mut Self {
274 unsafe { ffi::ghostty_key_event_set_key(self.inner.as_raw(), key.into()) }
275 self
276 }
277
278 #[must_use]
280 pub fn key(&self) -> Key {
281 Key::try_from(unsafe { ffi::ghostty_key_event_get_key(self.inner.as_raw()) })
282 .unwrap_or(Key::Unidentified)
283 }
284
285 pub fn set_mods(&mut self, mods: Mods) -> &mut Self {
287 unsafe { ffi::ghostty_key_event_set_mods(self.inner.as_raw(), mods.bits()) }
288 self
289 }
290
291 #[must_use]
293 pub fn mods(&self) -> Mods {
294 Mods::from_bits_retain(unsafe { ffi::ghostty_key_event_get_mods(self.inner.as_raw()) })
295 }
296
297 pub fn set_consumed_mods(&mut self, mods: Mods) -> &mut Self {
299 unsafe { ffi::ghostty_key_event_set_consumed_mods(self.inner.as_raw(), mods.bits()) }
300 self
301 }
302
303 #[must_use]
305 pub fn consumed_mods(&self) -> Mods {
306 Mods::from_bits_retain(unsafe {
307 ffi::ghostty_key_event_get_consumed_mods(self.inner.as_raw())
308 })
309 }
310
311 pub fn set_composing(&mut self, composing: bool) -> &mut Self {
313 unsafe { ffi::ghostty_key_event_set_composing(self.inner.as_raw(), composing) }
314 self
315 }
316
317 #[must_use]
319 pub fn is_composing(&self) -> bool {
320 unsafe { ffi::ghostty_key_event_get_composing(self.inner.as_raw()) }
321 }
322
323 pub fn set_utf8<S: Into<String>>(&mut self, text: Option<S>) -> &mut Self {
334 self.text = text.map(Into::into);
335
336 match &self.text {
337 Some(text) => unsafe {
338 ffi::ghostty_key_event_set_utf8(
339 self.inner.as_raw(),
340 text.as_ptr().cast(),
341 text.len(),
342 );
343 },
344 None => unsafe {
345 ffi::ghostty_key_event_set_utf8(self.inner.as_raw(), std::ptr::null(), 0);
346 },
347 }
348 self
349 }
350
351 pub fn utf8(&mut self) -> Option<&str> {
353 self.text.as_deref()
356 }
357
358 pub fn set_unshifted_codepoint(&mut self, codepoint: char) -> &mut Self {
360 unsafe {
361 ffi::ghostty_key_event_set_unshifted_codepoint(self.inner.as_raw(), codepoint.into());
362 }
363 self
364 }
365
366 #[must_use]
368 pub fn unshifted_codepoint(&self) -> char {
369 unsafe {
370 char::from_u32_unchecked(ffi::ghostty_key_event_get_unshifted_codepoint(
371 self.inner.as_raw(),
372 ))
373 }
374 }
375}
376
377impl Drop for Event<'_> {
378 fn drop(&mut self) {
379 unsafe { ffi::ghostty_key_event_free(self.inner.as_raw()) }
380 }
381}
382
383#[repr(u32)]
384#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
385#[non_exhaustive]
386#[expect(missing_docs, reason = "self-explanatory")]
387pub enum Key {
388 Unidentified = 0,
389 Backquote = 1,
390 Backslash = 2,
391 BracketLeft = 3,
392 BracketRight = 4,
393 Comma = 5,
394 Digit0 = 6,
395 Digit1 = 7,
396 Digit2 = 8,
397 Digit3 = 9,
398 Digit4 = 10,
399 Digit5 = 11,
400 Digit6 = 12,
401 Digit7 = 13,
402 Digit8 = 14,
403 Digit9 = 15,
404 Equal = 16,
405 IntlBackslash = 17,
406 IntlRo = 18,
407 IntlYen = 19,
408 A = 20,
409 B = 21,
410 C = 22,
411 D = 23,
412 E = 24,
413 F = 25,
414 G = 26,
415 H = 27,
416 I = 28,
417 J = 29,
418 K = 30,
419 L = 31,
420 M = 32,
421 N = 33,
422 O = 34,
423 P = 35,
424 Q = 36,
425 R = 37,
426 S = 38,
427 T = 39,
428 U = 40,
429 V = 41,
430 W = 42,
431 X = 43,
432 Y = 44,
433 Z = 45,
434 Minus = 46,
435 Period = 47,
436 Quote = 48,
437 Semicolon = 49,
438 Slash = 50,
439 AltLeft = 51,
440 AltRight = 52,
441 Backspace = 53,
442 CapsLock = 54,
443 ContextMenu = 55,
444 ControlLeft = 56,
445 ControlRight = 57,
446 Enter = 58,
447 MetaLeft = 59,
448 MetaRight = 60,
449 ShiftLeft = 61,
450 ShiftRight = 62,
451 Space = 63,
452 Tab = 64,
453 Convert = 65,
454 KanaMode = 66,
455 NonConvert = 67,
456 Delete = 68,
457 End = 69,
458 Help = 70,
459 Home = 71,
460 Insert = 72,
461 PageDown = 73,
462 PageUp = 74,
463 ArrowDown = 75,
464 ArrowLeft = 76,
465 ArrowRight = 77,
466 ArrowUp = 78,
467 NumLock = 79,
468 Numpad0 = 80,
469 Numpad1 = 81,
470 Numpad2 = 82,
471 Numpad3 = 83,
472 Numpad4 = 84,
473 Numpad5 = 85,
474 Numpad6 = 86,
475 Numpad7 = 87,
476 Numpad8 = 88,
477 Numpad9 = 89,
478 NumpadAdd = 90,
479 NumpadBackspace = 91,
480 NumpadClear = 92,
481 NumpadClearEntry = 93,
482 NumpadComma = 94,
483 NumpadDecimal = 95,
484 NumpadDivide = 96,
485 NumpadEnter = 97,
486 NumpadEqual = 98,
487 NumpadMemoryAdd = 99,
488 NumpadMemoryClear = 100,
489 NumpadMemoryRecall = 101,
490 NumpadMemoryStore = 102,
491 NumpadMemorySubtract = 103,
492 NumpadMultiply = 104,
493 NumpadParenLeft = 105,
494 NumpadParenRight = 106,
495 NumpadSubtract = 107,
496 NumpadSeparator = 108,
497 NumpadUp = 109,
498 NumpadDown = 110,
499 NumpadRight = 111,
500 NumpadLeft = 112,
501 NumpadBegin = 113,
502 NumpadHome = 114,
503 NumpadEnd = 115,
504 NumpadInsert = 116,
505 NumpadDelete = 117,
506 NumpadPageUp = 118,
507 NumpadPageDown = 119,
508 Escape = 120,
509 F1 = 121,
510 F2 = 122,
511 F3 = 123,
512 F4 = 124,
513 F5 = 125,
514 F6 = 126,
515 F7 = 127,
516 F8 = 128,
517 F9 = 129,
518 F10 = 130,
519 F11 = 131,
520 F12 = 132,
521 F13 = 133,
522 F14 = 134,
523 F15 = 135,
524 F16 = 136,
525 F17 = 137,
526 F18 = 138,
527 F19 = 139,
528 F20 = 140,
529 F21 = 141,
530 F22 = 142,
531 F23 = 143,
532 F24 = 144,
533 F25 = 145,
534 Fn = 146,
535 FnLock = 147,
536 PrintScreen = 148,
537 ScrollLock = 149,
538 Pause = 150,
539 BrowserBack = 151,
540 BrowserFavorites = 152,
541 BrowserForward = 153,
542 BrowserHome = 154,
543 BrowserRefresh = 155,
544 BrowserSearch = 156,
545 BrowserStop = 157,
546 Eject = 158,
547 LaunchApp1 = 159,
548 LaunchApp2 = 160,
549 LaunchMail = 161,
550 MediaPlayPause = 162,
551 MediaSelect = 163,
552 MediaStop = 164,
553 MediaTrackNext = 165,
554 MediaTrackPrevious = 166,
555 Power = 167,
556 Sleep = 168,
557 AudioVolumeDown = 169,
558 AudioVolumeMute = 170,
559 AudioVolumeUp = 171,
560 WakeUp = 172,
561 Copy = 173,
562 Cut = 174,
563 Paste = 175,
564}
565
566#[repr(u32)]
568#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
569#[non_exhaustive]
570pub enum Action {
571 Press = ffi::KeyAction::PRESS,
573 Release = ffi::KeyAction::RELEASE,
575 Repeat = ffi::KeyAction::REPEAT,
577}
578
579#[repr(u32)]
584#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
585pub enum OptionAsAlt {
586 False = ffi::OptionAsAlt::FALSE,
588 True = ffi::OptionAsAlt::TRUE,
590 Left = ffi::OptionAsAlt::LEFT,
592 Right = ffi::OptionAsAlt::RIGHT,
594}
595
596bitflags::bitflags! {
597 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
608 pub struct Mods: u16 {
609 const SHIFT = ffi::MODS_SHIFT;
611 const ALT = ffi::MODS_ALT;
613 const CTRL = ffi::MODS_CTRL;
615 const SUPER = ffi::MODS_SUPER;
617 const CAPS_LOCK = ffi::MODS_CAPS_LOCK;
619 const NUM_LOCK = ffi::MODS_NUM_LOCK;
621 const SHIFT_SIDE = ffi::MODS_SHIFT_SIDE;
625 const ALT_SIDE = ffi::MODS_ALT_SIDE;
629 const CTRL_SIDE = ffi::MODS_CTRL_SIDE;
633 const SUPER_SIDE = ffi::MODS_SUPER_SIDE;
637 }
638
639 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
643 pub struct KittyKeyFlags: u8 {
644 const DISABLED = ffi::KITTY_KEY_DISABLED;
646 const DISAMBIGUATE = ffi::KITTY_KEY_DISAMBIGUATE;
648 const REPORT_EVENTS = ffi::KITTY_KEY_REPORT_EVENTS;
650 const REPORT_ALTERNATES = ffi::KITTY_KEY_REPORT_ALTERNATES;
652 const REPORT_ALL = ffi::KITTY_KEY_REPORT_ALL;
654 const REPORT_ASSOCIATED = ffi::KITTY_KEY_REPORT_ASSOCIATED;
656 const ALL = ffi::KITTY_KEY_ALL;
658 }
659}