Fast and reasonably complete (framebuffer) terminal emulator (Zig fork)
1#include <stdint.h> 2#include <stddef.h> 3#include <stdbool.h> 4 5#include "term.h" 6 7static const uint32_t col256[] = { 8 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 9 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, 10 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, 11 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 12 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 13 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 14 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, 15 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 16 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 17 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 18 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, 19 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 20 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, 21 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 22 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 23 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 24 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 25 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, 26 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 27 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 28 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 29 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, 30 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, 31 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 32 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 33 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 34 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 35 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 36 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 37 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee 38}; 39 40// Tries to implement this standard for terminfo 41// https://man7.org/linux/man-pages/man4/console_codes.4.html 42 43#define CHARSET_DEFAULT 0 44#define CHARSET_DEC_SPECIAL 1 45 46void term_context_reinit(struct term_context *ctx) { 47 ctx->tab_size = 8; 48 ctx->autoflush = true; 49 ctx->cursor_enabled = true; 50 ctx->scroll_enabled = true; 51 ctx->control_sequence = false; 52 ctx->csi = false; 53 ctx->escape = false; 54 ctx->osc = false; 55 ctx->osc_escape = false; 56 ctx->rrr = false; 57 ctx->discard_next = false; 58 ctx->bold = false; 59 ctx->bg_bold = false; 60 ctx->reverse_video = false; 61 ctx->dec_private = false; 62 ctx->insert_mode = false; 63 ctx->unicode_remaining = 0; 64 ctx->g_select = 0; 65 ctx->charsets[0] = CHARSET_DEFAULT; 66 ctx->charsets[1] = CHARSET_DEC_SPECIAL; 67 ctx->current_charset = 0; 68 ctx->escape_offset = 0; 69 ctx->esc_values_i = 0; 70 ctx->saved_cursor_x = 0; 71 ctx->saved_cursor_y = 0; 72 ctx->current_primary = (size_t)-1; 73 ctx->current_bg = (size_t)-1; 74 ctx->scroll_top_margin = 0; 75 ctx->scroll_bottom_margin = ctx->rows; 76 ctx->oob_output = TERM_OOB_OUTPUT_ONLCR; 77} 78 79static void term_putchar(struct term_context *ctx, uint8_t c); 80 81void term_write(struct term_context *ctx, const char *buf, size_t count) { 82 for (size_t i = 0; i < count; i++) { 83 term_putchar(ctx, buf[i]); 84 } 85 86 if (ctx->autoflush) { 87 ctx->double_buffer_flush(ctx); 88 } 89} 90 91static void sgr(struct term_context *ctx) { 92 size_t i = 0; 93 94 if (!ctx->esc_values_i) 95 goto def; 96 97 for (; i < ctx->esc_values_i; i++) { 98 size_t offset; 99 100 if (ctx->esc_values[i] == 0) { 101def: 102 if (ctx->reverse_video) { 103 ctx->reverse_video = false; 104 ctx->swap_palette(ctx); 105 } 106 ctx->bold = false; 107 ctx->bg_bold = false; 108 ctx->current_primary = (size_t)-1; 109 ctx->current_bg = (size_t)-1; 110 ctx->set_text_bg_default(ctx); 111 ctx->set_text_fg_default(ctx); 112 continue; 113 } 114 115 else if (ctx->esc_values[i] == 1) { 116 ctx->bold = true; 117 if (ctx->current_primary != (size_t)-1) { 118 if (!ctx->reverse_video) { 119 ctx->set_text_fg_bright(ctx, ctx->current_primary); 120 } else { 121 ctx->set_text_bg_bright(ctx, ctx->current_primary); 122 } 123 } else { 124 if (!ctx->reverse_video) { 125 ctx->set_text_fg_default_bright(ctx); 126 } else { 127 ctx->set_text_bg_default_bright(ctx); 128 } 129 } 130 continue; 131 } 132 133 else if (ctx->esc_values[i] == 5) { 134 ctx->bg_bold = true; 135 if (ctx->current_bg != (size_t)-1) { 136 if (!ctx->reverse_video) { 137 ctx->set_text_bg_bright(ctx, ctx->current_bg); 138 } else { 139 ctx->set_text_fg_bright(ctx, ctx->current_bg); 140 } 141 } else { 142 if (!ctx->reverse_video) { 143 ctx->set_text_bg_default_bright(ctx); 144 } else { 145 ctx->set_text_fg_default_bright(ctx); 146 } 147 } 148 continue; 149 } 150 151 else if (ctx->esc_values[i] == 22) { 152 ctx->bold = false; 153 if (ctx->current_primary != (size_t)-1) { 154 if (!ctx->reverse_video) { 155 ctx->set_text_fg(ctx, ctx->current_primary); 156 } else { 157 ctx->set_text_bg(ctx, ctx->current_primary); 158 } 159 } else { 160 if (!ctx->reverse_video) { 161 ctx->set_text_fg_default(ctx); 162 } else { 163 ctx->set_text_bg_default(ctx); 164 } 165 } 166 continue; 167 } 168 169 else if (ctx->esc_values[i] == 25) { 170 ctx->bg_bold = false; 171 if (ctx->current_bg != (size_t)-1) { 172 if (!ctx->reverse_video) { 173 ctx->set_text_bg(ctx, ctx->current_bg); 174 } else { 175 ctx->set_text_fg(ctx, ctx->current_bg); 176 } 177 } else { 178 if (!ctx->reverse_video) { 179 ctx->set_text_bg_default(ctx); 180 } else { 181 ctx->set_text_fg_default(ctx); 182 } 183 } 184 continue; 185 } 186 187 else if (ctx->esc_values[i] >= 30 && ctx->esc_values[i] <= 37) { 188 offset = 30; 189 ctx->current_primary = ctx->esc_values[i] - offset; 190 191 if (ctx->reverse_video) { 192 goto set_bg; 193 } 194 195set_fg: 196 if ((ctx->bold && !ctx->reverse_video) 197 || (ctx->bg_bold && ctx->reverse_video)) { 198 ctx->set_text_fg_bright(ctx, ctx->esc_values[i] - offset); 199 } else { 200 ctx->set_text_fg(ctx, ctx->esc_values[i] - offset); 201 } 202 continue; 203 } 204 205 else if (ctx->esc_values[i] >= 40 && ctx->esc_values[i] <= 47) { 206 offset = 40; 207 ctx->current_bg = ctx->esc_values[i] - offset; 208 209 if (ctx->reverse_video) { 210 goto set_fg; 211 } 212 213set_bg: 214 if ((ctx->bold && ctx->reverse_video) 215 || (ctx->bg_bold && !ctx->reverse_video)) { 216 ctx->set_text_bg_bright(ctx, ctx->esc_values[i] - offset); 217 } else { 218 ctx->set_text_bg(ctx, ctx->esc_values[i] - offset); 219 } 220 continue; 221 } 222 223 else if (ctx->esc_values[i] >= 90 && ctx->esc_values[i] <= 97) { 224 offset = 90; 225 ctx->current_primary = ctx->esc_values[i] - offset; 226 227 if (ctx->reverse_video) { 228 goto set_bg_bright; 229 } 230 231set_fg_bright: 232 ctx->set_text_fg_bright(ctx, ctx->esc_values[i] - offset); 233 continue; 234 } 235 236 else if (ctx->esc_values[i] >= 100 && ctx->esc_values[i] <= 107) { 237 offset = 100; 238 ctx->current_bg = ctx->esc_values[i] - offset; 239 240 if (ctx->reverse_video) { 241 goto set_fg_bright; 242 } 243 244set_bg_bright: 245 ctx->set_text_bg_bright(ctx, ctx->esc_values[i] - offset); 246 continue; 247 } 248 249 else if (ctx->esc_values[i] == 39) { 250 ctx->current_primary = (size_t)-1; 251 252 if (ctx->reverse_video) { 253 ctx->swap_palette(ctx); 254 } 255 256 if (!ctx->bold) { 257 ctx->set_text_fg_default(ctx); 258 } else { 259 ctx->set_text_fg_default_bright(ctx); 260 } 261 262 if (ctx->reverse_video) { 263 ctx->swap_palette(ctx); 264 } 265 266 continue; 267 } 268 269 else if (ctx->esc_values[i] == 49) { 270 ctx->current_bg = (size_t)-1; 271 272 if (ctx->reverse_video) { 273 ctx->swap_palette(ctx); 274 } 275 276 if (!ctx->bg_bold) { 277 ctx->set_text_bg_default(ctx); 278 } else { 279 ctx->set_text_bg_default_bright(ctx); 280 } 281 282 if (ctx->reverse_video) { 283 ctx->swap_palette(ctx); 284 } 285 286 continue; 287 } 288 289 else if (ctx->esc_values[i] == 7) { 290 if (!ctx->reverse_video) { 291 ctx->reverse_video = true; 292 ctx->swap_palette(ctx); 293 } 294 continue; 295 } 296 297 else if (ctx->esc_values[i] == 27) { 298 if (ctx->reverse_video) { 299 ctx->reverse_video = false; 300 ctx->swap_palette(ctx); 301 } 302 continue; 303 } 304 305 // 256/RGB 306 else if (ctx->esc_values[i] == 38 || ctx->esc_values[i] == 48) { 307 bool fg = ctx->esc_values[i] == 38; 308 309 i++; 310 if (i >= ctx->esc_values_i) { 311 break; 312 } 313 314 switch (ctx->esc_values[i]) { 315 case 2: { // RGB 316 if (i + 3 >= ctx->esc_values_i) { 317 goto out; 318 } 319 320 uint32_t rgb_value = 0; 321 322 rgb_value |= ctx->esc_values[i + 1] << 16; 323 rgb_value |= ctx->esc_values[i + 2] << 8; 324 rgb_value |= ctx->esc_values[i + 3]; 325 326 i += 3; 327 328 (fg ? ctx->set_text_fg_rgb : ctx->set_text_bg_rgb)(ctx, rgb_value); 329 330 break; 331 } 332 case 5: { // 256 colors 333 if (i + 1 >= ctx->esc_values_i) { 334 goto out; 335 } 336 337 uint32_t col = ctx->esc_values[i + 1]; 338 339 i++; 340 341 if (col < 8) { 342 (fg ? ctx->set_text_fg : ctx->set_text_bg)(ctx, col); 343 } else if (col < 16) { 344 (fg ? ctx->set_text_fg_bright : ctx->set_text_bg_bright)(ctx, col - 8); 345 } else { 346 uint32_t rgb_value = col256[col - 16]; 347 (fg ? ctx->set_text_fg_rgb : ctx->set_text_bg_rgb)(ctx, rgb_value); 348 } 349 350 break; 351 } 352 default: continue; 353 } 354 } 355 } 356 357out:; 358} 359 360static void dec_private_parse(struct term_context *ctx, uint8_t c) { 361 ctx->dec_private = false; 362 363 if (ctx->esc_values_i == 0) { 364 return; 365 } 366 367 bool set; 368 369 switch (c) { 370 case 'h': 371 set = true; break; 372 case 'l': 373 set = false; break; 374 default: 375 return; 376 } 377 378 switch (ctx->esc_values[0]) { 379 case 25: { 380 if (set) { 381 ctx->cursor_enabled = true; 382 } else { 383 ctx->cursor_enabled = false; 384 } 385 return; 386 } 387 } 388 389 if (ctx->callback != NULL) { 390 ctx->callback(ctx, TERM_CB_DEC, ctx->esc_values_i, (uintptr_t)ctx->esc_values, c); 391 } 392} 393 394static void linux_private_parse(struct term_context *ctx) { 395 if (ctx->esc_values_i == 0) { 396 return; 397 } 398 399 if (ctx->callback != NULL) { 400 ctx->callback(ctx, TERM_CB_LINUX, ctx->esc_values_i, (uintptr_t)ctx->esc_values, 0); 401 } 402} 403 404static void mode_toggle(struct term_context *ctx, uint8_t c) { 405 if (ctx->esc_values_i == 0) { 406 return; 407 } 408 409 bool set; 410 411 switch (c) { 412 case 'h': 413 set = true; break; 414 case 'l': 415 set = false; break; 416 default: 417 return; 418 } 419 420 switch (ctx->esc_values[0]) { 421 case 4: 422 ctx->insert_mode = set; return; 423 } 424 425 if (ctx->callback != NULL) { 426 ctx->callback(ctx, TERM_CB_MODE, ctx->esc_values_i, (uintptr_t)ctx->esc_values, c); 427 } 428} 429 430static void osc_parse(struct term_context *ctx, uint8_t c) { 431 if (ctx->osc_escape && c == '\\') { 432 goto cleanup; 433 } 434 435 ctx->osc_escape = false; 436 437 switch (c) { 438 case 0x1b: 439 ctx->osc_escape = true; 440 break; 441 case '\a': 442 goto cleanup; 443 } 444 445 return; 446 447cleanup: 448 ctx->osc_escape = false; 449 ctx->osc = false; 450 ctx->escape = false; 451} 452 453static void control_sequence_parse(struct term_context *ctx, uint8_t c) { 454 if (ctx->escape_offset == 2) { 455 switch (c) { 456 case '[': 457 ctx->discard_next = true; 458 goto cleanup; 459 case '?': 460 ctx->dec_private = true; 461 return; 462 } 463 } 464 465 if (c >= '0' && c <= '9') { 466 if (ctx->esc_values_i == TERM_MAX_ESC_VALUES) { 467 return; 468 } 469 ctx->rrr = true; 470 ctx->esc_values[ctx->esc_values_i] *= 10; 471 ctx->esc_values[ctx->esc_values_i] += c - '0'; 472 return; 473 } 474 475 if (ctx->rrr == true) { 476 ctx->esc_values_i++; 477 ctx->rrr = false; 478 if (c == ';') 479 return; 480 } else if (c == ';') { 481 if (ctx->esc_values_i == TERM_MAX_ESC_VALUES) { 482 return; 483 } 484 ctx->esc_values[ctx->esc_values_i] = 0; 485 ctx->esc_values_i++; 486 return; 487 } 488 489 size_t esc_default; 490 switch (c) { 491 case 'J': case 'K': case 'q': 492 esc_default = 0; break; 493 default: 494 esc_default = 1; break; 495 } 496 497 for (size_t i = ctx->esc_values_i; i < TERM_MAX_ESC_VALUES; i++) { 498 ctx->esc_values[i] = esc_default; 499 } 500 501 if (ctx->dec_private == true) { 502 dec_private_parse(ctx, c); 503 goto cleanup; 504 } 505 506 bool r = ctx->scroll_enabled; 507 ctx->scroll_enabled = false; 508 size_t x, y; 509 ctx->get_cursor_pos(ctx, &x, &y); 510 511 switch (c) { 512 case 'F': 513 x = 0; 514 // FALLTHRU 515 case 'A': { 516 if (ctx->esc_values[0] > y) 517 ctx->esc_values[0] = y; 518 size_t orig_y = y; 519 size_t dest_y = y - ctx->esc_values[0]; 520 bool will_be_in_scroll_region = false; 521 if ((ctx->scroll_top_margin >= dest_y && ctx->scroll_top_margin <= orig_y) 522 || (ctx->scroll_bottom_margin >= dest_y && ctx->scroll_bottom_margin <= orig_y)) { 523 will_be_in_scroll_region = true; 524 } 525 if (will_be_in_scroll_region && dest_y < ctx->scroll_top_margin) { 526 dest_y = ctx->scroll_top_margin; 527 } 528 ctx->set_cursor_pos(ctx, x, dest_y); 529 break; 530 } 531 case 'E': 532 x = 0; 533 // FALLTHRU 534 case 'e': 535 case 'B': { 536 if (y + ctx->esc_values[0] > ctx->rows - 1) 537 ctx->esc_values[0] = (ctx->rows - 1) - y; 538 size_t orig_y = y; 539 size_t dest_y = y + ctx->esc_values[0]; 540 bool will_be_in_scroll_region = false; 541 if ((ctx->scroll_top_margin >= orig_y && ctx->scroll_top_margin <= dest_y) 542 || (ctx->scroll_bottom_margin >= orig_y && ctx->scroll_bottom_margin <= dest_y)) { 543 will_be_in_scroll_region = true; 544 } 545 if (will_be_in_scroll_region && dest_y >= ctx->scroll_bottom_margin) { 546 dest_y = ctx->scroll_bottom_margin - 1; 547 } 548 ctx->set_cursor_pos(ctx, x, dest_y); 549 break; 550 } 551 case 'a': 552 case 'C': 553 if (x + ctx->esc_values[0] > ctx->cols - 1) 554 ctx->esc_values[0] = (ctx->cols - 1) - x; 555 ctx->set_cursor_pos(ctx, x + ctx->esc_values[0], y); 556 break; 557 case 'D': 558 if (ctx->esc_values[0] > x) 559 ctx->esc_values[0] = x; 560 ctx->set_cursor_pos(ctx, x - ctx->esc_values[0], y); 561 break; 562 case 'c': 563 if (ctx->callback != NULL) { 564 ctx->callback(ctx, TERM_CB_PRIVATE_ID, 0, 0, 0); 565 } 566 break; 567 case 'd': 568 ctx->esc_values[0] -= 1; 569 if (ctx->esc_values[0] >= ctx->rows) 570 ctx->esc_values[0] = ctx->rows - 1; 571 ctx->set_cursor_pos(ctx, x, ctx->esc_values[0]); 572 break; 573 case 'G': 574 case '`': 575 ctx->esc_values[0] -= 1; 576 if (ctx->esc_values[0] >= ctx->cols) 577 ctx->esc_values[0] = ctx->cols - 1; 578 ctx->set_cursor_pos(ctx, ctx->esc_values[0], y); 579 break; 580 case 'H': 581 case 'f': 582 if (ctx->esc_values[0] != 0) { 583 ctx->esc_values[0]--; 584 } 585 if (ctx->esc_values[1] != 0) { 586 ctx->esc_values[1]--; 587 } 588 if (ctx->esc_values[1] >= ctx->cols) 589 ctx->esc_values[1] = ctx->cols - 1; 590 if (ctx->esc_values[0] >= ctx->rows) 591 ctx->esc_values[0] = ctx->rows - 1; 592 ctx->set_cursor_pos(ctx, ctx->esc_values[1], ctx->esc_values[0]); 593 break; 594 case 'M': 595 for (size_t i = 0; i < ctx->esc_values[0]; i++) { 596 ctx->scroll(ctx); 597 } 598 break; 599 case 'L': { 600 size_t old_scroll_top_margin = ctx->scroll_top_margin; 601 ctx->scroll_top_margin = y; 602 for (size_t i = 0; i < ctx->esc_values[0]; i++) { 603 ctx->revscroll(ctx); 604 } 605 ctx->scroll_top_margin = old_scroll_top_margin; 606 break; 607 } 608 case 'n': 609 switch (ctx->esc_values[0]) { 610 case 5: 611 if (ctx->callback != NULL) { 612 ctx->callback(ctx, TERM_CB_STATUS_REPORT, 0, 0, 0); 613 } 614 break; 615 case 6: 616 if (ctx->callback != NULL) { 617 ctx->callback(ctx, TERM_CB_POS_REPORT, x + 1, y + 1, 0); 618 } 619 break; 620 } 621 break; 622 case 'q': 623 if (ctx->callback != NULL) { 624 ctx->callback(ctx, TERM_CB_KBD_LEDS, ctx->esc_values[0], 0, 0); 625 } 626 break; 627 case 'J': 628 switch (ctx->esc_values[0]) { 629 case 0: { 630 size_t rows_remaining = ctx->rows - (y + 1); 631 size_t cols_diff = ctx->cols - (x + 1); 632 size_t to_clear = rows_remaining * ctx->cols + cols_diff + 1; 633 for (size_t i = 0; i < to_clear; i++) { 634 ctx->raw_putchar(ctx, ' '); 635 } 636 ctx->set_cursor_pos(ctx, x, y); 637 break; 638 } 639 case 1: { 640 ctx->set_cursor_pos(ctx, 0, 0); 641 bool b = false; 642 for (size_t yc = 0; yc < ctx->rows; yc++) { 643 for (size_t xc = 0; xc < ctx->cols; xc++) { 644 ctx->raw_putchar(ctx, ' '); 645 if (xc == x && yc == y) { 646 ctx->set_cursor_pos(ctx, x, y); 647 b = true; 648 break; 649 } 650 } 651 if (b == true) 652 break; 653 } 654 break; 655 } 656 case 2: 657 case 3: 658 ctx->clear(ctx, false); 659 break; 660 } 661 break; 662 case '@': 663 for (size_t i = ctx->cols - 1; ; i--) { 664 ctx->move_character(ctx, i + ctx->esc_values[0], y, i, y); 665 ctx->set_cursor_pos(ctx, i, y); 666 ctx->raw_putchar(ctx, ' '); 667 if (i == x) { 668 break; 669 } 670 } 671 ctx->set_cursor_pos(ctx, x, y); 672 break; 673 case 'P': 674 for (size_t i = x + ctx->esc_values[0]; i < ctx->cols; i++) 675 ctx->move_character(ctx, i - ctx->esc_values[0], y, i, y); 676 ctx->set_cursor_pos(ctx, ctx->cols - ctx->esc_values[0], y); 677 // FALLTHRU 678 case 'X': 679 for (size_t i = 0; i < ctx->esc_values[0]; i++) 680 ctx->raw_putchar(ctx, ' '); 681 ctx->set_cursor_pos(ctx, x, y); 682 break; 683 case 'm': 684 sgr(ctx); 685 break; 686 case 's': 687 ctx->get_cursor_pos(ctx, &ctx->saved_cursor_x, &ctx->saved_cursor_y); 688 break; 689 case 'u': 690 ctx->set_cursor_pos(ctx, ctx->saved_cursor_x, ctx->saved_cursor_y); 691 break; 692 case 'K': 693 switch (ctx->esc_values[0]) { 694 case 0: { 695 for (size_t i = x; i < ctx->cols; i++) 696 ctx->raw_putchar(ctx, ' '); 697 ctx->set_cursor_pos(ctx, x, y); 698 break; 699 } 700 case 1: { 701 ctx->set_cursor_pos(ctx, 0, y); 702 for (size_t i = 0; i < x; i++) 703 ctx->raw_putchar(ctx, ' '); 704 break; 705 } 706 case 2: { 707 ctx->set_cursor_pos(ctx, 0, y); 708 for (size_t i = 0; i < ctx->cols; i++) 709 ctx->raw_putchar(ctx, ' '); 710 ctx->set_cursor_pos(ctx, x, y); 711 break; 712 } 713 } 714 break; 715 case 'r': 716 if (ctx->esc_values[0] == 0) { 717 ctx->esc_values[0] = 1; 718 } 719 if (ctx->esc_values[1] == 0) { 720 ctx->esc_values[1] = 1; 721 } 722 ctx->scroll_top_margin = 0; 723 ctx->scroll_bottom_margin = ctx->rows; 724 if (ctx->esc_values_i > 0) { 725 ctx->scroll_top_margin = ctx->esc_values[0] - 1; 726 } 727 if (ctx->esc_values_i > 1) { 728 ctx->scroll_bottom_margin = ctx->esc_values[1]; 729 } 730 if (ctx->scroll_top_margin >= ctx->rows 731 || ctx->scroll_bottom_margin > ctx->rows 732 || ctx->scroll_top_margin >= (ctx->scroll_bottom_margin - 1)) { 733 ctx->scroll_top_margin = 0; 734 ctx->scroll_bottom_margin = ctx->rows; 735 } 736 ctx->set_cursor_pos(ctx, 0, 0); 737 break; 738 case 'l': 739 case 'h': 740 mode_toggle(ctx, c); 741 break; 742 case ']': 743 linux_private_parse(ctx); 744 break; 745 } 746 747 ctx->scroll_enabled = r; 748 749cleanup: 750 ctx->control_sequence = false; 751 ctx->escape = false; 752} 753 754static void restore_state(struct term_context *ctx) { 755 ctx->bold = ctx->saved_state_bold; 756 ctx->bg_bold = ctx->saved_state_bg_bold; 757 ctx->reverse_video = ctx->saved_state_reverse_video; 758 ctx->current_charset = ctx->saved_state_current_charset; 759 ctx->current_primary = ctx->saved_state_current_primary; 760 ctx->current_bg = ctx->saved_state_current_bg; 761 762 ctx->restore_state(ctx); 763} 764 765static void save_state(struct term_context *ctx) { 766 ctx->save_state(ctx); 767 768 ctx->saved_state_bold = ctx->bold; 769 ctx->saved_state_bg_bold = ctx->bg_bold; 770 ctx->saved_state_reverse_video = ctx->reverse_video; 771 ctx->saved_state_current_charset = ctx->current_charset; 772 ctx->saved_state_current_primary = ctx->current_primary; 773 ctx->saved_state_current_bg = ctx->current_bg; 774} 775 776static void escape_parse(struct term_context *ctx, uint8_t c) { 777 ctx->escape_offset++; 778 779 if (ctx->osc == true) { 780 osc_parse(ctx, c); 781 return; 782 } 783 784 if (ctx->control_sequence == true) { 785 control_sequence_parse(ctx, c); 786 return; 787 } 788 789 if (ctx->csi == true) { 790 ctx->csi = false; 791 goto is_csi; 792 } 793 794 size_t x, y; 795 ctx->get_cursor_pos(ctx, &x, &y); 796 797 switch (c) { 798 case ']': 799 ctx->osc_escape = false; 800 ctx->osc = true; 801 return; 802 case '[': 803is_csi: 804 for (size_t i = 0; i < TERM_MAX_ESC_VALUES; i++) 805 ctx->esc_values[i] = 0; 806 ctx->esc_values_i = 0; 807 ctx->rrr = false; 808 ctx->control_sequence = true; 809 return; 810 case '7': 811 save_state(ctx); 812 break; 813 case '8': 814 restore_state(ctx); 815 break; 816 case 'c': 817 term_context_reinit(ctx); 818 ctx->clear(ctx, true); 819 break; 820 case 'D': 821 if (y == ctx->scroll_bottom_margin - 1) { 822 ctx->scroll(ctx); 823 ctx->set_cursor_pos(ctx, x, y); 824 } else { 825 ctx->set_cursor_pos(ctx, x, y + 1); 826 } 827 break; 828 case 'E': 829 if (y == ctx->scroll_bottom_margin - 1) { 830 ctx->scroll(ctx); 831 ctx->set_cursor_pos(ctx, 0, y); 832 } else { 833 ctx->set_cursor_pos(ctx, 0, y + 1); 834 } 835 break; 836 case 'M': 837 // "Reverse linefeed" 838 if (y == ctx->scroll_top_margin) { 839 ctx->revscroll(ctx); 840 ctx->set_cursor_pos(ctx, 0, y); 841 } else { 842 ctx->set_cursor_pos(ctx, 0, y - 1); 843 } 844 break; 845 case 'Z': 846 if (ctx->callback != NULL) { 847 ctx->callback(ctx, TERM_CB_PRIVATE_ID, 0, 0, 0); 848 } 849 break; 850 case '(': 851 case ')': 852 ctx->g_select = c - '\''; 853 break; 854 case 0x1b: 855 if (ctx->in_bootloader == true) { 856 ctx->raw_putchar(ctx, c); 857 } 858 break; 859 } 860 861 ctx->escape = false; 862} 863 864static uint8_t dec_special_to_cp437(uint8_t c) { 865 switch (c) { 866 case '`': return 0x04; 867 case '0': return 0xdb; 868 case '-': return 0x18; 869 case ',': return 0x1b; 870 case '.': return 0x19; 871 case 'a': return 0xb1; 872 case 'f': return 0xf8; 873 case 'g': return 0xf1; 874 case 'h': return 0xb0; 875 case 'j': return 0xd9; 876 case 'k': return 0xbf; 877 case 'l': return 0xda; 878 case 'm': return 0xc0; 879 case 'n': return 0xc5; 880 case 'q': return 0xc4; 881 case 's': return 0x5f; 882 case 't': return 0xc3; 883 case 'u': return 0xb4; 884 case 'v': return 0xc1; 885 case 'w': return 0xc2; 886 case 'x': return 0xb3; 887 case 'y': return 0xf3; 888 case 'z': return 0xf2; 889 case '~': return 0xfa; 890 case '_': return 0xff; 891 case '+': return 0x1a; 892 case '{': return 0xe3; 893 case '}': return 0x9c; 894 } 895 896 return c; 897} 898 899// Following wcwidth related code inherited from: 900// https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c 901 902struct interval { 903 wchar_t first; 904 wchar_t last; 905}; 906 907/* auxiliary function for binary search in interval table */ 908static int bisearch(wchar_t ucs, const struct interval *table, int max) { 909 int min = 0; 910 int mid; 911 912 if (ucs < table[0].first || ucs > table[max].last) 913 return 0; 914 while (max >= min) { 915 mid = (min + max) / 2; 916 if (ucs > table[mid].last) 917 min = mid + 1; 918 else if (ucs < table[mid].first) 919 max = mid - 1; 920 else 921 return 1; 922 } 923 924 return 0; 925} 926 927int mk_wcwidth(wchar_t ucs) { 928 /* sorted list of non-overlapping intervals of non-spacing characters */ 929 /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ 930 static const struct interval combining[] = { 931 { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, 932 { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, 933 { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, 934 { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, 935 { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, 936 { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, 937 { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, 938 { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, 939 { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, 940 { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, 941 { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, 942 { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, 943 { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, 944 { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, 945 { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, 946 { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, 947 { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, 948 { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, 949 { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, 950 { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, 951 { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, 952 { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, 953 { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, 954 { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, 955 { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, 956 { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, 957 { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, 958 { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, 959 { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, 960 { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, 961 { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, 962 { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, 963 { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, 964 { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, 965 { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, 966 { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, 967 { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, 968 { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, 969 { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, 970 { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, 971 { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, 972 { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, 973 { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, 974 { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, 975 { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, 976 { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, 977 { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, 978 { 0xE0100, 0xE01EF } 979 }; 980 981 /* test for 8-bit control characters */ 982 if (ucs == 0) 983 return 0; 984 if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) 985 return 1; 986 987 /* binary search in table of non-spacing characters */ 988 if (bisearch(ucs, combining, 989 sizeof(combining) / sizeof(struct interval) - 1)) 990 return 0; 991 992 /* if we arrive here, ucs is not a combining or C0/C1 control character */ 993 994 return 1 + 995 (ucs >= 0x1100 && 996 (ucs <= 0x115f || /* Hangul Jamo init. consonants */ 997 ucs == 0x2329 || ucs == 0x232a || 998 (ucs >= 0x2e80 && ucs <= 0xa4cf && 999 ucs != 0x303f) || /* CJK ... Yi */ 1000 (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ 1001 (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ 1002 (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ 1003 (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ 1004 (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ 1005 (ucs >= 0xffe0 && ucs <= 0xffe6) || 1006 (ucs >= 0x20000 && ucs <= 0x2fffd) || 1007 (ucs >= 0x30000 && ucs <= 0x3fffd))); 1008} 1009 1010// End of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c inherited code 1011 1012static int unicode_to_cp437(uint64_t code_point) { 1013 switch (code_point) { 1014 case 0x263a: return 1; 1015 case 0x263b: return 2; 1016 case 0x2665: return 3; 1017 case 0x2666: return 4; 1018 case 0x2663: return 5; 1019 case 0x2660: return 6; 1020 case 0x2022: return 7; 1021 case 0x25d8: return 8; 1022 case 0x25cb: return 9; 1023 case 0x25d9: return 10; 1024 case 0x2642: return 11; 1025 case 0x2640: return 12; 1026 case 0x266a: return 13; 1027 case 0x266b: return 14; 1028 case 0x263c: return 15; 1029 case 0x25ba: return 16; 1030 case 0x25c4: return 17; 1031 case 0x2195: return 18; 1032 case 0x203c: return 19; 1033 case 0x00b6: return 20; 1034 case 0x00a7: return 21; 1035 case 0x25ac: return 22; 1036 case 0x21a8: return 23; 1037 case 0x2191: return 24; 1038 case 0x2193: return 25; 1039 case 0x2192: return 26; 1040 case 0x2190: return 27; 1041 case 0x221f: return 28; 1042 case 0x2194: return 29; 1043 case 0x25b2: return 30; 1044 case 0x25bc: return 31; 1045 1046 case 0x2302: return 127; 1047 case 0x00c7: return 128; 1048 case 0x00fc: return 129; 1049 case 0x00e9: return 130; 1050 case 0x00e2: return 131; 1051 case 0x00e4: return 132; 1052 case 0x00e0: return 133; 1053 case 0x00e5: return 134; 1054 case 0x00e7: return 135; 1055 case 0x00ea: return 136; 1056 case 0x00eb: return 137; 1057 case 0x00e8: return 138; 1058 case 0x00ef: return 139; 1059 case 0x00ee: return 140; 1060 case 0x00ec: return 141; 1061 case 0x00c4: return 142; 1062 case 0x00c5: return 143; 1063 case 0x00c9: return 144; 1064 case 0x00e6: return 145; 1065 case 0x00c6: return 146; 1066 case 0x00f4: return 147; 1067 case 0x00f6: return 148; 1068 case 0x00f2: return 149; 1069 case 0x00fb: return 150; 1070 case 0x00f9: return 151; 1071 case 0x00ff: return 152; 1072 case 0x00d6: return 153; 1073 case 0x00dc: return 154; 1074 case 0x00a2: return 155; 1075 case 0x00a3: return 156; 1076 case 0x00a5: return 157; 1077 case 0x20a7: return 158; 1078 case 0x0192: return 159; 1079 case 0x00e1: return 160; 1080 case 0x00ed: return 161; 1081 case 0x00f3: return 162; 1082 case 0x00fa: return 163; 1083 case 0x00f1: return 164; 1084 case 0x00d1: return 165; 1085 case 0x00aa: return 166; 1086 case 0x00ba: return 167; 1087 case 0x00bf: return 168; 1088 case 0x2310: return 169; 1089 case 0x00ac: return 170; 1090 case 0x00bd: return 171; 1091 case 0x00bc: return 172; 1092 case 0x00a1: return 173; 1093 case 0x00ab: return 174; 1094 case 0x00bb: return 175; 1095 case 0x2591: return 176; 1096 case 0x2592: return 177; 1097 case 0x2593: return 178; 1098 case 0x2502: return 179; 1099 case 0x2524: return 180; 1100 case 0x2561: return 181; 1101 case 0x2562: return 182; 1102 case 0x2556: return 183; 1103 case 0x2555: return 184; 1104 case 0x2563: return 185; 1105 case 0x2551: return 186; 1106 case 0x2557: return 187; 1107 case 0x255d: return 188; 1108 case 0x255c: return 189; 1109 case 0x255b: return 190; 1110 case 0x2510: return 191; 1111 case 0x2514: return 192; 1112 case 0x2534: return 193; 1113 case 0x252c: return 194; 1114 case 0x251c: return 195; 1115 case 0x2500: return 196; 1116 case 0x253c: return 197; 1117 case 0x255e: return 198; 1118 case 0x255f: return 199; 1119 case 0x255a: return 200; 1120 case 0x2554: return 201; 1121 case 0x2569: return 202; 1122 case 0x2566: return 203; 1123 case 0x2560: return 204; 1124 case 0x2550: return 205; 1125 case 0x256c: return 206; 1126 case 0x2567: return 207; 1127 case 0x2568: return 208; 1128 case 0x2564: return 209; 1129 case 0x2565: return 210; 1130 case 0x2559: return 211; 1131 case 0x2558: return 212; 1132 case 0x2552: return 213; 1133 case 0x2553: return 214; 1134 case 0x256b: return 215; 1135 case 0x256a: return 216; 1136 case 0x2518: return 217; 1137 case 0x250c: return 218; 1138 case 0x2588: return 219; 1139 case 0x2584: return 220; 1140 case 0x258c: return 221; 1141 case 0x2590: return 222; 1142 case 0x2580: return 223; 1143 case 0x03b1: return 224; 1144 case 0x00df: return 225; 1145 case 0x0393: return 226; 1146 case 0x03c0: return 227; 1147 case 0x03a3: return 228; 1148 case 0x03c3: return 229; 1149 case 0x00b5: return 230; 1150 case 0x03c4: return 231; 1151 case 0x03a6: return 232; 1152 case 0x0398: return 233; 1153 case 0x03a9: return 234; 1154 case 0x03b4: return 235; 1155 case 0x221e: return 236; 1156 case 0x03c6: return 237; 1157 case 0x03b5: return 238; 1158 case 0x2229: return 239; 1159 case 0x2261: return 240; 1160 case 0x00b1: return 241; 1161 case 0x2265: return 242; 1162 case 0x2264: return 243; 1163 case 0x2320: return 244; 1164 case 0x2321: return 245; 1165 case 0x00f7: return 246; 1166 case 0x2248: return 247; 1167 case 0x00b0: return 248; 1168 case 0x2219: return 249; 1169 case 0x00b7: return 250; 1170 case 0x221a: return 251; 1171 case 0x207f: return 252; 1172 case 0x00b2: return 253; 1173 case 0x25a0: return 254; 1174 } 1175 1176 return -1; 1177} 1178 1179static void term_putchar(struct term_context *ctx, uint8_t c) { 1180 if (ctx->discard_next || (ctx->in_bootloader == false && (c == 0x18 || c == 0x1a))) { 1181 ctx->discard_next = false; 1182 ctx->escape = false; 1183 ctx->csi = false; 1184 ctx->control_sequence = false; 1185 ctx->unicode_remaining = 0; 1186 ctx->osc = false; 1187 ctx->osc_escape = false; 1188 ctx->g_select = 0; 1189 return; 1190 } 1191 1192 if (ctx->unicode_remaining != 0) { 1193 if ((c & 0xc0) != 0x80) { 1194 ctx->unicode_remaining = 0; 1195 goto unicode_error; 1196 } 1197 1198 ctx->unicode_remaining--; 1199 ctx->code_point |= (c & 0x3f) << (6 * ctx->unicode_remaining); 1200 if (ctx->unicode_remaining != 0) { 1201 return; 1202 } 1203 1204 int cc = unicode_to_cp437(ctx->code_point); 1205 1206 if (cc == -1) { 1207 size_t replacement_width = mk_wcwidth(ctx->code_point); 1208 if (replacement_width > 0) { 1209 ctx->raw_putchar(ctx, 0xfe); 1210 } 1211 for (size_t i = 1; i < replacement_width; i++) { 1212 ctx->raw_putchar(ctx, ' '); 1213 } 1214 } else { 1215 ctx->raw_putchar(ctx, cc); 1216 } 1217 return; 1218 } 1219 1220unicode_error: 1221 if (c >= 0xc0 && c <= 0xf7 && ctx->in_bootloader == false) { 1222 if (c >= 0xc0 && c <= 0xdf) { 1223 ctx->unicode_remaining = 1; 1224 ctx->code_point = (c & 0x1f) << 6; 1225 } else if (c >= 0xe0 && c <= 0xef) { 1226 ctx->unicode_remaining = 2; 1227 ctx->code_point = (c & 0x0f) << (6 * 2); 1228 } else if (c >= 0xf0 && c <= 0xf7) { 1229 ctx->unicode_remaining = 3; 1230 ctx->code_point = (c & 0x07) << (6 * 3); 1231 } 1232 return; 1233 } 1234 1235 if (ctx->escape == true) { 1236 escape_parse(ctx, c); 1237 return; 1238 } 1239 1240 if (ctx->g_select) { 1241 ctx->g_select--; 1242 switch (c) { 1243 case 'B': 1244 ctx->charsets[ctx->g_select] = CHARSET_DEFAULT; break; 1245 case '0': 1246 ctx->charsets[ctx->g_select] = CHARSET_DEC_SPECIAL; break; 1247 } 1248 ctx->g_select = 0; 1249 return; 1250 } 1251 1252 size_t x, y; 1253 ctx->get_cursor_pos(ctx, &x, &y); 1254 1255 switch (c) { 1256 case 0x00: 1257 case 0x7f: 1258 return; 1259 case 0x9b: 1260 ctx->csi = true; 1261 // FALLTHRU 1262 case 0x1b: 1263 ctx->escape_offset = 0; 1264 ctx->escape = true; 1265 return; 1266 case '\t': 1267 if ((x / ctx->tab_size + 1) >= ctx->cols) { 1268 ctx->set_cursor_pos(ctx, ctx->cols - 1, y); 1269 return; 1270 } 1271 ctx->set_cursor_pos(ctx, (x / ctx->tab_size + 1) * ctx->tab_size, y); 1272 return; 1273 case 0x0b: 1274 case 0x0c: 1275 case '\n': 1276 if (y == ctx->scroll_bottom_margin - 1) { 1277 ctx->scroll(ctx); 1278 ctx->set_cursor_pos(ctx, (ctx->oob_output & TERM_OOB_OUTPUT_ONLCR) ? 0 : x, y); 1279 } else { 1280 ctx->set_cursor_pos(ctx, (ctx->oob_output & TERM_OOB_OUTPUT_ONLCR) ? 0 : x, y + 1); 1281 } 1282 return; 1283 case '\b': 1284 ctx->set_cursor_pos(ctx, x - 1, y); 1285 return; 1286 case '\r': 1287 ctx->set_cursor_pos(ctx, 0, y); 1288 return; 1289 case '\a': 1290 // The bell is handled by the kernel 1291 if (ctx->callback != NULL) { 1292 ctx->callback(ctx, TERM_CB_BELL, 0, 0, 0); 1293 } 1294 return; 1295 case 14: 1296 // Move to G1 set 1297 ctx->current_charset = 1; 1298 return; 1299 case 15: 1300 // Move to G0 set 1301 ctx->current_charset = 0; 1302 return; 1303 } 1304 1305 if (ctx->insert_mode == true) { 1306 for (size_t i = ctx->cols - 1; ; i--) { 1307 ctx->move_character(ctx, i + 1, y, i, y); 1308 if (i == x) { 1309 break; 1310 } 1311 } 1312 } 1313 1314 // Translate character set 1315 switch (ctx->charsets[ctx->current_charset]) { 1316 case CHARSET_DEFAULT: 1317 break; 1318 case CHARSET_DEC_SPECIAL: 1319 c = dec_special_to_cp437(c); 1320 break; 1321 } 1322 1323 ctx->raw_putchar(ctx, c); 1324}