1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * $FreeBSD$ 29 */ 30 31 #include <sys/cdefs.h> 32 #if defined(__FreeBSD__) && defined(_KERNEL) 33 #include <sys/param.h> 34 #include <sys/limits.h> 35 #include <sys/lock.h> 36 #include <sys/systm.h> 37 #define teken_assert(x) MPASS(x) 38 #else /* !(__FreeBSD__ && _KERNEL) */ 39 #include <sys/types.h> 40 #include <assert.h> 41 #include <limits.h> 42 #include <stdint.h> 43 #include <stdio.h> 44 #include <string.h> 45 #define teken_assert(x) assert(x) 46 #endif /* __FreeBSD__ && _KERNEL */ 47 48 /* debug messages */ 49 #define teken_printf(x,...) 50 51 /* Private flags for t_stateflags. */ 52 #define TS_FIRSTDIGIT 0x0001 /* First numeric digit in escape sequence. */ 53 #define TS_INSERT 0x0002 /* Insert mode. */ 54 #define TS_AUTOWRAP 0x0004 /* Autowrap. */ 55 #define TS_ORIGIN 0x0008 /* Origin mode. */ 56 #define TS_WRAPPED 0x0010 /* Next character should be printed on col 0. */ 57 #define TS_8BIT 0x0020 /* UTF-8 disabled. */ 58 #define TS_CONS25 0x0040 /* cons25 emulation. */ 59 #define TS_INSTRING 0x0080 /* Inside string. */ 60 #define TS_CURSORKEYS 0x0100 /* Cursor keys mode. */ 61 62 /* Character that blanks a cell. */ 63 #define BLANK ' ' 64 65 #include "teken.h" 66 #include "teken_wcwidth.h" 67 #include "teken_scs.h" 68 69 static teken_state_t teken_state_init; 70 71 /* 72 * Wrappers for hooks. 73 */ 74 75 static inline void 76 teken_funcs_bell(const teken_t *t) 77 { 78 79 teken_assert(t->t_funcs->tf_bell != NULL); 80 t->t_funcs->tf_bell(t->t_softc); 81 } 82 83 static inline void 84 teken_funcs_cursor(const teken_t *t) 85 { 86 87 teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row); 88 teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col); 89 90 teken_assert(t->t_funcs->tf_cursor != NULL); 91 t->t_funcs->tf_cursor(t->t_softc, &t->t_cursor); 92 } 93 94 static inline void 95 teken_funcs_putchar(const teken_t *t, const teken_pos_t *p, teken_char_t c, 96 const teken_attr_t *a) 97 { 98 99 teken_assert(p->tp_row < t->t_winsize.tp_row); 100 teken_assert(p->tp_col < t->t_winsize.tp_col); 101 102 teken_assert(t->t_funcs->tf_putchar != NULL); 103 t->t_funcs->tf_putchar(t->t_softc, p, c, a); 104 } 105 106 static inline void 107 teken_funcs_fill(const teken_t *t, const teken_rect_t *r, 108 const teken_char_t c, const teken_attr_t *a) 109 { 110 111 teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row); 112 teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row); 113 teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col); 114 teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col); 115 116 teken_assert(t->t_funcs->tf_fill != NULL); 117 t->t_funcs->tf_fill(t->t_softc, r, c, a); 118 } 119 120 static inline void 121 teken_funcs_copy(const teken_t *t, const teken_rect_t *r, const teken_pos_t *p) 122 { 123 124 teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row); 125 teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row); 126 teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col); 127 teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col); 128 teken_assert(p->tp_row + (r->tr_end.tp_row - r->tr_begin.tp_row) <= t->t_winsize.tp_row); 129 teken_assert(p->tp_col + (r->tr_end.tp_col - r->tr_begin.tp_col) <= t->t_winsize.tp_col); 130 131 teken_assert(t->t_funcs->tf_copy != NULL); 132 t->t_funcs->tf_copy(t->t_softc, r, p); 133 } 134 135 static inline void 136 teken_funcs_pre_input(const teken_t *t) 137 { 138 139 if (t->t_funcs->tf_pre_input != NULL) 140 t->t_funcs->tf_pre_input(t->t_softc); 141 } 142 143 static inline void 144 teken_funcs_post_input(const teken_t *t) 145 { 146 147 if (t->t_funcs->tf_post_input != NULL) 148 t->t_funcs->tf_post_input(t->t_softc); 149 } 150 151 static inline void 152 teken_funcs_param(const teken_t *t, int cmd, unsigned int value) 153 { 154 155 teken_assert(t->t_funcs->tf_param != NULL); 156 t->t_funcs->tf_param(t->t_softc, cmd, value); 157 } 158 159 static inline void 160 teken_funcs_respond(const teken_t *t, const void *buf, size_t len) 161 { 162 163 teken_assert(t->t_funcs->tf_respond != NULL); 164 t->t_funcs->tf_respond(t->t_softc, buf, len); 165 } 166 167 #include "teken_subr.h" 168 #include "teken_subr_compat.h" 169 170 /* 171 * Programming interface. 172 */ 173 174 void 175 teken_init(teken_t *t, const teken_funcs_t *tf, void *softc) 176 { 177 teken_pos_t tp = { .tp_row = 24, .tp_col = 80 }; 178 179 t->t_funcs = tf; 180 t->t_softc = softc; 181 182 t->t_nextstate = teken_state_init; 183 t->t_stateflags = 0; 184 t->t_utf8_left = 0; 185 186 t->t_defattr.ta_format = 0; 187 t->t_defattr.ta_fgcolor = TC_WHITE; 188 t->t_defattr.ta_bgcolor = TC_BLACK; 189 teken_subr_do_reset(t); 190 191 teken_set_winsize(t, &tp); 192 } 193 194 static void 195 teken_input_char(teken_t *t, teken_char_t c) 196 { 197 198 /* 199 * There is no support for DCS and OSC. Just discard strings 200 * until we receive characters that may indicate string 201 * termination. 202 */ 203 if (t->t_stateflags & TS_INSTRING) { 204 switch (c) { 205 case '\x1B': 206 t->t_stateflags &= ~TS_INSTRING; 207 break; 208 case '\a': 209 t->t_stateflags &= ~TS_INSTRING; 210 return; 211 default: 212 return; 213 } 214 } 215 216 switch (c) { 217 case '\0': 218 break; 219 case '\a': 220 teken_subr_bell(t); 221 break; 222 case '\b': 223 teken_subr_backspace(t); 224 break; 225 case '\n': 226 case '\x0B': 227 teken_subr_newline(t); 228 break; 229 case '\x0C': 230 teken_subr_newpage(t); 231 break; 232 case '\x0E': 233 if (t->t_stateflags & TS_CONS25) 234 t->t_nextstate(t, c); 235 else 236 t->t_curscs = 1; 237 break; 238 case '\x0F': 239 if (t->t_stateflags & TS_CONS25) 240 t->t_nextstate(t, c); 241 else 242 t->t_curscs = 0; 243 break; 244 case '\r': 245 teken_subr_carriage_return(t); 246 break; 247 case '\t': 248 teken_subr_horizontal_tab(t); 249 break; 250 default: 251 t->t_nextstate(t, c); 252 break; 253 } 254 255 /* Post-processing assertions. */ 256 teken_assert(t->t_cursor.tp_row >= t->t_originreg.ts_begin); 257 teken_assert(t->t_cursor.tp_row < t->t_originreg.ts_end); 258 teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row); 259 teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col); 260 teken_assert(t->t_saved_cursor.tp_row < t->t_winsize.tp_row); 261 teken_assert(t->t_saved_cursor.tp_col < t->t_winsize.tp_col); 262 teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row); 263 teken_assert(t->t_scrollreg.ts_begin < t->t_scrollreg.ts_end); 264 /* Origin region has to be window size or the same as scrollreg. */ 265 teken_assert((t->t_originreg.ts_begin == t->t_scrollreg.ts_begin && 266 t->t_originreg.ts_end == t->t_scrollreg.ts_end) || 267 (t->t_originreg.ts_begin == 0 && 268 t->t_originreg.ts_end == t->t_winsize.tp_row)); 269 } 270 271 static void 272 teken_input_byte(teken_t *t, unsigned char c) 273 { 274 275 /* 276 * UTF-8 handling. 277 */ 278 if ((c & 0x80) == 0x00 || t->t_stateflags & TS_8BIT) { 279 /* One-byte sequence. */ 280 t->t_utf8_left = 0; 281 teken_input_char(t, c); 282 } else if ((c & 0xe0) == 0xc0) { 283 /* Two-byte sequence. */ 284 t->t_utf8_left = 1; 285 t->t_utf8_partial = c & 0x1f; 286 } else if ((c & 0xf0) == 0xe0) { 287 /* Three-byte sequence. */ 288 t->t_utf8_left = 2; 289 t->t_utf8_partial = c & 0x0f; 290 } else if ((c & 0xf8) == 0xf0) { 291 /* Four-byte sequence. */ 292 t->t_utf8_left = 3; 293 t->t_utf8_partial = c & 0x07; 294 } else if ((c & 0xc0) == 0x80) { 295 if (t->t_utf8_left == 0) 296 return; 297 t->t_utf8_left--; 298 t->t_utf8_partial = (t->t_utf8_partial << 6) | (c & 0x3f); 299 if (t->t_utf8_left == 0) { 300 teken_printf("Got UTF-8 char %x\n", t->t_utf8_partial); 301 teken_input_char(t, t->t_utf8_partial); 302 } 303 } 304 } 305 306 void 307 teken_input(teken_t *t, const void *buf, size_t len) 308 { 309 const char *c = buf; 310 311 teken_funcs_pre_input(t); 312 while (len-- > 0) 313 teken_input_byte(t, *c++); 314 teken_funcs_post_input(t); 315 } 316 317 const teken_pos_t * 318 teken_get_cursor(const teken_t *t) 319 { 320 321 return (&t->t_cursor); 322 } 323 324 void 325 teken_set_cursor(teken_t *t, const teken_pos_t *p) 326 { 327 328 /* XXX: bounds checking with originreg! */ 329 teken_assert(p->tp_row < t->t_winsize.tp_row); 330 teken_assert(p->tp_col < t->t_winsize.tp_col); 331 332 t->t_cursor = *p; 333 } 334 335 const teken_attr_t * 336 teken_get_curattr(const teken_t *t) 337 { 338 339 return (&t->t_curattr); 340 } 341 342 void 343 teken_set_curattr(teken_t *t, const teken_attr_t *a) 344 { 345 346 t->t_curattr = *a; 347 } 348 349 const teken_attr_t * 350 teken_get_defattr(const teken_t *t) 351 { 352 353 return (&t->t_defattr); 354 } 355 356 void 357 teken_set_defattr(teken_t *t, const teken_attr_t *a) 358 { 359 360 t->t_curattr = t->t_saved_curattr = t->t_defattr = *a; 361 } 362 363 const teken_pos_t * 364 teken_get_winsize(const teken_t *t) 365 { 366 367 return (&t->t_winsize); 368 } 369 370 static void 371 teken_trim_cursor_pos(teken_t *t, const teken_pos_t *new) 372 { 373 const teken_pos_t *cur; 374 375 cur = &t->t_winsize; 376 377 if (cur->tp_row < new->tp_row || cur->tp_col < new->tp_col) 378 return; 379 if (t->t_cursor.tp_row >= new->tp_row) 380 t->t_cursor.tp_row = new->tp_row - 1; 381 if (t->t_cursor.tp_col >= new->tp_col) 382 t->t_cursor.tp_col = new->tp_col - 1; 383 } 384 385 void 386 teken_set_winsize(teken_t *t, const teken_pos_t *p) 387 { 388 389 teken_trim_cursor_pos(t, p); 390 t->t_winsize = *p; 391 teken_subr_do_reset(t); 392 } 393 394 void 395 teken_set_winsize_noreset(teken_t *t, const teken_pos_t *p) 396 { 397 398 teken_trim_cursor_pos(t, p); 399 t->t_winsize = *p; 400 teken_subr_do_resize(t); 401 } 402 403 void 404 teken_set_8bit(teken_t *t) 405 { 406 407 t->t_stateflags |= TS_8BIT; 408 } 409 410 void 411 teken_set_cons25(teken_t *t) 412 { 413 414 t->t_stateflags |= TS_CONS25; 415 } 416 417 /* 418 * State machine. 419 */ 420 421 static void 422 teken_state_switch(teken_t *t, teken_state_t *s) 423 { 424 425 t->t_nextstate = s; 426 t->t_curnum = 0; 427 t->t_stateflags |= TS_FIRSTDIGIT; 428 } 429 430 static int 431 teken_state_numbers(teken_t *t, teken_char_t c) 432 { 433 434 teken_assert(t->t_curnum < T_NUMSIZE); 435 436 if (c >= '0' && c <= '9') { 437 if (t->t_stateflags & TS_FIRSTDIGIT) { 438 /* First digit. */ 439 t->t_stateflags &= ~TS_FIRSTDIGIT; 440 t->t_nums[t->t_curnum] = c - '0'; 441 } else if (t->t_nums[t->t_curnum] < UINT_MAX / 100) { 442 /* 443 * There is no need to continue parsing input 444 * once the value exceeds the size of the 445 * terminal. It would only allow for integer 446 * overflows when performing arithmetic on the 447 * cursor position. 448 * 449 * Ignore any further digits if the value is 450 * already UINT_MAX / 100. 451 */ 452 t->t_nums[t->t_curnum] = 453 t->t_nums[t->t_curnum] * 10 + c - '0'; 454 } 455 return (1); 456 } else if (c == ';') { 457 if (t->t_stateflags & TS_FIRSTDIGIT) 458 t->t_nums[t->t_curnum] = 0; 459 460 /* Only allow a limited set of arguments. */ 461 if (++t->t_curnum == T_NUMSIZE) { 462 teken_state_switch(t, teken_state_init); 463 return (1); 464 } 465 466 t->t_stateflags |= TS_FIRSTDIGIT; 467 return (1); 468 } else { 469 if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) { 470 /* Finish off the last empty argument. */ 471 t->t_nums[t->t_curnum] = 0; 472 t->t_curnum++; 473 } else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) { 474 /* Also count the last argument. */ 475 t->t_curnum++; 476 } 477 } 478 479 return (0); 480 } 481 482 #define k TC_BLACK 483 #define b TC_BLUE 484 #define y TC_BROWN 485 #define c TC_CYAN 486 #define g TC_GREEN 487 #define m TC_MAGENTA 488 #define r TC_RED 489 #define w TC_WHITE 490 #define K (TC_BLACK | TC_LIGHT) 491 #define B (TC_BLUE | TC_LIGHT) 492 #define Y (TC_BROWN | TC_LIGHT) 493 #define C (TC_CYAN | TC_LIGHT) 494 #define G (TC_GREEN | TC_LIGHT) 495 #define M (TC_MAGENTA | TC_LIGHT) 496 #define R (TC_RED | TC_LIGHT) 497 #define W (TC_WHITE | TC_LIGHT) 498 499 /** 500 * The xterm-256 color map has steps of 0x28 (in the range 0-0xff), except 501 * for the first step which is 0x5f. Scale to the range 0-6 by dividing 502 * by 0x28 and rounding down. The range of 0-5 cannot represent the 503 * larger first step. 504 * 505 * This table is generated by the follow rules: 506 * - if all components are equal, the result is black for (0, 0, 0) and 507 * (2, 2, 2), else white; otherwise: 508 * - subtract the smallest component from all components 509 * - if this gives only one nonzero component, then that is the color 510 * - else if one component is 2 or more larger than the other nonzero one, 511 * then that component gives the color 512 * - else there are 2 nonzero components. The color is that of a small 513 * equal mixture of these components (cyan, yellow or magenta). E.g., 514 * (0, 5, 6) (Turquoise2) is a much purer cyan than (0, 2, 3) 515 * (DeepSkyBlue4), but we map both to cyan since we can't represent 516 * delicate shades of either blue or cyan and blue would be worse. 517 * Here it is important that components of 1 never occur. Blue would 518 * be twice as large as green in (0, 1, 2). 519 */ 520 static const teken_color_t teken_256to8tab[] = { 521 /* xterm normal colors: */ 522 k, r, g, y, b, m, c, w, 523 524 /* xterm bright colors: */ 525 k, r, g, y, b, m, c, w, 526 527 /* Red0 submap. */ 528 k, b, b, b, b, b, 529 g, c, c, b, b, b, 530 g, c, c, c, b, b, 531 g, g, c, c, c, b, 532 g, g, g, c, c, c, 533 g, g, g, g, c, c, 534 535 /* Red2 submap. */ 536 r, m, m, b, b, b, 537 y, k, b, b, b, b, 538 y, g, c, c, b, b, 539 g, g, c, c, c, b, 540 g, g, g, c, c, c, 541 g, g, g, g, c, c, 542 543 /* Red3 submap. */ 544 r, m, m, m, b, b, 545 y, r, m, m, b, b, 546 y, y, w, b, b, b, 547 y, y, g, c, c, b, 548 g, g, g, c, c, c, 549 g, g, g, g, c, c, 550 551 /* Red4 submap. */ 552 r, r, m, m, m, b, 553 r, r, m, m, m, b, 554 y, y, r, m, m, b, 555 y, y, y, w, b, b, 556 y, y, y, g, c, c, 557 g, g, g, g, c, c, 558 559 /* Red5 submap. */ 560 r, r, r, m, m, m, 561 r, r, r, m, m, m, 562 r, r, r, m, m, m, 563 y, y, y, r, m, m, 564 y, y, y, y, w, b, 565 y, y, y, y, g, c, 566 567 /* Red6 submap. */ 568 r, r, r, r, m, m, 569 r, r, r, r, m, m, 570 r, r, r, r, m, m, 571 r, r, r, r, m, m, 572 y, y, y, y, r, m, 573 y, y, y, y, y, w, 574 575 /* Grey submap. */ 576 k, k, k, k, k, k, 577 k, k, k, k, k, k, 578 w, w, w, w, w, w, 579 w, w, w, w, w, w, 580 }; 581 582 /* 583 * This table is generated from the previous one by setting TC_LIGHT for 584 * entries whose luminosity in the xterm256 color map is 60% or larger. 585 * Thus the previous table is currently not really needed. It will be 586 * used for different fine tuning of the tables. 587 */ 588 static const teken_color_t teken_256to16tab[] = { 589 /* xterm normal colors: */ 590 k, r, g, y, b, m, c, w, 591 592 /* xterm bright colors: */ 593 K, R, G, Y, B, M, C, W, 594 595 /* Red0 submap. */ 596 k, b, b, b, b, b, 597 g, c, c, b, b, b, 598 g, c, c, c, b, b, 599 g, g, c, c, c, b, 600 g, g, g, c, c, c, 601 g, g, g, g, c, c, 602 603 /* Red2 submap. */ 604 r, m, m, b, b, b, 605 y, K, b, b, B, B, 606 y, g, c, c, B, B, 607 g, g, c, c, C, B, 608 g, G, G, C, C, C, 609 g, G, G, G, C, C, 610 611 /* Red3 submap. */ 612 r, m, m, m, b, b, 613 y, r, m, m, B, B, 614 y, y, w, B, B, B, 615 y, y, G, C, C, B, 616 g, G, G, C, C, C, 617 g, G, G, G, C, C, 618 619 /* Red4 submap. */ 620 r, r, m, m, m, b, 621 r, r, m, m, M, B, 622 y, y, R, M, M, B, 623 y, y, Y, W, B, B, 624 y, Y, Y, G, C, C, 625 g, G, G, G, C, C, 626 627 /* Red5 submap. */ 628 r, r, r, m, m, m, 629 r, R, R, M, M, M, 630 r, R, R, M, M, M, 631 y, Y, Y, R, M, M, 632 y, Y, Y, Y, W, B, 633 y, Y, Y, Y, G, C, 634 635 /* Red6 submap. */ 636 r, r, r, r, m, m, 637 r, R, R, R, M, M, 638 r, R, R, R, M, M, 639 r, R, R, R, M, M, 640 y, Y, Y, Y, R, M, 641 y, Y, Y, Y, Y, W, 642 643 /* Grey submap. */ 644 k, k, k, k, k, k, 645 K, K, K, K, K, K, 646 w, w, w, w, w, w, 647 W, W, W, W, W, W, 648 }; 649 650 #undef k 651 #undef b 652 #undef y 653 #undef c 654 #undef g 655 #undef m 656 #undef r 657 #undef w 658 #undef K 659 #undef B 660 #undef Y 661 #undef C 662 #undef G 663 #undef M 664 #undef R 665 #undef W 666 667 teken_color_t 668 teken_256to8(teken_color_t c) 669 { 670 671 return (teken_256to8tab[c % 256]); 672 } 673 674 teken_color_t 675 teken_256to16(teken_color_t c) 676 { 677 678 return (teken_256to16tab[c % 256]); 679 } 680 681 static const char * const special_strings_cons25[] = { 682 [TKEY_UP] = "\x1B[A", [TKEY_DOWN] = "\x1B[B", 683 [TKEY_LEFT] = "\x1B[D", [TKEY_RIGHT] = "\x1B[C", 684 685 [TKEY_HOME] = "\x1B[H", [TKEY_END] = "\x1B[F", 686 [TKEY_INSERT] = "\x1B[L", [TKEY_DELETE] = "\x7F", 687 [TKEY_PAGE_UP] = "\x1B[I", [TKEY_PAGE_DOWN] = "\x1B[G", 688 689 [TKEY_F1] = "\x1B[M", [TKEY_F2] = "\x1B[N", 690 [TKEY_F3] = "\x1B[O", [TKEY_F4] = "\x1B[P", 691 [TKEY_F5] = "\x1B[Q", [TKEY_F6] = "\x1B[R", 692 [TKEY_F7] = "\x1B[S", [TKEY_F8] = "\x1B[T", 693 [TKEY_F9] = "\x1B[U", [TKEY_F10] = "\x1B[V", 694 [TKEY_F11] = "\x1B[W", [TKEY_F12] = "\x1B[X", 695 }; 696 697 static const char * const special_strings_ckeys[] = { 698 [TKEY_UP] = "\x1BOA", [TKEY_DOWN] = "\x1BOB", 699 [TKEY_LEFT] = "\x1BOD", [TKEY_RIGHT] = "\x1BOC", 700 701 [TKEY_HOME] = "\x1BOH", [TKEY_END] = "\x1BOF", 702 }; 703 704 static const char * const special_strings_normal[] = { 705 [TKEY_UP] = "\x1B[A", [TKEY_DOWN] = "\x1B[B", 706 [TKEY_LEFT] = "\x1B[D", [TKEY_RIGHT] = "\x1B[C", 707 708 [TKEY_HOME] = "\x1B[H", [TKEY_END] = "\x1B[F", 709 [TKEY_INSERT] = "\x1B[2~", [TKEY_DELETE] = "\x1B[3~", 710 [TKEY_PAGE_UP] = "\x1B[5~", [TKEY_PAGE_DOWN] = "\x1B[6~", 711 712 [TKEY_F1] = "\x1BOP", [TKEY_F2] = "\x1BOQ", 713 [TKEY_F3] = "\x1BOR", [TKEY_F4] = "\x1BOS", 714 [TKEY_F5] = "\x1B[15~", [TKEY_F6] = "\x1B[17~", 715 [TKEY_F7] = "\x1B[18~", [TKEY_F8] = "\x1B[19~", 716 [TKEY_F9] = "\x1B[20~", [TKEY_F10] = "\x1B[21~", 717 [TKEY_F11] = "\x1B[23~", [TKEY_F12] = "\x1B[24~", 718 }; 719 720 const char * 721 teken_get_sequence(const teken_t *t, unsigned int k) 722 { 723 724 /* Cons25 mode. */ 725 if (t->t_stateflags & TS_CONS25 && 726 k < sizeof special_strings_cons25 / sizeof(char *)) 727 return (special_strings_cons25[k]); 728 729 /* Cursor keys mode. */ 730 if (t->t_stateflags & TS_CURSORKEYS && 731 k < sizeof special_strings_ckeys / sizeof(char *)) 732 return (special_strings_ckeys[k]); 733 734 /* Default xterm sequences. */ 735 if (k < sizeof special_strings_normal / sizeof(char *)) 736 return (special_strings_normal[k]); 737 738 return (NULL); 739 } 740 741 #include "teken_state.h" 742