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