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