| 1 | /** |
| 2 | * @file modes.h |
| 3 | * |
| 4 | * Terminal mode utilities - pack and unpack ANSI/DEC mode identifiers. |
| 5 | */ |
| 6 | |
| 7 | #ifndef GHOSTTY_VT_MODES_H |
| 8 | #define GHOSTTY_VT_MODES_H |
| 9 | |
| 10 | /** @defgroup modes Mode Utilities |
| 11 | * |
| 12 | * Utilities for working with terminal modes. A mode is a compact |
| 13 | * 16-bit representation of a terminal mode identifier that encodes both |
| 14 | * the numeric mode value (up to 15 bits) and whether the mode is an ANSI |
| 15 | * mode or a DEC private mode (?-prefixed). |
| 16 | * |
| 17 | * The packed layout (least-significant bit first) is: |
| 18 | * - Bits 0–14: mode value (u15) |
| 19 | * - Bit 15: ANSI flag (0 = DEC private mode, 1 = ANSI mode) |
| 20 | * |
| 21 | * ## Example |
| 22 | * |
| 23 | * @snippet c-vt-modes/src/main.c modes-pack-unpack |
| 24 | * |
| 25 | * ## DECRPM Report Encoding |
| 26 | * |
| 27 | * Use ghostty_mode_report_encode() to encode a DECRPM response into a |
| 28 | * caller-provided buffer: |
| 29 | * |
| 30 | * @snippet c-vt-modes/src/main.c modes-decrpm |
| 31 | * |
| 32 | * @{ |
| 33 | */ |
| 34 | |
| 35 | #include <stdbool.h> |
| 36 | #include <stddef.h> |
| 37 | #include <stdint.h> |
| 38 | |
| 39 | #include <ghostty/vt/types.h> |
| 40 | |
| 41 | #ifdef __cplusplus |
| 42 | extern "C" { |
| 43 | #endif |
| 44 | |
| 45 | /** @name ANSI Modes |
| 46 | * Modes for standard ANSI modes. |
| 47 | * @{ |
| 48 | */ |
| 49 | #define GHOSTTY_MODE_KAM (ghostty_mode_new(2, true)) /**< Keyboard action (disable keyboard) */ |
| 50 | #define GHOSTTY_MODE_INSERT (ghostty_mode_new(4, true)) /**< Insert mode */ |
| 51 | #define GHOSTTY_MODE_SRM (ghostty_mode_new(12, true)) /**< Send/receive mode */ |
| 52 | #define GHOSTTY_MODE_LINEFEED (ghostty_mode_new(20, true)) /**< Linefeed/new line mode */ |
| 53 | /** @} */ |
| 54 | |
| 55 | /** @name DEC Private Modes |
| 56 | * Modes for DEC private modes (?-prefixed). |
| 57 | * @{ |
| 58 | */ |
| 59 | #define GHOSTTY_MODE_DECCKM (ghostty_mode_new(1, false)) /**< Cursor keys */ |
| 60 | #define GHOSTTY_MODE_132_COLUMN (ghostty_mode_new(3, false)) /**< 132/80 column mode */ |
| 61 | #define GHOSTTY_MODE_SLOW_SCROLL (ghostty_mode_new(4, false)) /**< Slow scroll */ |
| 62 | #define GHOSTTY_MODE_REVERSE_COLORS (ghostty_mode_new(5, false)) /**< Reverse video */ |
| 63 | #define GHOSTTY_MODE_ORIGIN (ghostty_mode_new(6, false)) /**< Origin mode */ |
| 64 | #define GHOSTTY_MODE_WRAPAROUND (ghostty_mode_new(7, false)) /**< Auto-wrap mode */ |
| 65 | #define GHOSTTY_MODE_AUTOREPEAT (ghostty_mode_new(8, false)) /**< Auto-repeat keys */ |
| 66 | #define GHOSTTY_MODE_X10_MOUSE (ghostty_mode_new(9, false)) /**< X10 mouse reporting */ |
| 67 | #define GHOSTTY_MODE_CURSOR_BLINKING (ghostty_mode_new(12, false)) /**< Cursor blink */ |
| 68 | #define GHOSTTY_MODE_CURSOR_VISIBLE (ghostty_mode_new(25, false)) /**< Cursor visible (DECTCEM) */ |
| 69 | #define GHOSTTY_MODE_ENABLE_MODE_3 (ghostty_mode_new(40, false)) /**< Allow 132 column mode */ |
| 70 | #define GHOSTTY_MODE_REVERSE_WRAP (ghostty_mode_new(45, false)) /**< Reverse wrap */ |
| 71 | #define GHOSTTY_MODE_ALT_SCREEN_LEGACY (ghostty_mode_new(47, false)) /**< Alternate screen (legacy) */ |
| 72 | #define GHOSTTY_MODE_KEYPAD_KEYS (ghostty_mode_new(66, false)) /**< Application keypad */ |
| 73 | #define GHOSTTY_MODE_BACKARROW_KEY_MODE (ghostty_mode_new(67, false)) /**< Backarrow key mode (DECBKM) */ |
| 74 | #define GHOSTTY_MODE_LEFT_RIGHT_MARGIN (ghostty_mode_new(69, false)) /**< Left/right margin mode */ |
| 75 | #define GHOSTTY_MODE_NORMAL_MOUSE (ghostty_mode_new(1000, false)) /**< Normal mouse tracking */ |
| 76 | #define GHOSTTY_MODE_BUTTON_MOUSE (ghostty_mode_new(1002, false)) /**< Button-event mouse tracking */ |
| 77 | #define GHOSTTY_MODE_ANY_MOUSE (ghostty_mode_new(1003, false)) /**< Any-event mouse tracking */ |
| 78 | #define GHOSTTY_MODE_FOCUS_EVENT (ghostty_mode_new(1004, false)) /**< Focus in/out events */ |
| 79 | #define GHOSTTY_MODE_UTF8_MOUSE (ghostty_mode_new(1005, false)) /**< UTF-8 mouse format */ |
| 80 | #define GHOSTTY_MODE_SGR_MOUSE (ghostty_mode_new(1006, false)) /**< SGR mouse format */ |
| 81 | #define GHOSTTY_MODE_ALT_SCROLL (ghostty_mode_new(1007, false)) /**< Alternate scroll mode */ |
| 82 | #define GHOSTTY_MODE_URXVT_MOUSE (ghostty_mode_new(1015, false)) /**< URxvt mouse format */ |
| 83 | #define GHOSTTY_MODE_SGR_PIXELS_MOUSE (ghostty_mode_new(1016, false)) /**< SGR-Pixels mouse format */ |
| 84 | #define GHOSTTY_MODE_NUMLOCK_KEYPAD (ghostty_mode_new(1035, false)) /**< Ignore keypad with NumLock */ |
| 85 | #define GHOSTTY_MODE_ALT_ESC_PREFIX (ghostty_mode_new(1036, false)) /**< Alt key sends ESC prefix */ |
| 86 | #define GHOSTTY_MODE_ALT_SENDS_ESC (ghostty_mode_new(1039, false)) /**< Alt sends escape */ |
| 87 | #define GHOSTTY_MODE_REVERSE_WRAP_EXT (ghostty_mode_new(1045, false)) /**< Extended reverse wrap */ |
| 88 | #define GHOSTTY_MODE_ALT_SCREEN (ghostty_mode_new(1047, false)) /**< Alternate screen */ |
| 89 | #define GHOSTTY_MODE_SAVE_CURSOR (ghostty_mode_new(1048, false)) /**< Save cursor (DECSC) */ |
| 90 | #define GHOSTTY_MODE_ALT_SCREEN_SAVE (ghostty_mode_new(1049, false)) /**< Alt screen + save cursor + clear */ |
| 91 | #define GHOSTTY_MODE_BRACKETED_PASTE (ghostty_mode_new(2004, false)) /**< Bracketed paste mode */ |
| 92 | #define GHOSTTY_MODE_SYNC_OUTPUT (ghostty_mode_new(2026, false)) /**< Synchronized output */ |
| 93 | #define GHOSTTY_MODE_GRAPHEME_CLUSTER (ghostty_mode_new(2027, false)) /**< Grapheme cluster mode */ |
| 94 | #define GHOSTTY_MODE_COLOR_SCHEME_REPORT (ghostty_mode_new(2031, false)) /**< Report color scheme */ |
| 95 | #define GHOSTTY_MODE_IN_BAND_RESIZE (ghostty_mode_new(2048, false)) /**< In-band size reports */ |
| 96 | /** @} */ |
| 97 | |
| 98 | /** |
| 99 | * A packed 16-bit terminal mode. |
| 100 | * |
| 101 | * Encodes a mode value (bits 0–14) and an ANSI flag (bit 15) into a |
| 102 | * single 16-bit integer. Use the inline helper functions to construct |
| 103 | * and inspect modes rather than manipulating bits directly. |
| 104 | */ |
| 105 | typedef uint16_t GhosttyMode; |
| 106 | |
| 107 | /** |
| 108 | * Create a mode from a mode value and ANSI flag. |
| 109 | * |
| 110 | * @param value The numeric mode value (0–32767) |
| 111 | * @param ansi true for an ANSI mode, false for a DEC private mode |
| 112 | * @return The packed mode |
| 113 | * |
| 114 | * @ingroup modes |
| 115 | */ |
| 116 | static inline GhosttyMode ghostty_mode_new(uint16_t value, bool ansi) { |
| 117 | return (GhosttyMode)((value & 0x7FFF) | ((uint16_t)ansi << 15)); |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * Extract the numeric mode value from a mode. |
| 122 | * |
| 123 | * @param mode The mode |
| 124 | * @return The mode value (0–32767) |
| 125 | * |
| 126 | * @ingroup modes |
| 127 | */ |
| 128 | static inline uint16_t ghostty_mode_value(GhosttyMode mode) { |
| 129 | return mode & 0x7FFF; |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * Check whether a mode represents an ANSI mode. |
| 134 | * |
| 135 | * @param mode The mode |
| 136 | * @return true if this is an ANSI mode, false if it is a DEC private mode |
| 137 | * |
| 138 | * @ingroup modes |
| 139 | */ |
| 140 | static inline bool ghostty_mode_ansi(GhosttyMode mode) { |
| 141 | return (mode >> 15) != 0; |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * DECRPM report state values. |
| 146 | * |
| 147 | * These correspond to the Ps2 parameter in a DECRPM response |
| 148 | * sequence (CSI ? Ps1 ; Ps2 $ y). |
| 149 | */ |
| 150 | typedef enum GHOSTTY_ENUM_TYPED { |
| 151 | /** Mode is not recognized */ |
| 152 | GHOSTTY_MODE_REPORT_NOT_RECOGNIZED = 0, |
| 153 | /** Mode is set (enabled) */ |
| 154 | GHOSTTY_MODE_REPORT_SET = 1, |
| 155 | /** Mode is reset (disabled) */ |
| 156 | GHOSTTY_MODE_REPORT_RESET = 2, |
| 157 | /** Mode is permanently set */ |
| 158 | GHOSTTY_MODE_REPORT_PERMANENTLY_SET = 3, |
| 159 | /** Mode is permanently reset */ |
| 160 | GHOSTTY_MODE_REPORT_PERMANENTLY_RESET = 4, |
| 161 | GHOSTTY_MODE_REPORT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, |
| 162 | } GhosttyModeReportState; |
| 163 | |
| 164 | /** |
| 165 | * Encode a DECRPM (DEC Private Mode Report) response sequence. |
| 166 | * |
| 167 | * Writes a mode report escape sequence into the provided buffer. |
| 168 | * The generated sequence has the form: |
| 169 | * - DEC private mode: CSI ? Ps1 ; Ps2 $ y |
| 170 | * - ANSI mode: CSI Ps1 ; Ps2 $ y |
| 171 | * |
| 172 | * If the buffer is too small, the function returns GHOSTTY_OUT_OF_SPACE |
| 173 | * and writes the required buffer size to @p out_written. The caller can |
| 174 | * then retry with a sufficiently sized buffer. |
| 175 | * |
| 176 | * @param mode The mode identifying the mode to report on |
| 177 | * @param state The report state for this mode |
| 178 | * @param buf Output buffer to write the encoded sequence into (may be NULL) |
| 179 | * @param buf_len Size of the output buffer in bytes |
| 180 | * @param[out] out_written On success, the number of bytes written. On |
| 181 | * GHOSTTY_OUT_OF_SPACE, the required buffer size. |
| 182 | * @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_SPACE if the buffer |
| 183 | * is too small |
| 184 | */ |
| 185 | GHOSTTY_API GhosttyResult ghostty_mode_report_encode( |
| 186 | GhosttyMode mode, |
| 187 | GhosttyModeReportState state, |
| 188 | char* buf, |
| 189 | size_t buf_len, |
| 190 | size_t* out_written); |
| 191 | |
| 192 | #ifdef __cplusplus |
| 193 | } |
| 194 | #endif |
| 195 | |
| 196 | /** @} */ |
| 197 | |
| 198 | #endif /* GHOSTTY_VT_MODES_H */ |