forked from
mitchellh.com/go-libghostty
Go bindings for libghostty-vt.
1package libghostty
2
3// Key encoder — encodes key events into terminal escape sequences.
4// Wraps the C APIs from key/encoder.h.
5
6/*
7#include <ghostty/vt.h>
8*/
9import "C"
10
11import "unsafe"
12
13// KeyEncoder encodes key events into terminal escape sequences,
14// supporting both legacy encoding and the Kitty Keyboard Protocol.
15// It maintains mutable encoding options and is not safe for concurrent
16// use.
17//
18// Basic usage:
19// 1. Create an encoder with NewKeyEncoder.
20// 2. Configure options with SetOpt* methods or SetOptFromTerminal.
21// 3. Create key events, encode them with Encode, and free them.
22// 4. Free the encoder with Close when done.
23//
24// C: GhosttyKeyEncoder
25type KeyEncoder struct {
26 ptr C.GhosttyKeyEncoder
27}
28
29// KittyKeyFlags is a bitmask of Kitty keyboard protocol flags.
30// C: GhosttyKittyKeyFlags
31type KittyKeyFlags uint8
32
33const (
34 // KittyKeyDisabled disables the Kitty keyboard protocol (all flags off).
35 KittyKeyDisabled KittyKeyFlags = C.GHOSTTY_KITTY_KEY_DISABLED
36
37 // KittyKeyDisambiguate enables disambiguating escape codes.
38 KittyKeyDisambiguate KittyKeyFlags = C.GHOSTTY_KITTY_KEY_DISAMBIGUATE
39
40 // KittyKeyReportEvents enables reporting key press and release events.
41 KittyKeyReportEvents KittyKeyFlags = C.GHOSTTY_KITTY_KEY_REPORT_EVENTS
42
43 // KittyKeyReportAlternates enables reporting alternate key codes.
44 KittyKeyReportAlternates KittyKeyFlags = C.GHOSTTY_KITTY_KEY_REPORT_ALTERNATES
45
46 // KittyKeyReportAll reports all key events including those normally
47 // handled by the terminal.
48 KittyKeyReportAll KittyKeyFlags = C.GHOSTTY_KITTY_KEY_REPORT_ALL
49
50 // KittyKeyReportAssociated reports associated text with key events.
51 KittyKeyReportAssociated KittyKeyFlags = C.GHOSTTY_KITTY_KEY_REPORT_ASSOCIATED
52
53 // KittyKeyAll enables all Kitty keyboard protocol flags.
54 KittyKeyAll KittyKeyFlags = C.GHOSTTY_KITTY_KEY_ALL
55)
56
57// OptionAsAlt determines whether the macOS "option" key is treated as
58// "alt".
59//
60// C: GhosttyOptionAsAlt
61type OptionAsAlt int
62
63const (
64 // OptionAsAltFalse means the option key is not treated as alt.
65 OptionAsAltFalse OptionAsAlt = C.GHOSTTY_OPTION_AS_ALT_FALSE
66
67 // OptionAsAltTrue means the option key is treated as alt.
68 OptionAsAltTrue OptionAsAlt = C.GHOSTTY_OPTION_AS_ALT_TRUE
69
70 // OptionAsAltLeft means only the left option key is treated as alt.
71 OptionAsAltLeft OptionAsAlt = C.GHOSTTY_OPTION_AS_ALT_LEFT
72
73 // OptionAsAltRight means only the right option key is treated as alt.
74 OptionAsAltRight OptionAsAlt = C.GHOSTTY_OPTION_AS_ALT_RIGHT
75)
76
77// KeyEncoderOption identifies an encoder configuration option for use
78// with SetOpt.
79//
80// C: GhosttyKeyEncoderOption
81type KeyEncoderOption int
82
83const (
84 // KeyEncoderOptCursorKeyApplication sets DEC mode 1: cursor key
85 // application mode (value: bool).
86 KeyEncoderOptCursorKeyApplication KeyEncoderOption = C.GHOSTTY_KEY_ENCODER_OPT_CURSOR_KEY_APPLICATION
87
88 // KeyEncoderOptKeypadKeyApplication sets DEC mode 66: keypad key
89 // application mode (value: bool).
90 KeyEncoderOptKeypadKeyApplication KeyEncoderOption = C.GHOSTTY_KEY_ENCODER_OPT_KEYPAD_KEY_APPLICATION
91
92 // KeyEncoderOptIgnoreKeypadWithNumlock sets DEC mode 1035: ignore
93 // keypad with numlock (value: bool).
94 KeyEncoderOptIgnoreKeypadWithNumlock KeyEncoderOption = C.GHOSTTY_KEY_ENCODER_OPT_IGNORE_KEYPAD_WITH_NUMLOCK
95
96 // KeyEncoderOptAltEscPrefix sets DEC mode 1036: alt sends escape
97 // prefix (value: bool).
98 KeyEncoderOptAltEscPrefix KeyEncoderOption = C.GHOSTTY_KEY_ENCODER_OPT_ALT_ESC_PREFIX
99
100 // KeyEncoderOptModifyOtherKeysState2 sets xterm modifyOtherKeys
101 // mode 2 (value: bool).
102 KeyEncoderOptModifyOtherKeysState2 KeyEncoderOption = C.GHOSTTY_KEY_ENCODER_OPT_MODIFY_OTHER_KEYS_STATE_2
103
104 // KeyEncoderOptKittyFlags sets Kitty keyboard protocol flags
105 // (value: KittyKeyFlags bitmask).
106 KeyEncoderOptKittyFlags KeyEncoderOption = C.GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS
107
108 // KeyEncoderOptMacOSOptionAsAlt sets the macOS option-as-alt
109 // setting (value: OptionAsAlt).
110 KeyEncoderOptMacOSOptionAsAlt KeyEncoderOption = C.GHOSTTY_KEY_ENCODER_OPT_MACOS_OPTION_AS_ALT
111
112 // KeyEncoderOptBackarrowKeyMode sets backarrow key mode (value: bool).
113 // When false (default), backspace emits 0x7f; when true, 0x08.
114 KeyEncoderOptBackarrowKeyMode KeyEncoderOption = C.GHOSTTY_KEY_ENCODER_OPT_BACKARROW_KEY_MODE
115)
116
117// NewKeyEncoder creates a new key encoder with default options.
118// The encoder must be freed with Close when no longer needed.
119func NewKeyEncoder() (*KeyEncoder, error) {
120 var ptr C.GhosttyKeyEncoder
121 if err := resultError(C.ghostty_key_encoder_new(nil, &ptr)); err != nil {
122 return nil, err
123 }
124 return &KeyEncoder{ptr: ptr}, nil
125}
126
127// Close frees the underlying key encoder handle. After this call,
128// the encoder must not be used.
129func (enc *KeyEncoder) Close() {
130 C.ghostty_key_encoder_free(enc.ptr)
131}
132
133// SetOptBool sets a boolean encoder option. Use this for options
134// that accept a bool value (most encoder options).
135func (enc *KeyEncoder) SetOptBool(opt KeyEncoderOption, val bool) {
136 v := C.bool(val)
137 C.ghostty_key_encoder_setopt(enc.ptr, C.GhosttyKeyEncoderOption(opt), unsafe.Pointer(&v))
138}
139
140// SetOptKittyFlags sets the Kitty keyboard protocol flags on the
141// encoder.
142func (enc *KeyEncoder) SetOptKittyFlags(flags KittyKeyFlags) {
143 v := C.GhosttyKittyKeyFlags(flags)
144 C.ghostty_key_encoder_setopt(enc.ptr, C.GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS, unsafe.Pointer(&v))
145}
146
147// SetOptOptionAsAlt sets the macOS option-as-alt behavior on the
148// encoder.
149func (enc *KeyEncoder) SetOptOptionAsAlt(val OptionAsAlt) {
150 v := C.GhosttyOptionAsAlt(val)
151 C.ghostty_key_encoder_setopt(enc.ptr, C.GHOSTTY_KEY_ENCODER_OPT_MACOS_OPTION_AS_ALT, unsafe.Pointer(&v))
152}
153
154// SetOptFromTerminal reads the terminal's current modes and flags and
155// applies them to the encoder's options. This sets cursor key
156// application mode, keypad mode, alt escape prefix, modifyOtherKeys
157// state, and Kitty keyboard protocol flags from the terminal state.
158//
159// Note that the macOS option-as-alt option cannot be determined from
160// terminal state and is reset to OptionAsAltFalse by this call. Use
161// SetOptOptionAsAlt afterward if needed. The caller must serialize
162// access to both the encoder and the terminal during this call.
163func (enc *KeyEncoder) SetOptFromTerminal(t *Terminal) {
164 C.ghostty_key_encoder_setopt_from_terminal(enc.ptr, t.ptr)
165}
166
167// Encode encodes a key event into a terminal escape sequence and
168// returns the result as a byte slice. Not all key events produce
169// output (e.g. unmodified modifier keys); in that case, a nil slice
170// and nil error are returned.
171func (enc *KeyEncoder) Encode(event *KeyEvent) ([]byte, error) {
172 // Most escape sequences fit in 128 bytes. Try with a stack buffer
173 // first; fall back to a larger heap allocation if needed.
174 var buf [128]byte
175 var outLen C.size_t
176 result := C.ghostty_key_encoder_encode(
177 enc.ptr,
178 event.ptr,
179 (*C.char)(unsafe.Pointer(&buf[0])),
180 C.size_t(len(buf)),
181 &outLen,
182 )
183
184 if result == C.GHOSTTY_SUCCESS {
185 if outLen == 0 {
186 return nil, nil
187 }
188 out := make([]byte, outLen)
189 copy(out, buf[:outLen])
190 return out, nil
191 }
192
193 if result == C.GHOSTTY_OUT_OF_SPACE {
194 // outLen contains the required buffer size.
195 dynBuf := make([]byte, outLen)
196 var written C.size_t
197 if err := resultError(C.ghostty_key_encoder_encode(
198 enc.ptr,
199 event.ptr,
200 (*C.char)(unsafe.Pointer(&dynBuf[0])),
201 outLen,
202 &written,
203 )); err != nil {
204 return nil, err
205 }
206 if written == 0 {
207 return nil, nil
208 }
209 return dynBuf[:written], nil
210 }
211
212 return nil, &Error{Result: Result(result)}
213}