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