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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Polled I/O safe ANSI terminal emulator module; 31 * Supporting TERM types 'sun' and 'sun-color, parsing 32 * ANSI x3.64 escape sequences, and the like. (See wscons(7d) 33 * for more information). 34 * 35 * IMPORTANT: 36 * 37 * The functions in this file *must* be able to function in 38 * standalone mode, ie. on a quiesced system. In that state, 39 * access is single threaded, only one CPU is running. 40 * System services are NOT available. 41 * 42 * The following restrictions pertain to every function 43 * in this file: 44 * 45 * - CANNOT use the DDI or LDI interfaces 46 * - CANNOT call system services 47 * - CANNOT use mutexes 48 * - CANNOT wait for interrupts 49 * - CANNOT allocate memory 50 * 51 */ 52 53 #include <sys/types.h> 54 #include <sys/ascii.h> 55 #include <sys/visual_io.h> 56 #include <sys/font.h> 57 #include <sys/tem.h> 58 #include <sys/tem_impl.h> 59 #include <sys/ksynch.h> 60 #include <sys/sysmacros.h> 61 #include <sys/mutex.h> 62 63 static void tem_display(struct tem *, 64 struct vis_consdisplay *, 65 cred_t *, enum called_from); 66 static void tem_cursor(struct tem *, 67 struct vis_conscursor *, 68 cred_t *, enum called_from); 69 static void tem_control(struct tem *, uchar_t, 70 cred_t *, enum called_from); 71 static void tem_setparam(struct tem *, int, int); 72 static void tem_selgraph(struct tem *); 73 static void tem_chkparam(struct tem *, uchar_t, 74 cred_t *, enum called_from); 75 static void tem_getparams(struct tem *, uchar_t, 76 cred_t *, enum called_from); 77 static void tem_outch(struct tem *, uchar_t, 78 cred_t *, enum called_from); 79 static void tem_parse(struct tem *, uchar_t, 80 cred_t *, enum called_from); 81 82 static void tem_new_line(struct tem *, 83 cred_t *, enum called_from); 84 static void tem_cr(struct tem *); 85 static void tem_lf(struct tem *, 86 cred_t *, enum called_from); 87 static void tem_send_data(struct tem *, cred_t *, 88 enum called_from); 89 static void tem_cls(struct tem *, 90 cred_t *, enum called_from); 91 static void tem_tab(struct tem *, 92 cred_t *, enum called_from); 93 static void tem_back_tab(struct tem *, 94 cred_t *, enum called_from); 95 static void tem_clear_tabs(struct tem *, int); 96 static void tem_set_tab(struct tem *); 97 static void tem_mv_cursor(struct tem *, int, int, 98 cred_t *, enum called_from); 99 static void tem_shift(struct tem *, int, int, 100 cred_t *, enum called_from); 101 static void tem_scroll(struct tem *, int, int, 102 int, int, cred_t *, enum called_from); 103 static void tem_clear_chars(struct tem *tem, 104 int count, screen_pos_t row, screen_pos_t col, 105 cred_t *credp, enum called_from called_from); 106 static void tem_copy_area(struct tem *tem, 107 screen_pos_t s_col, screen_pos_t s_row, 108 screen_pos_t e_col, screen_pos_t e_row, 109 screen_pos_t t_col, screen_pos_t t_row, 110 cred_t *credp, enum called_from called_from); 111 static void tem_image_display(struct tem *, uchar_t *, 112 int, int, screen_pos_t, screen_pos_t, 113 cred_t *, enum called_from); 114 static void tem_bell(struct tem *tem, 115 enum called_from called_from); 116 static void tem_get_color(struct tem *tem, 117 text_color_t *fg, text_color_t *bg); 118 static void tem_pix_clear_prom_output(struct tem *tem, 119 cred_t *credp, enum called_from called_from); 120 121 /* BEGIN CSTYLED */ 122 /* Bk Rd Gr Br Bl Mg Cy Wh */ 123 static text_color_t fg_dim_xlate[] = { 1, 5, 3, 7, 2, 6, 4, 8 }; 124 static text_color_t fg_brt_xlate[] = { 9, 13, 11, 15, 10, 14, 12, 0 }; 125 static text_color_t bg_xlate[] = { 1, 5, 3, 7, 2, 6, 4, 0 }; 126 /* END CSTYLED */ 127 128 129 text_cmap_t cmap4_to_24 = { 130 /* BEGIN CSTYLED */ 131 /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 132 Wh+ Bk Bl Gr Cy Rd Mg Br Wh Bk+ Bl+ Gr+ Cy+ Rd+ Mg+ Yw */ 133 0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff, 134 0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff, 135 0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00 136 /* END CSTYLED */ 137 }; 138 139 #define PIX4TO32(pix4) (pixel32_t)( \ 140 cmap4_to_24.red[pix4] << 16 | \ 141 cmap4_to_24.green[pix4] << 8 | \ 142 cmap4_to_24.blue[pix4]) 143 144 /* 145 * Fonts are statically linked with this module. At some point an 146 * RFE might be desireable to allow dynamic font loading. The 147 * original intention to facilitate dynamic fonts can be seen 148 * by examining the data structures and set_font(). As much of 149 * the original code is retained but modified to be suited to 150 * traversing a list of static fonts. 151 */ 152 extern struct fontlist fonts[]; 153 154 #define DEFAULT_FONT_DATA font_data_12x22 155 156 extern bitmap_data_t font_data_12x22; 157 extern bitmap_data_t font_data_7x14; 158 extern bitmap_data_t font_data_6x10; 159 /* 160 * Must be sorted by font size in descending order 161 */ 162 struct fontlist fonts[] = { 163 { &font_data_12x22, NULL }, 164 { &font_data_7x14, NULL }, 165 { &font_data_6x10, NULL }, 166 { NULL, NULL } 167 }; 168 169 #define INVERSE(ch) (ch ^ 0xff) 170 171 #define BIT_TO_PIX(tem, c, fg, bg) { \ 172 ASSERT((tem)->state->in_fp.f_bit2pix != NULL); \ 173 (void) (*(tem)->state->in_fp.f_bit2pix)((tem), (c), (fg), (bg));\ 174 } 175 176 void 177 tem_check_first_time( 178 struct tem *tem, 179 cred_t *credp, 180 enum called_from called_from) 181 { 182 static int first_time = 1; 183 184 /* 185 * Realign the console cursor. We did this in tem_init(). 186 * However, drivers in the console stream may emit additional 187 * messages before we are ready. This causes text overwrite 188 * on the screen. This is a workaround. 189 */ 190 if (first_time && tem->state->display_mode == VIS_TEXT) { 191 tem_text_cursor(tem, VIS_GET_CURSOR, credp, called_from); 192 tem_align_cursor(tem); 193 } 194 first_time = 0; 195 196 } 197 198 /* 199 * This entry point handles output requests from restricted contexts like 200 * kmdb, where services like mutexes are not available. This function 201 * is entered when OBP or when a kernel debugger (such as kmdb) 202 * are generating console output. In those cases, power management 203 * concerns are handled by the abort sequence initiation (ie. when 204 * the user hits L1+A or the equivalent to enter OBP or the debugger.). 205 * It is also entered when the kernel is panicing. 206 */ 207 void 208 tem_polled_write( 209 struct tem *tem, 210 uchar_t *buf, 211 int len) 212 { 213 214 ASSERT(tem->hdl != NULL); 215 216 tem_check_first_time(tem, kcred, CALLED_FROM_STANDALONE); 217 tem_terminal_emulate(tem, buf, len, NULL, CALLED_FROM_STANDALONE); 218 } 219 220 221 /* 222 * This is the main entry point into the terminal emulator. 223 * 224 * For each data message coming downstream, ANSI assumes that it is composed 225 * of ASCII characters, which are treated as a byte-stream input to the 226 * parsing state machine. All data is parsed immediately -- there is 227 * no enqueing. 228 */ 229 void 230 tem_terminal_emulate( 231 struct tem *tem, 232 uchar_t *buf, 233 int len, 234 cred_t *credp, 235 enum called_from called_from) 236 { 237 struct tem_state *tems = tem->state; 238 239 ASSERT((called_from == CALLED_FROM_STANDALONE) || 240 MUTEX_HELD(&tem->lock)); 241 242 (*tems->in_fp.f_cursor) 243 (tem, VIS_HIDE_CURSOR, credp, called_from); 244 245 for (; len > 0; len--, buf++) { 246 tem_parse(tem, *buf, credp, called_from); 247 } 248 249 /* 250 * Send the data we just got to the framebuffer. 251 */ 252 tem_send_data(tem, credp, called_from); 253 254 (*tems->in_fp.f_cursor) 255 (tem, VIS_DISPLAY_CURSOR, credp, called_from); 256 } 257 258 /* 259 * Display an rectangular image on the frame buffer using the 260 * mechanism appropriate for the system state being called 261 * from quiesced or normal (ie. use polled I/O vs. layered ioctls) 262 */ 263 static void 264 tem_display( 265 struct tem *tem, 266 struct vis_consdisplay *pda, 267 cred_t *credp, 268 enum called_from called_from) 269 { 270 if (called_from == CALLED_FROM_STANDALONE) 271 tem->fb_polledio->display(tem->fb_polledio->arg, pda); 272 else 273 tem_display_layered(tem, pda, credp); 274 } 275 276 /* 277 * Copy a rectangle from one location to another on the frame buffer 278 * using the mechanism appropriate for the system state being called 279 * from, quiesced or normal (ie. use polled I/O vs. layered ioctls) 280 */ 281 void 282 tem_copy( 283 struct tem *tem, 284 struct vis_conscopy *pca, 285 cred_t *credp, 286 enum called_from called_from) 287 { 288 if (called_from == CALLED_FROM_STANDALONE) 289 tem->fb_polledio->copy(tem->fb_polledio->arg, pca); 290 else 291 tem_copy_layered(tem, pca, credp); 292 } 293 294 /* 295 * Display or hide a rectangular block text cursor of a specificsize 296 * at a specific location on frame buffer* using the mechanism 297 * appropriate for the system state being called from, quisced or 298 * normal (ie. use polled I/O vs. layered ioctls). 299 */ 300 static void 301 tem_cursor( 302 struct tem *tem, 303 struct vis_conscursor *pca, 304 cred_t *credp, 305 enum called_from called_from) 306 { 307 if (called_from == CALLED_FROM_STANDALONE) 308 tem->fb_polledio->cursor(tem->fb_polledio->arg, pca); 309 else 310 tem_cursor_layered(tem, pca, credp); 311 } 312 313 /* 314 * send the appropriate control message or set state based on the 315 * value of the control character ch 316 */ 317 318 static void 319 tem_control( 320 struct tem *tem, 321 uchar_t ch, 322 cred_t *credp, 323 enum called_from called_from) 324 { 325 struct tem_state *tems = tem->state; 326 327 ASSERT((called_from == CALLED_FROM_STANDALONE) || 328 MUTEX_HELD(&tem->lock)); 329 330 tems->a_state = A_STATE_START; 331 switch (ch) { 332 case A_BEL: 333 tem_bell(tem, called_from); 334 break; 335 336 case A_BS: 337 tem_mv_cursor(tem, 338 tems->a_c_cursor.row, 339 tems->a_c_cursor.col - 1, 340 credp, called_from); 341 break; 342 343 case A_HT: 344 tem_tab(tem, credp, called_from); 345 break; 346 347 case A_NL: 348 /* 349 * tem_send_data(tem, credp, called_from); 350 * tem_new_line(tem, credp, called_from); 351 * break; 352 */ 353 354 case A_VT: 355 tem_send_data(tem, credp, called_from); 356 tem_lf(tem, credp, called_from); 357 break; 358 359 case A_FF: 360 tem_send_data(tem, credp, called_from); 361 tem_cls(tem, credp, called_from); 362 break; 363 364 case A_CR: 365 tem_send_data(tem, credp, called_from); 366 tem_cr(tem); 367 break; 368 369 case A_ESC: 370 tems->a_state = A_STATE_ESC; 371 break; 372 373 case A_CSI: 374 { 375 int i; 376 tems->a_curparam = 0; 377 tems->a_paramval = 0; 378 tems->a_gotparam = B_FALSE; 379 /* clear the parameters */ 380 for (i = 0; i < TEM_MAXPARAMS; i++) 381 tems->a_params[i] = -1; 382 tems->a_state = A_STATE_CSI; 383 } 384 break; 385 386 case A_GS: 387 tem_back_tab(tem, credp, called_from); 388 break; 389 390 default: 391 break; 392 } 393 } 394 395 396 /* 397 * if parameters [0..count - 1] are not set, set them to the value 398 * of newparam. 399 */ 400 401 static void 402 tem_setparam(struct tem *tem, int count, int newparam) 403 { 404 int i; 405 406 for (i = 0; i < count; i++) { 407 if (tem->state->a_params[i] == -1) 408 tem->state->a_params[i] = newparam; 409 } 410 } 411 412 413 /* 414 * select graphics mode based on the param vals stored in a_params 415 */ 416 static void 417 tem_selgraph(struct tem *tem) 418 { 419 struct tem_state *tems = tem->state; 420 int curparam; 421 int count = 0; 422 int param; 423 424 curparam = tems->a_curparam; 425 do { 426 param = tems->a_params[count]; 427 428 switch (param) { 429 case -1: 430 case 0: 431 if (tems->a_flags & TEM_ATTR_SCREEN_REVERSE) { 432 tems->a_flags |= TEM_ATTR_REVERSE; 433 } else { 434 tems->a_flags &= ~TEM_ATTR_REVERSE; 435 } 436 tems->a_flags = tem->init_color.a_flags; 437 tems->fg_color = tem->init_color.fg_color; 438 tems->bg_color = tem->init_color.bg_color; 439 break; 440 441 case 1: /* Bold Intense */ 442 tems->a_flags |= TEM_ATTR_BOLD; 443 break; 444 445 case 2: /* Faint Intense */ 446 tems->a_flags &= ~TEM_ATTR_BOLD; 447 break; 448 449 case 5: /* Blink */ 450 tems->a_flags |= TEM_ATTR_BLINK; 451 break; 452 453 case 7: /* Reverse video */ 454 if (tems->a_flags & TEM_ATTR_SCREEN_REVERSE) { 455 tems->a_flags &= ~TEM_ATTR_REVERSE; 456 } else { 457 tems->a_flags |= TEM_ATTR_REVERSE; 458 } 459 break; 460 461 case 30: /* black (grey) foreground */ 462 case 31: /* red (light red) foreground */ 463 case 32: /* green (light green) foreground */ 464 case 33: /* brown (yellow) foreground */ 465 case 34: /* blue (light blue) foreground */ 466 case 35: /* magenta (light magenta) foreground */ 467 case 36: /* cyan (light cyan) foreground */ 468 case 37: /* white (bright white) foreground */ 469 tems->fg_color = param - 30; 470 break; 471 472 case 40: /* black (grey) background */ 473 case 41: /* red (light red) background */ 474 case 42: /* green (light green) background */ 475 case 43: /* brown (yellow) background */ 476 case 44: /* blue (light blue) background */ 477 case 45: /* magenta (light magenta) background */ 478 case 46: /* cyan (light cyan) background */ 479 case 47: /* white (bright white) background */ 480 tems->bg_color = param - 40; 481 break; 482 483 default: 484 break; 485 } 486 count++; 487 curparam--; 488 489 } while (curparam > 0); 490 491 492 tems->a_state = A_STATE_START; 493 } 494 495 /* 496 * perform the appropriate action for the escape sequence 497 * 498 * General rule: This code does not validate the arguments passed. 499 * It assumes that the next lower level will do so. 500 */ 501 static void 502 tem_chkparam( 503 struct tem *tem, 504 uchar_t ch, 505 cred_t *credp, 506 enum called_from called_from) 507 { 508 struct tem_state *tems = tem->state; 509 int i; 510 int row; 511 int col; 512 513 ASSERT((called_from == CALLED_FROM_STANDALONE) || 514 MUTEX_HELD(&tem->lock)); 515 516 row = tems->a_c_cursor.row; 517 col = tems->a_c_cursor.col; 518 519 switch (ch) { 520 521 case 'm': /* select terminal graphics mode */ 522 tem_send_data(tem, credp, called_from); 523 tem_selgraph(tem); 524 break; 525 526 case '@': /* insert char */ 527 tem_setparam(tem, 1, 1); 528 tem_shift(tem, tems->a_params[0], TEM_SHIFT_RIGHT, 529 credp, called_from); 530 break; 531 532 case 'A': /* cursor up */ 533 tem_setparam(tem, 1, 1); 534 tem_mv_cursor(tem, row - tems->a_params[0], col, 535 credp, called_from); 536 break; 537 538 case 'd': /* VPA - vertical position absolute */ 539 tem_setparam(tem, 1, 1); 540 tem_mv_cursor(tem, tems->a_params[0] - 1, col, 541 credp, called_from); 542 break; 543 544 case 'e': /* VPR - vertical position relative */ 545 case 'B': /* cursor down */ 546 tem_setparam(tem, 1, 1); 547 tem_mv_cursor(tem, row + tems->a_params[0], col, 548 credp, called_from); 549 break; 550 551 case 'a': /* HPR - horizontal position relative */ 552 case 'C': /* cursor right */ 553 tem_setparam(tem, 1, 1); 554 tem_mv_cursor(tem, row, col + tems->a_params[0], 555 credp, called_from); 556 break; 557 558 case '`': /* HPA - horizontal position absolute */ 559 tem_setparam(tem, 1, 1); 560 tem_mv_cursor(tem, row, tems->a_params[0] - 1, 561 credp, called_from); 562 break; 563 564 case 'D': /* cursor left */ 565 tem_setparam(tem, 1, 1); 566 tem_mv_cursor(tem, row, col - tems->a_params[0], 567 credp, called_from); 568 break; 569 570 case 'E': /* CNL cursor next line */ 571 tem_setparam(tem, 1, 1); 572 tem_mv_cursor(tem, row + tems->a_params[0], 0, 573 credp, called_from); 574 break; 575 576 case 'F': /* CPL cursor previous line */ 577 tem_setparam(tem, 1, 1); 578 tem_mv_cursor(tem, row - tems->a_params[0], 0, 579 credp, called_from); 580 break; 581 582 case 'G': /* cursor horizontal position */ 583 tem_setparam(tem, 1, 1); 584 tem_mv_cursor(tem, row, tems->a_params[0] - 1, 585 credp, called_from); 586 break; 587 588 case 'g': /* clear tabs */ 589 tem_setparam(tem, 1, 0); 590 tem_clear_tabs(tem, tems->a_params[0]); 591 break; 592 593 case 'f': /* HVP Horizontal and Vertical Position */ 594 case 'H': /* CUP position cursor */ 595 tem_setparam(tem, 2, 1); 596 tem_mv_cursor(tem, 597 tems->a_params[0] - 1, 598 tems->a_params[1] - 1, 599 credp, called_from); 600 break; 601 602 case 'I': /* CHT - Cursor Horizontal Tab */ 603 /* Not implemented */ 604 break; 605 606 case 'J': /* ED - Erase in Display */ 607 tem_send_data(tem, credp, called_from); 608 tem_setparam(tem, 1, 0); 609 switch (tems->a_params[0]) { 610 case 0: 611 /* erase cursor to end of screen */ 612 /* FIRST erase cursor to end of line */ 613 tem_clear_chars(tem, 614 tems->a_c_dimension.width - 615 tems->a_c_cursor.col, 616 tems->a_c_cursor.row, 617 tems->a_c_cursor.col, credp, called_from); 618 619 /* THEN erase lines below the cursor */ 620 for (row = tems->a_c_cursor.row + 1; 621 row < tems->a_c_dimension.height; 622 row++) { 623 tem_clear_chars(tem, 624 tems->a_c_dimension.width, 625 row, 0, credp, called_from); 626 } 627 break; 628 629 case 1: 630 /* erase beginning of screen to cursor */ 631 /* FIRST erase lines above the cursor */ 632 for (row = 0; 633 row < tems->a_c_cursor.row; 634 row++) { 635 tem_clear_chars(tem, 636 tems->a_c_dimension.width, 637 row, 0, credp, called_from); 638 } 639 /* THEN erase beginning of line to cursor */ 640 tem_clear_chars(tem, 641 tems->a_c_cursor.col + 1, 642 tems->a_c_cursor.row, 643 0, credp, called_from); 644 break; 645 646 case 2: 647 /* erase whole screen */ 648 for (row = 0; 649 row < tems->a_c_dimension.height; 650 row++) { 651 tem_clear_chars(tem, 652 tems->a_c_dimension.width, 653 row, 0, credp, called_from); 654 } 655 break; 656 } 657 break; 658 659 case 'K': /* EL - Erase in Line */ 660 tem_send_data(tem, credp, called_from); 661 tem_setparam(tem, 1, 0); 662 switch (tems->a_params[0]) { 663 case 0: 664 /* erase cursor to end of line */ 665 tem_clear_chars(tem, 666 (tems->a_c_dimension.width - 667 tems->a_c_cursor.col), 668 tems->a_c_cursor.row, 669 tems->a_c_cursor.col, 670 credp, called_from); 671 break; 672 673 case 1: 674 /* erase beginning of line to cursor */ 675 tem_clear_chars(tem, 676 tems->a_c_cursor.col + 1, 677 tems->a_c_cursor.row, 678 0, credp, called_from); 679 break; 680 681 case 2: 682 /* erase whole line */ 683 tem_clear_chars(tem, 684 tems->a_c_dimension.width, 685 tems->a_c_cursor.row, 686 0, credp, called_from); 687 break; 688 } 689 break; 690 691 case 'L': /* insert line */ 692 tem_send_data(tem, credp, called_from); 693 tem_setparam(tem, 1, 1); 694 tem_scroll(tem, 695 tems->a_c_cursor.row, 696 tems->a_c_dimension.height - 1, 697 tems->a_params[0], TEM_SCROLL_DOWN, 698 credp, called_from); 699 break; 700 701 case 'M': /* delete line */ 702 tem_send_data(tem, credp, called_from); 703 tem_setparam(tem, 1, 1); 704 tem_scroll(tem, 705 tems->a_c_cursor.row, 706 tems->a_c_dimension.height - 1, 707 tems->a_params[0], TEM_SCROLL_UP, 708 credp, called_from); 709 break; 710 711 case 'P': /* DCH - delete char */ 712 tem_setparam(tem, 1, 1); 713 tem_shift(tem, tems->a_params[0], TEM_SHIFT_LEFT, 714 credp, called_from); 715 break; 716 717 case 'S': /* scroll up */ 718 tem_send_data(tem, credp, called_from); 719 tem_setparam(tem, 1, 1); 720 tem_scroll(tem, 0, 721 tems->a_c_dimension.height - 1, 722 tems->a_params[0], TEM_SCROLL_UP, 723 credp, called_from); 724 break; 725 726 case 'T': /* scroll down */ 727 tem_send_data(tem, credp, called_from); 728 tem_setparam(tem, 1, 1); 729 tem_scroll(tem, 0, 730 tems->a_c_dimension.height - 1, 731 tems->a_params[0], TEM_SCROLL_DOWN, 732 credp, called_from); 733 break; 734 735 case 'X': /* erase char */ 736 tem_setparam(tem, 1, 1); 737 tem_clear_chars(tem, 738 tems->a_params[0], 739 tems->a_c_cursor.row, 740 tems->a_c_cursor.col, 741 credp, called_from); 742 break; 743 744 case 'Z': /* cursor backward tabulation */ 745 tem_setparam(tem, 1, 1); 746 747 /* 748 * Rule exception - We do sanity checking here. 749 * 750 * Restrict the count to a sane value to keep from 751 * looping for a long time. There can't be more than one 752 * tab stop per column, so use that as a limit. 753 */ 754 if (tems->a_params[0] > tems->a_c_dimension.width) 755 tems->a_params[0] = tems->a_c_dimension.width; 756 757 for (i = 0; i < tems->a_params[0]; i++) 758 tem_back_tab(tem, credp, called_from); 759 break; 760 } 761 tems->a_state = A_STATE_START; 762 } 763 764 765 /* 766 * Gather the parameters of an ANSI escape sequence 767 */ 768 static void 769 tem_getparams(struct tem *tem, uchar_t ch, 770 cred_t *credp, enum called_from called_from) 771 { 772 struct tem_state *tems = tem->state; 773 774 ASSERT((called_from == CALLED_FROM_STANDALONE) || 775 MUTEX_HELD(&tem->lock)); 776 777 if (ch >= '0' && ch <= '9') { 778 tems->a_paramval = ((tems->a_paramval * 10) + (ch - '0')); 779 tems->a_gotparam = B_TRUE; /* Remember got parameter */ 780 return; /* Return immediately */ 781 } else if (tems->a_state == A_STATE_CSI_EQUAL || 782 tems->a_state == A_STATE_CSI_QMARK) { 783 tems->a_state = A_STATE_START; 784 } else { 785 if (tems->a_curparam < TEM_MAXPARAMS) { 786 if (tems->a_gotparam) { 787 /* get the parameter value */ 788 tems->a_params[tems->a_curparam] = 789 tems->a_paramval; 790 } 791 tems->a_curparam++; 792 } 793 794 if (ch == ';') { 795 /* Restart parameter search */ 796 tems->a_gotparam = B_FALSE; 797 tems->a_paramval = 0; /* No parame value yet */ 798 } else { 799 /* Handle escape sequence */ 800 tem_chkparam(tem, ch, credp, called_from); 801 } 802 } 803 } 804 805 /* 806 * Add character to internal buffer. 807 * When its full, send it to the next layer. 808 */ 809 810 static void 811 tem_outch(struct tem *tem, uchar_t ch, 812 cred_t *credp, enum called_from called_from) 813 { 814 815 ASSERT((called_from == CALLED_FROM_STANDALONE) || 816 MUTEX_HELD(&tem->lock)); 817 818 /* buffer up the character until later */ 819 820 tem->state->a_outbuf[tem->state->a_outindex++] = ch; 821 tem->state->a_c_cursor.col++; 822 if (tem->state->a_c_cursor.col >= tem->state->a_c_dimension.width) { 823 tem_send_data(tem, credp, called_from); 824 tem_new_line(tem, credp, called_from); 825 } 826 } 827 828 static void 829 tem_new_line(struct tem *tem, 830 cred_t *credp, enum called_from called_from) 831 { 832 tem_cr(tem); 833 tem_lf(tem, credp, called_from); 834 } 835 836 static void 837 tem_cr(struct tem *tem) 838 { 839 tem->state->a_c_cursor.col = 0; 840 tem_align_cursor(tem); 841 } 842 843 static void 844 tem_lf(struct tem *tem, 845 cred_t *credp, enum called_from called_from) 846 { 847 struct tem_state *tems = tem->state; 848 int row; 849 850 ASSERT((called_from == CALLED_FROM_STANDALONE) || 851 MUTEX_HELD(&tem->lock)); 852 853 /* 854 * Sanity checking notes: 855 * . a_nscroll was validated when it was set. 856 * . Regardless of that, tem_scroll and tem_mv_cursor will prevent 857 * anything bad from happening. 858 */ 859 row = tems->a_c_cursor.row + 1; 860 861 if (row >= tems->a_c_dimension.height) { 862 if (tems->a_nscroll != 0) { 863 tem_scroll(tem, 0, 864 tems->a_c_dimension.height - 1, 865 tems->a_nscroll, TEM_SCROLL_UP, 866 credp, called_from); 867 row = tems->a_c_dimension.height - 868 tems->a_nscroll; 869 } else { /* no scroll */ 870 /* 871 * implement Esc[#r when # is zero. This means no 872 * scroll but just return cursor to top of screen, 873 * do not clear screen. 874 */ 875 row = 0; 876 } 877 } 878 879 tem_mv_cursor(tem, row, tems->a_c_cursor.col, 880 credp, called_from); 881 882 if (tems->a_nscroll == 0) { 883 /* erase rest of cursor line */ 884 tem_clear_chars(tem, 885 tems->a_c_dimension.width - 886 tems->a_c_cursor.col, 887 tems->a_c_cursor.row, 888 tems->a_c_cursor.col, 889 credp, called_from); 890 891 } 892 893 tem_align_cursor(tem); 894 } 895 896 static void 897 tem_send_data(struct tem *tem, cred_t *credp, 898 enum called_from called_from) 899 { 900 struct tem_state *tems = tem->state; 901 text_color_t fg_color; 902 text_color_t bg_color; 903 904 ASSERT((called_from == CALLED_FROM_STANDALONE) || 905 MUTEX_HELD(&tem->lock)); 906 907 if (tems->a_outindex != 0) { 908 909 if (tems->a_flags & TEM_ATTR_REVERSE) { 910 fg_color = ansi_fg_to_solaris(tem, 911 tems->bg_color); 912 bg_color = ansi_bg_to_solaris(tem, 913 tems->fg_color); 914 } else { 915 fg_color = ansi_fg_to_solaris(tem, 916 tems->fg_color); 917 bg_color = ansi_bg_to_solaris(tem, 918 tems->bg_color); 919 } 920 921 /* 922 * Call the primitive to render this data. 923 */ 924 (*tems->in_fp.f_display)(tem, 925 tems->a_outbuf, 926 tems->a_outindex, 927 tems->a_s_cursor.row, 928 tems->a_s_cursor.col, 929 fg_color, bg_color, 930 credp, called_from); 931 tems->a_outindex = 0; 932 } 933 tem_align_cursor(tem); 934 } 935 936 937 /* 938 * We have just done something to the current output point. Reset the start 939 * point for the buffered data in a_outbuf. There shouldn't be any data 940 * buffered yet. 941 */ 942 void 943 tem_align_cursor(struct tem *tem) 944 { 945 tem->state->a_s_cursor.row = tem->state->a_c_cursor.row; 946 tem->state->a_s_cursor.col = tem->state->a_c_cursor.col; 947 } 948 949 950 951 /* 952 * State machine parser based on the current state and character input 953 * major terminations are to control character or normal character 954 */ 955 956 static void 957 tem_parse(struct tem *tem, uchar_t ch, 958 cred_t *credp, enum called_from called_from) 959 { 960 struct tem_state *tems = tem->state; 961 int i; 962 963 ASSERT((called_from == CALLED_FROM_STANDALONE) || 964 MUTEX_HELD(&tem->lock)); 965 966 if (tems->a_state == A_STATE_START) { /* Normal state? */ 967 if (ch == A_CSI || ch == A_ESC || ch < ' ') /* Control? */ 968 tem_control(tem, ch, credp, called_from); 969 else 970 /* Display */ 971 tem_outch(tem, ch, credp, called_from); 972 return; 973 } 974 975 /* In <ESC> sequence */ 976 if (tems->a_state != A_STATE_ESC) { /* Need to get parameters? */ 977 if (tems->a_state != A_STATE_CSI) { 978 tem_getparams(tem, ch, credp, called_from); 979 return; 980 } 981 982 switch (ch) { 983 case '?': 984 tems->a_state = A_STATE_CSI_QMARK; 985 return; 986 case '=': 987 tems->a_state = A_STATE_CSI_EQUAL; 988 return; 989 case 's': 990 /* 991 * As defined below, this sequence 992 * saves the cursor. However, Sun 993 * defines ESC[s as reset. We resolved 994 * the conflict by selecting reset as it 995 * is exported in the termcap file for 996 * sun-mon, while the "save cursor" 997 * definition does not exist anywhere in 998 * /etc/termcap. 999 * However, having no coherent 1000 * definition of reset, we have not 1001 * implemented it. 1002 */ 1003 1004 /* 1005 * Original code 1006 * tems->a_r_cursor.row = tems->a_c_cursor.row; 1007 * tems->a_r_cursor.col = tems->a_c_cursor.col; 1008 * tems->a_state = A_STATE_START; 1009 */ 1010 1011 tems->a_state = A_STATE_START; 1012 return; 1013 case 'u': 1014 tem_mv_cursor(tem, tems->a_r_cursor.row, 1015 tems->a_r_cursor.col, credp, called_from); 1016 tems->a_state = A_STATE_START; 1017 return; 1018 case 'p': /* sunbow */ 1019 tem_send_data(tem, credp, called_from); 1020 /* 1021 * Don't set anything if we are 1022 * already as we want to be. 1023 */ 1024 if (tems->a_flags & TEM_ATTR_SCREEN_REVERSE) { 1025 tems->a_flags &= ~TEM_ATTR_SCREEN_REVERSE; 1026 /* 1027 * If we have switched the characters to be the 1028 * inverse from the screen, then switch them as 1029 * well to keep them the inverse of the screen. 1030 */ 1031 if (tems->a_flags & TEM_ATTR_REVERSE) { 1032 tems->a_flags &= ~TEM_ATTR_REVERSE; 1033 } else { 1034 tems->a_flags |= TEM_ATTR_REVERSE; 1035 } 1036 } 1037 tem_cls(tem, credp, called_from); 1038 tems->a_state = A_STATE_START; 1039 return; 1040 case 'q': /* sunwob */ 1041 tem_send_data(tem, credp, called_from); 1042 /* 1043 * Don't set anything if we are 1044 * already where as we want to be. 1045 */ 1046 if (!(tems->a_flags & TEM_ATTR_SCREEN_REVERSE)) { 1047 tems->a_flags |= TEM_ATTR_SCREEN_REVERSE; 1048 /* 1049 * If we have switched the characters to be the 1050 * inverse from the screen, then switch them as 1051 * well to keep them the inverse of the screen. 1052 */ 1053 if (!(tems->a_flags & TEM_ATTR_REVERSE)) { 1054 tems->a_flags |= TEM_ATTR_REVERSE; 1055 } else { 1056 tems->a_flags &= ~TEM_ATTR_REVERSE; 1057 } 1058 } 1059 1060 tem_cls(tem, credp, called_from); 1061 tems->a_state = A_STATE_START; 1062 return; 1063 case 'r': /* sunscrl */ 1064 /* 1065 * Rule exception: check for validity here. 1066 */ 1067 tems->a_nscroll = tems->a_paramval; 1068 if (tems->a_nscroll > tems->a_c_dimension.height) 1069 tems->a_nscroll = tems->a_c_dimension.height; 1070 if (tems->a_nscroll < 0) 1071 tems->a_nscroll = 1; 1072 tems->a_state = A_STATE_START; 1073 return; 1074 default: 1075 tem_getparams(tem, ch, credp, called_from); 1076 return; 1077 } 1078 } 1079 1080 /* Previous char was <ESC> */ 1081 if (ch == '[') { 1082 tems->a_curparam = 0; 1083 tems->a_paramval = 0; 1084 tems->a_gotparam = B_FALSE; 1085 /* clear the parameters */ 1086 for (i = 0; i < TEM_MAXPARAMS; i++) 1087 tems->a_params[i] = -1; 1088 tems->a_state = A_STATE_CSI; 1089 } else if (ch == 'Q') { /* <ESC>Q ? */ 1090 tems->a_state = A_STATE_START; 1091 } else if (ch == 'C') { /* <ESC>C ? */ 1092 tems->a_state = A_STATE_START; 1093 } else { 1094 tems->a_state = A_STATE_START; 1095 if (ch == 'c') 1096 /* ESC c resets display */ 1097 tem_reset_display(tem, credp, called_from, 1, NULL); 1098 else if (ch == 'H') 1099 /* ESC H sets a tab */ 1100 tem_set_tab(tem); 1101 else if (ch == '7') { 1102 /* ESC 7 Save Cursor position */ 1103 tems->a_r_cursor.row = tems->a_c_cursor.row; 1104 tems->a_r_cursor.col = tems->a_c_cursor.col; 1105 } else if (ch == '8') 1106 /* ESC 8 Restore Cursor position */ 1107 tem_mv_cursor(tem, tems->a_r_cursor.row, 1108 tems->a_r_cursor.col, credp, called_from); 1109 /* check for control chars */ 1110 else if (ch < ' ') 1111 tem_control(tem, ch, credp, called_from); 1112 else 1113 tem_outch(tem, ch, credp, called_from); 1114 } 1115 } 1116 1117 /* ARGSUSED */ 1118 static void 1119 tem_bell(struct tem *tem, enum called_from called_from) 1120 { 1121 if (called_from == CALLED_FROM_STANDALONE) 1122 beep_polled(BEEP_CONSOLE); 1123 else 1124 beep(BEEP_CONSOLE); 1125 } 1126 1127 1128 static void 1129 tem_scroll(tem_t *tem, int start, int end, int count, int direction, 1130 cred_t *credp, enum called_from called_from) 1131 { 1132 struct tem_state *tems = tem->state; 1133 int row; 1134 int lines_affected; 1135 1136 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1137 MUTEX_HELD(&tem->lock)); 1138 1139 lines_affected = end - start + 1; 1140 if (count > lines_affected) 1141 count = lines_affected; 1142 if (count <= 0) 1143 return; 1144 1145 switch (direction) { 1146 case TEM_SCROLL_UP: 1147 if (count < lines_affected) { 1148 tem_copy_area(tem, 0, start + count, 1149 tems->a_c_dimension.width - 1, end, 1150 0, start, credp, called_from); 1151 } 1152 for (row = (end - count) + 1; row <= end; row++) { 1153 tem_clear_chars(tem, tems->a_c_dimension.width, 1154 row, 0, credp, called_from); 1155 } 1156 break; 1157 1158 case TEM_SCROLL_DOWN: 1159 if (count < lines_affected) { 1160 tem_copy_area(tem, 0, start, 1161 tems->a_c_dimension.width - 1, 1162 end - count, 0, start + count, 1163 credp, called_from); 1164 } 1165 for (row = start; row < start + count; row++) { 1166 tem_clear_chars(tem, tems->a_c_dimension.width, 1167 row, 0, credp, called_from); 1168 } 1169 break; 1170 } 1171 } 1172 1173 static void 1174 tem_copy_area(struct tem *tem, 1175 screen_pos_t s_col, screen_pos_t s_row, 1176 screen_pos_t e_col, screen_pos_t e_row, 1177 screen_pos_t t_col, screen_pos_t t_row, 1178 cred_t *credp, enum called_from called_from) 1179 { 1180 struct tem_state *tems = tem->state; 1181 int rows; 1182 int cols; 1183 1184 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1185 MUTEX_HELD(&tem->lock)); 1186 1187 if (s_col < 0 || s_row < 0 || 1188 e_col < 0 || e_row < 0 || 1189 t_col < 0 || t_row < 0 || 1190 s_col >= tems->a_c_dimension.width || 1191 e_col >= tems->a_c_dimension.width || 1192 t_col >= tems->a_c_dimension.width || 1193 s_row >= tems->a_c_dimension.height || 1194 e_row >= tems->a_c_dimension.height || 1195 t_row >= tems->a_c_dimension.height) 1196 return; 1197 1198 if (s_row > e_row || s_col > e_col) 1199 return; 1200 1201 rows = e_row - s_row + 1; 1202 cols = e_col - s_col + 1; 1203 if (t_row + rows > tems->a_c_dimension.height || 1204 t_col + cols > tems->a_c_dimension.width) 1205 return; 1206 1207 (*tems->in_fp.f_copy)(tem, s_col, s_row, 1208 e_col, e_row, t_col, t_row, credp, called_from); 1209 } 1210 1211 static void 1212 tem_clear_chars(struct tem *tem, int count, screen_pos_t row, 1213 screen_pos_t col, cred_t *credp, enum called_from called_from) 1214 { 1215 struct tem_state *tems = tem->state; 1216 1217 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1218 MUTEX_HELD(&tem->lock)); 1219 1220 if (row < 0 || row >= tems->a_c_dimension.height || 1221 col < 0 || col >= tems->a_c_dimension.width || 1222 count < 0) 1223 return; 1224 1225 /* 1226 * Note that very large values of "count" could cause col+count 1227 * to overflow, so we check "count" independently. 1228 */ 1229 if (count > tems->a_c_dimension.width || 1230 col + count > tems->a_c_dimension.width) 1231 count = tems->a_c_dimension.width - col; 1232 1233 (*tems->in_fp.f_cls)(tem, count, row, col, credp, called_from); 1234 } 1235 1236 void 1237 tem_text_display(struct tem *tem, uchar_t *string, 1238 int count, screen_pos_t row, screen_pos_t col, 1239 text_color_t fg_color, text_color_t bg_color, 1240 cred_t *credp, enum called_from called_from) 1241 { 1242 struct vis_consdisplay da; 1243 1244 da.data = string; 1245 da.width = count; 1246 da.row = row; 1247 da.col = col; 1248 1249 da.fg_color = fg_color; 1250 da.bg_color = bg_color; 1251 1252 tem_display(tem, &da, credp, called_from); 1253 } 1254 1255 /* 1256 * This function is used to blit a rectangular color image, 1257 * unperturbed on the underlying framebuffer, to render 1258 * icons and pictures. The data is a pixel pattern that 1259 * fills a rectangle bounded to the width and height parameters. 1260 * The color pixel data must to be pre-adjusted by the caller 1261 * for the current video depth. 1262 * 1263 * This function is unused now. 1264 */ 1265 void 1266 tem_image_display(struct tem *tem, uchar_t *image, 1267 int height, int width, screen_pos_t row, screen_pos_t col, 1268 cred_t *credp, enum called_from called_from) 1269 { 1270 struct vis_consdisplay da; 1271 1272 da.data = image; 1273 da.width = width; 1274 da.height = height; 1275 da.row = row; 1276 da.col = col; 1277 1278 tem_display(tem, &da, credp, called_from); 1279 } 1280 1281 1282 void 1283 tem_text_copy(struct tem *tem, 1284 screen_pos_t s_col, screen_pos_t s_row, 1285 screen_pos_t e_col, screen_pos_t e_row, 1286 screen_pos_t t_col, screen_pos_t t_row, 1287 cred_t *credp, enum called_from called_from) 1288 { 1289 struct vis_conscopy da; 1290 1291 da.s_row = s_row; 1292 da.s_col = s_col; 1293 da.e_row = e_row; 1294 da.e_col = e_col; 1295 da.t_row = t_row; 1296 da.t_col = t_col; 1297 1298 tem_copy(tem, &da, credp, called_from); 1299 } 1300 1301 void 1302 tem_text_cls(struct tem *tem, 1303 int count, screen_pos_t row, screen_pos_t col, cred_t *credp, 1304 enum called_from called_from) 1305 { 1306 struct vis_consdisplay da; 1307 1308 da.data = tem->state->a_blank_line; 1309 da.width = count; 1310 da.row = row; 1311 da.col = col; 1312 1313 tem_get_color(tem, &da.fg_color, &da.bg_color); 1314 tem_display(tem, &da, credp, called_from); 1315 } 1316 1317 1318 1319 void 1320 tem_pix_display(struct tem *tem, 1321 uchar_t *string, int count, 1322 screen_pos_t row, screen_pos_t col, 1323 text_color_t fg_color, text_color_t bg_color, 1324 cred_t *credp, enum called_from called_from) 1325 { 1326 struct tem_state *tems = tem->state; 1327 struct vis_consdisplay da; 1328 int i; 1329 da.data = (uchar_t *)tems->a_pix_data; 1330 da.width = tems->a_font.width; 1331 da.height = tems->a_font.height; 1332 da.row = (row * da.height) + tems->a_p_offset.y; 1333 da.col = (col * da.width) + tems->a_p_offset.x; 1334 1335 for (i = 0; i < count; i++) { 1336 BIT_TO_PIX(tem, string[i], fg_color, bg_color); 1337 tem_display(tem, &da, credp, called_from); 1338 da.col += da.width; 1339 } 1340 } 1341 1342 void 1343 tem_pix_copy(struct tem *tem, 1344 screen_pos_t s_col, screen_pos_t s_row, 1345 screen_pos_t e_col, screen_pos_t e_row, 1346 screen_pos_t t_col, screen_pos_t t_row, 1347 cred_t *credp, 1348 enum called_from called_from) 1349 { 1350 struct tem_state *tems = tem->state; 1351 struct vis_conscopy ma; 1352 static boolean_t need_clear = B_TRUE; 1353 1354 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1355 MUTEX_HELD(&tem->lock)); 1356 1357 if (need_clear && tems->first_line > 0) { 1358 /* 1359 * Clear OBP output above our kernel console term 1360 * when our kernel console term begins to scroll up, 1361 * we hope it is user friendly. 1362 * (Also see comments on tem_pix_clear_prom_output) 1363 * 1364 * This is only one time call. 1365 */ 1366 tem_pix_clear_prom_output(tem, credp, called_from); 1367 } 1368 need_clear = B_FALSE; 1369 1370 ma.s_row = s_row * tems->a_font.height + tems->a_p_offset.y; 1371 ma.e_row = (e_row + 1) * tems->a_font.height + tems->a_p_offset.y - 1; 1372 ma.t_row = t_row * tems->a_font.height + tems->a_p_offset.y; 1373 1374 /* 1375 * Check if we're in process of clearing OBP's columns area, 1376 * which only happens when term scrolls up a whole line. 1377 */ 1378 if (tems->first_line > 0 && t_row < s_row && t_col == 0 && 1379 e_col == tems->a_c_dimension.width - 1) { 1380 /* 1381 * We need to clear OBP's columns area outside our kernel 1382 * console term. So that we set ma.e_col to entire row here. 1383 */ 1384 ma.s_col = s_col * tems->a_font.width; 1385 ma.e_col = tems->a_p_dimension.width - 1; 1386 1387 ma.t_col = t_col * tems->a_font.width; 1388 } else { 1389 ma.s_col = s_col * tems->a_font.width + tems->a_p_offset.x; 1390 ma.e_col = (e_col + 1) * tems->a_font.width + 1391 tems->a_p_offset.x - 1; 1392 ma.t_col = t_col * tems->a_font.width + tems->a_p_offset.x; 1393 } 1394 1395 tem_copy(tem, &ma, credp, called_from); 1396 1397 if (tems->first_line > 0 && t_row < s_row) { 1398 /* We have scrolled up (s_row - t_row) rows. */ 1399 tems->first_line -= (s_row - t_row); 1400 if (tems->first_line <= 0) { 1401 /* All OBP rows have been cleared. */ 1402 tems->first_line = 0; 1403 } 1404 } 1405 1406 } 1407 1408 /* 1409 * This function only clears count of columns in one row 1410 */ 1411 void 1412 tem_pix_cls(struct tem *tem, int count, 1413 screen_pos_t row, screen_pos_t col, cred_t *credp, 1414 enum called_from called_from) 1415 { 1416 struct tem_state *tems = tem->state; 1417 1418 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1419 MUTEX_HELD(&tem->lock)); 1420 1421 tem_pix_cls_range(tem, row, 1, tems->a_p_offset.y, 1422 col, count, tems->a_p_offset.x, B_FALSE, credp, called_from); 1423 } 1424 1425 /* 1426 * This function clears OBP output above our kernel console term area 1427 * because OBP's term may have a bigger terminal window than that of 1428 * our kernel console term. So we need to clear OBP output garbage outside 1429 * of our kernel console term at a proper time, which is when the first 1430 * row output of our kernel console term scrolls at the first screen line. 1431 * 1432 * _________________________________ 1433 * | _____________________ | ---> OBP's bigger term window 1434 * | | | | 1435 * |___| | | 1436 * | | | | | 1437 * | | | | | 1438 * |_|_|___________________|_______| 1439 * | | | ---> first line 1440 * | |___________________|---> our kernel console term window 1441 * | 1442 * |---> columns area to be cleared 1443 * 1444 * This function only takes care of the output above our kernel console term, 1445 * and tem_prom_scroll_up takes care of columns area outside of our kernel 1446 * console term. 1447 */ 1448 static void 1449 tem_pix_clear_prom_output(struct tem *tem, cred_t *credp, 1450 enum called_from called_from) 1451 { 1452 struct tem_state *tems = tem->state; 1453 int nrows, ncols, width, height; 1454 1455 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1456 MUTEX_HELD(&tem->lock)); 1457 1458 width = tems->a_font.width; 1459 height = tems->a_font.height; 1460 1461 nrows = (tems->a_p_offset.y + (height - 1))/ height; 1462 ncols = (tems->a_p_dimension.width + (width - 1))/ width; 1463 1464 tem_pix_cls_range(tem, 0, nrows, 0, 0, ncols, 0, 1465 B_FALSE, credp, called_from); 1466 } 1467 1468 /* 1469 * clear the whole screen for pixel mode 1470 */ 1471 static void 1472 tem_pix_clear_entire_screen(struct tem *tem, cred_t *credp, 1473 enum called_from called_from) 1474 { 1475 struct tem_state *tems = tem->state; 1476 int nrows, ncols, width, height; 1477 1478 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1479 MUTEX_HELD(&tem->lock)); 1480 1481 width = tems->a_font.width; 1482 height = tems->a_font.height; 1483 1484 nrows = (tems->a_p_dimension.height + (height - 1))/ height; 1485 ncols = (tems->a_p_dimension.width + (width - 1))/ width; 1486 1487 tem_pix_cls_range(tem, 0, nrows, 0, 0, ncols, 0, 1488 B_FALSE, credp, called_from); 1489 1490 tems->a_c_cursor.row = 0; 1491 tems->a_c_cursor.col = 0; 1492 tem_align_cursor(tem); 1493 1494 /* 1495 * Since the whole screen is cleared, we don't need 1496 * to clear OBP output later. 1497 */ 1498 if (tems->first_line > 0) { 1499 tems->first_line = 0; 1500 } 1501 } 1502 1503 /* 1504 * clear the whole screen 1505 */ 1506 static void 1507 tem_cls(struct tem *tem, 1508 cred_t *credp, enum called_from called_from) 1509 { 1510 struct tem_state *tems = tem->state; 1511 int row; 1512 1513 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1514 MUTEX_HELD(&tem->lock)); 1515 1516 if (tems->display_mode == VIS_TEXT) { 1517 for (row = 0; row < tems->a_c_dimension.height; row++) { 1518 tem_clear_chars(tem, tems->a_c_dimension.width, 1519 row, 0, credp, called_from); 1520 } 1521 tems->a_c_cursor.row = 0; 1522 tems->a_c_cursor.col = 0; 1523 tem_align_cursor(tem); 1524 return; 1525 } 1526 1527 ASSERT(tems->display_mode == VIS_PIXEL); 1528 1529 tem_pix_clear_entire_screen(tem, credp, called_from); 1530 } 1531 1532 static void 1533 tem_back_tab(struct tem *tem, 1534 cred_t *credp, enum called_from called_from) 1535 { 1536 struct tem_state *tems = tem->state; 1537 int i; 1538 screen_pos_t tabstop; 1539 1540 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1541 MUTEX_HELD(&tem->lock)); 1542 1543 tabstop = 0; 1544 1545 for (i = tems->a_ntabs - 1; i >= 0; i--) { 1546 if (tems->a_tabs[i] < tems->a_c_cursor.col) { 1547 tabstop = tems->a_tabs[i]; 1548 break; 1549 } 1550 } 1551 1552 tem_mv_cursor(tem, tems->a_c_cursor.row, 1553 tabstop, credp, called_from); 1554 } 1555 1556 static void 1557 tem_tab(struct tem *tem, 1558 cred_t *credp, enum called_from called_from) 1559 { 1560 struct tem_state *tems = tem->state; 1561 int i; 1562 screen_pos_t tabstop; 1563 1564 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1565 MUTEX_HELD(&tem->lock)); 1566 1567 tabstop = tems->a_c_dimension.width - 1; 1568 1569 for (i = 0; i < tems->a_ntabs; i++) { 1570 if (tems->a_tabs[i] > tems->a_c_cursor.col) { 1571 tabstop = tems->a_tabs[i]; 1572 break; 1573 } 1574 } 1575 1576 tem_mv_cursor(tem, tems->a_c_cursor.row, 1577 tabstop, credp, called_from); 1578 } 1579 1580 static void 1581 tem_set_tab(struct tem *tem) 1582 { 1583 struct tem_state *tems = tem->state; 1584 int i; 1585 int j; 1586 1587 if (tems->a_ntabs == TEM_MAXTAB) 1588 return; 1589 if (tems->a_ntabs == 0 || 1590 tems->a_tabs[tems->a_ntabs] < tems->a_c_cursor.col) { 1591 tems->a_tabs[tems->a_ntabs++] = tems->a_c_cursor.col; 1592 return; 1593 } 1594 for (i = 0; i < tems->a_ntabs; i++) { 1595 if (tems->a_tabs[i] == tems->a_c_cursor.col) 1596 return; 1597 if (tems->a_tabs[i] > tems->a_c_cursor.col) { 1598 for (j = tems->a_ntabs - 1; j >= i; j--) 1599 tems->a_tabs[j+ 1] = tems->a_tabs[j]; 1600 tems->a_tabs[i] = tems->a_c_cursor.col; 1601 tems->a_ntabs++; 1602 return; 1603 } 1604 } 1605 } 1606 1607 static void 1608 tem_clear_tabs(struct tem *tem, int action) 1609 { 1610 struct tem_state *tems = tem->state; 1611 int i; 1612 int j; 1613 1614 switch (action) { 1615 case 3: /* clear all tabs */ 1616 tems->a_ntabs = 0; 1617 break; 1618 case 0: /* clr tab at cursor */ 1619 1620 for (i = 0; i < tems->a_ntabs; i++) { 1621 if (tems->a_tabs[i] == tems->a_c_cursor.col) { 1622 tems->a_ntabs--; 1623 for (j = i; j < tems->a_ntabs; j++) 1624 tems->a_tabs[j] = tems->a_tabs[j + 1]; 1625 return; 1626 } 1627 } 1628 break; 1629 } 1630 } 1631 1632 static void 1633 tem_mv_cursor(struct tem *tem, int row, int col, 1634 cred_t *credp, enum called_from called_from) 1635 { 1636 struct tem_state *tems = tem->state; 1637 1638 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1639 MUTEX_HELD(&tem->lock)); 1640 1641 /* 1642 * Sanity check and bounds enforcement. Out of bounds requests are 1643 * clipped to the screen boundaries. This seems to be what SPARC 1644 * does. 1645 */ 1646 if (row < 0) 1647 row = 0; 1648 if (row >= tems->a_c_dimension.height) 1649 row = tems->a_c_dimension.height - 1; 1650 if (col < 0) 1651 col = 0; 1652 if (col >= tems->a_c_dimension.width) 1653 col = tems->a_c_dimension.width - 1; 1654 1655 tem_send_data(tem, credp, called_from); 1656 tems->a_c_cursor.row = row; 1657 tems->a_c_cursor.col = col; 1658 tem_align_cursor(tem); 1659 } 1660 1661 /* ARGSUSED */ 1662 void 1663 tem_reset_emulator(struct tem *tem, 1664 cred_t *credp, enum called_from called_from, 1665 tem_color_t *pcolor) 1666 { 1667 struct tem_state *tems = tem->state; 1668 int j; 1669 1670 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1671 MUTEX_HELD(&tem->lock)); 1672 1673 tems->a_c_cursor.row = 0; 1674 tems->a_c_cursor.col = 0; 1675 tems->a_r_cursor.row = 0; 1676 tems->a_r_cursor.col = 0; 1677 tems->a_s_cursor.row = 0; 1678 tems->a_s_cursor.col = 0; 1679 tems->a_outindex = 0; 1680 tems->a_state = A_STATE_START; 1681 tems->a_gotparam = B_FALSE; 1682 tems->a_curparam = 0; 1683 tems->a_paramval = 0; 1684 tems->a_nscroll = 1; 1685 1686 if (pcolor != NULL) { 1687 /* use customized settings */ 1688 tems->fg_color = pcolor->fg_color; 1689 tems->bg_color = pcolor->bg_color; 1690 tems->a_flags = pcolor->a_flags; 1691 } else { 1692 /* use initial settings */ 1693 tems->fg_color = tem->init_color.fg_color; 1694 tems->bg_color = tem->init_color.bg_color; 1695 tems->a_flags = tem->init_color.a_flags; 1696 } 1697 1698 /* 1699 * set up the initial tab stops 1700 */ 1701 tems->a_ntabs = 0; 1702 for (j = 8; j < tems->a_c_dimension.width; j += 8) 1703 tems->a_tabs[tems->a_ntabs++] = (screen_pos_t)j; 1704 1705 for (j = 0; j < TEM_MAXPARAMS; j++) 1706 tems->a_params[j] = 0; 1707 } 1708 1709 void 1710 tem_reset_display(struct tem *tem, 1711 cred_t *credp, enum called_from called_from, int clear_txt, 1712 tem_color_t *pcolor) 1713 { 1714 struct tem_state *tems = tem->state; 1715 1716 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1717 MUTEX_HELD(&tem->lock)); 1718 1719 tem_reset_emulator(tem, credp, called_from, pcolor); 1720 tem_reset_colormap(tem, credp, called_from); 1721 1722 if (clear_txt) { 1723 (*tems->in_fp.f_cursor)(tem, 1724 VIS_HIDE_CURSOR, credp, called_from); 1725 1726 tem_cls(tem, credp, called_from); 1727 1728 (*tems->in_fp.f_cursor)(tem, 1729 VIS_DISPLAY_CURSOR, credp, called_from); 1730 } 1731 1732 tems->a_initialized = 1; 1733 } 1734 1735 1736 static void 1737 tem_shift( 1738 struct tem *tem, 1739 int count, 1740 int direction, 1741 cred_t *credp, 1742 enum called_from called_from) 1743 { 1744 struct tem_state *tems = tem->state; 1745 int rest_of_line; 1746 1747 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1748 MUTEX_HELD(&tem->lock)); 1749 1750 rest_of_line = tems->a_c_dimension.width - tems->a_c_cursor.col; 1751 if (count > rest_of_line) 1752 count = rest_of_line; 1753 1754 if (count <= 0) 1755 return; 1756 1757 switch (direction) { 1758 case TEM_SHIFT_LEFT: 1759 if (count < rest_of_line) { 1760 tem_copy_area(tem, 1761 tems->a_c_cursor.col + count, 1762 tems->a_c_cursor.row, 1763 tems->a_c_dimension.width - 1, 1764 tems->a_c_cursor.row, 1765 tems->a_c_cursor.col, 1766 tems->a_c_cursor.row, 1767 credp, called_from); 1768 } 1769 1770 tem_clear_chars(tem, count, tems->a_c_cursor.row, 1771 (tems->a_c_dimension.width - count), credp, 1772 called_from); 1773 break; 1774 case TEM_SHIFT_RIGHT: 1775 if (count < rest_of_line) { 1776 tem_copy_area(tem, 1777 tems->a_c_cursor.col, 1778 tems->a_c_cursor.row, 1779 tems->a_c_dimension.width - count - 1, 1780 tems->a_c_cursor.row, 1781 tems->a_c_cursor.col + count, 1782 tems->a_c_cursor.row, 1783 credp, called_from); 1784 } 1785 1786 tem_clear_chars(tem, count, tems->a_c_cursor.row, 1787 tems->a_c_cursor.col, credp, called_from); 1788 break; 1789 } 1790 } 1791 1792 void 1793 tem_text_cursor(struct tem *tem, short action, 1794 cred_t *credp, enum called_from called_from) 1795 { 1796 struct tem_state *tems = tem->state; 1797 struct vis_conscursor ca; 1798 1799 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1800 MUTEX_HELD(&tem->lock)); 1801 1802 ca.row = tems->a_c_cursor.row; 1803 ca.col = tems->a_c_cursor.col; 1804 ca.action = action; 1805 1806 tem_cursor(tem, &ca, credp, called_from); 1807 1808 if (action == VIS_GET_CURSOR) { 1809 tems->a_c_cursor.row = ca.row; 1810 tems->a_c_cursor.col = ca.col; 1811 } 1812 } 1813 1814 1815 void 1816 tem_pix_cursor(struct tem *tem, short action, 1817 cred_t *credp, enum called_from called_from) 1818 { 1819 struct tem_state *tems = tem->state; 1820 struct vis_conscursor ca; 1821 1822 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1823 MUTEX_HELD(&tem->lock)); 1824 1825 ca.row = tems->a_c_cursor.row * tems->a_font.height + 1826 tems->a_p_offset.y; 1827 ca.col = tems->a_c_cursor.col * tems->a_font.width + 1828 tems->a_p_offset.x; 1829 ca.width = tems->a_font.width; 1830 ca.height = tems->a_font.height; 1831 if (tems->a_pdepth == 8 || tems->a_pdepth == 4) { 1832 if (tems->a_flags & TEM_ATTR_REVERSE) { 1833 ca.fg_color.mono = TEM_TEXT_WHITE; 1834 ca.bg_color.mono = TEM_TEXT_BLACK; 1835 } else { 1836 ca.fg_color.mono = TEM_TEXT_BLACK; 1837 ca.bg_color.mono = TEM_TEXT_WHITE; 1838 } 1839 } else if (tems->a_pdepth == 24 || tems->a_pdepth == 32) { 1840 if (tems->a_flags & TEM_ATTR_REVERSE) { 1841 ca.fg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED; 1842 ca.fg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN; 1843 ca.fg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE; 1844 1845 ca.bg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED; 1846 ca.bg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN; 1847 ca.bg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE; 1848 } else { 1849 ca.fg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED; 1850 ca.fg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN; 1851 ca.fg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE; 1852 1853 ca.bg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED; 1854 ca.bg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN; 1855 ca.bg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE; 1856 } 1857 } 1858 1859 ca.action = action; 1860 1861 tem_cursor(tem, &ca, credp, called_from); 1862 } 1863 1864 #define BORDER_PIXELS 10 1865 void 1866 set_font(struct font *f, short *rows, short *cols, short height, short width) 1867 { 1868 bitmap_data_t *font_selected = NULL; 1869 struct fontlist *fl; 1870 1871 /* 1872 * Find best font for these dimensions, or use default 1873 * 1874 * A 1 pixel border is the absolute minimum we could have 1875 * as a border around the text window (BORDER_PIXELS = 2), 1876 * however a slightly larger border not only looks better 1877 * but for the fonts currently statically built into the 1878 * emulator causes much better font selection for the 1879 * normal range of screen resolutions. 1880 */ 1881 for (fl = fonts; fl->data; fl++) { 1882 if ((((*rows * fl->data->height) + BORDER_PIXELS) <= height) && 1883 (((*cols * fl->data->width) + BORDER_PIXELS) <= width)) { 1884 font_selected = fl->data; 1885 break; 1886 } 1887 } 1888 /* 1889 * The minus 2 is to make sure we have at least a 1 pixel 1890 * boarder around the entire screen. 1891 */ 1892 if (font_selected == NULL) { 1893 if (((*rows * DEFAULT_FONT_DATA.height) > height) || 1894 ((*cols * DEFAULT_FONT_DATA.width) > width)) { 1895 *rows = (height - 2) / DEFAULT_FONT_DATA.height; 1896 *cols = (width - 2) / DEFAULT_FONT_DATA.width; 1897 } 1898 font_selected = &DEFAULT_FONT_DATA; 1899 } 1900 1901 f->width = font_selected->width; 1902 f->height = font_selected->height; 1903 bcopy((caddr_t)font_selected->encoding, (caddr_t)f->char_ptr, 1904 sizeof (f->char_ptr)); 1905 f->image_data = font_selected->image; 1906 1907 } 1908 1909 /* 1910 * bit_to_pix4 is for 4-bit frame buffers. It will write one output byte 1911 * for each 2 bits of input bitmap. It inverts the input bits before 1912 * doing the output translation, for reverse video. 1913 * 1914 * Assuming foreground is 0001 and background is 0000... 1915 * An input data byte of 0x53 will output the bit pattern 1916 * 00000001 00000001 00000000 00010001. 1917 */ 1918 1919 void 1920 bit_to_pix4( 1921 struct tem *tem, 1922 uchar_t c, 1923 text_color_t fg_color, 1924 text_color_t bg_color) 1925 { 1926 struct tem_state *tems = tem->state; 1927 int row; 1928 int byte; 1929 int i; 1930 uint8_t *cp; 1931 uint8_t data; 1932 uint8_t nibblett; 1933 int bytes_wide; 1934 uint8_t *dest; 1935 1936 dest = (uint8_t *)tems->a_pix_data; 1937 1938 cp = tems->a_font.char_ptr[c]; 1939 bytes_wide = (tems->a_font.width + 7) / 8; 1940 1941 for (row = 0; row < tems->a_font.height; row++) { 1942 for (byte = 0; byte < bytes_wide; byte++) { 1943 data = *cp++; 1944 for (i = 0; i < 4; i++) { 1945 nibblett = (data >> ((3-i) * 2)) & 0x3; 1946 switch (nibblett) { 1947 case 0x0: 1948 *dest++ = bg_color << 4 | bg_color; 1949 break; 1950 case 0x1: 1951 *dest++ = bg_color << 4 | fg_color; 1952 break; 1953 case 0x2: 1954 *dest++ = fg_color << 4 | bg_color; 1955 break; 1956 case 0x3: 1957 *dest++ = fg_color << 4 | fg_color; 1958 break; 1959 } 1960 } 1961 } 1962 } 1963 } 1964 1965 /* 1966 * bit_to_pix8 is for 8-bit frame buffers. It will write one output byte 1967 * for each bit of input bitmap. It inverts the input bits before 1968 * doing the output translation, for reverse video. 1969 * 1970 * Assuming foreground is 00000001 and background is 00000000... 1971 * An input data byte of 0x53 will output the bit pattern 1972 * 0000000 000000001 00000000 00000001 00000000 00000000 00000001 00000001. 1973 */ 1974 1975 void 1976 bit_to_pix8( 1977 struct tem *tem, 1978 uchar_t c, 1979 text_color_t fg_color, 1980 text_color_t bg_color) 1981 { 1982 struct tem_state *tems = tem->state; 1983 int row; 1984 int byte; 1985 int i; 1986 uint8_t *cp; 1987 uint8_t data; 1988 int bytes_wide; 1989 uint8_t mask; 1990 int bitsleft, nbits; 1991 uint8_t *dest; 1992 1993 dest = (uint8_t *)tems->a_pix_data; 1994 1995 cp = tems->a_font.char_ptr[c]; 1996 bytes_wide = (tems->a_font.width + 7) / 8; 1997 1998 for (row = 0; row < tems->a_font.height; row++) { 1999 bitsleft = tems->a_font.width; 2000 for (byte = 0; byte < bytes_wide; byte++) { 2001 data = *cp++; 2002 mask = 0x80; 2003 nbits = MIN(8, bitsleft); 2004 bitsleft -= nbits; 2005 for (i = 0; i < nbits; i++) { 2006 *dest++ = (data & mask ? fg_color: bg_color); 2007 mask = mask >> 1; 2008 } 2009 } 2010 } 2011 } 2012 2013 /* 2014 * bit_to_pix24 is for 24-bit frame buffers. It will write four output bytes 2015 * for each bit of input bitmap. It inverts the input bits before 2016 * doing the output translation, for reverse video. Note that each 2017 * 24-bit RGB value is finally stored in a 32-bit unsigned int, with the 2018 * high-order byte set to zero. 2019 * 2020 * Assuming foreground is 00000000 11111111 11111111 11111111 2021 * and background is 00000000 00000000 00000000 00000000 2022 * An input data byte of 0x53 will output the bit pattern 2023 * 2024 * 00000000 00000000 00000000 00000000 2025 * 00000000 11111111 11111111 11111111 2026 * 00000000 00000000 00000000 00000000 2027 * 00000000 11111111 11111111 11111111 2028 * 00000000 00000000 00000000 00000000 2029 * 00000000 00000000 00000000 00000000 2030 * 00000000 11111111 11111111 11111111 2031 * 00000000 11111111 11111111 11111111 2032 * 2033 */ 2034 typedef uint32_t pixel32_t; 2035 2036 void 2037 bit_to_pix24( 2038 struct tem *tem, 2039 uchar_t c, 2040 text_color_t fg_color4, 2041 text_color_t bg_color4) 2042 { 2043 struct tem_state *tems = tem->state; 2044 int row; 2045 int byte; 2046 int i; 2047 uint8_t *cp; 2048 uint8_t data; 2049 int bytes_wide; 2050 int bitsleft, nbits; 2051 2052 pixel32_t fg_color32, bg_color32, *destp; 2053 2054 ASSERT(fg_color4 < 16 && bg_color4 < 16); 2055 2056 fg_color32 = PIX4TO32(fg_color4); 2057 bg_color32 = PIX4TO32(bg_color4); 2058 2059 destp = (pixel32_t *)tems->a_pix_data; 2060 cp = tems->a_font.char_ptr[c]; 2061 bytes_wide = (tems->a_font.width + 7) / 8; 2062 2063 for (row = 0; row < tems->a_font.height; row++) { 2064 bitsleft = tems->a_font.width; 2065 for (byte = 0; byte < bytes_wide; byte++) { 2066 data = *cp++; 2067 nbits = MIN(8, bitsleft); 2068 bitsleft -= nbits; 2069 for (i = 0; i < nbits; i++) { 2070 *destp++ = ((data << i) & 0x80 ? 2071 fg_color32 : bg_color32); 2072 } 2073 } 2074 } 2075 } 2076 2077 /* ARGSUSED */ 2078 text_color_t 2079 ansi_bg_to_solaris(struct tem *tem, int ansi) 2080 { 2081 return (bg_xlate[ansi]); 2082 } 2083 2084 text_color_t 2085 ansi_fg_to_solaris(struct tem *tem, int ansi) 2086 { 2087 if (tem->state->a_flags & TEM_ATTR_BOLD) 2088 return (fg_brt_xlate[ansi]); 2089 else 2090 return (fg_dim_xlate[ansi]); 2091 } 2092 2093 static void 2094 tem_get_color(struct tem *tem, text_color_t *fg, text_color_t *bg) 2095 { 2096 if (tem->state->a_flags & TEM_ATTR_SCREEN_REVERSE) { 2097 *fg = ansi_fg_to_solaris(tem, 2098 DEFAULT_ANSI_BACKGROUND); 2099 *bg = ansi_bg_to_solaris(tem, 2100 DEFAULT_ANSI_FOREGROUND); 2101 } else { 2102 *fg = ansi_fg_to_solaris(tem, 2103 DEFAULT_ANSI_FOREGROUND); 2104 *bg = ansi_bg_to_solaris(tem, 2105 DEFAULT_ANSI_BACKGROUND); 2106 } 2107 } 2108 2109 /* 2110 * Clear a rectangle of screen for pixel mode. 2111 * 2112 * arguments: 2113 * row: start row# 2114 * nrows: the number of rows to clear 2115 * offset_y: the offset of height in pixels to begin clear 2116 * col: start col# 2117 * ncols: the number of cols to clear 2118 * offset_x: the offset of width in pixels to begin clear 2119 * scroll_up: whether this function is called during sroll up, 2120 * which is called only once. 2121 */ 2122 void 2123 tem_pix_cls_range(tem_t *tem, 2124 screen_pos_t row, int nrows, int offset_y, 2125 screen_pos_t col, int ncols, int offset_x, 2126 boolean_t sroll_up, cred_t *credp, 2127 enum called_from called_from) 2128 { 2129 struct tem_state *tems = tem->state; 2130 struct vis_consdisplay da; 2131 int i, j; 2132 int row_add = 0; 2133 text_color_t fg_color; 2134 text_color_t bg_color; 2135 2136 ASSERT((called_from == CALLED_FROM_STANDALONE) || 2137 MUTEX_HELD(&tem->lock)); 2138 2139 if (sroll_up) 2140 row_add = tems->a_c_dimension.height - 1; 2141 2142 da.width = tems->a_font.width; 2143 da.height = tems->a_font.height; 2144 2145 tem_get_color(tem, &fg_color, &bg_color); 2146 2147 BIT_TO_PIX(tem, ' ', fg_color, bg_color); 2148 da.data = (uchar_t *)tems->a_pix_data; 2149 2150 for (i = 0; i < nrows; i++, row++) { 2151 da.row = (row + row_add) * da.height + offset_y; 2152 da.col = col * da.width + offset_x; 2153 for (j = 0; j < ncols; j++) { 2154 tem_display(tem, &da, credp, called_from); 2155 da.col += da.width; 2156 } 2157 } 2158 } 2159