1/**
2 * @file encoder.h
3 *
4 * Key event encoding to terminal escape sequences.
5 */
6
7#ifndef GHOSTTY_VT_KEY_ENCODER_H
8#define GHOSTTY_VT_KEY_ENCODER_H
9
10#include <stddef.h>
11#include <stdint.h>
12#include <ghostty/vt/types.h>
13#include <ghostty/vt/allocator.h>
14#include <ghostty/vt/terminal.h>
15#include <ghostty/vt/key/event.h>
16
17/**
18 * Opaque handle to a key encoder instance.
19 *
20 * This handle represents a key encoder that converts key events into terminal
21 * escape sequences.
22 *
23 * @ingroup key
24 */
25typedef struct GhosttyKeyEncoderImpl *GhosttyKeyEncoder;
26
27/**
28 * Kitty keyboard protocol flags.
29 *
30 * Bitflags representing the various modes of the Kitty keyboard protocol.
31 * These can be combined using bitwise OR operations. Valid values all
32 * start with `GHOSTTY_KITTY_KEY_`.
33 *
34 * @ingroup key
35 */
36typedef uint8_t GhosttyKittyKeyFlags;
37
38/** Kitty keyboard protocol disabled (all flags off) */
39#define GHOSTTY_KITTY_KEY_DISABLED 0
40
41/** Disambiguate escape codes */
42#define GHOSTTY_KITTY_KEY_DISAMBIGUATE (1 << 0)
43
44/** Report key press and release events */
45#define GHOSTTY_KITTY_KEY_REPORT_EVENTS (1 << 1)
46
47/** Report alternate key codes */
48#define GHOSTTY_KITTY_KEY_REPORT_ALTERNATES (1 << 2)
49
50/** Report all key events including those normally handled by the terminal */
51#define GHOSTTY_KITTY_KEY_REPORT_ALL (1 << 3)
52
53/** Report associated text with key events */
54#define GHOSTTY_KITTY_KEY_REPORT_ASSOCIATED (1 << 4)
55
56/** All Kitty keyboard protocol flags enabled */
57#define GHOSTTY_KITTY_KEY_ALL (GHOSTTY_KITTY_KEY_DISAMBIGUATE | GHOSTTY_KITTY_KEY_REPORT_EVENTS | GHOSTTY_KITTY_KEY_REPORT_ALTERNATES | GHOSTTY_KITTY_KEY_REPORT_ALL | GHOSTTY_KITTY_KEY_REPORT_ASSOCIATED)
58
59/**
60 * macOS option key behavior.
61 *
62 * Determines whether the "option" key on macOS is treated as "alt" or not.
63 * See the Ghostty `macos-option-as-alt` configuration option for more details.
64 *
65 * @ingroup key
66 */
67typedef enum GHOSTTY_ENUM_TYPED {
68 /** Option key is not treated as alt */
69 GHOSTTY_OPTION_AS_ALT_FALSE = 0,
70 /** Option key is treated as alt */
71 GHOSTTY_OPTION_AS_ALT_TRUE = 1,
72 /** Only left option key is treated as alt */
73 GHOSTTY_OPTION_AS_ALT_LEFT = 2,
74 /** Only right option key is treated as alt */
75 GHOSTTY_OPTION_AS_ALT_RIGHT = 3,
76 GHOSTTY_OPTION_AS_ALT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
77} GhosttyOptionAsAlt;
78
79/**
80 * Key encoder option identifiers.
81 *
82 * These values are used with ghostty_key_encoder_setopt() to configure
83 * the behavior of the key encoder.
84 *
85 * @ingroup key
86 */
87typedef enum GHOSTTY_ENUM_TYPED {
88 /** Terminal DEC mode 1: cursor key application mode (value: bool) */
89 GHOSTTY_KEY_ENCODER_OPT_CURSOR_KEY_APPLICATION = 0,
90
91 /** Terminal DEC mode 66: keypad key application mode (value: bool) */
92 GHOSTTY_KEY_ENCODER_OPT_KEYPAD_KEY_APPLICATION = 1,
93
94 /** Terminal DEC mode 1035: ignore keypad with numlock (value: bool) */
95 GHOSTTY_KEY_ENCODER_OPT_IGNORE_KEYPAD_WITH_NUMLOCK = 2,
96
97 /** Terminal DEC mode 1036: alt sends escape prefix (value: bool) */
98 GHOSTTY_KEY_ENCODER_OPT_ALT_ESC_PREFIX = 3,
99
100 /** xterm modifyOtherKeys mode 2 (value: bool) */
101 GHOSTTY_KEY_ENCODER_OPT_MODIFY_OTHER_KEYS_STATE_2 = 4,
102
103 /** Kitty keyboard protocol flags (value: GhosttyKittyKeyFlags bitmask) */
104 GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS = 5,
105
106 /** macOS option-as-alt setting (value: GhosttyOptionAsAlt) */
107 GHOSTTY_KEY_ENCODER_OPT_MACOS_OPTION_AS_ALT = 6,
108
109 /** Backarrow key mode (value: bool)
110 * See https://vt100.net/dec/ek-vt3xx-tp-002.pdf page 170
111 * If `false` (the default), `backspace` emits 0x7f
112 * If `true`, `backspace` emits 0x08
113 */
114 GHOSTTY_KEY_ENCODER_OPT_BACKARROW_KEY_MODE = 7,
115
116 GHOSTTY_KEY_ENCODER_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
117} GhosttyKeyEncoderOption;
118
119/**
120 * Create a new key encoder instance.
121 *
122 * Creates a new key encoder with default options. The encoder can be configured
123 * using ghostty_key_encoder_setopt() and must be freed using
124 * ghostty_key_encoder_free() when no longer needed.
125 *
126 * @param allocator Pointer to the allocator to use for memory management, or NULL to use the default allocator
127 * @param encoder Pointer to store the created encoder handle
128 * @return GHOSTTY_SUCCESS on success, or an error code on failure
129 *
130 * @ingroup key
131 */
132GHOSTTY_API GhosttyResult ghostty_key_encoder_new(const GhosttyAllocator *allocator, GhosttyKeyEncoder *encoder);
133
134/**
135 * Free a key encoder instance.
136 *
137 * Releases all resources associated with the key encoder. After this call,
138 * the encoder handle becomes invalid and must not be used.
139 *
140 * @param encoder The encoder handle to free (may be NULL)
141 *
142 * @ingroup key
143 */
144GHOSTTY_API void ghostty_key_encoder_free(GhosttyKeyEncoder encoder);
145
146/**
147 * Set an option on the key encoder.
148 *
149 * Configures the behavior of the key encoder. Options control various aspects
150 * of encoding such as terminal modes (cursor key application mode, keypad mode),
151 * protocol selection (Kitty keyboard protocol flags), and platform-specific
152 * behaviors (macOS option-as-alt).
153 *
154 * If you are using a terminal instance, you can set the key encoding
155 * options based on the active terminal state (e.g. legacy vs Kitty mode
156 * and associated flags) with ghostty_key_encoder_setopt_from_terminal().
157 *
158 * A null pointer value does nothing. It does not reset the value to the
159 * default. The setopt call will do nothing.
160 *
161 * @param encoder The encoder handle, must not be NULL
162 * @param option The option to set
163 * @param value Pointer to the value to set (type depends on the option)
164 *
165 * @ingroup key
166 */
167GHOSTTY_API void ghostty_key_encoder_setopt(GhosttyKeyEncoder encoder, GhosttyKeyEncoderOption option, const void *value);
168
169/**
170 * Set encoder options from a terminal's current state.
171 *
172 * Reads the terminal's current modes and flags and applies them to the
173 * encoder's options. This sets cursor key application mode, keypad mode,
174 * alt escape prefix, modifyOtherKeys state, and Kitty keyboard protocol
175 * flags from the terminal state.
176 *
177 * Note that the `macos_option_as_alt` option cannot be determined from
178 * terminal state and is reset to `GHOSTTY_OPTION_AS_ALT_FALSE` by this
179 * call. Use ghostty_key_encoder_setopt() to set it afterward if needed.
180 *
181 * @param encoder The encoder handle, must not be NULL
182 * @param terminal The terminal handle, must not be NULL
183 *
184 * @ingroup key
185 */
186GHOSTTY_API void ghostty_key_encoder_setopt_from_terminal(GhosttyKeyEncoder encoder, GhosttyTerminal terminal);
187
188/**
189 * Encode a key event into a terminal escape sequence.
190 *
191 * Converts a key event into the appropriate terminal escape sequence based on
192 * the encoder's current options. The sequence is written to the provided buffer.
193 *
194 * Not all key events produce output. For example, unmodified modifier keys
195 * typically don't generate escape sequences. Check the out_len parameter to
196 * determine if any data was written.
197 *
198 * If the output buffer is too small, this function returns GHOSTTY_OUT_OF_SPACE
199 * and out_len will contain the required buffer size. The caller can then
200 * allocate a larger buffer and call the function again.
201 *
202 * @param encoder The encoder handle, must not be NULL
203 * @param event The key event to encode, must not be NULL
204 * @param out_buf Buffer to write the encoded sequence to
205 * @param out_buf_size Size of the output buffer in bytes
206 * @param out_len Pointer to store the number of bytes written (may be NULL)
207 * @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_SPACE if buffer too small, or other error code
208 *
209 * ## Example: Calculate required buffer size
210 *
211 * @code{.c}
212 * // Query the required size with a NULL buffer (always returns OUT_OF_SPACE)
213 * size_t required = 0;
214 * GhosttyResult result = ghostty_key_encoder_encode(encoder, event, NULL, 0, &required);
215 * assert(result == GHOSTTY_OUT_OF_SPACE);
216 *
217 * // Allocate buffer of required size
218 * char *buf = malloc(required);
219 *
220 * // Encode with properly sized buffer
221 * size_t written = 0;
222 * result = ghostty_key_encoder_encode(encoder, event, buf, required, &written);
223 * assert(result == GHOSTTY_SUCCESS);
224 *
225 * // Use the encoded sequence...
226 *
227 * free(buf);
228 * @endcode
229 *
230 * ## Example: Direct encoding with static buffer
231 *
232 * @code{.c}
233 * // Most escape sequences are short, so a static buffer often suffices
234 * char buf[128];
235 * size_t written = 0;
236 * GhosttyResult result = ghostty_key_encoder_encode(encoder, event, buf, sizeof(buf), &written);
237 *
238 * if (result == GHOSTTY_SUCCESS) {
239 * // Write the encoded sequence to the terminal
240 * write(pty_fd, buf, written);
241 * } else if (result == GHOSTTY_OUT_OF_SPACE) {
242 * // Buffer too small, written contains required size
243 * char *dynamic_buf = malloc(written);
244 * result = ghostty_key_encoder_encode(encoder, event, dynamic_buf, written, &written);
245 * assert(result == GHOSTTY_SUCCESS);
246 * write(pty_fd, dynamic_buf, written);
247 * free(dynamic_buf);
248 * }
249 * @endcode
250 *
251 * @ingroup key
252 */
253GHOSTTY_API GhosttyResult ghostty_key_encoder_encode(GhosttyKeyEncoder encoder, GhosttyKeyEvent event, char *out_buf, size_t out_buf_size, size_t *out_len);
254
255#endif /* GHOSTTY_VT_KEY_ENCODER_H */