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