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