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