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