1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 static void teken_subr_cursor_up(teken_t *, unsigned int); 30 static void teken_subr_erase_line(const teken_t *, unsigned int); 31 static void teken_subr_regular_character(teken_t *, teken_char_t); 32 static void teken_subr_reset_to_initial_state(teken_t *); 33 static void teken_subr_save_cursor(teken_t *); 34 35 static inline int 36 teken_tab_isset(const teken_t *t, unsigned int col) 37 { 38 unsigned int b, o; 39 40 if (col >= T_NUMCOL) 41 return ((col % 8) == 0); 42 43 b = col / (sizeof(unsigned int) * 8); 44 o = col % (sizeof(unsigned int) * 8); 45 46 return (t->t_tabstops[b] & (1U << o)); 47 } 48 49 static inline void 50 teken_tab_clear(teken_t *t, unsigned int col) 51 { 52 unsigned int b, o; 53 54 if (col >= T_NUMCOL) 55 return; 56 57 b = col / (sizeof(unsigned int) * 8); 58 o = col % (sizeof(unsigned int) * 8); 59 60 t->t_tabstops[b] &= ~(1U << o); 61 } 62 63 static inline void 64 teken_tab_set(teken_t *t, unsigned int col) 65 { 66 unsigned int b, o; 67 68 if (col >= T_NUMCOL) 69 return; 70 71 b = col / (sizeof(unsigned int) * 8); 72 o = col % (sizeof(unsigned int) * 8); 73 74 t->t_tabstops[b] |= 1U << o; 75 } 76 77 static void 78 teken_tab_default(teken_t *t) 79 { 80 unsigned int i; 81 82 memset(t->t_tabstops, 0, T_NUMCOL / 8); 83 84 for (i = 8; i < T_NUMCOL; i += 8) 85 teken_tab_set(t, i); 86 } 87 88 static void 89 teken_subr_do_scroll(const teken_t *t, int amount) 90 { 91 teken_rect_t tr; 92 teken_pos_t tp; 93 94 teken_assert(t->t_cursor.tp_row <= t->t_winsize.tp_row); 95 teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row); 96 teken_assert(amount != 0); 97 98 /* Copy existing data 1 line up. */ 99 if (amount > 0) { 100 /* Scroll down. */ 101 102 /* Copy existing data up. */ 103 if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) { 104 tr.tr_begin.tp_row = t->t_scrollreg.ts_begin + amount; 105 tr.tr_begin.tp_col = 0; 106 tr.tr_end.tp_row = t->t_scrollreg.ts_end; 107 tr.tr_end.tp_col = t->t_winsize.tp_col; 108 tp.tp_row = t->t_scrollreg.ts_begin; 109 tp.tp_col = 0; 110 teken_funcs_copy(t, &tr, &tp); 111 112 tr.tr_begin.tp_row = t->t_scrollreg.ts_end - amount; 113 } else { 114 tr.tr_begin.tp_row = t->t_scrollreg.ts_begin; 115 } 116 117 /* Clear the last lines. */ 118 tr.tr_begin.tp_col = 0; 119 tr.tr_end.tp_row = t->t_scrollreg.ts_end; 120 tr.tr_end.tp_col = t->t_winsize.tp_col; 121 teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); 122 } else { 123 /* Scroll up. */ 124 amount = -amount; 125 126 /* Copy existing data down. */ 127 if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) { 128 tr.tr_begin.tp_row = t->t_scrollreg.ts_begin; 129 tr.tr_begin.tp_col = 0; 130 tr.tr_end.tp_row = t->t_scrollreg.ts_end - amount; 131 tr.tr_end.tp_col = t->t_winsize.tp_col; 132 tp.tp_row = t->t_scrollreg.ts_begin + amount; 133 tp.tp_col = 0; 134 teken_funcs_copy(t, &tr, &tp); 135 136 tr.tr_end.tp_row = t->t_scrollreg.ts_begin + amount; 137 } else { 138 tr.tr_end.tp_row = t->t_scrollreg.ts_end; 139 } 140 141 /* Clear the first lines. */ 142 tr.tr_begin.tp_row = t->t_scrollreg.ts_begin; 143 tr.tr_begin.tp_col = 0; 144 tr.tr_end.tp_col = t->t_winsize.tp_col; 145 teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); 146 } 147 } 148 149 static ssize_t 150 teken_subr_do_cpr(const teken_t *t, unsigned int cmd, char response[16]) 151 { 152 153 switch (cmd) { 154 case 5: /* Operating status. */ 155 strcpy(response, "0n"); 156 return (2); 157 case 6: { /* Cursor position. */ 158 int len; 159 160 len = snprintf(response, 16, "%u;%uR", 161 (t->t_cursor.tp_row - t->t_originreg.ts_begin) + 1, 162 t->t_cursor.tp_col + 1); 163 164 if (len >= 16) 165 return (-1); 166 return (len); 167 } 168 case 15: /* Printer status. */ 169 strcpy(response, "13n"); 170 return (3); 171 case 25: /* UDK status. */ 172 strcpy(response, "20n"); 173 return (3); 174 case 26: /* Keyboard status. */ 175 strcpy(response, "27;1n"); 176 return (5); 177 default: 178 teken_printf("Unknown DSR\n"); 179 return (-1); 180 } 181 } 182 183 static void 184 teken_subr_alignment_test(teken_t *t) 185 { 186 teken_rect_t tr; 187 188 t->t_cursor.tp_row = t->t_cursor.tp_col = 0; 189 t->t_scrollreg.ts_begin = 0; 190 t->t_scrollreg.ts_end = t->t_winsize.tp_row; 191 t->t_originreg = t->t_scrollreg; 192 t->t_stateflags &= ~(TS_WRAPPED|TS_ORIGIN); 193 teken_funcs_cursor(t); 194 195 tr.tr_begin.tp_row = 0; 196 tr.tr_begin.tp_col = 0; 197 tr.tr_end = t->t_winsize; 198 teken_funcs_fill(t, &tr, 'E', &t->t_defattr); 199 } 200 201 static void 202 teken_subr_backspace(teken_t *t) 203 { 204 205 if (t->t_stateflags & TS_CONS25) { 206 if (t->t_cursor.tp_col == 0) { 207 if (t->t_cursor.tp_row == t->t_originreg.ts_begin) 208 return; 209 t->t_cursor.tp_row--; 210 t->t_cursor.tp_col = t->t_winsize.tp_col - 1; 211 } else { 212 t->t_cursor.tp_col--; 213 } 214 } else { 215 if (t->t_cursor.tp_col == 0) 216 return; 217 218 t->t_cursor.tp_col--; 219 t->t_stateflags &= ~TS_WRAPPED; 220 } 221 222 teken_funcs_cursor(t); 223 } 224 225 static void 226 teken_subr_bell(const teken_t *t) 227 { 228 229 teken_funcs_bell(t); 230 } 231 232 static void 233 teken_subr_carriage_return(teken_t *t) 234 { 235 236 t->t_cursor.tp_col = 0; 237 t->t_stateflags &= ~TS_WRAPPED; 238 teken_funcs_cursor(t); 239 } 240 241 static void 242 teken_subr_cursor_backward(teken_t *t, unsigned int ncols) 243 { 244 245 if (ncols > t->t_cursor.tp_col) 246 t->t_cursor.tp_col = 0; 247 else 248 t->t_cursor.tp_col -= ncols; 249 t->t_stateflags &= ~TS_WRAPPED; 250 teken_funcs_cursor(t); 251 } 252 253 static void 254 teken_subr_cursor_backward_tabulation(teken_t *t, unsigned int ntabs) 255 { 256 257 do { 258 /* Stop when we've reached the beginning of the line. */ 259 if (t->t_cursor.tp_col == 0) 260 break; 261 262 t->t_cursor.tp_col--; 263 264 /* Tab marker set. */ 265 if (teken_tab_isset(t, t->t_cursor.tp_col)) 266 ntabs--; 267 } while (ntabs > 0); 268 269 teken_funcs_cursor(t); 270 } 271 272 static void 273 teken_subr_cursor_down(teken_t *t, unsigned int nrows) 274 { 275 276 if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) 277 t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1; 278 else 279 t->t_cursor.tp_row += nrows; 280 t->t_stateflags &= ~TS_WRAPPED; 281 teken_funcs_cursor(t); 282 } 283 284 static void 285 teken_subr_cursor_forward(teken_t *t, unsigned int ncols) 286 { 287 288 if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) 289 t->t_cursor.tp_col = t->t_winsize.tp_col - 1; 290 else 291 t->t_cursor.tp_col += ncols; 292 t->t_stateflags &= ~TS_WRAPPED; 293 teken_funcs_cursor(t); 294 } 295 296 static void 297 teken_subr_cursor_forward_tabulation(teken_t *t, unsigned int ntabs) 298 { 299 300 do { 301 /* Stop when we've reached the end of the line. */ 302 if (t->t_cursor.tp_col == t->t_winsize.tp_col - 1) 303 break; 304 305 t->t_cursor.tp_col++; 306 307 /* Tab marker set. */ 308 if (teken_tab_isset(t, t->t_cursor.tp_col)) 309 ntabs--; 310 } while (ntabs > 0); 311 312 teken_funcs_cursor(t); 313 } 314 315 static void 316 teken_subr_cursor_next_line(teken_t *t, unsigned int ncols) 317 { 318 319 t->t_cursor.tp_col = 0; 320 teken_subr_cursor_down(t, ncols); 321 } 322 323 static void 324 teken_subr_cursor_position(teken_t *t, unsigned int row, unsigned int col) 325 { 326 327 row = (row - 1) + t->t_originreg.ts_begin; 328 t->t_cursor.tp_row = row < t->t_originreg.ts_end ? 329 row : t->t_originreg.ts_end - 1; 330 331 col--; 332 t->t_cursor.tp_col = col < t->t_winsize.tp_col ? 333 col : t->t_winsize.tp_col - 1; 334 335 t->t_stateflags &= ~TS_WRAPPED; 336 teken_funcs_cursor(t); 337 } 338 339 static void 340 teken_subr_cursor_position_report(const teken_t *t, unsigned int cmd) 341 { 342 char response[18] = "\x1B["; 343 ssize_t len; 344 345 len = teken_subr_do_cpr(t, cmd, response + 2); 346 if (len < 0) 347 return; 348 349 teken_funcs_respond(t, response, len + 2); 350 } 351 352 static void 353 teken_subr_cursor_previous_line(teken_t *t, unsigned int ncols) 354 { 355 356 t->t_cursor.tp_col = 0; 357 teken_subr_cursor_up(t, ncols); 358 } 359 360 static void 361 teken_subr_cursor_up(teken_t *t, unsigned int nrows) 362 { 363 364 if (t->t_scrollreg.ts_begin + nrows >= t->t_cursor.tp_row) 365 t->t_cursor.tp_row = t->t_scrollreg.ts_begin; 366 else 367 t->t_cursor.tp_row -= nrows; 368 t->t_stateflags &= ~TS_WRAPPED; 369 teken_funcs_cursor(t); 370 } 371 372 static void 373 teken_subr_set_cursor_style(teken_t *t __unused, unsigned int style __unused) 374 { 375 376 /* TODO */ 377 378 /* 379 * CSI Ps SP q 380 * Set cursor style (DECSCUSR), VT520. 381 * Ps = 0 -> blinking block. 382 * Ps = 1 -> blinking block (default). 383 * Ps = 2 -> steady block. 384 * Ps = 3 -> blinking underline. 385 * Ps = 4 -> steady underline. 386 * Ps = 5 -> blinking bar (xterm). 387 * Ps = 6 -> steady bar (xterm). 388 */ 389 } 390 391 static void 392 teken_subr_delete_character(const teken_t *t, unsigned int ncols) 393 { 394 teken_rect_t tr; 395 396 tr.tr_begin.tp_row = t->t_cursor.tp_row; 397 tr.tr_end.tp_row = t->t_cursor.tp_row + 1; 398 tr.tr_end.tp_col = t->t_winsize.tp_col; 399 400 if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) { 401 tr.tr_begin.tp_col = t->t_cursor.tp_col; 402 } else { 403 /* Copy characters to the left. */ 404 tr.tr_begin.tp_col = t->t_cursor.tp_col + ncols; 405 teken_funcs_copy(t, &tr, &t->t_cursor); 406 407 tr.tr_begin.tp_col = t->t_winsize.tp_col - ncols; 408 } 409 410 /* Blank trailing columns. */ 411 teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); 412 } 413 414 static void 415 teken_subr_delete_line(const teken_t *t, unsigned int nrows) 416 { 417 teken_rect_t tr; 418 419 /* Ignore if outside scrolling region. */ 420 if (t->t_cursor.tp_row < t->t_scrollreg.ts_begin || 421 t->t_cursor.tp_row >= t->t_scrollreg.ts_end) 422 return; 423 424 tr.tr_begin.tp_col = 0; 425 tr.tr_end.tp_row = t->t_scrollreg.ts_end; 426 tr.tr_end.tp_col = t->t_winsize.tp_col; 427 428 if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) { 429 tr.tr_begin.tp_row = t->t_cursor.tp_row; 430 } else { 431 teken_pos_t tp; 432 433 /* Copy rows up. */ 434 tr.tr_begin.tp_row = t->t_cursor.tp_row + nrows; 435 tp.tp_row = t->t_cursor.tp_row; 436 tp.tp_col = 0; 437 teken_funcs_copy(t, &tr, &tp); 438 439 tr.tr_begin.tp_row = t->t_scrollreg.ts_end - nrows; 440 } 441 442 /* Blank trailing rows. */ 443 teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); 444 } 445 446 static void 447 teken_subr_device_control_string(teken_t *t) 448 { 449 450 teken_printf("Unsupported device control string\n"); 451 t->t_stateflags |= TS_INSTRING; 452 } 453 454 static void 455 teken_subr_device_status_report(const teken_t *t, unsigned int cmd) 456 { 457 char response[19] = "\x1B[?"; 458 ssize_t len; 459 460 len = teken_subr_do_cpr(t, cmd, response + 3); 461 if (len < 0) 462 return; 463 464 teken_funcs_respond(t, response, len + 3); 465 } 466 467 static void 468 teken_subr_double_height_double_width_line_top(const teken_t *t) 469 { 470 471 (void)t; 472 teken_printf("double height double width top\n"); 473 } 474 475 static void 476 teken_subr_double_height_double_width_line_bottom(const teken_t *t) 477 { 478 479 (void)t; 480 teken_printf("double height double width bottom\n"); 481 } 482 483 static void 484 teken_subr_erase_character(const teken_t *t, unsigned int ncols) 485 { 486 teken_rect_t tr; 487 488 tr.tr_begin = t->t_cursor; 489 tr.tr_end.tp_row = t->t_cursor.tp_row + 1; 490 491 if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) 492 tr.tr_end.tp_col = t->t_winsize.tp_col; 493 else 494 tr.tr_end.tp_col = t->t_cursor.tp_col + ncols; 495 496 teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); 497 } 498 499 static void 500 teken_subr_erase_display(const teken_t *t, unsigned int mode) 501 { 502 teken_rect_t r; 503 504 r.tr_begin.tp_col = 0; 505 r.tr_end.tp_col = t->t_winsize.tp_col; 506 507 switch (mode) { 508 case 1: /* Erase from the top to the cursor. */ 509 teken_subr_erase_line(t, 1); 510 511 /* Erase lines above. */ 512 if (t->t_cursor.tp_row == 0) 513 return; 514 r.tr_begin.tp_row = 0; 515 r.tr_end.tp_row = t->t_cursor.tp_row; 516 break; 517 case 2: /* Erase entire display. */ 518 r.tr_begin.tp_row = 0; 519 r.tr_end.tp_row = t->t_winsize.tp_row; 520 break; 521 default: /* Erase from cursor to the bottom. */ 522 teken_subr_erase_line(t, 0); 523 524 /* Erase lines below. */ 525 if (t->t_cursor.tp_row == t->t_winsize.tp_row - 1) 526 return; 527 r.tr_begin.tp_row = t->t_cursor.tp_row + 1; 528 r.tr_end.tp_row = t->t_winsize.tp_row; 529 break; 530 } 531 532 teken_funcs_fill(t, &r, BLANK, &t->t_curattr); 533 } 534 535 static void 536 teken_subr_erase_line(const teken_t *t, unsigned int mode) 537 { 538 teken_rect_t r; 539 540 r.tr_begin.tp_row = t->t_cursor.tp_row; 541 r.tr_end.tp_row = t->t_cursor.tp_row + 1; 542 543 switch (mode) { 544 case 1: /* Erase from the beginning of the line to the cursor. */ 545 r.tr_begin.tp_col = 0; 546 r.tr_end.tp_col = t->t_cursor.tp_col + 1; 547 break; 548 case 2: /* Erase entire line. */ 549 r.tr_begin.tp_col = 0; 550 r.tr_end.tp_col = t->t_winsize.tp_col; 551 break; 552 default: /* Erase from cursor to the end of the line. */ 553 r.tr_begin.tp_col = t->t_cursor.tp_col; 554 r.tr_end.tp_col = t->t_winsize.tp_col; 555 break; 556 } 557 558 teken_funcs_fill(t, &r, BLANK, &t->t_curattr); 559 } 560 561 static void 562 teken_subr_g0_scs_special_graphics(teken_t *t) 563 { 564 565 t->t_scs[0] = teken_scs_special_graphics; 566 } 567 568 static void 569 teken_subr_g0_scs_uk_national(teken_t *t) 570 { 571 572 t->t_scs[0] = teken_scs_uk_national; 573 } 574 575 static void 576 teken_subr_g0_scs_us_ascii(teken_t *t) 577 { 578 579 t->t_scs[0] = teken_scs_us_ascii; 580 } 581 582 static void 583 teken_subr_g1_scs_special_graphics(teken_t *t) 584 { 585 586 t->t_scs[1] = teken_scs_special_graphics; 587 } 588 589 static void 590 teken_subr_g1_scs_uk_national(teken_t *t) 591 { 592 593 t->t_scs[1] = teken_scs_uk_national; 594 } 595 596 static void 597 teken_subr_g1_scs_us_ascii(teken_t *t) 598 { 599 600 t->t_scs[1] = teken_scs_us_ascii; 601 } 602 603 static void 604 teken_subr_horizontal_position_absolute(teken_t *t, unsigned int col) 605 { 606 607 col--; 608 t->t_cursor.tp_col = col < t->t_winsize.tp_col ? 609 col : t->t_winsize.tp_col - 1; 610 611 t->t_stateflags &= ~TS_WRAPPED; 612 teken_funcs_cursor(t); 613 } 614 615 static void 616 teken_subr_horizontal_tab(teken_t *t) 617 { 618 619 teken_subr_cursor_forward_tabulation(t, 1); 620 } 621 622 static void 623 teken_subr_horizontal_tab_set(teken_t *t) 624 { 625 626 teken_tab_set(t, t->t_cursor.tp_col); 627 } 628 629 static void 630 teken_subr_index(teken_t *t) 631 { 632 633 if (t->t_cursor.tp_row < t->t_scrollreg.ts_end - 1) { 634 t->t_cursor.tp_row++; 635 t->t_stateflags &= ~TS_WRAPPED; 636 teken_funcs_cursor(t); 637 } else { 638 teken_subr_do_scroll(t, 1); 639 } 640 } 641 642 static void 643 teken_subr_insert_character(const teken_t *t, unsigned int ncols) 644 { 645 teken_rect_t tr; 646 647 tr.tr_begin = t->t_cursor; 648 tr.tr_end.tp_row = t->t_cursor.tp_row + 1; 649 650 if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) { 651 tr.tr_end.tp_col = t->t_winsize.tp_col; 652 } else { 653 teken_pos_t tp; 654 655 /* Copy characters to the right. */ 656 tr.tr_end.tp_col = t->t_winsize.tp_col - ncols; 657 tp.tp_row = t->t_cursor.tp_row; 658 tp.tp_col = t->t_cursor.tp_col + ncols; 659 teken_funcs_copy(t, &tr, &tp); 660 661 tr.tr_end.tp_col = t->t_cursor.tp_col + ncols; 662 } 663 664 /* Blank current location. */ 665 teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); 666 } 667 668 static void 669 teken_subr_insert_line(const teken_t *t, unsigned int nrows) 670 { 671 teken_rect_t tr; 672 673 /* Ignore if outside scrolling region. */ 674 if (t->t_cursor.tp_row < t->t_scrollreg.ts_begin || 675 t->t_cursor.tp_row >= t->t_scrollreg.ts_end) 676 return; 677 678 tr.tr_begin.tp_row = t->t_cursor.tp_row; 679 tr.tr_begin.tp_col = 0; 680 tr.tr_end.tp_col = t->t_winsize.tp_col; 681 682 if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) { 683 tr.tr_end.tp_row = t->t_scrollreg.ts_end; 684 } else { 685 teken_pos_t tp; 686 687 /* Copy lines down. */ 688 tr.tr_end.tp_row = t->t_scrollreg.ts_end - nrows; 689 tp.tp_row = t->t_cursor.tp_row + nrows; 690 tp.tp_col = 0; 691 teken_funcs_copy(t, &tr, &tp); 692 693 tr.tr_end.tp_row = t->t_cursor.tp_row + nrows; 694 } 695 696 /* Blank current location. */ 697 teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); 698 } 699 700 static void 701 teken_subr_keypad_application_mode(const teken_t *t) 702 { 703 704 teken_funcs_param(t, TP_KEYPADAPP, 1); 705 } 706 707 static void 708 teken_subr_keypad_numeric_mode(const teken_t *t) 709 { 710 711 teken_funcs_param(t, TP_KEYPADAPP, 0); 712 } 713 714 static void 715 teken_subr_newline(teken_t *t) 716 { 717 718 t->t_cursor.tp_row++; 719 720 if (t->t_cursor.tp_row >= t->t_scrollreg.ts_end) { 721 teken_subr_do_scroll(t, 1); 722 t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1; 723 } 724 725 t->t_stateflags &= ~TS_WRAPPED; 726 teken_funcs_cursor(t); 727 } 728 729 static void 730 teken_subr_newpage(teken_t *t) 731 { 732 733 if (t->t_stateflags & TS_CONS25) { 734 teken_rect_t tr; 735 736 /* Clear screen. */ 737 tr.tr_begin.tp_row = t->t_originreg.ts_begin; 738 tr.tr_begin.tp_col = 0; 739 tr.tr_end.tp_row = t->t_originreg.ts_end; 740 tr.tr_end.tp_col = t->t_winsize.tp_col; 741 teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); 742 743 /* Cursor at top left. */ 744 t->t_cursor.tp_row = t->t_originreg.ts_begin; 745 t->t_cursor.tp_col = 0; 746 t->t_stateflags &= ~TS_WRAPPED; 747 teken_funcs_cursor(t); 748 } else { 749 teken_subr_newline(t); 750 } 751 } 752 753 static void 754 teken_subr_next_line(teken_t *t) 755 { 756 757 t->t_cursor.tp_col = 0; 758 teken_subr_newline(t); 759 } 760 761 static void 762 teken_subr_operating_system_command(teken_t *t) 763 { 764 765 teken_printf("Unsupported operating system command\n"); 766 t->t_stateflags |= TS_INSTRING; 767 } 768 769 static void 770 teken_subr_pan_down(const teken_t *t, unsigned int nrows) 771 { 772 773 teken_subr_do_scroll(t, (int)nrows); 774 } 775 776 static void 777 teken_subr_pan_up(const teken_t *t, unsigned int nrows) 778 { 779 780 teken_subr_do_scroll(t, -(int)nrows); 781 } 782 783 static void 784 teken_subr_primary_device_attributes(const teken_t *t, unsigned int request) 785 { 786 787 if (request == 0) { 788 const char response[] = "\x1B[?1;2c"; 789 790 teken_funcs_respond(t, response, sizeof response - 1); 791 } else { 792 teken_printf("Unknown DA1\n"); 793 } 794 } 795 796 static void 797 teken_subr_do_putchar(teken_t *t, const teken_pos_t *tp, teken_char_t c, 798 int width) 799 { 800 801 t->t_last = c; 802 if (t->t_stateflags & TS_INSERT && 803 tp->tp_col < t->t_winsize.tp_col - width) { 804 teken_rect_t ctr; 805 teken_pos_t ctp; 806 807 /* Insert mode. Move existing characters to the right. */ 808 ctr.tr_begin = *tp; 809 ctr.tr_end.tp_row = tp->tp_row + 1; 810 ctr.tr_end.tp_col = t->t_winsize.tp_col - width; 811 ctp.tp_row = tp->tp_row; 812 ctp.tp_col = tp->tp_col + width; 813 teken_funcs_copy(t, &ctr, &ctp); 814 } 815 816 teken_funcs_putchar(t, tp, c, &t->t_curattr); 817 818 if (width == 2 && tp->tp_col + 1 < t->t_winsize.tp_col) { 819 teken_pos_t tp2; 820 teken_attr_t attr; 821 822 /* Print second half of CJK fullwidth character. */ 823 tp2.tp_row = tp->tp_row; 824 tp2.tp_col = tp->tp_col + 1; 825 attr = t->t_curattr; 826 attr.ta_format |= TF_CJK_RIGHT; 827 teken_funcs_putchar(t, &tp2, c, &attr); 828 } 829 } 830 831 static void 832 teken_subr_regular_character(teken_t *t, teken_char_t c) 833 { 834 int width; 835 836 if (t->t_stateflags & TS_8BIT) { 837 if (!(t->t_stateflags & TS_CONS25) && (c <= 0x1b || c == 0x7f)) 838 return; 839 c = teken_scs_process(t, c); 840 width = 1; 841 } else { 842 c = teken_scs_process(t, c); 843 width = teken_wcwidth(c); 844 /* XXX: Don't process zero-width characters yet. */ 845 if (width <= 0) 846 return; 847 } 848 849 if (t->t_stateflags & TS_CONS25) { 850 teken_subr_do_putchar(t, &t->t_cursor, c, width); 851 t->t_cursor.tp_col += width; 852 853 if (t->t_cursor.tp_col >= t->t_winsize.tp_col) { 854 if (t->t_cursor.tp_row == t->t_scrollreg.ts_end - 1) { 855 /* Perform scrolling. */ 856 teken_subr_do_scroll(t, 1); 857 } else { 858 /* No scrolling needed. */ 859 if (t->t_cursor.tp_row < 860 t->t_winsize.tp_row - 1) 861 t->t_cursor.tp_row++; 862 } 863 t->t_cursor.tp_col = 0; 864 } 865 } else if (t->t_stateflags & TS_AUTOWRAP && 866 ((t->t_stateflags & TS_WRAPPED && 867 t->t_cursor.tp_col + 1 == t->t_winsize.tp_col) || 868 t->t_cursor.tp_col + width > t->t_winsize.tp_col)) { 869 teken_pos_t tp; 870 871 /* 872 * Perform line wrapping, if: 873 * - Autowrapping is enabled, and 874 * - We're in the wrapped state at the last column, or 875 * - The character to be printed does not fit anymore. 876 */ 877 if (t->t_cursor.tp_row == t->t_scrollreg.ts_end - 1) { 878 /* Perform scrolling. */ 879 teken_subr_do_scroll(t, 1); 880 tp.tp_row = t->t_scrollreg.ts_end - 1; 881 } else { 882 /* No scrolling needed. */ 883 tp.tp_row = t->t_cursor.tp_row + 1; 884 if (tp.tp_row == t->t_winsize.tp_row) { 885 /* 886 * Corner case: regular character 887 * outside scrolling region, but at the 888 * bottom of the screen. 889 */ 890 teken_subr_do_putchar(t, &t->t_cursor, 891 c, width); 892 return; 893 } 894 } 895 896 tp.tp_col = 0; 897 teken_subr_do_putchar(t, &tp, c, width); 898 899 t->t_cursor.tp_row = tp.tp_row; 900 t->t_cursor.tp_col = width; 901 t->t_stateflags &= ~TS_WRAPPED; 902 } else { 903 /* No line wrapping needed. */ 904 teken_subr_do_putchar(t, &t->t_cursor, c, width); 905 t->t_cursor.tp_col += width; 906 907 if (t->t_cursor.tp_col >= t->t_winsize.tp_col) { 908 t->t_stateflags |= TS_WRAPPED; 909 t->t_cursor.tp_col = t->t_winsize.tp_col - 1; 910 } else { 911 t->t_stateflags &= ~TS_WRAPPED; 912 } 913 } 914 915 teken_funcs_cursor(t); 916 } 917 918 static void 919 teken_subr_reset_dec_mode(teken_t *t, unsigned int cmd) 920 { 921 922 switch (cmd) { 923 case 1: /* Cursor keys mode. */ 924 t->t_stateflags &= ~TS_CURSORKEYS; 925 break; 926 case 2: /* DECANM: ANSI/VT52 mode. */ 927 teken_printf("DECRST VT52\n"); 928 break; 929 case 3: /* 132 column mode. */ 930 teken_funcs_param(t, TP_132COLS, 0); 931 teken_subr_reset_to_initial_state(t); 932 break; 933 case 5: /* Inverse video. */ 934 teken_printf("DECRST inverse video\n"); 935 break; 936 case 6: /* Origin mode. */ 937 t->t_stateflags &= ~TS_ORIGIN; 938 t->t_originreg.ts_begin = 0; 939 t->t_originreg.ts_end = t->t_winsize.tp_row; 940 t->t_cursor.tp_row = t->t_cursor.tp_col = 0; 941 t->t_stateflags &= ~TS_WRAPPED; 942 teken_funcs_cursor(t); 943 break; 944 case 7: /* Autowrap mode. */ 945 t->t_stateflags &= ~TS_AUTOWRAP; 946 break; 947 case 8: /* Autorepeat mode. */ 948 teken_funcs_param(t, TP_AUTOREPEAT, 0); 949 break; 950 case 25: /* Hide cursor. */ 951 teken_funcs_param(t, TP_SHOWCURSOR, 0); 952 break; 953 case 40: /* Disallow 132 columns. */ 954 teken_printf("DECRST allow 132\n"); 955 break; 956 case 45: /* Disable reverse wraparound. */ 957 teken_printf("DECRST reverse wraparound\n"); 958 break; 959 case 47: /* Switch to alternate buffer. */ 960 teken_printf("Switch to alternate buffer\n"); 961 break; 962 case 1000: /* Mouse input. */ 963 teken_funcs_param(t, TP_MOUSE, 0); 964 break; 965 default: 966 teken_printf("Unknown DECRST: %u\n", cmd); 967 } 968 } 969 970 static void 971 teken_subr_reset_mode(teken_t *t, unsigned int cmd) 972 { 973 974 switch (cmd) { 975 case 4: 976 t->t_stateflags &= ~TS_INSERT; 977 break; 978 default: 979 teken_printf("Unknown reset mode: %u\n", cmd); 980 } 981 } 982 983 static void 984 teken_subr_do_resize(teken_t *t) 985 { 986 987 t->t_scrollreg.ts_begin = 0; 988 t->t_scrollreg.ts_end = t->t_winsize.tp_row; 989 t->t_originreg = t->t_scrollreg; 990 } 991 992 static void 993 teken_subr_do_reset(teken_t *t) 994 { 995 996 t->t_curattr = t->t_defattr; 997 t->t_cursor.tp_row = t->t_cursor.tp_col = 0; 998 t->t_scrollreg.ts_begin = 0; 999 t->t_scrollreg.ts_end = t->t_winsize.tp_row; 1000 t->t_originreg = t->t_scrollreg; 1001 t->t_stateflags &= TS_8BIT | TS_CONS25 | TS_CONS25KEYS; 1002 t->t_stateflags |= TS_AUTOWRAP; 1003 1004 t->t_scs[0] = teken_scs_us_ascii; 1005 t->t_scs[1] = teken_scs_us_ascii; 1006 t->t_curscs = 0; 1007 1008 teken_subr_save_cursor(t); 1009 teken_tab_default(t); 1010 } 1011 1012 static void 1013 teken_subr_reset_to_initial_state(teken_t *t) 1014 { 1015 1016 teken_subr_do_reset(t); 1017 teken_subr_erase_display(t, 2); 1018 teken_funcs_param(t, TP_SHOWCURSOR, 1); 1019 teken_funcs_cursor(t); 1020 } 1021 1022 static void 1023 teken_subr_restore_cursor(teken_t *t) 1024 { 1025 1026 t->t_cursor = t->t_saved_cursor; 1027 t->t_curattr = t->t_saved_curattr; 1028 t->t_scs[t->t_curscs] = t->t_saved_curscs; 1029 t->t_stateflags &= ~TS_WRAPPED; 1030 1031 /* Get out of origin mode when the cursor is moved outside. */ 1032 if (t->t_cursor.tp_row < t->t_originreg.ts_begin || 1033 t->t_cursor.tp_row >= t->t_originreg.ts_end) { 1034 t->t_stateflags &= ~TS_ORIGIN; 1035 t->t_originreg.ts_begin = 0; 1036 t->t_originreg.ts_end = t->t_winsize.tp_row; 1037 } 1038 1039 teken_funcs_cursor(t); 1040 } 1041 1042 static void 1043 teken_subr_reverse_index(teken_t *t) 1044 { 1045 1046 if (t->t_cursor.tp_row > t->t_scrollreg.ts_begin) { 1047 t->t_cursor.tp_row--; 1048 t->t_stateflags &= ~TS_WRAPPED; 1049 teken_funcs_cursor(t); 1050 } else { 1051 teken_subr_do_scroll(t, -1); 1052 } 1053 } 1054 1055 static void 1056 teken_subr_save_cursor(teken_t *t) 1057 { 1058 1059 t->t_saved_cursor = t->t_cursor; 1060 t->t_saved_curattr = t->t_curattr; 1061 t->t_saved_curscs = t->t_scs[t->t_curscs]; 1062 } 1063 1064 static void 1065 teken_subr_secondary_device_attributes(const teken_t *t, unsigned int request) 1066 { 1067 1068 if (request == 0) { 1069 const char response[] = "\x1B[>0;10;0c"; 1070 teken_funcs_respond(t, response, sizeof response - 1); 1071 } else { 1072 teken_printf("Unknown DA2\n"); 1073 } 1074 } 1075 1076 static void 1077 teken_subr_set_dec_mode(teken_t *t, unsigned int cmd) 1078 { 1079 1080 switch (cmd) { 1081 case 1: /* Cursor keys mode. */ 1082 t->t_stateflags |= TS_CURSORKEYS; 1083 break; 1084 case 2: /* DECANM: ANSI/VT52 mode. */ 1085 teken_printf("DECSET VT52\n"); 1086 break; 1087 case 3: /* 132 column mode. */ 1088 teken_funcs_param(t, TP_132COLS, 1); 1089 teken_subr_reset_to_initial_state(t); 1090 break; 1091 case 5: /* Inverse video. */ 1092 teken_printf("DECSET inverse video\n"); 1093 break; 1094 case 6: /* Origin mode. */ 1095 t->t_stateflags |= TS_ORIGIN; 1096 t->t_originreg = t->t_scrollreg; 1097 t->t_cursor.tp_row = t->t_scrollreg.ts_begin; 1098 t->t_cursor.tp_col = 0; 1099 t->t_stateflags &= ~TS_WRAPPED; 1100 teken_funcs_cursor(t); 1101 break; 1102 case 7: /* Autowrap mode. */ 1103 t->t_stateflags |= TS_AUTOWRAP; 1104 break; 1105 case 8: /* Autorepeat mode. */ 1106 teken_funcs_param(t, TP_AUTOREPEAT, 1); 1107 break; 1108 case 25: /* Display cursor. */ 1109 teken_funcs_param(t, TP_SHOWCURSOR, 1); 1110 break; 1111 case 40: /* Allow 132 columns. */ 1112 teken_printf("DECSET allow 132\n"); 1113 break; 1114 case 45: /* Enable reverse wraparound. */ 1115 teken_printf("DECSET reverse wraparound\n"); 1116 break; 1117 case 47: /* Switch to alternate buffer. */ 1118 teken_printf("Switch away from alternate buffer\n"); 1119 break; 1120 case 1000: /* Mouse input. */ 1121 teken_funcs_param(t, TP_MOUSE, 1); 1122 break; 1123 default: 1124 teken_printf("Unknown DECSET: %u\n", cmd); 1125 } 1126 } 1127 1128 static void 1129 teken_subr_set_mode(teken_t *t, unsigned int cmd) 1130 { 1131 1132 switch (cmd) { 1133 case 4: 1134 teken_printf("Insert mode\n"); 1135 t->t_stateflags |= TS_INSERT; 1136 break; 1137 default: 1138 teken_printf("Unknown set mode: %u\n", cmd); 1139 } 1140 } 1141 1142 static void 1143 teken_subr_set_graphic_rendition(teken_t *t, unsigned int ncmds, 1144 const unsigned int cmds[]) 1145 { 1146 unsigned int i, n; 1147 1148 /* No attributes means reset. */ 1149 if (ncmds == 0) { 1150 t->t_curattr = t->t_defattr; 1151 return; 1152 } 1153 1154 for (i = 0; i < ncmds; i++) { 1155 n = cmds[i]; 1156 1157 switch (n) { 1158 case 0: /* Reset. */ 1159 t->t_curattr = t->t_defattr; 1160 break; 1161 case 1: /* Bold. */ 1162 t->t_curattr.ta_format |= TF_BOLD; 1163 break; 1164 case 4: /* Underline. */ 1165 t->t_curattr.ta_format |= TF_UNDERLINE; 1166 break; 1167 case 5: /* Blink. */ 1168 t->t_curattr.ta_format |= TF_BLINK; 1169 break; 1170 case 7: /* Reverse. */ 1171 t->t_curattr.ta_format |= TF_REVERSE; 1172 break; 1173 case 22: /* Remove bold. */ 1174 t->t_curattr.ta_format &= ~TF_BOLD; 1175 break; 1176 case 24: /* Remove underline. */ 1177 t->t_curattr.ta_format &= ~TF_UNDERLINE; 1178 break; 1179 case 25: /* Remove blink. */ 1180 t->t_curattr.ta_format &= ~TF_BLINK; 1181 break; 1182 case 27: /* Remove reverse. */ 1183 t->t_curattr.ta_format &= ~TF_REVERSE; 1184 break; 1185 case 30: /* Set foreground color: black */ 1186 case 31: /* Set foreground color: red */ 1187 case 32: /* Set foreground color: green */ 1188 case 33: /* Set foreground color: brown */ 1189 case 34: /* Set foreground color: blue */ 1190 case 35: /* Set foreground color: magenta */ 1191 case 36: /* Set foreground color: cyan */ 1192 case 37: /* Set foreground color: white */ 1193 t->t_curattr.ta_fgcolor = n - 30; 1194 break; 1195 case 38: /* Set foreground color: 256 color mode */ 1196 if (i + 2 >= ncmds || cmds[i + 1] != 5) 1197 continue; 1198 t->t_curattr.ta_fgcolor = cmds[i + 2]; 1199 i += 2; 1200 break; 1201 case 39: /* Set default foreground color. */ 1202 t->t_curattr.ta_fgcolor = t->t_defattr.ta_fgcolor; 1203 break; 1204 case 40: /* Set background color: black */ 1205 case 41: /* Set background color: red */ 1206 case 42: /* Set background color: green */ 1207 case 43: /* Set background color: brown */ 1208 case 44: /* Set background color: blue */ 1209 case 45: /* Set background color: magenta */ 1210 case 46: /* Set background color: cyan */ 1211 case 47: /* Set background color: white */ 1212 t->t_curattr.ta_bgcolor = n - 40; 1213 break; 1214 case 48: /* Set background color: 256 color mode */ 1215 if (i + 2 >= ncmds || cmds[i + 1] != 5) 1216 continue; 1217 t->t_curattr.ta_bgcolor = cmds[i + 2]; 1218 i += 2; 1219 break; 1220 case 49: /* Set default background color. */ 1221 t->t_curattr.ta_bgcolor = t->t_defattr.ta_bgcolor; 1222 break; 1223 case 90: /* Set bright foreground color: black */ 1224 case 91: /* Set bright foreground color: red */ 1225 case 92: /* Set bright foreground color: green */ 1226 case 93: /* Set bright foreground color: brown */ 1227 case 94: /* Set bright foreground color: blue */ 1228 case 95: /* Set bright foreground color: magenta */ 1229 case 96: /* Set bright foreground color: cyan */ 1230 case 97: /* Set bright foreground color: white */ 1231 t->t_curattr.ta_fgcolor = (n - 90) + 8; 1232 break; 1233 case 100: /* Set bright background color: black */ 1234 case 101: /* Set bright background color: red */ 1235 case 102: /* Set bright background color: green */ 1236 case 103: /* Set bright background color: brown */ 1237 case 104: /* Set bright background color: blue */ 1238 case 105: /* Set bright background color: magenta */ 1239 case 106: /* Set bright background color: cyan */ 1240 case 107: /* Set bright background color: white */ 1241 t->t_curattr.ta_bgcolor = (n - 100) + 8; 1242 break; 1243 default: 1244 teken_printf("unsupported attribute %u\n", n); 1245 } 1246 } 1247 } 1248 1249 static void 1250 teken_subr_set_top_and_bottom_margins(teken_t *t, unsigned int top, 1251 unsigned int bottom) 1252 { 1253 1254 /* Adjust top row number. */ 1255 if (top > 0) 1256 top--; 1257 /* Adjust bottom row number. */ 1258 if (bottom == 0 || bottom > t->t_winsize.tp_row) 1259 bottom = t->t_winsize.tp_row; 1260 1261 /* Invalid arguments. */ 1262 if (top >= bottom - 1) { 1263 top = 0; 1264 bottom = t->t_winsize.tp_row; 1265 } 1266 1267 /* Apply scrolling region. */ 1268 t->t_scrollreg.ts_begin = top; 1269 t->t_scrollreg.ts_end = bottom; 1270 if (t->t_stateflags & TS_ORIGIN) 1271 t->t_originreg = t->t_scrollreg; 1272 1273 /* Home cursor to the top left of the scrolling region. */ 1274 t->t_cursor.tp_row = t->t_originreg.ts_begin; 1275 t->t_cursor.tp_col = 0; 1276 t->t_stateflags &= ~TS_WRAPPED; 1277 teken_funcs_cursor(t); 1278 } 1279 1280 static void 1281 teken_subr_single_height_double_width_line(const teken_t *t) 1282 { 1283 1284 (void)t; 1285 teken_printf("single height double width???\n"); 1286 } 1287 1288 static void 1289 teken_subr_single_height_single_width_line(const teken_t *t) 1290 { 1291 1292 (void)t; 1293 teken_printf("single height single width???\n"); 1294 } 1295 1296 static void 1297 teken_subr_string_terminator(const teken_t *t) 1298 { 1299 1300 (void)t; 1301 /* 1302 * Strings are already terminated in teken_input_char() when ^[ 1303 * is inserted. 1304 */ 1305 } 1306 1307 static void 1308 teken_subr_tab_clear(teken_t *t, unsigned int cmd) 1309 { 1310 1311 switch (cmd) { 1312 case 0: 1313 teken_tab_clear(t, t->t_cursor.tp_col); 1314 break; 1315 case 3: 1316 memset(t->t_tabstops, 0, T_NUMCOL / 8); 1317 break; 1318 default: 1319 break; 1320 } 1321 } 1322 1323 static void 1324 teken_subr_vertical_position_absolute(teken_t *t, unsigned int row) 1325 { 1326 1327 row = (row - 1) + t->t_originreg.ts_begin; 1328 t->t_cursor.tp_row = row < t->t_originreg.ts_end ? 1329 row : t->t_originreg.ts_end - 1; 1330 1331 t->t_stateflags &= ~TS_WRAPPED; 1332 teken_funcs_cursor(t); 1333 } 1334 1335 static void 1336 teken_subr_repeat_last_graphic_char(teken_t *t, unsigned int rpts) 1337 { 1338 unsigned int max_repetitions; 1339 1340 max_repetitions = t->t_winsize.tp_row * t->t_winsize.tp_col; 1341 if (rpts > max_repetitions) 1342 rpts = max_repetitions; 1343 for (; t->t_last != 0 && rpts > 0; rpts--) 1344 teken_subr_regular_character(t, t->t_last); 1345 } 1346