1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2016 Joyent, Inc. 26 * Copyright 2021 Toomas Soome <tsoome@me.com> 27 * Copyright 2021 RackTop Systems, Inc. 28 */ 29 30 /* 31 * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and 32 * the like. 33 * 34 * How Virtual Terminal Emulator Works: 35 * 36 * Every virtual terminal is associated with a tem_vt_state structure 37 * and maintains a virtual screen buffer in tvs_screen_buf, which contains 38 * all the characters which should be shown on the physical screen when 39 * the terminal is activated. 40 * 41 * Data written to a virtual terminal is composed of characters which 42 * should be displayed on the screen when this virtual terminal is 43 * activated, fg/bg colors of these characters, and other control 44 * information (escape sequence, etc). 45 * 46 * When data is passed to a virtual terminal it first is parsed for 47 * control information by tem_parse(). Subsequently the character 48 * and color data are written to tvs_screen_buf. 49 * They are saved in buffer in order to refresh the screen when this 50 * terminal is activated. If the terminal is currently active, the data 51 * (characters and colors) are also written to the physical screen by 52 * invoking a callback function, tem_text_callbacks() or tem_pix_callbacks(). 53 * 54 * When rendering data to the framebuffer, if the framebuffer is in 55 * VIS_PIXEL mode, the character data will first be converted to pixel 56 * data using tem_pix_bit2pix(), and then the pixels get displayed 57 * on the physical screen. We only store the character and color data in 58 * tem_vt_state since the bit2pix conversion only happens when actually 59 * rendering to the physical framebuffer. 60 * 61 * Color support: 62 * Text mode can only support standard system colors, 4-bit [0-15] indexed. 63 * On framebuffer devices, we can aditionally use [16-255] or truecolor. 64 * Additional colors can be used via CSI 38 and CSI 48 sequences. 65 * CSI 38/48;5 is using indexed colors [0-255], CSI 38/48;2 does 66 * specify color by RGB triple. 67 * 68 * While sending glyphs to display, we need to process glyph attributes: 69 * TEM_ATTR_BOLD will cause BOLD font to be used (or BRIGHT color if we 70 * we use indexed color [0-7]). 71 * We ignore TEM_ATTR_BRIGHT_FG/TEM_ATTR_BRIGHT_BG with RGB colors. 72 * TEM_ATTR_REVERSE and TEM_ATTR_SCREEN_REVERSE will cause fg and bg to be 73 * swapped. 74 */ 75 76 #include <stand.h> 77 #include <sys/ascii.h> 78 #include <sys/errno.h> 79 #include <sys/tem_impl.h> 80 #ifdef _HAVE_TEM_FIRMWARE 81 #include <sys/promif.h> 82 #endif /* _HAVE_TEM_FIRMWARE */ 83 #include <sys/consplat.h> 84 #include <sys/kd.h> 85 #include <stdbool.h> 86 87 /* Terminal emulator internal helper functions */ 88 static void tems_setup_terminal(struct vis_devinit *, size_t, size_t); 89 static void tems_modechange_callback(struct vis_modechg_arg *, 90 struct vis_devinit *); 91 92 static void tems_reset_colormap(void); 93 94 static void tem_free_buf(struct tem_vt_state *); 95 static void tem_internal_init(struct tem_vt_state *, bool, bool); 96 static void tems_get_initial_color(tem_color_t *pcolor); 97 98 static void tem_control(struct tem_vt_state *, uint8_t); 99 static void tem_setparam(struct tem_vt_state *, int, int); 100 static void tem_selgraph(struct tem_vt_state *); 101 static void tem_chkparam(struct tem_vt_state *, uint8_t); 102 static void tem_getparams(struct tem_vt_state *, uint8_t); 103 static void tem_outch(struct tem_vt_state *, tem_char_t); 104 static void tem_parse(struct tem_vt_state *, tem_char_t); 105 106 static void tem_new_line(struct tem_vt_state *); 107 static void tem_cr(struct tem_vt_state *); 108 static void tem_lf(struct tem_vt_state *); 109 static void tem_send_data(struct tem_vt_state *); 110 static void tem_cls(struct tem_vt_state *); 111 static void tem_tab(struct tem_vt_state *); 112 static void tem_back_tab(struct tem_vt_state *); 113 static void tem_clear_tabs(struct tem_vt_state *, int); 114 static void tem_set_tab(struct tem_vt_state *); 115 static void tem_mv_cursor(struct tem_vt_state *, int, int); 116 static void tem_shift(struct tem_vt_state *, int, int); 117 static void tem_scroll(struct tem_vt_state *, int, int, int, int); 118 static void tem_clear_chars(struct tem_vt_state *tem, 119 int count, screen_pos_t row, screen_pos_t col); 120 static void tem_copy_area(struct tem_vt_state *tem, 121 screen_pos_t s_col, screen_pos_t s_row, 122 screen_pos_t e_col, screen_pos_t e_row, 123 screen_pos_t t_col, screen_pos_t t_row); 124 static void tem_bell(struct tem_vt_state *tem); 125 static void tem_pix_clear_prom_output(struct tem_vt_state *tem); 126 127 static void tem_virtual_cls(struct tem_vt_state *, size_t, screen_pos_t, 128 screen_pos_t); 129 static void tem_virtual_display(struct tem_vt_state *, term_char_t *, 130 size_t, screen_pos_t, screen_pos_t); 131 static void tem_align_cursor(struct tem_vt_state *tem); 132 133 static void tem_check_first_time(struct tem_vt_state *tem); 134 static void tem_reset_display(struct tem_vt_state *, bool, bool); 135 static void tem_terminal_emulate(struct tem_vt_state *, uint8_t *, int); 136 static void tem_text_cursor(struct tem_vt_state *, short); 137 static void tem_text_cls(struct tem_vt_state *, 138 int count, screen_pos_t row, screen_pos_t col); 139 static void tem_pix_display(struct tem_vt_state *, term_char_t *, 140 int, screen_pos_t, screen_pos_t); 141 static void tem_pix_copy(struct tem_vt_state *, 142 screen_pos_t, screen_pos_t, 143 screen_pos_t, screen_pos_t, 144 screen_pos_t, screen_pos_t); 145 static void tem_pix_cursor(struct tem_vt_state *, short); 146 static void tem_get_attr(struct tem_vt_state *, text_color_t *, 147 text_color_t *, text_attr_t *, uint8_t); 148 static void tem_get_color(struct tem_vt_state *, 149 text_color_t *, text_color_t *, term_char_t *); 150 static void tem_set_color(text_color_t *, color_t *); 151 static void tem_pix_align(struct tem_vt_state *); 152 static void tem_text_display(struct tem_vt_state *, term_char_t *, int, 153 screen_pos_t, screen_pos_t); 154 static void tem_text_copy(struct tem_vt_state *, 155 screen_pos_t, screen_pos_t, screen_pos_t, screen_pos_t, 156 screen_pos_t, screen_pos_t); 157 static void tem_pix_bit2pix(struct tem_vt_state *, term_char_t *); 158 static void tem_pix_cls_range(struct tem_vt_state *, screen_pos_t, int, 159 int, screen_pos_t, int, int, bool); 160 static void tem_pix_cls(struct tem_vt_state *, int, 161 screen_pos_t, screen_pos_t); 162 163 static void bit_to_pix32(struct tem_vt_state *tem, tem_char_t c, 164 text_color_t fg_color, text_color_t bg_color); 165 166 /* 167 * Globals 168 */ 169 tem_state_t tems; /* common term info */ 170 171 tem_callbacks_t tem_text_callbacks = { 172 .tsc_display = &tem_text_display, 173 .tsc_copy = &tem_text_copy, 174 .tsc_cursor = &tem_text_cursor, 175 .tsc_bit2pix = NULL, 176 .tsc_cls = &tem_text_cls 177 }; 178 tem_callbacks_t tem_pix_callbacks = { 179 .tsc_display = &tem_pix_display, 180 .tsc_copy = &tem_pix_copy, 181 .tsc_cursor = &tem_pix_cursor, 182 .tsc_bit2pix = &tem_pix_bit2pix, 183 .tsc_cls = &tem_pix_cls 184 }; 185 186 #define tem_callback_display (*tems.ts_callbacks->tsc_display) 187 #define tem_callback_copy (*tems.ts_callbacks->tsc_copy) 188 #define tem_callback_cursor (*tems.ts_callbacks->tsc_cursor) 189 #define tem_callback_cls (*tems.ts_callbacks->tsc_cls) 190 #define tem_callback_bit2pix (*tems.ts_callbacks->tsc_bit2pix) 191 192 static void 193 tem_add(struct tem_vt_state *tem) 194 { 195 list_insert_head(&tems.ts_list, tem); 196 } 197 198 /* 199 * This is the main entry point to the module. It handles output requests 200 * during normal system operation, when (e.g.) mutexes are available. 201 */ 202 void 203 tem_write(tem_vt_state_t tem_arg, uint8_t *buf, ssize_t len) 204 { 205 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 206 207 if (tems.ts_initialized == 0 || tem->tvs_initialized == 0) { 208 return; 209 } 210 211 tem_check_first_time(tem); 212 tem_terminal_emulate(tem, buf, len); 213 } 214 215 static void 216 tem_internal_init(struct tem_vt_state *ptem, 217 bool init_color, bool clear_screen) 218 { 219 size_t size, width, height; 220 221 if (tems.ts_display_mode == VIS_PIXEL) { 222 ptem->tvs_pix_data_size = tems.ts_pix_data_size; 223 ptem->tvs_pix_data = malloc(ptem->tvs_pix_data_size); 224 } 225 226 ptem->tvs_stateflags = TVS_AUTOWRAP; 227 228 width = tems.ts_c_dimension.width; 229 height = tems.ts_c_dimension.height; 230 231 size = width * sizeof (tem_char_t); 232 ptem->tvs_outbuf = malloc(size); 233 if (ptem->tvs_outbuf == NULL) 234 panic("out of memory in tem_internal_init()\n"); 235 236 ptem->tvs_maxtab = width / 8; 237 ptem->tvs_tabs = calloc(ptem->tvs_maxtab, sizeof (*ptem->tvs_tabs)); 238 if (ptem->tvs_tabs == NULL) 239 panic("out of memory in tem_internal_init()\n"); 240 241 tem_reset_display(ptem, clear_screen, init_color); 242 243 ptem->tvs_utf8_left = 0; 244 ptem->tvs_utf8_partial = 0; 245 246 ptem->tvs_initialized = true; 247 248 /* 249 * Out of memory is not fatal there, without the screen history, 250 * we can not optimize the screen copy. 251 */ 252 size = width * height * sizeof (term_char_t); 253 ptem->tvs_screen_buf = malloc(size); 254 tem_virtual_cls(ptem, width * height, 0, 0); 255 } 256 257 int 258 tem_initialized(tem_vt_state_t tem_arg) 259 { 260 struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg; 261 262 return (ptem->tvs_initialized); 263 } 264 265 tem_vt_state_t 266 tem_init(void) 267 { 268 struct tem_vt_state *ptem; 269 270 ptem = calloc(1, sizeof (struct tem_vt_state)); 271 if (ptem == NULL) 272 return ((tem_vt_state_t)ptem); 273 274 ptem->tvs_isactive = false; 275 ptem->tvs_fbmode = KD_TEXT; 276 277 /* 278 * A tem is regarded as initialized only after tem_internal_init(), 279 * will be set at the end of tem_internal_init(). 280 */ 281 ptem->tvs_initialized = 0; 282 283 if (!tems.ts_initialized) { 284 /* 285 * Only happens during early console configuration. 286 */ 287 tem_add(ptem); 288 return ((tem_vt_state_t)ptem); 289 } 290 291 tem_internal_init(ptem, true, false); 292 tem_add(ptem); 293 294 return ((tem_vt_state_t)ptem); 295 } 296 297 /* 298 * re-init the tem after video mode has changed and tems_info has 299 * been re-inited. 300 */ 301 static void 302 tem_reinit(struct tem_vt_state *tem, bool reset_display) 303 { 304 tem_free_buf(tem); /* only free virtual buffers */ 305 306 /* reserve color */ 307 tem_internal_init(tem, false, reset_display); 308 } 309 310 static void 311 tem_free_buf(struct tem_vt_state *tem) 312 { 313 free(tem->tvs_outbuf); 314 tem->tvs_outbuf = NULL; 315 316 free(tem->tvs_pix_data); 317 tem->tvs_pix_data = NULL; 318 319 free(tem->tvs_screen_buf); 320 tem->tvs_screen_buf = NULL; 321 322 free(tem->tvs_tabs); 323 tem->tvs_tabs = NULL; 324 } 325 326 static int 327 tems_failed(bool finish_ioctl) 328 { 329 if (finish_ioctl && tems.ts_hdl != NULL) 330 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_DEVFINI, NULL); 331 332 tems.ts_hdl = NULL; 333 return (ENXIO); 334 } 335 336 /* 337 * Only called once during boot 338 */ 339 int 340 tem_info_init(struct console *cp) 341 { 342 int ret; 343 struct vis_devinit temargs; 344 size_t height = 0; 345 size_t width = 0; 346 struct tem_vt_state *p; 347 348 if (tems.ts_initialized) { 349 return (0); 350 } 351 352 list_create(&tems.ts_list, sizeof (struct tem_vt_state), 353 __offsetof(struct tem_vt_state, tvs_list_node)); 354 tems.ts_active = NULL; 355 356 tems.ts_hdl = cp; 357 bzero(&temargs, sizeof (temargs)); 358 temargs.modechg_cb = (vis_modechg_cb_t)tems_modechange_callback; 359 temargs.modechg_arg = NULL; 360 361 /* 362 * Initialize the console and get the device parameters 363 */ 364 if (cp->c_ioctl(cp, VIS_DEVINIT, &temargs) != 0) { 365 printf("terminal emulator: Compatible fb not found\n"); 366 ret = tems_failed(false); 367 return (ret); 368 } 369 370 /* Make sure the fb driver and terminal emulator versions match */ 371 if (temargs.version != VIS_CONS_REV) { 372 printf( 373 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) " 374 "of console fb driver not supported\n", temargs.version); 375 ret = tems_failed(true); 376 return (ret); 377 } 378 379 /* other sanity checks */ 380 if (!((temargs.depth == 4) || (temargs.depth == 8) || 381 (temargs.depth == 15) || (temargs.depth == 16) || 382 (temargs.depth == 24) || (temargs.depth == 32))) { 383 printf("terminal emulator: unsupported depth\n"); 384 ret = tems_failed(true); 385 return (ret); 386 } 387 388 if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) { 389 printf("terminal emulator: unsupported mode\n"); 390 ret = tems_failed(true); 391 return (ret); 392 } 393 394 plat_tem_get_prom_size(&height, &width); 395 396 /* 397 * Initialize the common terminal emulator info 398 */ 399 tems_setup_terminal(&temargs, height, width); 400 401 tems_reset_colormap(); 402 tems_get_initial_color(&tems.ts_init_color); 403 404 tems.ts_initialized = 1; /* initialization flag */ 405 406 for (p = list_head(&tems.ts_list); p != NULL; 407 p = list_next(&tems.ts_list, p)) { 408 tem_internal_init(p, true, false); 409 if (temargs.mode == VIS_PIXEL) 410 tem_pix_align(p); 411 } 412 413 return (0); 414 } 415 416 #define TEMS_DEPTH_DIFF 0x01 417 #define TEMS_DIMENSION_DIFF 0x02 418 419 static uint8_t 420 tems_check_videomode(struct vis_devinit *tp) 421 { 422 uint8_t result = 0; 423 424 if (tems.ts_pdepth != tp->depth) 425 result |= TEMS_DEPTH_DIFF; 426 427 if (tp->mode == VIS_TEXT) { 428 if (tems.ts_c_dimension.width != tp->width || 429 tems.ts_c_dimension.height != tp->height) 430 result |= TEMS_DIMENSION_DIFF; 431 } else { 432 if (tems.ts_p_dimension.width != tp->width || 433 tems.ts_p_dimension.height != tp->height) 434 result |= TEMS_DIMENSION_DIFF; 435 } 436 if (tems.update_font == true) 437 result |= TEMS_DIMENSION_DIFF; 438 439 return (result); 440 } 441 442 static int 443 env_screen_nounset(struct env_var *ev __unused) 444 { 445 if (tems.ts_p_dimension.width == 0 && 446 tems.ts_p_dimension.height == 0) 447 return (0); 448 return (EPERM); 449 } 450 451 static void 452 tems_setup_font(screen_size_t height, screen_size_t width) 453 { 454 bitmap_data_t *font_data; 455 456 /* 457 * set_font() will select an appropriate sized font for 458 * the number of rows and columns selected. If we don't 459 * have a font that will fit, then it will use the 460 * default builtin font and adjust the rows and columns 461 * to fit on the screen. 462 */ 463 font_data = set_font(&tems.ts_c_dimension.height, 464 &tems.ts_c_dimension.width, height, width); 465 466 if (font_data == NULL) 467 panic("out of memory"); 468 469 /* 470 * To use loaded font, we assign the loaded font data to tems.ts_font. 471 * In case of next load, the previously loaded data is freed 472 * when loading the new font. 473 */ 474 for (int i = 0; i < VFNT_MAPS; i++) { 475 tems.ts_font.vf_map[i] = 476 font_data->font->vf_map[i]; 477 tems.ts_font.vf_map_count[i] = 478 font_data->font->vf_map_count[i]; 479 } 480 481 tems.ts_font.vf_bytes = font_data->font->vf_bytes; 482 tems.ts_font.vf_width = font_data->font->vf_width; 483 tems.ts_font.vf_height = font_data->font->vf_height; 484 } 485 486 static void 487 tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width) 488 { 489 char env[8]; 490 491 tems.ts_pdepth = tp->depth; 492 tems.ts_linebytes = tp->linebytes; 493 tems.ts_display_mode = tp->mode; 494 tems.ts_color_map = tp->color_map; 495 496 switch (tp->mode) { 497 case VIS_TEXT: 498 /* Set fake pixel dimensions to assist set_font() */ 499 tems.ts_p_dimension.width = 0; 500 tems.ts_p_dimension.height = 0; 501 tems.ts_c_dimension.width = tp->width; 502 tems.ts_c_dimension.height = tp->height; 503 tems.ts_callbacks = &tem_text_callbacks; 504 505 tems_setup_font(16 * tp->height + BORDER_PIXELS, 506 8 * tp->width + BORDER_PIXELS); 507 508 /* ensure the following are not set for text mode */ 509 unsetenv("screen-height"); 510 unsetenv("screen-width"); 511 break; 512 513 case VIS_PIXEL: 514 /* 515 * First check to see if the user has specified a screen size. 516 * If so, use those values. Else use 34x80 as the default. 517 */ 518 if (width == 0) { 519 width = TEM_DEFAULT_COLS; 520 height = TEM_DEFAULT_ROWS; 521 } 522 tems.ts_c_dimension.height = (screen_size_t)height; 523 tems.ts_c_dimension.width = (screen_size_t)width; 524 tems.ts_p_dimension.height = tp->height; 525 tems.ts_p_dimension.width = tp->width; 526 tems.ts_callbacks = &tem_pix_callbacks; 527 528 tems_setup_font(tp->height, tp->width); 529 530 snprintf(env, sizeof (env), "%d", tems.ts_p_dimension.height); 531 env_setenv("screen-height", EV_VOLATILE | EV_NOHOOK, env, 532 env_noset, env_screen_nounset); 533 snprintf(env, sizeof (env), "%d", tems.ts_p_dimension.width); 534 env_setenv("screen-width", EV_VOLATILE | EV_NOHOOK, env, 535 env_noset, env_screen_nounset); 536 537 tems.ts_p_offset.y = (tems.ts_p_dimension.height - 538 (tems.ts_c_dimension.height * tems.ts_font.vf_height)) / 2; 539 tems.ts_p_offset.x = (tems.ts_p_dimension.width - 540 (tems.ts_c_dimension.width * tems.ts_font.vf_width)) / 2; 541 tems.ts_pix_data_size = 542 tems.ts_font.vf_width * tems.ts_font.vf_height; 543 tems.ts_pix_data_size *= 4; 544 tems.ts_pdepth = tp->depth; 545 546 break; 547 } 548 549 tems.update_font = false; 550 551 snprintf(env, sizeof (env), "%d", tems.ts_c_dimension.height); 552 env_setenv("screen-#rows", EV_VOLATILE | EV_NOHOOK, env, 553 env_noset, env_nounset); 554 snprintf(env, sizeof (env), "%d", tems.ts_c_dimension.width); 555 env_setenv("screen-#cols", EV_VOLATILE | EV_NOHOOK, env, 556 env_noset, env_nounset); 557 558 snprintf(env, sizeof (env), "%dx%d", tems.ts_font.vf_width, 559 tems.ts_font.vf_height); 560 env_setenv("screen-font", EV_VOLATILE | EV_NOHOOK, env, NULL, 561 NULL); 562 } 563 564 /* 565 * This is a callback function that we register with the frame 566 * buffer driver layered underneath. It gets invoked from 567 * the underlying frame buffer driver to reconfigure the terminal 568 * emulator to a new screen size and depth in conjunction with 569 * framebuffer videomode changes. 570 * Here we keep the foreground/background color and attributes, 571 * which may be different with the initial settings, so that 572 * the color won't change while the framebuffer videomode changes. 573 * And we also reset the kernel terminal emulator and clear the 574 * whole screen. 575 */ 576 /* ARGSUSED */ 577 void 578 tems_modechange_callback(struct vis_modechg_arg *arg __unused, 579 struct vis_devinit *devinit) 580 { 581 uint8_t diff; 582 struct tem_vt_state *p; 583 tem_modechg_cb_t cb; 584 tem_modechg_cb_arg_t cb_arg; 585 size_t height = 0; 586 size_t width = 0; 587 int state; 588 589 diff = tems_check_videomode(devinit); 590 if (diff == 0) { 591 /* 592 * This is color related change, reset color and redraw the 593 * screen. Only need to reinit the active tem. 594 */ 595 struct tem_vt_state *active = tems.ts_active; 596 tems_get_initial_color(&tems.ts_init_color); 597 active->tvs_fg_color = tems.ts_init_color.fg_color; 598 active->tvs_bg_color = tems.ts_init_color.bg_color; 599 active->tvs_flags = tems.ts_init_color.a_flags; 600 tem_reinit(active, true); 601 return; 602 } 603 604 diff = diff & TEMS_DIMENSION_DIFF; 605 606 if (diff == 0) { 607 /* 608 * Only need to reinit the active tem. 609 */ 610 struct tem_vt_state *active = tems.ts_active; 611 tems.ts_pdepth = devinit->depth; 612 /* color depth did change, reset colors */ 613 tems_reset_colormap(); 614 tems_get_initial_color(&tems.ts_init_color); 615 tem_reinit(active, true); 616 617 return; 618 } 619 620 plat_tem_get_prom_size(&height, &width); 621 622 state = tems.ts_initialized; 623 tems.ts_initialized = 0; /* stop all output */ 624 tems_setup_terminal(devinit, height, width); 625 626 tems_reset_colormap(); 627 tems_get_initial_color(&tems.ts_init_color); 628 tems.ts_initialized = state; /* restore state */ 629 630 for (p = list_head(&tems.ts_list); p != NULL; 631 p = list_next(&tems.ts_list, p)) { 632 tem_reinit(p, p->tvs_isactive); 633 } 634 635 636 if (tems.ts_modechg_cb == NULL) { 637 return; 638 } 639 640 cb = tems.ts_modechg_cb; 641 cb_arg = tems.ts_modechg_arg; 642 643 cb(cb_arg); 644 } 645 646 /* 647 * This function is used to clear entire screen via the underlying framebuffer 648 * driver. 649 */ 650 int 651 tems_cls(struct vis_consclear *pda) 652 { 653 if (tems.ts_hdl == NULL) 654 return (1); 655 return (tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCLEAR, pda)); 656 } 657 658 /* 659 * This function is used to display a rectangular blit of data 660 * of a given size and location via the underlying framebuffer driver. 661 * The blit can be as small as a pixel or as large as the screen. 662 */ 663 void 664 tems_display(struct vis_consdisplay *pda) 665 { 666 if (tems.ts_hdl != NULL) 667 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSDISPLAY, pda); 668 } 669 670 /* 671 * This function is used to invoke a block copy operation in the 672 * underlying framebuffer driver. Rectangle copies are how scrolling 673 * is implemented, as well as horizontal text shifting escape seqs. 674 * such as from vi when deleting characters and words. 675 */ 676 void 677 tems_copy(struct vis_conscopy *pma) 678 { 679 if (tems.ts_hdl != NULL) 680 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCOPY, pma); 681 } 682 683 /* 684 * This function is used to show or hide a rectangluar monochrom 685 * pixel inverting, text block cursor via the underlying framebuffer. 686 */ 687 void 688 tems_cursor(struct vis_conscursor *pca) 689 { 690 if (tems.ts_hdl != NULL) 691 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCURSOR, pca); 692 } 693 694 static void 695 tem_kdsetmode(int mode) 696 { 697 if (tems.ts_hdl != NULL) { 698 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, KDSETMODE, 699 (void *)(intptr_t)mode); 700 } 701 } 702 703 static void 704 tems_reset_colormap(void) 705 { 706 struct vis_cmap cm; 707 708 switch (tems.ts_pdepth) { 709 case 8: 710 cm.index = 0; 711 cm.count = 16; 712 /* 8-bits (1/3 of TrueColor 24) */ 713 cm.red = (uint8_t *)cmap4_to_24.red; 714 /* 8-bits (1/3 of TrueColor 24) */ 715 cm.blue = (uint8_t *)cmap4_to_24.blue; 716 /* 8-bits (1/3 of TrueColor 24) */ 717 cm.green = (uint8_t *)cmap4_to_24.green; 718 if (tems.ts_hdl != NULL) 719 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, 720 VIS_PUTCMAP, &cm); 721 break; 722 } 723 } 724 725 void 726 tem_get_size(uint16_t *r, uint16_t *c, uint16_t *x, uint16_t *y) 727 { 728 *r = (uint16_t)tems.ts_c_dimension.height; 729 *c = (uint16_t)tems.ts_c_dimension.width; 730 *x = (uint16_t)tems.ts_p_dimension.width; 731 *y = (uint16_t)tems.ts_p_dimension.height; 732 } 733 734 /* 735 * Loader extension. Store important data in environment. Intended to be used 736 * just before booting the OS to make the data available in kernel 737 * environment module. 738 */ 739 void 740 tem_save_state(void) 741 { 742 struct tem_vt_state *active = tems.ts_active; 743 char buf[80]; 744 745 /* 746 * We already have in environment: 747 * tem.inverse, tem.inverse_screen 748 * tem.fg_color, tem.bg_color. 749 * Add the cursor data. 750 */ 751 752 if (active != NULL) { 753 snprintf(buf, sizeof (buf), "%d", active->tvs_c_cursor.col); 754 setenv("tem.cursor.col", buf, 1); 755 snprintf(buf, sizeof (buf), "%d", active->tvs_c_cursor.row); 756 setenv("tem.cursor.row", buf, 1); 757 snprintf(buf, sizeof (buf), "%d", active->tvs_c_cursor.col * 758 tems.ts_font.vf_width + tems.ts_p_offset.x); 759 setenv("tem.cursor.origin.x", buf, 1); 760 snprintf(buf, sizeof (buf), "%d", active->tvs_c_cursor.row * 761 tems.ts_font.vf_height + tems.ts_p_offset.y); 762 setenv("tem.cursor.origin.y", buf, 1); 763 if (!active->tvs_cursor_hidden) 764 setenv("tem.cursor.visible", "YES", 1); 765 } 766 } 767 768 void 769 tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg) 770 { 771 tems.ts_modechg_cb = func; 772 tems.ts_modechg_arg = arg; 773 } 774 775 /* 776 * This function is to scroll up the OBP output, which has 777 * different screen height and width with our kernel console. 778 */ 779 static void 780 tem_prom_scroll_up(struct tem_vt_state *tem, int nrows) 781 { 782 struct vis_conscopy ma; 783 int ncols, width; 784 785 /* copy */ 786 ma.s_row = nrows * tems.ts_font.vf_height; 787 ma.e_row = tems.ts_p_dimension.height - 1; 788 ma.t_row = 0; 789 790 ma.s_col = 0; 791 ma.e_col = tems.ts_p_dimension.width - 1; 792 ma.t_col = 0; 793 794 tems_copy(&ma); 795 796 /* clear */ 797 width = tems.ts_font.vf_width; 798 ncols = (tems.ts_p_dimension.width + (width - 1)) / width; 799 800 tem_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y, 801 0, ncols, 0, true); 802 } 803 804 /* 805 * This function is to compute the starting row of the console, according to 806 * PROM cursor's position. Here we have to take different fonts into account. 807 */ 808 static int 809 tem_adjust_row(struct tem_vt_state *tem, int prom_row) 810 { 811 int tem_row; 812 int tem_y; 813 int prom_charheight = 0; 814 int prom_window_top = 0; 815 int scroll_up_lines; 816 817 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top); 818 if (prom_charheight == 0) 819 prom_charheight = tems.ts_font.vf_height; 820 821 tem_y = (prom_row + 1) * prom_charheight + prom_window_top - 822 tems.ts_p_offset.y; 823 tem_row = (tem_y + tems.ts_font.vf_height - 1) / 824 tems.ts_font.vf_height - 1; 825 826 if (tem_row < 0) { 827 tem_row = 0; 828 } else if (tem_row >= (tems.ts_c_dimension.height - 1)) { 829 /* 830 * Scroll up the prom outputs if the PROM cursor's position is 831 * below our tem's lower boundary. 832 */ 833 scroll_up_lines = tem_row - 834 (tems.ts_c_dimension.height - 1); 835 tem_prom_scroll_up(tem, scroll_up_lines); 836 tem_row = tems.ts_c_dimension.height - 1; 837 } 838 839 return (tem_row); 840 } 841 842 static void 843 tem_pix_align(struct tem_vt_state *tem) 844 { 845 uint32_t row = 0; 846 uint32_t col = 0; 847 848 if (plat_stdout_is_framebuffer()) { 849 plat_tem_hide_prom_cursor(); 850 851 /* 852 * We are getting the current cursor position in pixel 853 * mode so that we don't over-write the console output 854 * during boot. 855 */ 856 plat_tem_get_prom_pos(&row, &col); 857 858 /* 859 * Adjust the row if necessary when the font of our 860 * kernel console tem is different with that of prom 861 * tem. 862 */ 863 row = tem_adjust_row(tem, row); 864 865 /* first line of our kernel console output */ 866 tem->tvs_first_line = row + 1; 867 868 /* re-set and align cursor position */ 869 tem->tvs_s_cursor.row = tem->tvs_c_cursor.row = 870 (screen_pos_t)row; 871 tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0; 872 } else { 873 tem_reset_display(tem, true, true); 874 } 875 } 876 877 static void 878 tems_get_inverses(bool *p_inverse, bool *p_inverse_screen) 879 { 880 int i_inverse = 0; 881 int i_inverse_screen = 0; 882 883 plat_tem_get_inverses(&i_inverse, &i_inverse_screen); 884 885 *p_inverse = i_inverse != 0; 886 *p_inverse_screen = i_inverse_screen != 0; 887 } 888 889 /* 890 * Get the foreground/background color and attributes from environment. 891 */ 892 static void 893 tems_get_initial_color(tem_color_t *pcolor) 894 { 895 bool inverse, inverse_screen; 896 unsigned short flags = 0; 897 uint8_t fg, bg; 898 899 fg = DEFAULT_ANSI_FOREGROUND; 900 bg = DEFAULT_ANSI_BACKGROUND; 901 plat_tem_get_colors(&fg, &bg); 902 pcolor->fg_color.n = fg; 903 pcolor->bg_color.n = bg; 904 905 tems_get_inverses(&inverse, &inverse_screen); 906 if (inverse) 907 flags |= TEM_ATTR_REVERSE; 908 if (inverse_screen) 909 flags |= TEM_ATTR_SCREEN_REVERSE; 910 911 if (flags != 0) { 912 /* 913 * The reverse attribute is set. 914 * In case of black on white we want bright white for BG. 915 */ 916 if (pcolor->fg_color.n == ANSI_COLOR_WHITE) 917 flags |= TEM_ATTR_BRIGHT_BG; 918 919 /* 920 * For white on black, unset the bright attribute we 921 * had set to have bright white background. 922 */ 923 if (pcolor->fg_color.n == ANSI_COLOR_BLACK) 924 flags &= ~TEM_ATTR_BRIGHT_BG; 925 } else { 926 /* 927 * In case of black on white we want bright white for BG. 928 */ 929 if (pcolor->bg_color.n == ANSI_COLOR_WHITE) 930 flags |= TEM_ATTR_BRIGHT_BG; 931 } 932 933 pcolor->a_flags = flags; 934 } 935 936 void 937 tem_activate(tem_vt_state_t tem_arg, bool unblank) 938 { 939 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 940 941 tems.ts_active = tem; 942 tem->tvs_isactive = true; 943 944 tem_kdsetmode(tem->tvs_fbmode); 945 946 if (unblank) 947 tem_cls(tem); 948 } 949 950 static void 951 tem_check_first_time(struct tem_vt_state *tem) 952 { 953 static int first_time = 1; 954 955 /* 956 * Realign the console cursor. We did this in tem_init(). 957 * However, drivers in the console stream may emit additional 958 * messages before we are ready. This causes text overwrite 959 * on the screen. This is a workaround. 960 */ 961 if (!first_time) 962 return; 963 964 first_time = 0; 965 if (tems.ts_display_mode == VIS_TEXT) 966 tem_text_cursor(tem, VIS_GET_CURSOR); 967 else 968 tem_pix_cursor(tem, VIS_GET_CURSOR); 969 tem_align_cursor(tem); 970 } 971 972 /* Process partial UTF-8 sequence. */ 973 static void 974 tem_input_partial(struct tem_vt_state *tem) 975 { 976 unsigned i; 977 tem_char_t c; 978 979 if (tem->tvs_utf8_left == 0) 980 return; 981 982 for (i = 0; i < sizeof (tem->tvs_utf8_partial); i++) { 983 c = (tem->tvs_utf8_partial >> (24 - (i << 3))) & 0xff; 984 if (c != 0) { 985 tem_parse(tem, c); 986 } 987 } 988 tem->tvs_utf8_left = 0; 989 tem->tvs_utf8_partial = 0; 990 } 991 992 /* 993 * Handle UTF-8 sequences. 994 */ 995 static void 996 tem_input_byte(struct tem_vt_state *tem, uint8_t c) 997 { 998 /* 999 * Check for UTF-8 code points. In case of error fall back to 1000 * 8-bit code. As we only have 8859-1 fonts for console, this will set 1001 * the limits on what chars we actually can display, therefore we 1002 * have to return to this code once we have solved the font issue. 1003 */ 1004 if ((c & 0x80) == 0x00) { 1005 /* One-byte sequence. */ 1006 tem_input_partial(tem); 1007 tem_parse(tem, c); 1008 return; 1009 } 1010 if ((c & 0xe0) == 0xc0) { 1011 /* Two-byte sequence. */ 1012 tem_input_partial(tem); 1013 tem->tvs_utf8_left = 1; 1014 tem->tvs_utf8_partial = c; 1015 return; 1016 } 1017 if ((c & 0xf0) == 0xe0) { 1018 /* Three-byte sequence. */ 1019 tem_input_partial(tem); 1020 tem->tvs_utf8_left = 2; 1021 tem->tvs_utf8_partial = c; 1022 return; 1023 } 1024 if ((c & 0xf8) == 0xf0) { 1025 /* Four-byte sequence. */ 1026 tem_input_partial(tem); 1027 tem->tvs_utf8_left = 3; 1028 tem->tvs_utf8_partial = c; 1029 return; 1030 } 1031 if ((c & 0xc0) == 0x80) { 1032 /* Invalid state? */ 1033 if (tem->tvs_utf8_left == 0) { 1034 tem_parse(tem, c); 1035 return; 1036 } 1037 tem->tvs_utf8_left--; 1038 tem->tvs_utf8_partial = (tem->tvs_utf8_partial << 8) | c; 1039 if (tem->tvs_utf8_left == 0) { 1040 tem_char_t v, u; 1041 uint8_t b; 1042 1043 /* 1044 * Transform the sequence of 2 to 4 bytes to 1045 * unicode number. 1046 */ 1047 v = 0; 1048 u = tem->tvs_utf8_partial; 1049 b = (u >> 24) & 0xff; 1050 if (b != 0) { /* Four-byte sequence */ 1051 v = b & 0x07; 1052 b = (u >> 16) & 0xff; 1053 v = (v << 6) | (b & 0x3f); 1054 b = (u >> 8) & 0xff; 1055 v = (v << 6) | (b & 0x3f); 1056 b = u & 0xff; 1057 v = (v << 6) | (b & 0x3f); 1058 } else if ((b = (u >> 16) & 0xff) != 0) { 1059 v = b & 0x0f; /* Three-byte sequence */ 1060 b = (u >> 8) & 0xff; 1061 v = (v << 6) | (b & 0x3f); 1062 b = u & 0xff; 1063 v = (v << 6) | (b & 0x3f); 1064 } else if ((b = (u >> 8) & 0xff) != 0) { 1065 v = b & 0x1f; /* Two-byte sequence */ 1066 b = u & 0xff; 1067 v = (v << 6) | (b & 0x3f); 1068 } 1069 1070 tem_parse(tem, v); 1071 tem->tvs_utf8_partial = 0; 1072 } 1073 return; 1074 } 1075 /* Anything left is illegal in UTF-8 sequence. */ 1076 tem_input_partial(tem); 1077 tem_parse(tem, c); 1078 } 1079 1080 /* 1081 * This is the main entry point into the terminal emulator. 1082 * 1083 * For each data message coming downstream, ANSI assumes that it is composed 1084 * of ASCII characters, which are treated as a byte-stream input to the 1085 * parsing state machine. All data is parsed immediately -- there is 1086 * no enqueing. 1087 */ 1088 static void 1089 tem_terminal_emulate(struct tem_vt_state *tem, uint8_t *buf, int len) 1090 { 1091 if (tem->tvs_isactive && !tem->tvs_cursor_hidden) 1092 tem_callback_cursor(tem, VIS_HIDE_CURSOR); 1093 1094 for (; len > 0; len--, buf++) 1095 tem_input_byte(tem, *buf); 1096 1097 /* 1098 * Send the data we just got to the framebuffer. 1099 */ 1100 tem_send_data(tem); 1101 1102 if (tem->tvs_isactive && !tem->tvs_cursor_hidden) 1103 tem_callback_cursor(tem, VIS_DISPLAY_CURSOR); 1104 } 1105 1106 /* 1107 * send the appropriate control message or set state based on the 1108 * value of the control character ch 1109 */ 1110 1111 static void 1112 tem_control(struct tem_vt_state *tem, uint8_t ch) 1113 { 1114 tem->tvs_state = A_STATE_START; 1115 switch (ch) { 1116 case A_BEL: 1117 tem_bell(tem); 1118 break; 1119 1120 case A_BS: 1121 tem_mv_cursor(tem, 1122 tem->tvs_c_cursor.row, 1123 tem->tvs_c_cursor.col - 1); 1124 break; 1125 1126 case A_HT: 1127 tem_tab(tem); 1128 break; 1129 1130 case A_NL: 1131 /* 1132 * tem_send_data(tem, credp, called_from); 1133 * tem_new_line(tem, credp, called_from); 1134 * break; 1135 */ 1136 1137 case A_VT: 1138 tem_send_data(tem); 1139 tem_lf(tem); 1140 break; 1141 1142 case A_FF: 1143 tem_send_data(tem); 1144 tem_cls(tem); 1145 break; 1146 1147 case A_CR: 1148 tem_send_data(tem); 1149 tem_cr(tem); 1150 break; 1151 1152 case A_ESC: 1153 tem->tvs_state = A_STATE_ESC; 1154 break; 1155 1156 case A_CSI: 1157 tem->tvs_curparam = 0; 1158 tem->tvs_paramval = 0; 1159 tem->tvs_gotparam = false; 1160 /* clear the parameters */ 1161 for (int i = 0; i < TEM_MAXPARAMS; i++) 1162 tem->tvs_params[i] = -1; 1163 tem->tvs_state = A_STATE_CSI; 1164 break; 1165 1166 case A_GS: 1167 tem_back_tab(tem); 1168 break; 1169 1170 default: 1171 break; 1172 } 1173 } 1174 1175 1176 /* 1177 * if parameters [0..count - 1] are not set, set them to the value 1178 * of newparam. 1179 */ 1180 1181 static void 1182 tem_setparam(struct tem_vt_state *tem, int count, int newparam) 1183 { 1184 int i; 1185 1186 for (i = 0; i < count; i++) { 1187 if (tem->tvs_params[i] == -1) 1188 tem->tvs_params[i] = newparam; 1189 } 1190 } 1191 1192 /* 1193 * For colors 0-15 the tem is using color code translation 1194 * from sun colors to vga (dim_xlate and brt_xlate tables, see tem_get_color). 1195 * Colors 16-255 are used without translation. 1196 */ 1197 static void 1198 tem_select_color(struct tem_vt_state *tem, int color, bool fg) 1199 { 1200 if (color < 0 || color > 255) 1201 return; 1202 1203 /* VGA text mode only does support 16 colors. */ 1204 if (tems.ts_display_mode == VIS_TEXT && color > 15) 1205 return; 1206 1207 /* Switch to use indexed colors. */ 1208 if (fg == true) { 1209 tem->tvs_flags &= ~TEM_ATTR_RGB_FG; 1210 tem->tvs_fg_color.n = color; 1211 } else { 1212 tem->tvs_flags &= ~TEM_ATTR_RGB_BG; 1213 tem->tvs_bg_color.n = color; 1214 } 1215 1216 /* 1217 * For colors 0-7, make sure the BRIGHT attribute is not set. 1218 */ 1219 if (color < 8) { 1220 if (fg == true) 1221 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG; 1222 else 1223 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG; 1224 return; 1225 } 1226 1227 /* 1228 * For colors 8-15, we use color codes 0-7 and set BRIGHT attribute. 1229 */ 1230 if (color < 16) { 1231 if (fg == true) { 1232 tem->tvs_fg_color.n -= 8; 1233 tem->tvs_flags |= TEM_ATTR_BRIGHT_FG; 1234 } else { 1235 tem->tvs_bg_color.n -= 8; 1236 tem->tvs_flags |= TEM_ATTR_BRIGHT_BG; 1237 } 1238 } 1239 } 1240 1241 /* 1242 * select graphics mode based on the param vals stored in a_params 1243 */ 1244 static void 1245 tem_selgraph(struct tem_vt_state *tem) 1246 { 1247 int curparam; 1248 int count = 0; 1249 int param; 1250 int r, g, b; 1251 1252 tem->tvs_state = A_STATE_START; 1253 1254 curparam = tem->tvs_curparam; 1255 do { 1256 param = tem->tvs_params[count]; 1257 1258 switch (param) { 1259 case -1: 1260 case 0: 1261 /* reset to initial normal settings */ 1262 tem->tvs_fg_color = tems.ts_init_color.fg_color; 1263 tem->tvs_bg_color = tems.ts_init_color.bg_color; 1264 tem->tvs_flags = tems.ts_init_color.a_flags; 1265 break; 1266 1267 case 1: /* Bold Intense */ 1268 tem->tvs_flags |= TEM_ATTR_BOLD; 1269 break; 1270 1271 case 2: /* Faint Intense */ 1272 tem->tvs_flags &= ~TEM_ATTR_BOLD; 1273 break; 1274 1275 case 4: /* Underline */ 1276 tem->tvs_flags |= TEM_ATTR_UNDERLINE; 1277 break; 1278 1279 case 5: /* Blink */ 1280 tem->tvs_flags |= TEM_ATTR_BLINK; 1281 break; 1282 1283 case 7: /* Reverse video */ 1284 if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) { 1285 tem->tvs_flags &= ~TEM_ATTR_REVERSE; 1286 } else { 1287 tem->tvs_flags |= TEM_ATTR_REVERSE; 1288 } 1289 break; 1290 1291 case 22: /* Remove Bold */ 1292 tem->tvs_flags &= ~TEM_ATTR_BOLD; 1293 break; 1294 1295 case 24: /* Remove Underline */ 1296 tem->tvs_flags &= ~TEM_ATTR_UNDERLINE; 1297 break; 1298 1299 case 25: /* Remove Blink */ 1300 tem->tvs_flags &= ~TEM_ATTR_BLINK; 1301 break; 1302 1303 case 27: /* Remove Reverse */ 1304 if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) { 1305 tem->tvs_flags |= TEM_ATTR_REVERSE; 1306 } else { 1307 tem->tvs_flags &= ~TEM_ATTR_REVERSE; 1308 } 1309 break; 1310 1311 case 30: /* black (grey) foreground */ 1312 case 31: /* red (light red) foreground */ 1313 case 32: /* green (light green) foreground */ 1314 case 33: /* brown (yellow) foreground */ 1315 case 34: /* blue (light blue) foreground */ 1316 case 35: /* magenta (light magenta) foreground */ 1317 case 36: /* cyan (light cyan) foreground */ 1318 case 37: /* white (bright white) foreground */ 1319 tem->tvs_fg_color.n = param - 30; 1320 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG; 1321 tem->tvs_flags &= ~TEM_ATTR_RGB_FG; 1322 break; 1323 1324 case 38: 1325 /* 1326 * We should have 3 parameters for 256 colors and 1327 * 5 parameters for 24-bit colors. 1328 */ 1329 if (curparam < 3) { 1330 curparam = 0; 1331 break; 1332 } 1333 1334 /* 1335 * 256 and truecolor needs depth > 8, but 1336 * we still need to process the sequence. 1337 */ 1338 count++; 1339 curparam--; 1340 param = tem->tvs_params[count]; 1341 switch (param) { 1342 case 2: /* RGB colors */ 1343 if (curparam < 4) { 1344 curparam = 0; 1345 break; 1346 } 1347 r = tem->tvs_params[++count]; 1348 g = tem->tvs_params[++count]; 1349 b = tem->tvs_params[++count]; 1350 curparam -= 3; 1351 if (r < 0 || r > 255 || g < 0 || g > 255 || 1352 b < 0 || b > 255) 1353 break; 1354 1355 if (tems.ts_display_mode == VIS_PIXEL && 1356 tems.ts_pdepth > 8) { 1357 tem->tvs_flags |= TEM_ATTR_RGB_FG; 1358 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG; 1359 tem->tvs_fg_color.rgb.a = 1360 tem->tvs_alpha; 1361 tem->tvs_fg_color.rgb.r = r; 1362 tem->tvs_fg_color.rgb.g = g; 1363 tem->tvs_fg_color.rgb.b = b; 1364 } 1365 break; 1366 case 5: /* 256 colors */ 1367 count++; 1368 curparam--; 1369 tem_select_color(tem, tem->tvs_params[count], 1370 true); 1371 break; 1372 default: 1373 curparam = 0; 1374 break; 1375 } 1376 break; 1377 1378 case 39: 1379 /* 1380 * Reset the foreground colour and brightness. 1381 */ 1382 tem->tvs_fg_color = tems.ts_init_color.fg_color; 1383 tem->tvs_flags &= ~TEM_ATTR_RGB_FG; 1384 if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_FG) 1385 tem->tvs_flags |= TEM_ATTR_BRIGHT_FG; 1386 else 1387 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG; 1388 break; 1389 1390 case 40: /* black (grey) background */ 1391 case 41: /* red (light red) background */ 1392 case 42: /* green (light green) background */ 1393 case 43: /* brown (yellow) background */ 1394 case 44: /* blue (light blue) background */ 1395 case 45: /* magenta (light magenta) background */ 1396 case 46: /* cyan (light cyan) background */ 1397 case 47: /* white (bright white) background */ 1398 tem->tvs_bg_color.n = param - 40; 1399 tem->tvs_flags &= ~TEM_ATTR_RGB_BG; 1400 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG; 1401 break; 1402 1403 case 48: 1404 /* 1405 * We should have 3 parameters for 256 colors and 1406 * 5 parameters for 24-bit colors. 1407 */ 1408 /* We should have at least 3 parameters */ 1409 if (curparam < 3) { 1410 curparam = 0; 1411 break; 1412 } 1413 1414 /* 1415 * 256 and truecolor needs depth > 8, but 1416 * we still need to process the sequence. 1417 */ 1418 count++; 1419 curparam--; 1420 param = tem->tvs_params[count]; 1421 switch (param) { 1422 case 2: /* RGB colors */ 1423 if (curparam < 4) { 1424 curparam = 0; 1425 break; 1426 } 1427 r = tem->tvs_params[++count]; 1428 g = tem->tvs_params[++count]; 1429 b = tem->tvs_params[++count]; 1430 curparam -= 3; 1431 if (r < 0 || r > 255 || g < 0 || g > 255 || 1432 b < 0 || b > 255) 1433 break; 1434 1435 if (tems.ts_display_mode == VIS_PIXEL && 1436 tems.ts_pdepth > 8) { 1437 tem->tvs_flags |= TEM_ATTR_RGB_BG; 1438 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG; 1439 tem->tvs_bg_color.rgb.a = 1440 tem->tvs_alpha; 1441 tem->tvs_bg_color.rgb.r = r; 1442 tem->tvs_bg_color.rgb.g = g; 1443 tem->tvs_bg_color.rgb.b = b; 1444 } 1445 break; 1446 case 5: /* 256 colors */ 1447 count++; 1448 curparam--; 1449 tem_select_color(tem, tem->tvs_params[count], 1450 false); 1451 break; 1452 default: 1453 curparam = 0; 1454 break; 1455 } 1456 break; 1457 1458 case 49: 1459 /* 1460 * Reset the background colour and brightness. 1461 */ 1462 tem->tvs_bg_color = tems.ts_init_color.bg_color; 1463 tem->tvs_flags &= ~TEM_ATTR_RGB_BG; 1464 if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_BG) 1465 tem->tvs_flags |= TEM_ATTR_BRIGHT_BG; 1466 else 1467 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG; 1468 break; 1469 1470 case 90: /* black (grey) foreground */ 1471 case 91: /* red (light red) foreground */ 1472 case 92: /* green (light green) foreground */ 1473 case 93: /* brown (yellow) foreground */ 1474 case 94: /* blue (light blue) foreground */ 1475 case 95: /* magenta (light magenta) foreground */ 1476 case 96: /* cyan (light cyan) foreground */ 1477 case 97: /* white (bright white) foreground */ 1478 tem->tvs_fg_color.n = param - 90; 1479 tem->tvs_flags |= TEM_ATTR_BRIGHT_FG; 1480 tem->tvs_flags &= ~TEM_ATTR_RGB_FG; 1481 break; 1482 1483 case 100: /* black (grey) background */ 1484 case 101: /* red (light red) background */ 1485 case 102: /* green (light green) background */ 1486 case 103: /* brown (yellow) background */ 1487 case 104: /* blue (light blue) background */ 1488 case 105: /* magenta (light magenta) background */ 1489 case 106: /* cyan (light cyan) background */ 1490 case 107: /* white (bright white) background */ 1491 tem->tvs_bg_color.n = param - 100; 1492 tem->tvs_flags |= TEM_ATTR_BRIGHT_BG; 1493 tem->tvs_flags &= ~TEM_ATTR_RGB_BG; 1494 break; 1495 1496 default: 1497 break; 1498 } 1499 count++; 1500 curparam--; 1501 1502 } while (curparam > 0); 1503 } 1504 1505 /* 1506 * perform the appropriate action for the escape sequence 1507 * 1508 * General rule: This code does not validate the arguments passed. 1509 * It assumes that the next lower level will do so. 1510 */ 1511 static void 1512 tem_chkparam(struct tem_vt_state *tem, uint8_t ch) 1513 { 1514 int i; 1515 int row; 1516 int col; 1517 1518 row = tem->tvs_c_cursor.row; 1519 col = tem->tvs_c_cursor.col; 1520 1521 switch (ch) { 1522 1523 case 'm': /* select terminal graphics mode */ 1524 tem_send_data(tem); 1525 tem_selgraph(tem); 1526 break; 1527 1528 case '@': /* insert char */ 1529 tem_setparam(tem, 1, 1); 1530 tem_shift(tem, tem->tvs_params[0], TEM_SHIFT_RIGHT); 1531 break; 1532 1533 case 'A': /* cursor up */ 1534 tem_setparam(tem, 1, 1); 1535 tem_mv_cursor(tem, row - tem->tvs_params[0], col); 1536 break; 1537 1538 case 'd': /* VPA - vertical position absolute */ 1539 tem_setparam(tem, 1, 1); 1540 tem_mv_cursor(tem, tem->tvs_params[0] - 1, col); 1541 break; 1542 1543 case 'e': /* VPR - vertical position relative */ 1544 case 'B': /* cursor down */ 1545 tem_setparam(tem, 1, 1); 1546 tem_mv_cursor(tem, row + tem->tvs_params[0], col); 1547 break; 1548 1549 case 'a': /* HPR - horizontal position relative */ 1550 case 'C': /* cursor right */ 1551 tem_setparam(tem, 1, 1); 1552 tem_mv_cursor(tem, row, col + tem->tvs_params[0]); 1553 break; 1554 1555 case '`': /* HPA - horizontal position absolute */ 1556 tem_setparam(tem, 1, 1); 1557 tem_mv_cursor(tem, row, tem->tvs_params[0] - 1); 1558 break; 1559 1560 case 'D': /* cursor left */ 1561 tem_setparam(tem, 1, 1); 1562 tem_mv_cursor(tem, row, col - tem->tvs_params[0]); 1563 break; 1564 1565 case 'E': /* CNL cursor next line */ 1566 tem_setparam(tem, 1, 1); 1567 tem_mv_cursor(tem, row + tem->tvs_params[0], 0); 1568 break; 1569 1570 case 'F': /* CPL cursor previous line */ 1571 tem_setparam(tem, 1, 1); 1572 tem_mv_cursor(tem, row - tem->tvs_params[0], 0); 1573 break; 1574 1575 case 'G': /* cursor horizontal position */ 1576 tem_setparam(tem, 1, 1); 1577 tem_mv_cursor(tem, row, tem->tvs_params[0] - 1); 1578 break; 1579 1580 case 'g': /* clear tabs */ 1581 tem_setparam(tem, 1, 0); 1582 tem_clear_tabs(tem, tem->tvs_params[0]); 1583 break; 1584 1585 case 'f': /* HVP Horizontal and Vertical Position */ 1586 case 'H': /* CUP position cursor */ 1587 tem_setparam(tem, 2, 1); 1588 tem_mv_cursor(tem, 1589 tem->tvs_params[0] - 1, tem->tvs_params[1] - 1); 1590 break; 1591 1592 case 'I': /* CHT - Cursor Horizontal Tab */ 1593 /* Not implemented */ 1594 break; 1595 1596 case 'J': /* ED - Erase in Display */ 1597 tem_send_data(tem); 1598 tem_setparam(tem, 1, 0); 1599 switch (tem->tvs_params[0]) { 1600 case 0: 1601 /* erase cursor to end of screen */ 1602 /* FIRST erase cursor to end of line */ 1603 tem_clear_chars(tem, 1604 tems.ts_c_dimension.width - 1605 tem->tvs_c_cursor.col, 1606 tem->tvs_c_cursor.row, 1607 tem->tvs_c_cursor.col); 1608 1609 /* THEN erase lines below the cursor */ 1610 for (row = tem->tvs_c_cursor.row + 1; 1611 row < tems.ts_c_dimension.height; 1612 row++) { 1613 tem_clear_chars(tem, 1614 tems.ts_c_dimension.width, row, 0); 1615 } 1616 break; 1617 1618 case 1: 1619 /* erase beginning of screen to cursor */ 1620 /* FIRST erase lines above the cursor */ 1621 for (row = 0; 1622 row < tem->tvs_c_cursor.row; 1623 row++) { 1624 tem_clear_chars(tem, 1625 tems.ts_c_dimension.width, row, 0); 1626 } 1627 /* THEN erase beginning of line to cursor */ 1628 tem_clear_chars(tem, 1629 tem->tvs_c_cursor.col + 1, 1630 tem->tvs_c_cursor.row, 0); 1631 break; 1632 1633 case 2: 1634 /* erase whole screen */ 1635 for (row = 0; 1636 row < tems.ts_c_dimension.height; 1637 row++) { 1638 tem_clear_chars(tem, 1639 tems.ts_c_dimension.width, row, 0); 1640 } 1641 break; 1642 } 1643 break; 1644 1645 case 'K': /* EL - Erase in Line */ 1646 tem_send_data(tem); 1647 tem_setparam(tem, 1, 0); 1648 switch (tem->tvs_params[0]) { 1649 case 0: 1650 /* erase cursor to end of line */ 1651 tem_clear_chars(tem, 1652 (tems.ts_c_dimension.width - 1653 tem->tvs_c_cursor.col), 1654 tem->tvs_c_cursor.row, 1655 tem->tvs_c_cursor.col); 1656 break; 1657 1658 case 1: 1659 /* erase beginning of line to cursor */ 1660 tem_clear_chars(tem, 1661 tem->tvs_c_cursor.col + 1, 1662 tem->tvs_c_cursor.row, 0); 1663 break; 1664 1665 case 2: 1666 /* erase whole line */ 1667 tem_clear_chars(tem, 1668 tems.ts_c_dimension.width, 1669 tem->tvs_c_cursor.row, 0); 1670 break; 1671 } 1672 break; 1673 1674 case 'L': /* insert line */ 1675 tem_send_data(tem); 1676 tem_setparam(tem, 1, 1); 1677 tem_scroll(tem, 1678 tem->tvs_c_cursor.row, 1679 tems.ts_c_dimension.height - 1, 1680 tem->tvs_params[0], TEM_SCROLL_DOWN); 1681 break; 1682 1683 case 'M': /* delete line */ 1684 tem_send_data(tem); 1685 tem_setparam(tem, 1, 1); 1686 tem_scroll(tem, 1687 tem->tvs_c_cursor.row, 1688 tems.ts_c_dimension.height - 1, 1689 tem->tvs_params[0], TEM_SCROLL_UP); 1690 break; 1691 1692 case 'P': /* DCH - delete char */ 1693 tem_setparam(tem, 1, 1); 1694 tem_shift(tem, tem->tvs_params[0], TEM_SHIFT_LEFT); 1695 break; 1696 1697 case 'S': /* scroll up */ 1698 tem_send_data(tem); 1699 tem_setparam(tem, 1, 1); 1700 tem_scroll(tem, 0, 1701 tems.ts_c_dimension.height - 1, 1702 tem->tvs_params[0], TEM_SCROLL_UP); 1703 break; 1704 1705 case 'T': /* scroll down */ 1706 tem_send_data(tem); 1707 tem_setparam(tem, 1, 1); 1708 tem_scroll(tem, 0, 1709 tems.ts_c_dimension.height - 1, 1710 tem->tvs_params[0], TEM_SCROLL_DOWN); 1711 break; 1712 1713 case 'X': /* erase char */ 1714 tem_setparam(tem, 1, 1); 1715 tem_clear_chars(tem, 1716 tem->tvs_params[0], 1717 tem->tvs_c_cursor.row, 1718 tem->tvs_c_cursor.col); 1719 break; 1720 1721 case 'Z': /* cursor backward tabulation */ 1722 tem_setparam(tem, 1, 1); 1723 1724 /* 1725 * Rule exception - We do sanity checking here. 1726 * 1727 * Restrict the count to a sane value to keep from 1728 * looping for a long time. There can't be more than one 1729 * tab stop per column, so use that as a limit. 1730 */ 1731 if (tem->tvs_params[0] > tems.ts_c_dimension.width) 1732 tem->tvs_params[0] = tems.ts_c_dimension.width; 1733 1734 for (i = 0; i < tem->tvs_params[0]; i++) 1735 tem_back_tab(tem); 1736 break; 1737 } 1738 tem->tvs_state = A_STATE_START; 1739 } 1740 1741 static void 1742 tem_chkparam_qmark(struct tem_vt_state *tem, tem_char_t ch) 1743 { 1744 switch (ch) { 1745 case 'h': /* DEC private mode set */ 1746 tem_setparam(tem, 1, 1); 1747 switch (tem->tvs_params[0]) { 1748 case 7: /* Autowrap mode. */ 1749 tem->tvs_stateflags |= TVS_AUTOWRAP; 1750 break; 1751 1752 case 25: /* show cursor */ 1753 /* 1754 * Note that cursor is not displayed either way 1755 * at this entry point. Clearing the flag ensures 1756 * that on exit from tem_safe_terminal_emulate 1757 * we will display the cursor. 1758 */ 1759 tem_send_data(tem); 1760 tem->tvs_cursor_hidden = false; 1761 break; 1762 } 1763 break; 1764 case 'l': 1765 /* DEC private mode reset */ 1766 tem_setparam(tem, 1, 1); 1767 switch (tem->tvs_params[0]) { 1768 case 7: /* Autowrap mode. */ 1769 tem->tvs_stateflags &= ~TVS_AUTOWRAP; 1770 break; 1771 1772 case 25: /* hide cursor */ 1773 /* 1774 * Note that the cursor is not displayed already. 1775 * This is true regardless of the flag state. 1776 * Setting this flag ensures we won't display it 1777 * on exit from tem_safe_terminal_emulate. 1778 */ 1779 tem_send_data(tem); 1780 tem->tvs_cursor_hidden = true; 1781 break; 1782 } 1783 break; 1784 } 1785 tem->tvs_state = A_STATE_START; 1786 } 1787 1788 /* 1789 * Gather the parameters of an ANSI escape sequence 1790 */ 1791 static void 1792 tem_getparams(struct tem_vt_state *tem, uint8_t ch) 1793 { 1794 if (isdigit(ch)) { 1795 tem->tvs_paramval = ((tem->tvs_paramval * 10) + (ch - '0')); 1796 tem->tvs_gotparam = true; /* Remember got parameter */ 1797 return; /* Return immediately */ 1798 } else if (tem->tvs_state == A_STATE_CSI_EQUAL) { 1799 tem->tvs_state = A_STATE_START; 1800 } else if (tem->tvs_state == A_STATE_CSI_QMARK) { 1801 if (tem->tvs_curparam < TEM_MAXPARAMS) { 1802 if (tem->tvs_gotparam) { 1803 /* get the parameter value */ 1804 tem->tvs_params[tem->tvs_curparam] = 1805 tem->tvs_paramval; 1806 } 1807 tem->tvs_curparam++; 1808 } 1809 if (ch == ';') { 1810 /* Restart parameter search */ 1811 tem->tvs_gotparam = false; 1812 tem->tvs_paramval = 0; /* No parameter value yet */ 1813 } else { 1814 /* Handle escape sequence */ 1815 tem_chkparam_qmark(tem, ch); 1816 } 1817 } else { 1818 if (tem->tvs_curparam < TEM_MAXPARAMS) { 1819 if (tem->tvs_gotparam) { 1820 /* get the parameter value */ 1821 tem->tvs_params[tem->tvs_curparam] = 1822 tem->tvs_paramval; 1823 } 1824 tem->tvs_curparam++; 1825 } 1826 1827 if (ch == ';') { 1828 /* Restart parameter search */ 1829 tem->tvs_gotparam = false; 1830 tem->tvs_paramval = 0; /* No parameter value yet */ 1831 } else { 1832 /* Handle escape sequence */ 1833 tem_chkparam(tem, ch); 1834 } 1835 } 1836 } 1837 1838 /* 1839 * Add character to internal buffer. 1840 * When its full, send it to the next layer. 1841 */ 1842 static void 1843 tem_outch(struct tem_vt_state *tem, tem_char_t ch) 1844 { 1845 text_color_t fg; 1846 text_color_t bg; 1847 text_attr_t attr; 1848 1849 /* We have autowrap enabled and we did wrap - get cursor to new line */ 1850 if ((tem->tvs_stateflags & (TVS_AUTOWRAP | TVS_WRAPPED)) == 1851 (TVS_AUTOWRAP | TVS_WRAPPED)) { 1852 tem_new_line(tem); 1853 } 1854 1855 /* buffer up the character until later */ 1856 tem_get_attr(tem, &fg, &bg, &attr, TEM_ATTR_REVERSE); 1857 tem->tvs_outbuf[tem->tvs_outindex].tc_char = ch | TEM_ATTR(attr); 1858 tem->tvs_outbuf[tem->tvs_outindex].tc_fg_color = fg; 1859 tem->tvs_outbuf[tem->tvs_outindex].tc_bg_color = bg; 1860 tem->tvs_outindex++; 1861 tem->tvs_c_cursor.col++; 1862 if (tem->tvs_c_cursor.col >= tems.ts_c_dimension.width) { 1863 tem->tvs_stateflags |= TVS_WRAPPED; 1864 tem->tvs_c_cursor.col--; 1865 tem_send_data(tem); 1866 } else { 1867 tem->tvs_stateflags &= ~TVS_WRAPPED; 1868 } 1869 } 1870 1871 static void 1872 tem_new_line(struct tem_vt_state *tem) 1873 { 1874 tem_cr(tem); 1875 tem_lf(tem); 1876 } 1877 1878 static void 1879 tem_cr(struct tem_vt_state *tem) 1880 { 1881 tem->tvs_c_cursor.col = 0; 1882 tem->tvs_stateflags &= ~TVS_WRAPPED; 1883 tem_align_cursor(tem); 1884 } 1885 1886 static void 1887 tem_lf(struct tem_vt_state *tem) 1888 { 1889 int row; 1890 1891 tem->tvs_stateflags &= ~TVS_WRAPPED; 1892 /* 1893 * Sanity checking notes: 1894 * . a_nscroll was validated when it was set. 1895 * . Regardless of that, tem_scroll and tem_mv_cursor 1896 * will prevent anything bad from happening. 1897 */ 1898 row = tem->tvs_c_cursor.row + 1; 1899 1900 if (row >= tems.ts_c_dimension.height) { 1901 if (tem->tvs_nscroll != 0) { 1902 tem_scroll(tem, 0, 1903 tems.ts_c_dimension.height - 1, 1904 tem->tvs_nscroll, TEM_SCROLL_UP); 1905 row = tems.ts_c_dimension.height - 1906 tem->tvs_nscroll; 1907 } else { /* no scroll */ 1908 /* 1909 * implement Esc[#r when # is zero. This means no 1910 * scroll but just return cursor to top of screen, 1911 * do not clear screen. 1912 */ 1913 row = 0; 1914 } 1915 } 1916 1917 tem_mv_cursor(tem, row, tem->tvs_c_cursor.col); 1918 1919 if (tem->tvs_nscroll == 0) { 1920 /* erase rest of cursor line */ 1921 tem_clear_chars(tem, 1922 tems.ts_c_dimension.width - 1923 tem->tvs_c_cursor.col, 1924 tem->tvs_c_cursor.row, 1925 tem->tvs_c_cursor.col); 1926 1927 } 1928 1929 tem_align_cursor(tem); 1930 } 1931 1932 static void 1933 tem_send_data(struct tem_vt_state *tem) 1934 { 1935 if (tem->tvs_outindex == 0) { 1936 tem_align_cursor(tem); 1937 return; 1938 } 1939 1940 tem_virtual_display(tem, tem->tvs_outbuf, tem->tvs_outindex, 1941 tem->tvs_s_cursor.row, tem->tvs_s_cursor.col); 1942 1943 if (tem->tvs_isactive) { 1944 /* 1945 * Call the primitive to render this data. 1946 */ 1947 tem_callback_display(tem, 1948 tem->tvs_outbuf, tem->tvs_outindex, 1949 tem->tvs_s_cursor.row, tem->tvs_s_cursor.col); 1950 } 1951 1952 tem->tvs_outindex = 0; 1953 1954 tem_align_cursor(tem); 1955 } 1956 1957 1958 /* 1959 * We have just done something to the current output point. Reset the start 1960 * point for the buffered data in a_outbuf. There shouldn't be any data 1961 * buffered yet. 1962 */ 1963 static void 1964 tem_align_cursor(struct tem_vt_state *tem) 1965 { 1966 tem->tvs_s_cursor.row = tem->tvs_c_cursor.row; 1967 tem->tvs_s_cursor.col = tem->tvs_c_cursor.col; 1968 } 1969 1970 /* 1971 * State machine parser based on the current state and character input 1972 * major terminations are to control character or normal character 1973 */ 1974 1975 static void 1976 tem_parse(struct tem_vt_state *tem, tem_char_t ch) 1977 { 1978 int i; 1979 1980 if (tem->tvs_state == A_STATE_START) { /* Normal state? */ 1981 if (ch == A_CSI || ch == A_ESC || ch < ' ') { 1982 /* Control */ 1983 tem_control(tem, ch); 1984 } else { 1985 /* Display */ 1986 tem_outch(tem, ch); 1987 } 1988 return; 1989 } 1990 1991 /* In <ESC> sequence */ 1992 if (tem->tvs_state != A_STATE_ESC) { /* Need to get parameters? */ 1993 if (tem->tvs_state != A_STATE_CSI) { 1994 tem_getparams(tem, ch); 1995 return; 1996 } 1997 1998 switch (ch) { 1999 case '?': 2000 tem->tvs_state = A_STATE_CSI_QMARK; 2001 return; 2002 case '=': 2003 tem->tvs_state = A_STATE_CSI_EQUAL; 2004 return; 2005 case 's': 2006 /* 2007 * As defined below, this sequence 2008 * saves the cursor. However, Sun 2009 * defines ESC[s as reset. We resolved 2010 * the conflict by selecting reset as it 2011 * is exported in the termcap file for 2012 * sun-mon, while the "save cursor" 2013 * definition does not exist anywhere in 2014 * /etc/termcap. 2015 * However, having no coherent 2016 * definition of reset, we have not 2017 * implemented it. 2018 */ 2019 2020 /* 2021 * Original code 2022 * tem->tvs_r_cursor.row = tem->tvs_c_cursor.row; 2023 * tem->tvs_r_cursor.col = tem->tvs_c_cursor.col; 2024 * tem->tvs_state = A_STATE_START; 2025 */ 2026 2027 tem->tvs_state = A_STATE_START; 2028 return; 2029 case 'u': 2030 tem_mv_cursor(tem, tem->tvs_r_cursor.row, 2031 tem->tvs_r_cursor.col); 2032 tem->tvs_state = A_STATE_START; 2033 return; 2034 case 'p': /* sunbow */ 2035 tem_send_data(tem); 2036 /* 2037 * Don't set anything if we are 2038 * already as we want to be. 2039 */ 2040 if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) { 2041 tem->tvs_flags &= ~TEM_ATTR_SCREEN_REVERSE; 2042 /* 2043 * If we have switched the characters to be the 2044 * inverse from the screen, then switch them as 2045 * well to keep them the inverse of the screen. 2046 */ 2047 if (tem->tvs_flags & TEM_ATTR_REVERSE) 2048 tem->tvs_flags &= ~TEM_ATTR_REVERSE; 2049 else 2050 tem->tvs_flags |= TEM_ATTR_REVERSE; 2051 } 2052 tem_cls(tem); 2053 tem->tvs_state = A_STATE_START; 2054 return; 2055 case 'q': /* sunwob */ 2056 tem_send_data(tem); 2057 /* 2058 * Don't set anything if we are 2059 * already where as we want to be. 2060 */ 2061 if (!(tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE)) { 2062 tem->tvs_flags |= TEM_ATTR_SCREEN_REVERSE; 2063 /* 2064 * If we have switched the characters to be the 2065 * inverse from the screen, then switch them as 2066 * well to keep them the inverse of the screen. 2067 */ 2068 if (!(tem->tvs_flags & TEM_ATTR_REVERSE)) 2069 tem->tvs_flags |= TEM_ATTR_REVERSE; 2070 else 2071 tem->tvs_flags &= ~TEM_ATTR_REVERSE; 2072 } 2073 2074 tem_cls(tem); 2075 tem->tvs_state = A_STATE_START; 2076 return; 2077 case 'r': /* sunscrl */ 2078 /* 2079 * Rule exception: check for validity here. 2080 */ 2081 tem->tvs_nscroll = tem->tvs_paramval; 2082 if (tem->tvs_nscroll > tems.ts_c_dimension.height) 2083 tem->tvs_nscroll = tems.ts_c_dimension.height; 2084 if (tem->tvs_nscroll < 0) 2085 tem->tvs_nscroll = 1; 2086 tem->tvs_state = A_STATE_START; 2087 return; 2088 default: 2089 tem_getparams(tem, ch); 2090 return; 2091 } 2092 } 2093 2094 /* Previous char was <ESC> */ 2095 if (ch == '[') { 2096 tem->tvs_curparam = 0; 2097 tem->tvs_paramval = 0; 2098 tem->tvs_gotparam = false; 2099 /* clear the parameters */ 2100 for (i = 0; i < TEM_MAXPARAMS; i++) 2101 tem->tvs_params[i] = -1; 2102 tem->tvs_state = A_STATE_CSI; 2103 } else if (ch == 'Q') { /* <ESC>Q ? */ 2104 tem->tvs_state = A_STATE_START; 2105 } else if (ch == 'C') { /* <ESC>C ? */ 2106 tem->tvs_state = A_STATE_START; 2107 } else { 2108 tem->tvs_state = A_STATE_START; 2109 if (ch == 'c') { 2110 /* ESC c resets display */ 2111 tem_reset_display(tem, true, true); 2112 } else if (ch == 'H') { 2113 /* ESC H sets a tab */ 2114 tem_set_tab(tem); 2115 } else if (ch == '7') { 2116 /* ESC 7 Save Cursor position */ 2117 tem->tvs_r_cursor.row = tem->tvs_c_cursor.row; 2118 tem->tvs_r_cursor.col = tem->tvs_c_cursor.col; 2119 } else if (ch == '8') { 2120 /* ESC 8 Restore Cursor position */ 2121 tem_mv_cursor(tem, tem->tvs_r_cursor.row, 2122 tem->tvs_r_cursor.col); 2123 /* check for control chars */ 2124 } else if (ch < ' ') { 2125 tem_control(tem, ch); 2126 } else { 2127 tem_outch(tem, ch); 2128 } 2129 } 2130 } 2131 2132 /* ARGSUSED */ 2133 static void 2134 tem_bell(struct tem_vt_state *tem __unused) 2135 { 2136 /* (void) beep(BEEP_CONSOLE); */ 2137 } 2138 2139 2140 static void 2141 tem_scroll(struct tem_vt_state *tem, int start, int end, int count, 2142 int direction) 2143 { 2144 int row; 2145 int lines_affected; 2146 2147 lines_affected = end - start + 1; 2148 if (count > lines_affected) 2149 count = lines_affected; 2150 if (count <= 0) 2151 return; 2152 2153 switch (direction) { 2154 case TEM_SCROLL_UP: 2155 if (count < lines_affected) { 2156 tem_copy_area(tem, 0, start + count, 2157 tems.ts_c_dimension.width - 1, end, 0, start); 2158 } 2159 for (row = (end - count) + 1; row <= end; row++) { 2160 tem_clear_chars(tem, tems.ts_c_dimension.width, row, 0); 2161 } 2162 break; 2163 2164 case TEM_SCROLL_DOWN: 2165 if (count < lines_affected) { 2166 tem_copy_area(tem, 0, start, 2167 tems.ts_c_dimension.width - 1, 2168 end - count, 0, start + count); 2169 } 2170 for (row = start; row < start + count; row++) { 2171 tem_clear_chars(tem, tems.ts_c_dimension.width, row, 0); 2172 } 2173 break; 2174 } 2175 } 2176 2177 static int 2178 tem_copy_width(term_char_t *src, term_char_t *dst, int cols) 2179 { 2180 int width = cols - 1; 2181 2182 while (width >= 0) { 2183 /* We do not have image bits to compare, stop there. */ 2184 if (TEM_CHAR_ATTR(src[width].tc_char) == TEM_ATTR_IMAGE || 2185 TEM_CHAR_ATTR(dst[width].tc_char) == TEM_ATTR_IMAGE) 2186 break; 2187 2188 /* 2189 * Find difference on line, compare char with its attributes 2190 * and colors. 2191 */ 2192 if (src[width].tc_char != dst[width].tc_char || 2193 src[width].tc_fg_color.n != dst[width].tc_fg_color.n || 2194 src[width].tc_bg_color.n != dst[width].tc_bg_color.n) { 2195 break; 2196 } 2197 width--; 2198 } 2199 return (width + 1); 2200 } 2201 2202 static void 2203 tem_copy_area(struct tem_vt_state *tem, 2204 screen_pos_t s_col, screen_pos_t s_row, 2205 screen_pos_t e_col, screen_pos_t e_row, 2206 screen_pos_t t_col, screen_pos_t t_row) 2207 { 2208 size_t soffset, toffset; 2209 term_char_t *src, *dst; 2210 int rows; 2211 int cols; 2212 2213 if (s_col < 0 || s_row < 0 || 2214 e_col < 0 || e_row < 0 || 2215 t_col < 0 || t_row < 0 || 2216 s_col >= tems.ts_c_dimension.width || 2217 e_col >= tems.ts_c_dimension.width || 2218 t_col >= tems.ts_c_dimension.width || 2219 s_row >= tems.ts_c_dimension.height || 2220 e_row >= tems.ts_c_dimension.height || 2221 t_row >= tems.ts_c_dimension.height) 2222 return; 2223 2224 if (s_row > e_row || s_col > e_col) 2225 return; 2226 2227 rows = e_row - s_row + 1; 2228 cols = e_col - s_col + 1; 2229 if (t_row + rows > tems.ts_c_dimension.height || 2230 t_col + cols > tems.ts_c_dimension.width) 2231 return; 2232 2233 if (tem->tvs_screen_buf == NULL) { 2234 if (tem->tvs_isactive) { 2235 tem_callback_copy(tem, s_col, s_row, 2236 e_col, e_row, t_col, t_row); 2237 } 2238 return; 2239 } 2240 2241 soffset = s_col + s_row * tems.ts_c_dimension.width; 2242 toffset = t_col + t_row * tems.ts_c_dimension.width; 2243 src = tem->tvs_screen_buf + soffset; 2244 dst = tem->tvs_screen_buf + toffset; 2245 2246 /* 2247 * Copy line by line. We determine the length by comparing the 2248 * screen content from cached text in tvs_screen_buf. 2249 */ 2250 if (toffset <= soffset) { 2251 for (int i = 0; i < rows; i++) { 2252 int increment = i * tems.ts_c_dimension.width; 2253 int width; 2254 2255 width = tem_copy_width(src + increment, 2256 dst + increment, cols); 2257 memmove(dst + increment, src + increment, 2258 width * sizeof (term_char_t)); 2259 if (tem->tvs_isactive) { 2260 tem_callback_copy(tem, s_col, s_row + i, 2261 e_col - cols + width, s_row + i, 2262 t_col, t_row + i); 2263 } 2264 } 2265 } else { 2266 for (int i = rows - 1; i >= 0; i--) { 2267 int increment = i * tems.ts_c_dimension.width; 2268 int width; 2269 2270 width = tem_copy_width(src + increment, 2271 dst + increment, cols); 2272 memmove(dst + increment, src + increment, 2273 width * sizeof (term_char_t)); 2274 if (tem->tvs_isactive) { 2275 tem_callback_copy(tem, s_col, s_row + i, 2276 e_col - cols + width, s_row + i, 2277 t_col, t_row + i); 2278 } 2279 } 2280 } 2281 } 2282 2283 static void 2284 tem_clear_chars(struct tem_vt_state *tem, int count, screen_pos_t row, 2285 screen_pos_t col) 2286 { 2287 if (row < 0 || row >= tems.ts_c_dimension.height || 2288 col < 0 || col >= tems.ts_c_dimension.width || 2289 count < 0) 2290 return; 2291 2292 /* 2293 * Note that very large values of "count" could cause col+count 2294 * to overflow, so we check "count" independently. 2295 */ 2296 if (count > tems.ts_c_dimension.width || 2297 col + count > tems.ts_c_dimension.width) 2298 count = tems.ts_c_dimension.width - col; 2299 2300 tem_virtual_cls(tem, count, row, col); 2301 2302 if (!tem->tvs_isactive) 2303 return; 2304 2305 tem_callback_cls(tem, count, row, col); 2306 } 2307 2308 static void 2309 tem_text_display(struct tem_vt_state *tem __unused, term_char_t *string, 2310 int count, screen_pos_t row, screen_pos_t col) 2311 { 2312 struct vis_consdisplay da; 2313 int i; 2314 tem_char_t c; 2315 text_color_t bg, fg; 2316 2317 if (count == 0) 2318 return; 2319 2320 da.data = (unsigned char *)&c; 2321 da.width = 1; 2322 da.row = row; 2323 da.col = col; 2324 2325 for (i = 0; i < count; i++) { 2326 tem_get_color(tem, &fg, &bg, &string[i]); 2327 tem_set_color(&fg, &da.fg_color); 2328 tem_set_color(&bg, &da.bg_color); 2329 c = TEM_CHAR(string[i].tc_char); 2330 tems_display(&da); 2331 da.col++; 2332 } 2333 } 2334 2335 /* 2336 * This function is used to mark a rectangular image area so the scrolling 2337 * will know we need to copy the data from there. 2338 */ 2339 void 2340 tem_image_display(struct tem_vt_state *tem, screen_pos_t s_row, 2341 screen_pos_t s_col, screen_pos_t e_row, screen_pos_t e_col) 2342 { 2343 screen_pos_t i, j; 2344 term_char_t c; 2345 2346 c.tc_char = TEM_ATTR(TEM_ATTR_IMAGE); 2347 2348 for (i = s_row; i <= e_row; i++) { 2349 for (j = s_col; j <= e_col; j++) { 2350 tem_virtual_display(tem, &c, 1, i, j); 2351 } 2352 } 2353 } 2354 2355 /*ARGSUSED*/ 2356 static void 2357 tem_text_copy(struct tem_vt_state *tem __unused, 2358 screen_pos_t s_col, screen_pos_t s_row, 2359 screen_pos_t e_col, screen_pos_t e_row, 2360 screen_pos_t t_col, screen_pos_t t_row) 2361 { 2362 struct vis_conscopy da; 2363 2364 da.s_row = s_row; 2365 da.s_col = s_col; 2366 da.e_row = e_row; 2367 da.e_col = e_col; 2368 da.t_row = t_row; 2369 da.t_col = t_col; 2370 tems_copy(&da); 2371 } 2372 2373 static void 2374 tem_text_cls(struct tem_vt_state *tem, 2375 int count, screen_pos_t row, screen_pos_t col) 2376 { 2377 text_attr_t attr; 2378 term_char_t c; 2379 int i; 2380 2381 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr, 2382 TEM_ATTR_SCREEN_REVERSE); 2383 c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' '; 2384 2385 if (count > tems.ts_c_dimension.width || 2386 col + count > tems.ts_c_dimension.width) 2387 count = tems.ts_c_dimension.width - col; 2388 2389 for (i = 0; i < count; i++) 2390 tem_text_display(tem, &c, 1, row, col++); 2391 2392 } 2393 2394 static void 2395 tem_pix_display(struct tem_vt_state *tem, 2396 term_char_t *string, int count, 2397 screen_pos_t row, screen_pos_t col) 2398 { 2399 struct vis_consdisplay da; 2400 int i; 2401 2402 da.data = (uint8_t *)tem->tvs_pix_data; 2403 da.width = tems.ts_font.vf_width; 2404 da.height = tems.ts_font.vf_height; 2405 da.row = (row * da.height) + tems.ts_p_offset.y; 2406 da.col = (col * da.width) + tems.ts_p_offset.x; 2407 2408 for (i = 0; i < count; i++) { 2409 tem_callback_bit2pix(tem, &string[i]); 2410 tems_display(&da); 2411 da.col += da.width; 2412 } 2413 } 2414 2415 static void 2416 tem_pix_copy(struct tem_vt_state *tem, 2417 screen_pos_t s_col, screen_pos_t s_row, 2418 screen_pos_t e_col, screen_pos_t e_row, 2419 screen_pos_t t_col, screen_pos_t t_row) 2420 { 2421 struct vis_conscopy ma; 2422 static bool need_clear = true; 2423 2424 if (need_clear && tem->tvs_first_line > 0) { 2425 /* 2426 * Clear OBP output above our kernel console term 2427 * when our kernel console term begins to scroll up, 2428 * we hope it is user friendly. 2429 * (Also see comments on tem_pix_clear_prom_output) 2430 * 2431 * This is only one time call. 2432 */ 2433 tem_pix_clear_prom_output(tem); 2434 } 2435 need_clear = false; 2436 2437 ma.s_row = s_row * tems.ts_font.vf_height + tems.ts_p_offset.y; 2438 ma.e_row = (e_row + 1) * tems.ts_font.vf_height + 2439 tems.ts_p_offset.y - 1; 2440 ma.t_row = t_row * tems.ts_font.vf_height + tems.ts_p_offset.y; 2441 2442 /* 2443 * Check if we're in process of clearing OBP's columns area, 2444 * which only happens when term scrolls up a whole line. 2445 */ 2446 if (tem->tvs_first_line > 0 && t_row < s_row && t_col == 0 && 2447 e_col == tems.ts_c_dimension.width - 1) { 2448 /* 2449 * We need to clear OBP's columns area outside our kernel 2450 * console term. So that we set ma.e_col to entire row here. 2451 */ 2452 ma.s_col = s_col * tems.ts_font.vf_width; 2453 ma.e_col = tems.ts_p_dimension.width - 1; 2454 2455 ma.t_col = t_col * tems.ts_font.vf_width; 2456 } else { 2457 ma.s_col = s_col * tems.ts_font.vf_width + tems.ts_p_offset.x; 2458 ma.e_col = (e_col + 1) * tems.ts_font.vf_width + 2459 tems.ts_p_offset.x - 1; 2460 ma.t_col = t_col * tems.ts_font.vf_width + tems.ts_p_offset.x; 2461 } 2462 2463 tems_copy(&ma); 2464 2465 if (tem->tvs_first_line > 0 && t_row < s_row) { 2466 /* We have scrolled up (s_row - t_row) rows. */ 2467 tem->tvs_first_line -= (s_row - t_row); 2468 if (tem->tvs_first_line <= 0) { 2469 /* All OBP rows have been cleared. */ 2470 tem->tvs_first_line = 0; 2471 } 2472 } 2473 } 2474 2475 static void 2476 tem_pix_bit2pix(struct tem_vt_state *tem, term_char_t *c) 2477 { 2478 text_color_t fg, bg; 2479 2480 tem_get_color(tem, &fg, &bg, c); 2481 bit_to_pix32(tem, c->tc_char, fg, bg); 2482 } 2483 2484 2485 /* 2486 * This function only clears count of columns in one row 2487 */ 2488 static void 2489 tem_pix_cls(struct tem_vt_state *tem, int count, 2490 screen_pos_t row, screen_pos_t col) 2491 { 2492 tem_pix_cls_range(tem, row, 1, tems.ts_p_offset.y, 2493 col, count, tems.ts_p_offset.x, false); 2494 } 2495 2496 /* 2497 * This function clears OBP output above our kernel console term area 2498 * because OBP's term may have a bigger terminal window than that of 2499 * our kernel console term. So we need to clear OBP output garbage outside 2500 * of our kernel console term at a proper time, which is when the first 2501 * row output of our kernel console term scrolls at the first screen line. 2502 * 2503 * _________________________________ 2504 * | _____________________ | ---> OBP's bigger term window 2505 * | | | | 2506 * |___| | | 2507 * | | | | | 2508 * | | | | | 2509 * |_|_|___________________|_______| 2510 * | | | ---> first line 2511 * | |___________________|---> our kernel console term window 2512 * | 2513 * |---> columns area to be cleared 2514 * 2515 * This function only takes care of the output above our kernel console term, 2516 * and tem_prom_scroll_up takes care of columns area outside of our kernel 2517 * console term. 2518 */ 2519 static void 2520 tem_pix_clear_prom_output(struct tem_vt_state *tem) 2521 { 2522 int nrows, ncols, width, height, offset; 2523 2524 width = tems.ts_font.vf_width; 2525 height = tems.ts_font.vf_height; 2526 offset = tems.ts_p_offset.y % height; 2527 2528 nrows = tems.ts_p_offset.y / height; 2529 ncols = (tems.ts_p_dimension.width + (width - 1)) / width; 2530 2531 if (nrows > 0) 2532 tem_pix_cls_range(tem, 0, nrows, offset, 0, ncols, 0, false); 2533 } 2534 2535 /* 2536 * Clear the whole screen and reset the cursor to start point. 2537 */ 2538 static void 2539 tem_cls(struct tem_vt_state *tem) 2540 { 2541 struct vis_consclear cl; 2542 text_color_t fg_color; 2543 text_color_t bg_color; 2544 text_attr_t attr; 2545 term_char_t c; 2546 int row; 2547 2548 for (row = 0; row < tems.ts_c_dimension.height; row++) { 2549 tem_virtual_cls(tem, tems.ts_c_dimension.width, row, 0); 2550 } 2551 2552 if (!tem->tvs_isactive) 2553 return; 2554 2555 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr, 2556 TEM_ATTR_SCREEN_REVERSE); 2557 c.tc_char = TEM_ATTR(attr); 2558 2559 tem_get_color(tem, &fg_color, &bg_color, &c); 2560 tem_set_color(&bg_color, &cl.bg_color); 2561 (void) tems_cls(&cl); 2562 2563 tem->tvs_c_cursor.row = 0; 2564 tem->tvs_c_cursor.col = 0; 2565 tem_align_cursor(tem); 2566 } 2567 2568 static void 2569 tem_back_tab(struct tem_vt_state *tem) 2570 { 2571 int i; 2572 screen_pos_t tabstop; 2573 2574 tabstop = 0; 2575 2576 for (i = tem->tvs_ntabs - 1; i >= 0; i--) { 2577 if (tem->tvs_tabs[i] < tem->tvs_c_cursor.col) { 2578 tabstop = tem->tvs_tabs[i]; 2579 break; 2580 } 2581 } 2582 2583 tem_mv_cursor(tem, tem->tvs_c_cursor.row, tabstop); 2584 } 2585 2586 static void 2587 tem_tab(struct tem_vt_state *tem) 2588 { 2589 size_t i; 2590 screen_pos_t tabstop; 2591 2592 tabstop = tems.ts_c_dimension.width - 1; 2593 2594 for (i = 0; i < tem->tvs_ntabs; i++) { 2595 if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) { 2596 tabstop = tem->tvs_tabs[i]; 2597 break; 2598 } 2599 } 2600 2601 tem_mv_cursor(tem, tem->tvs_c_cursor.row, tabstop); 2602 } 2603 2604 static void 2605 tem_set_tab(struct tem_vt_state *tem) 2606 { 2607 size_t i, j; 2608 2609 if (tem->tvs_ntabs == tem->tvs_maxtab) 2610 return; 2611 if (tem->tvs_ntabs == 0 || 2612 tem->tvs_tabs[tem->tvs_ntabs] < tem->tvs_c_cursor.col) { 2613 tem->tvs_tabs[tem->tvs_ntabs++] = tem->tvs_c_cursor.col; 2614 return; 2615 } 2616 for (i = 0; i < tem->tvs_ntabs; i++) { 2617 if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) 2618 return; 2619 if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) { 2620 for (j = tem->tvs_ntabs - 1; j >= i; j--) 2621 tem->tvs_tabs[j+ 1] = tem->tvs_tabs[j]; 2622 tem->tvs_tabs[i] = tem->tvs_c_cursor.col; 2623 tem->tvs_ntabs++; 2624 return; 2625 } 2626 } 2627 } 2628 2629 static void 2630 tem_clear_tabs(struct tem_vt_state *tem, int action) 2631 { 2632 size_t i, j; 2633 2634 switch (action) { 2635 case 3: /* clear all tabs */ 2636 tem->tvs_ntabs = 0; 2637 break; 2638 case 0: /* clr tab at cursor */ 2639 2640 for (i = 0; i < tem->tvs_ntabs; i++) { 2641 if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) { 2642 tem->tvs_ntabs--; 2643 for (j = i; j < tem->tvs_ntabs; j++) 2644 tem->tvs_tabs[j] = tem->tvs_tabs[j + 1]; 2645 return; 2646 } 2647 } 2648 break; 2649 } 2650 } 2651 2652 static void 2653 tem_mv_cursor(struct tem_vt_state *tem, int row, int col) 2654 { 2655 /* 2656 * Sanity check and bounds enforcement. Out of bounds requests are 2657 * clipped to the screen boundaries. This seems to be what SPARC 2658 * does. 2659 */ 2660 if (row < 0) 2661 row = 0; 2662 if (row >= tems.ts_c_dimension.height) 2663 row = tems.ts_c_dimension.height - 1; 2664 if (col < 0) 2665 col = 0; 2666 if (col >= tems.ts_c_dimension.width) { 2667 tem->tvs_stateflags |= TVS_WRAPPED; 2668 col = tems.ts_c_dimension.width - 1; 2669 } else { 2670 tem->tvs_stateflags &= ~TVS_WRAPPED; 2671 } 2672 2673 tem_send_data(tem); 2674 tem->tvs_c_cursor.row = (screen_pos_t)row; 2675 tem->tvs_c_cursor.col = (screen_pos_t)col; 2676 tem_align_cursor(tem); 2677 } 2678 2679 /* ARGSUSED */ 2680 static void 2681 tem_reset_emulator(struct tem_vt_state *tem, bool init_color) 2682 { 2683 int j; 2684 2685 tem->tvs_c_cursor.row = 0; 2686 tem->tvs_c_cursor.col = 0; 2687 tem->tvs_r_cursor.row = 0; 2688 tem->tvs_r_cursor.col = 0; 2689 tem->tvs_s_cursor.row = 0; 2690 tem->tvs_s_cursor.col = 0; 2691 tem->tvs_outindex = 0; 2692 tem->tvs_state = A_STATE_START; 2693 tem->tvs_gotparam = false; 2694 tem->tvs_curparam = 0; 2695 tem->tvs_paramval = 0; 2696 tem->tvs_nscroll = 1; 2697 2698 if (init_color) { 2699 /* use initial settings */ 2700 tem->tvs_alpha = 0xff; 2701 tem->tvs_fg_color = tems.ts_init_color.fg_color; 2702 tem->tvs_bg_color = tems.ts_init_color.bg_color; 2703 tem->tvs_flags = tems.ts_init_color.a_flags; 2704 } 2705 2706 /* 2707 * set up the initial tab stops 2708 */ 2709 tem->tvs_ntabs = 0; 2710 for (j = 8; j < tems.ts_c_dimension.width; j += 8) 2711 tem->tvs_tabs[tem->tvs_ntabs++] = (screen_pos_t)j; 2712 2713 for (j = 0; j < TEM_MAXPARAMS; j++) 2714 tem->tvs_params[j] = 0; 2715 } 2716 2717 static void 2718 tem_reset_display(struct tem_vt_state *tem, bool clear_txt, bool init_color) 2719 { 2720 tem_reset_emulator(tem, init_color); 2721 2722 if (clear_txt) { 2723 if (tem->tvs_isactive) 2724 tem_callback_cursor(tem, VIS_HIDE_CURSOR); 2725 2726 tem_cls(tem); 2727 2728 if (tem->tvs_isactive) 2729 tem_callback_cursor(tem, VIS_DISPLAY_CURSOR); 2730 } 2731 } 2732 2733 static void 2734 tem_shift(struct tem_vt_state *tem, int count, int direction) 2735 { 2736 int rest_of_line; 2737 2738 rest_of_line = tems.ts_c_dimension.width - tem->tvs_c_cursor.col; 2739 if (count > rest_of_line) 2740 count = rest_of_line; 2741 2742 if (count <= 0) 2743 return; 2744 2745 switch (direction) { 2746 case TEM_SHIFT_LEFT: 2747 if (count < rest_of_line) { 2748 tem_copy_area(tem, 2749 tem->tvs_c_cursor.col + count, 2750 tem->tvs_c_cursor.row, 2751 tems.ts_c_dimension.width - 1, 2752 tem->tvs_c_cursor.row, 2753 tem->tvs_c_cursor.col, 2754 tem->tvs_c_cursor.row); 2755 } 2756 2757 tem_clear_chars(tem, count, tem->tvs_c_cursor.row, 2758 (tems.ts_c_dimension.width - count)); 2759 break; 2760 case TEM_SHIFT_RIGHT: 2761 if (count < rest_of_line) { 2762 tem_copy_area(tem, 2763 tem->tvs_c_cursor.col, 2764 tem->tvs_c_cursor.row, 2765 tems.ts_c_dimension.width - count - 1, 2766 tem->tvs_c_cursor.row, 2767 tem->tvs_c_cursor.col + count, 2768 tem->tvs_c_cursor.row); 2769 } 2770 2771 tem_clear_chars(tem, count, tem->tvs_c_cursor.row, 2772 tem->tvs_c_cursor.col); 2773 break; 2774 } 2775 } 2776 2777 static void 2778 tem_text_cursor(struct tem_vt_state *tem, short action) 2779 { 2780 struct vis_conscursor ca; 2781 2782 ca.row = tem->tvs_c_cursor.row; 2783 ca.col = tem->tvs_c_cursor.col; 2784 ca.action = action; 2785 2786 tems_cursor(&ca); 2787 2788 if (action == VIS_GET_CURSOR) { 2789 tem->tvs_c_cursor.row = ca.row; 2790 tem->tvs_c_cursor.col = ca.col; 2791 } 2792 } 2793 2794 static void 2795 tem_pix_cursor(struct tem_vt_state *tem, short action) 2796 { 2797 struct vis_conscursor ca; 2798 text_color_t fg, bg; 2799 term_char_t c; 2800 text_attr_t attr; 2801 2802 ca.row = tem->tvs_c_cursor.row * tems.ts_font.vf_height + 2803 tems.ts_p_offset.y; 2804 ca.col = tem->tvs_c_cursor.col * tems.ts_font.vf_width + 2805 tems.ts_p_offset.x; 2806 ca.width = tems.ts_font.vf_width; 2807 ca.height = tems.ts_font.vf_height; 2808 2809 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr, 2810 TEM_ATTR_REVERSE); 2811 c.tc_char = TEM_ATTR(attr); 2812 2813 tem_get_color(tem, &fg, &bg, &c); 2814 tem_set_color(&fg, &ca.fg_color); 2815 tem_set_color(&bg, &ca.bg_color); 2816 2817 ca.action = action; 2818 2819 tems_cursor(&ca); 2820 2821 if (action == VIS_GET_CURSOR) { 2822 tem->tvs_c_cursor.row = 0; 2823 tem->tvs_c_cursor.col = 0; 2824 2825 if (ca.row != 0) { 2826 tem->tvs_c_cursor.row = (ca.row - tems.ts_p_offset.y) / 2827 tems.ts_font.vf_height; 2828 } 2829 if (ca.col != 0) { 2830 tem->tvs_c_cursor.col = (ca.col - tems.ts_p_offset.x) / 2831 tems.ts_font.vf_width; 2832 } 2833 } 2834 } 2835 2836 static void 2837 bit_to_pix32(struct tem_vt_state *tem, 2838 tem_char_t c, text_color_t fg, text_color_t bg) 2839 { 2840 uint32_t *dest; 2841 2842 dest = (uint32_t *)tem->tvs_pix_data; 2843 font_bit_to_pix32(&tems.ts_font, dest, c, fg.n, bg.n); 2844 } 2845 2846 /* 2847 * flag: TEM_ATTR_SCREEN_REVERSE or TEM_ATTR_REVERSE 2848 */ 2849 static void 2850 tem_get_attr(struct tem_vt_state *tem, text_color_t *fg, 2851 text_color_t *bg, text_attr_t *attr, uint8_t flag) 2852 { 2853 if (tem->tvs_flags & flag) { 2854 *fg = tem->tvs_bg_color; 2855 *bg = tem->tvs_fg_color; 2856 } else { 2857 *fg = tem->tvs_fg_color; 2858 *bg = tem->tvs_bg_color; 2859 } 2860 2861 if (attr != NULL) 2862 *attr = tem->tvs_flags; 2863 } 2864 2865 static void 2866 tem_get_color(struct tem_vt_state *tem, text_color_t *fg, text_color_t *bg, 2867 term_char_t *c) 2868 { 2869 bool bold_font; 2870 2871 *fg = c->tc_fg_color; 2872 *bg = c->tc_bg_color; 2873 2874 bold_font = tems.ts_font.vf_map_count[VFNT_MAP_BOLD] != 0; 2875 2876 /* 2877 * If we have both normal and bold font components, 2878 * we use bold font for TEM_ATTR_BOLD. 2879 * The bright color is traditionally used with TEM_ATTR_BOLD, 2880 * in case there is no bold font. 2881 */ 2882 if (!TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_FG) && 2883 c->tc_fg_color.n < XLATE_NCOLORS) { 2884 if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_BRIGHT_FG) || 2885 (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_BOLD) && !bold_font)) 2886 fg->n = brt_xlate[c->tc_fg_color.n]; 2887 else 2888 fg->n = dim_xlate[c->tc_fg_color.n]; 2889 } 2890 2891 if (!TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_BG) && 2892 c->tc_bg_color.n < XLATE_NCOLORS) { 2893 if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_BRIGHT_BG)) 2894 bg->n = brt_xlate[c->tc_bg_color.n]; 2895 else 2896 bg->n = dim_xlate[c->tc_bg_color.n]; 2897 } 2898 2899 if (tems.ts_display_mode == VIS_TEXT) 2900 return; 2901 2902 /* 2903 * Translate fg and bg to RGB colors. 2904 */ 2905 if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_FG)) { 2906 fg->n = rgb_to_color(&rgb_info, 2907 fg->rgb.a, fg->rgb.r, fg->rgb.g, fg->rgb.b); 2908 } else { 2909 fg->n = rgb_color_map(&rgb_info, fg->n, tem->tvs_alpha); 2910 } 2911 2912 if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_BG)) { 2913 bg->n = rgb_to_color(&rgb_info, 2914 bg->rgb.a, bg->rgb.r, bg->rgb.g, bg->rgb.b); 2915 } else { 2916 bg->n = rgb_color_map(&rgb_info, bg->n, tem->tvs_alpha); 2917 } 2918 } 2919 2920 static void 2921 tem_set_color(text_color_t *t, color_t *c) 2922 { 2923 switch (tems.ts_pdepth) { 2924 case 4: 2925 c->four = t->n & 0xFF; 2926 break; 2927 default: 2928 /* gfx module is expecting all pixel data in 32-bit colors */ 2929 *(uint32_t *)c = t->n; 2930 break; 2931 } 2932 } 2933 2934 void 2935 tem_get_colors(tem_vt_state_t tem_arg, text_color_t *fg, text_color_t *bg) 2936 { 2937 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 2938 text_attr_t attr; 2939 term_char_t c; 2940 2941 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr, 2942 TEM_ATTR_REVERSE); 2943 c.tc_char = TEM_ATTR(attr); 2944 tem_get_color(tem, fg, bg, &c); 2945 } 2946 2947 /* 2948 * Clear a rectangle of screen for pixel mode. 2949 * 2950 * arguments: 2951 * row: start row# 2952 * nrows: the number of rows to clear 2953 * offset_y: the offset of height in pixels to begin clear 2954 * col: start col# 2955 * ncols: the number of cols to clear 2956 * offset_x: the offset of width in pixels to begin clear 2957 * scroll_up: whether this function is called during sroll up, 2958 * which is called only once. 2959 */ 2960 static void 2961 tem_pix_cls_range(struct tem_vt_state *tem, 2962 screen_pos_t row, int nrows, int offset_y, 2963 screen_pos_t col, int ncols, int offset_x, 2964 bool sroll_up) 2965 { 2966 struct vis_consdisplay da; 2967 int i, j; 2968 int row_add = 0; 2969 term_char_t c; 2970 text_attr_t attr; 2971 2972 if (sroll_up) 2973 row_add = tems.ts_c_dimension.height - 1; 2974 2975 da.width = tems.ts_font.vf_width; 2976 da.height = tems.ts_font.vf_height; 2977 2978 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr, 2979 TEM_ATTR_SCREEN_REVERSE); 2980 /* Make sure we will not draw underlines */ 2981 c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' '; 2982 2983 tem_callback_bit2pix(tem, &c); 2984 da.data = (uint8_t *)tem->tvs_pix_data; 2985 2986 for (i = 0; i < nrows; i++, row++) { 2987 da.row = (row + row_add) * da.height + offset_y; 2988 da.col = col * da.width + offset_x; 2989 for (j = 0; j < ncols; j++) { 2990 tems_display(&da); 2991 da.col += da.width; 2992 } 2993 } 2994 } 2995 2996 /* 2997 * virtual screen operations 2998 */ 2999 static void 3000 tem_virtual_display(struct tem_vt_state *tem, term_char_t *string, 3001 size_t count, screen_pos_t row, screen_pos_t col) 3002 { 3003 size_t i, width; 3004 term_char_t *addr; 3005 3006 if (tem->tvs_screen_buf == NULL) 3007 return; 3008 3009 if (row < 0 || row >= tems.ts_c_dimension.height || 3010 col < 0 || col >= tems.ts_c_dimension.width || 3011 col + count > (size_t)tems.ts_c_dimension.width) 3012 return; 3013 3014 width = tems.ts_c_dimension.width; 3015 addr = tem->tvs_screen_buf + (row * width + col); 3016 for (i = 0; i < count; i++) { 3017 *addr++ = string[i]; 3018 } 3019 } 3020 3021 static void 3022 tem_virtual_cls(struct tem_vt_state *tem, size_t count, 3023 screen_pos_t row, screen_pos_t col) 3024 { 3025 term_char_t c; 3026 text_attr_t attr; 3027 3028 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr, 3029 TEM_ATTR_SCREEN_REVERSE); 3030 /* Make sure we will not draw underlines */ 3031 c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' '; 3032 3033 while (count > 0) { 3034 tem_virtual_display(tem, &c, 1, row, col); 3035 col++; 3036 count--; 3037 } 3038 } 3039