vterm.c (8039B)
1 #include <stdarg.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 #include "auto/config.h" 7 #include "nvim/memory.h" 8 #include "nvim/vterm/screen.h" 9 #include "nvim/vterm/state.h" 10 #include "nvim/vterm/vterm.h" 11 #include "nvim/vterm/vterm_internal_defs.h" 12 13 #include "vterm/vterm.c.generated.h" 14 15 // ***************** 16 // * API functions * 17 // ***************** 18 19 static void *default_malloc(size_t size, void *allocdata) 20 { 21 void *ptr = xmalloc(size); 22 if (ptr) { 23 memset(ptr, 0, size); 24 } 25 return ptr; 26 } 27 28 static void default_free(void *ptr, void *allocdata) 29 { 30 xfree(ptr); 31 } 32 33 static VTermAllocatorFunctions default_allocator = { 34 .malloc = &default_malloc, 35 .free = &default_free, 36 }; 37 38 /// Convenient shortcut for default cases 39 VTerm *vterm_new(int rows, int cols) 40 { 41 return vterm_build(&(const struct VTermBuilder){ 42 .rows = rows, 43 .cols = cols, 44 }); 45 } 46 47 // A handy macro for defaulting values out of builder fields 48 #define DEFAULT(v, def) ((v) ? (v) : (def)) 49 50 VTerm *vterm_build(const struct VTermBuilder *builder) 51 { 52 const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator); 53 54 // Need to bootstrap using the allocator function directly 55 VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata); 56 57 vt->allocator = allocator; 58 vt->allocdata = builder->allocdata; 59 60 vt->rows = builder->rows; 61 vt->cols = builder->cols; 62 63 vt->parser.state = NORMAL; 64 65 vt->parser.callbacks = NULL; 66 vt->parser.cbdata = NULL; 67 68 vt->parser.emit_nul = false; 69 70 vt->outfunc = NULL; 71 vt->outdata = NULL; 72 73 vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096); 74 vt->outbuffer_cur = 0; 75 vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len); 76 77 vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096); 78 vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len); 79 80 return vt; 81 } 82 83 void vterm_free(VTerm *vt) 84 { 85 if (vt->screen) { 86 vterm_screen_free(vt->screen); 87 } 88 89 if (vt->state) { 90 vterm_state_free(vt->state); 91 } 92 93 vterm_allocator_free(vt, vt->outbuffer); 94 vterm_allocator_free(vt, vt->tmpbuffer); 95 96 vterm_allocator_free(vt, vt); 97 } 98 99 void *vterm_allocator_malloc(VTerm *vt, size_t size) 100 { 101 return (*vt->allocator->malloc)(size, vt->allocdata); 102 } 103 104 void vterm_allocator_free(VTerm *vt, void *ptr) 105 { 106 (*vt->allocator->free)(ptr, vt->allocdata); 107 } 108 109 void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp) 110 { 111 if (rowsp) { 112 *rowsp = vt->rows; 113 } 114 if (colsp) { 115 *colsp = vt->cols; 116 } 117 } 118 119 void vterm_set_size(VTerm *vt, int rows, int cols) 120 { 121 if (rows < 1 || cols < 1) { 122 return; 123 } 124 125 vt->rows = rows; 126 vt->cols = cols; 127 128 if (vt->parser.callbacks && vt->parser.callbacks->resize) { 129 (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata); 130 } 131 } 132 133 void vterm_set_utf8(VTerm *vt, int is_utf8) 134 { 135 vt->mode.utf8 = (unsigned)is_utf8; 136 } 137 138 void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user) 139 { 140 vt->outfunc = func; 141 vt->outdata = user; 142 } 143 144 void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len) 145 { 146 if (vt->outfunc) { 147 (vt->outfunc)(bytes, len, vt->outdata); 148 return; 149 } 150 151 if (len > vt->outbuffer_len - vt->outbuffer_cur) { 152 return; 153 } 154 155 memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len); 156 vt->outbuffer_cur += len; 157 } 158 159 void vterm_push_output_sprintf(VTerm *vt, const char *format, ...) 160 FUNC_ATTR_PRINTF(2, 3) 161 { 162 va_list args; 163 va_start(args, format); 164 size_t len = (size_t)vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len, format, args); 165 vterm_push_output_bytes(vt, vt->tmpbuffer, len); 166 va_end(args); 167 } 168 169 void vterm_push_output_sprintf_ctrl(VTerm *vt, uint8_t ctrl, const char *fmt, ...) 170 FUNC_ATTR_PRINTF(3, 4) 171 { 172 size_t cur; 173 174 if (ctrl >= 0x80 && !vt->mode.ctrl8bit) { 175 cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40); 176 } else { 177 cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl); 178 } 179 180 if (cur >= vt->tmpbuffer_len) { 181 return; 182 } 183 184 va_list args; 185 va_start(args, fmt); 186 cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args); 187 va_end(args); 188 189 if (cur >= vt->tmpbuffer_len) { 190 return; 191 } 192 193 vterm_push_output_bytes(vt, vt->tmpbuffer, cur); 194 } 195 196 void vterm_push_output_sprintf_str(VTerm *vt, uint8_t ctrl, bool term, const char *fmt, ...) 197 FUNC_ATTR_PRINTF(4, 5) 198 { 199 size_t cur = 0; 200 201 if (ctrl) { 202 if (ctrl >= 0x80 && !vt->mode.ctrl8bit) { 203 cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40); 204 } else { 205 cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl); 206 } 207 208 if (cur >= vt->tmpbuffer_len) { 209 return; 210 } 211 } 212 213 va_list args; 214 va_start(args, fmt); 215 cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args); 216 va_end(args); 217 218 if (cur >= vt->tmpbuffer_len) { 219 return; 220 } 221 222 if (term) { 223 cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, 224 vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST 225 226 if (cur >= vt->tmpbuffer_len) { 227 return; 228 } 229 } 230 231 vterm_push_output_bytes(vt, vt->tmpbuffer, cur); 232 } 233 234 VTermValueType vterm_get_attr_type(VTermAttr attr) 235 { 236 switch (attr) { 237 case VTERM_ATTR_BOLD: 238 return VTERM_VALUETYPE_BOOL; 239 case VTERM_ATTR_UNDERLINE: 240 return VTERM_VALUETYPE_INT; 241 case VTERM_ATTR_ITALIC: 242 return VTERM_VALUETYPE_BOOL; 243 case VTERM_ATTR_BLINK: 244 return VTERM_VALUETYPE_BOOL; 245 case VTERM_ATTR_REVERSE: 246 return VTERM_VALUETYPE_BOOL; 247 case VTERM_ATTR_CONCEAL: 248 return VTERM_VALUETYPE_BOOL; 249 case VTERM_ATTR_STRIKE: 250 return VTERM_VALUETYPE_BOOL; 251 case VTERM_ATTR_FONT: 252 return VTERM_VALUETYPE_INT; 253 case VTERM_ATTR_FOREGROUND: 254 return VTERM_VALUETYPE_COLOR; 255 case VTERM_ATTR_BACKGROUND: 256 return VTERM_VALUETYPE_COLOR; 257 case VTERM_ATTR_SMALL: 258 return VTERM_VALUETYPE_BOOL; 259 case VTERM_ATTR_BASELINE: 260 return VTERM_VALUETYPE_INT; 261 case VTERM_ATTR_URI: 262 return VTERM_VALUETYPE_INT; 263 case VTERM_ATTR_DIM: 264 return VTERM_VALUETYPE_BOOL; 265 case VTERM_ATTR_OVERLINE: 266 return VTERM_VALUETYPE_BOOL; 267 268 case VTERM_N_ATTRS: 269 return 0; 270 } 271 return 0; // UNREACHABLE 272 } 273 274 void vterm_scroll_rect(VTermRect rect, int downward, int rightward, 275 int (*moverect)(VTermRect src, VTermRect dest, void *user), 276 int (*eraserect)(VTermRect rect, int selective, void *user), void *user) 277 { 278 VTermRect src; 279 VTermRect dest; 280 281 if (abs(downward) >= rect.end_row - rect.start_row 282 || abs(rightward) >= rect.end_col - rect.start_col) { 283 // Scroll more than area; just erase the lot 284 (*eraserect)(rect, 0, user); 285 return; 286 } 287 288 if (rightward >= 0) { 289 // rect: [XXX................] 290 // src: [----------------] 291 // dest: [----------------] 292 dest.start_col = rect.start_col; 293 dest.end_col = rect.end_col - rightward; 294 src.start_col = rect.start_col + rightward; 295 src.end_col = rect.end_col; 296 } else { 297 // rect: [................XXX] 298 // src: [----------------] 299 // dest: [----------------] 300 int leftward = -rightward; 301 dest.start_col = rect.start_col + leftward; 302 dest.end_col = rect.end_col; 303 src.start_col = rect.start_col; 304 src.end_col = rect.end_col - leftward; 305 } 306 307 if (downward >= 0) { 308 dest.start_row = rect.start_row; 309 dest.end_row = rect.end_row - downward; 310 src.start_row = rect.start_row + downward; 311 src.end_row = rect.end_row; 312 } else { 313 int upward = -downward; 314 dest.start_row = rect.start_row + upward; 315 dest.end_row = rect.end_row; 316 src.start_row = rect.start_row; 317 src.end_row = rect.end_row - upward; 318 } 319 320 if (moverect) { 321 (*moverect)(dest, src, user); 322 } 323 324 if (downward > 0) { 325 rect.start_row = rect.end_row - downward; 326 } else if (downward < 0) { 327 rect.end_row = rect.start_row - downward; 328 } 329 330 if (rightward > 0) { 331 rect.start_col = rect.end_col - rightward; 332 } else if (rightward < 0) { 333 rect.end_col = rect.start_col - rightward; 334 } 335 336 (*eraserect)(rect, 0, user); 337 }