1 #define DEFINE_INLINES 2 3 // vim: set sw=2 : 4 #include "vterm_internal.h" 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <stdarg.h> 9 #include <string.h> 10 11 #include "utf8.h" 12 13 /***************** 14 * API functions * 15 *****************/ 16 17 static void *default_malloc(size_t size, void *allocdata UNUSED) 18 { 19 void *ptr = malloc(size); 20 if(ptr) 21 memset(ptr, 0, size); 22 return ptr; 23 } 24 25 static void default_free(void *ptr, void *allocdata UNUSED) 26 { 27 free(ptr); 28 } 29 30 static VTermAllocatorFunctions default_allocator = { 31 &default_malloc, // malloc 32 &default_free // free 33 }; 34 35 VTerm *vterm_new(int rows, int cols) 36 { 37 return vterm_new_with_allocator(rows, cols, &default_allocator, NULL); 38 } 39 40 VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata) 41 { 42 /* Need to bootstrap using the allocator function directly */ 43 VTerm *vt = (*funcs->malloc)(sizeof(VTerm), allocdata); 44 45 if (vt == NULL) 46 return NULL; 47 vt->allocator = funcs; 48 vt->allocdata = allocdata; 49 50 vt->rows = rows; 51 vt->cols = cols; 52 53 vt->parser.state = NORMAL; 54 55 vt->parser.callbacks = NULL; 56 vt->parser.cbdata = NULL; 57 58 vt->outfunc = NULL; 59 vt->outdata = NULL; 60 61 vt->outbuffer_len = 200; 62 vt->outbuffer_cur = 0; 63 vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len); 64 65 vt->tmpbuffer_len = 64; 66 vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len); 67 68 if (vt->tmpbuffer == NULL 69 || vt->outbuffer == NULL 70 || vt->tmpbuffer == NULL) 71 { 72 vterm_allocator_free(vt, vt->outbuffer); 73 vterm_allocator_free(vt, vt->tmpbuffer); 74 vterm_allocator_free(vt, vt); 75 return NULL; 76 } 77 78 return vt; 79 } 80 81 void vterm_free(VTerm *vt) 82 { 83 if(vt->screen) 84 vterm_screen_free(vt->screen); 85 86 if(vt->state) 87 vterm_state_free(vt->state); 88 89 vterm_allocator_free(vt, vt->outbuffer); 90 vterm_allocator_free(vt, vt->tmpbuffer); 91 92 vterm_allocator_free(vt, vt); 93 } 94 95 INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size) 96 { 97 return (*vt->allocator->malloc)(size, vt->allocdata); 98 } 99 100 /* 101 * Free "ptr" unless it is NULL. 102 */ 103 INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr) 104 { 105 if (ptr) 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 if(colsp) 114 *colsp = vt->cols; 115 } 116 117 void vterm_set_size(VTerm *vt, int rows, int cols) 118 { 119 vt->rows = rows; 120 vt->cols = cols; 121 122 if(vt->parser.callbacks && vt->parser.callbacks->resize) 123 (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata); 124 } 125 126 int vterm_get_utf8(const VTerm *vt) 127 { 128 return vt->mode.utf8; 129 } 130 131 void vterm_set_utf8(VTerm *vt, int is_utf8) 132 { 133 vt->mode.utf8 = is_utf8; 134 } 135 136 void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user) 137 { 138 vt->outfunc = func; 139 vt->outdata = user; 140 } 141 142 INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len) 143 { 144 if(vt->outfunc) { 145 (vt->outfunc)(bytes, len, vt->outdata); 146 return; 147 } 148 149 if(len > vt->outbuffer_len - vt->outbuffer_cur) { 150 DEBUG_LOG("vterm_push_output_bytes(): buffer overflow; dropping output\n"); 151 return; 152 } 153 154 memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len); 155 vt->outbuffer_cur += len; 156 } 157 158 INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args) 159 { 160 size_t len; 161 #ifndef VSNPRINTF 162 // When vsnprintf() is not available (C90) fall back to vsprintf(). 163 char buffer[1024]; // 1Kbyte is enough for everybody, right? 164 #endif 165 166 #ifdef VSNPRINTF 167 len = VSNPRINTF(vt->tmpbuffer, vt->tmpbuffer_len, format, args); 168 vterm_push_output_bytes(vt, vt->tmpbuffer, len); 169 #else 170 len = vsprintf(buffer, format, args); 171 vterm_push_output_bytes(vt, buffer, len); 172 #endif 173 } 174 175 INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...) 176 { 177 va_list args; 178 va_start(args, format); 179 vterm_push_output_vsprintf(vt, format, args); 180 va_end(args); 181 } 182 183 INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...) 184 { 185 size_t cur; 186 va_list args; 187 188 if(ctrl >= 0x80 && !vt->mode.ctrl8bit) 189 cur = SNPRINTF(vt->tmpbuffer, vt->tmpbuffer_len, 190 ESC_S "%c", ctrl - 0x40); 191 else 192 cur = SNPRINTF(vt->tmpbuffer, vt->tmpbuffer_len, 193 "%c", ctrl); 194 if(cur >= vt->tmpbuffer_len) 195 return; 196 vterm_push_output_bytes(vt, vt->tmpbuffer, cur); 197 198 va_start(args, fmt); 199 vterm_push_output_vsprintf(vt, fmt, args); 200 va_end(args); 201 } 202 203 INTERNAL void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...) 204 { 205 size_t cur; 206 va_list args; 207 208 cur = SNPRINTF(vt->tmpbuffer, vt->tmpbuffer_len, 209 vt->mode.ctrl8bit ? "\x90" : ESC_S "P"); // DCS 210 211 if(cur >= vt->tmpbuffer_len) 212 return; 213 vterm_push_output_bytes(vt, vt->tmpbuffer, cur); 214 215 va_start(args, fmt); 216 vterm_push_output_vsprintf(vt, fmt, args); 217 va_end(args); 218 219 cur = SNPRINTF(vt->tmpbuffer, vt->tmpbuffer_len, 220 vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST 221 if(cur >= vt->tmpbuffer_len) 222 return; 223 vterm_push_output_bytes(vt, vt->tmpbuffer, cur); 224 } 225 226 size_t vterm_output_get_buffer_size(const VTerm *vt) 227 { 228 return vt->outbuffer_len; 229 } 230 231 size_t vterm_output_get_buffer_current(const VTerm *vt) 232 { 233 return vt->outbuffer_cur; 234 } 235 236 size_t vterm_output_get_buffer_remaining(const VTerm *vt) 237 { 238 return vt->outbuffer_len - vt->outbuffer_cur; 239 } 240 241 size_t vterm_output_read(VTerm *vt, char *buffer, size_t len) 242 { 243 if(len > vt->outbuffer_cur) 244 len = vt->outbuffer_cur; 245 246 memcpy(buffer, vt->outbuffer, len); 247 248 if(len < vt->outbuffer_cur) 249 memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len); 250 251 vt->outbuffer_cur -= len; 252 253 return len; 254 } 255 256 VTermValueType vterm_get_attr_type(VTermAttr attr) 257 { 258 switch(attr) { 259 case VTERM_ATTR_BOLD: return VTERM_VALUETYPE_BOOL; 260 case VTERM_ATTR_UNDERLINE: return VTERM_VALUETYPE_INT; 261 case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL; 262 case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL; 263 case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL; 264 case VTERM_ATTR_CONCEAL: return VTERM_VALUETYPE_BOOL; 265 case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL; 266 case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT; 267 case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR; 268 case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR; 269 270 case VTERM_N_ATTRS: return 0; 271 } 272 return 0; /* UNREACHABLE */ 273 } 274 275 VTermValueType vterm_get_prop_type(VTermProp prop) 276 { 277 switch(prop) { 278 case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL; 279 case VTERM_PROP_CURSORBLINK: return VTERM_VALUETYPE_BOOL; 280 case VTERM_PROP_ALTSCREEN: return VTERM_VALUETYPE_BOOL; 281 case VTERM_PROP_TITLE: return VTERM_VALUETYPE_STRING; 282 case VTERM_PROP_ICONNAME: return VTERM_VALUETYPE_STRING; 283 case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL; 284 case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT; 285 case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT; 286 case VTERM_PROP_CURSORCOLOR: return VTERM_VALUETYPE_STRING; 287 288 case VTERM_N_PROPS: return 0; 289 } 290 return 0; /* UNREACHABLE */ 291 } 292 293 void vterm_scroll_rect(VTermRect rect, 294 int downward, 295 int rightward, 296 int (*moverect)(VTermRect src, VTermRect dest, void *user), 297 int (*eraserect)(VTermRect rect, int selective, void *user), 298 void *user) 299 { 300 VTermRect src; 301 VTermRect dest; 302 303 if(abs(downward) >= rect.end_row - rect.start_row || 304 abs(rightward) >= rect.end_col - rect.start_col) { 305 /* Scroll more than area; just erase the lot */ 306 (*eraserect)(rect, 0, user); 307 return; 308 } 309 310 if(rightward >= 0) { 311 /* rect: [XXX................] 312 * src: [----------------] 313 * dest: [----------------] 314 */ 315 dest.start_col = rect.start_col; 316 dest.end_col = rect.end_col - rightward; 317 src.start_col = rect.start_col + rightward; 318 src.end_col = rect.end_col; 319 } 320 else { 321 /* rect: [................XXX] 322 * src: [----------------] 323 * dest: [----------------] 324 */ 325 int leftward = -rightward; 326 dest.start_col = rect.start_col + leftward; 327 dest.end_col = rect.end_col; 328 src.start_col = rect.start_col; 329 src.end_col = rect.end_col - leftward; 330 } 331 332 if(downward >= 0) { 333 dest.start_row = rect.start_row; 334 dest.end_row = rect.end_row - downward; 335 src.start_row = rect.start_row + downward; 336 src.end_row = rect.end_row; 337 } 338 else { 339 int upward = -downward; 340 dest.start_row = rect.start_row + upward; 341 dest.end_row = rect.end_row; 342 src.start_row = rect.start_row; 343 src.end_row = rect.end_row - upward; 344 } 345 346 if(moverect) 347 (*moverect)(dest, src, user); 348 349 if(downward > 0) 350 rect.start_row = rect.end_row - downward; 351 else if(downward < 0) 352 rect.end_row = rect.start_row - downward; 353 354 if(rightward > 0) 355 rect.start_col = rect.end_col - rightward; 356 else if(rightward < 0) 357 rect.end_col = rect.start_col - rightward; 358 359 (*eraserect)(rect, 0, user); 360 } 361 362 void vterm_copy_cells(VTermRect dest, 363 VTermRect src, 364 void (*copycell)(VTermPos dest, VTermPos src, void *user), 365 void *user) 366 { 367 int downward = src.start_row - dest.start_row; 368 int rightward = src.start_col - dest.start_col; 369 370 int init_row, test_row, init_col, test_col; 371 int inc_row, inc_col; 372 373 VTermPos pos; 374 375 if(downward < 0) { 376 init_row = dest.end_row - 1; 377 test_row = dest.start_row - 1; 378 inc_row = -1; 379 } 380 else /* downward >= 0 */ { 381 init_row = dest.start_row; 382 test_row = dest.end_row; 383 inc_row = +1; 384 } 385 386 if(rightward < 0) { 387 init_col = dest.end_col - 1; 388 test_col = dest.start_col - 1; 389 inc_col = -1; 390 } 391 else /* rightward >= 0 */ { 392 init_col = dest.start_col; 393 test_col = dest.end_col; 394 inc_col = +1; 395 } 396 397 for(pos.row = init_row; pos.row != test_row; pos.row += inc_row) 398 for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) { 399 VTermPos srcpos; 400 srcpos.row = pos.row + downward; 401 srcpos.col = pos.col + rightward; 402 (*copycell)(pos, srcpos, user); 403 } 404 } 405 406 void vterm_check_version(int major, int minor) 407 { 408 if(major != VTERM_VERSION_MAJOR) { 409 fprintf(stderr, "libvterm major version mismatch; %d (wants) != %d (library)\n", 410 major, VTERM_VERSION_MAJOR); 411 exit(1); 412 } 413 414 if(minor > VTERM_VERSION_MINOR) { 415 fprintf(stderr, "libvterm minor version mismatch; %d (wants) > %d (library)\n", 416 minor, VTERM_VERSION_MINOR); 417 exit(1); 418 } 419 420 // Happy 421 } 422