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_OSC:
 493                 {
 494                         int i;
 495                         tem->tvs_curparam = 0;
 496                         tem->tvs_paramval = 0;
 497                         tem->tvs_gotparam = B_FALSE;
 498                         /* clear the parameters */
 499                         for (i = 0; i < TEM_MAXPARAMS; i++)
 500                                 tem->tvs_params[i] = -1;
 501                         tem->tvs_state = A_STATE_OSC;
 502                 }
 503                 break;
 504 
 505         case A_GS:
 506                 tem_safe_back_tab(tem, credp, called_from);
 507                 break;
 508 
 509         default:
 510                 break;
 511         }
 512 }
 513 
 514 
 515 /*
 516  * if parameters [0..count - 1] are not set, set them to the value
 517  * of newparam.
 518  */
 519 
 520 static void
 521 tem_safe_setparam(struct tem_vt_state *tem, int count, int newparam)
 522 {
 523         int i;
 524 
 525         for (i = 0; i < count; i++) {
 526                 if (tem->tvs_params[i] == -1)
 527                         tem->tvs_params[i] = newparam;
 528         }
 529 }
 530 
 531 
 532 /*
 533  * select graphics mode based on the param vals stored in a_params
 534  */
 535 static void
 536 tem_safe_selgraph(struct tem_vt_state *tem)
 537 {
 538         int curparam;
 539         int count = 0;
 540         int param;
 541 
 542         tem->tvs_state = A_STATE_START;
 543 
 544         curparam = tem->tvs_curparam;
 545         do {
 546                 param = tem->tvs_params[count];
 547 
 548                 switch (param) {
 549                 case -1:
 550                 case 0:
 551                         /* reset to initial normal settings */
 552                         tem->tvs_fg_color = tems.ts_init_color.fg_color;
 553                         tem->tvs_bg_color = tems.ts_init_color.bg_color;
 554                         tem->tvs_flags = tems.ts_init_color.a_flags;
 555                         break;
 556 
 557                 case 1: /* Bold Intense */
 558                         tem->tvs_flags |= TEM_ATTR_BOLD;
 559                         break;
 560 
 561                 case 2: /* Faint Intense */
 562                         tem->tvs_flags &= ~TEM_ATTR_BOLD;
 563                         break;
 564 
 565                 case 4: /* Underline */
 566                         tem->tvs_flags |= TEM_ATTR_UNDERLINE;
 567                         break;
 568                 case 5: /* Blink */
 569                         tem->tvs_flags |= TEM_ATTR_BLINK;
 570                         break;
 571 
 572                 case 7: /* Reverse video */
 573                         if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
 574                                 tem->tvs_flags &= ~TEM_ATTR_REVERSE;
 575                         } else {
 576                                 tem->tvs_flags |= TEM_ATTR_REVERSE;
 577                         }
 578                         break;
 579 
 580                 case 22: /* Remove Bold */
 581                         tem->tvs_flags &= ~TEM_ATTR_BOLD;
 582                         break;
 583 
 584                 case 24: /* Remove Underline */
 585                         tem->tvs_flags &= ~TEM_ATTR_UNDERLINE;
 586                         break;
 587 
 588                 case 25: /* Remove Blink */
 589                         tem->tvs_flags &= ~TEM_ATTR_BLINK;
 590                         break;
 591 
 592                 case 27: /* Remove Reverse */
 593                         if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
 594                                 tem->tvs_flags |= TEM_ATTR_REVERSE;
 595                         } else {
 596                                 tem->tvs_flags &= ~TEM_ATTR_REVERSE;
 597                         }
 598                         break;
 599 
 600                 case 30: /* black       (grey)          foreground */
 601                 case 31: /* red         (light red)     foreground */
 602                 case 32: /* green       (light green)   foreground */
 603                 case 33: /* brown       (yellow)        foreground */
 604                 case 34: /* blue        (light blue)    foreground */
 605                 case 35: /* magenta     (light magenta) foreground */
 606                 case 36: /* cyan        (light cyan)    foreground */
 607                 case 37: /* white       (bright white)  foreground */
 608                         tem->tvs_fg_color = param - 30;
 609                         tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
 610                         break;
 611 
 612                 case 39:
 613                         /*
 614                          * Reset the foreground colour and brightness.
 615                          */
 616                         tem->tvs_fg_color = tems.ts_init_color.fg_color;
 617                         if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_FG)
 618                                 tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
 619                         else
 620                                 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
 621                         break;
 622 
 623                 case 40: /* black       (grey)          background */
 624                 case 41: /* red         (light red)     background */
 625                 case 42: /* green       (light green)   background */
 626                 case 43: /* brown       (yellow)        background */
 627                 case 44: /* blue        (light blue)    background */
 628                 case 45: /* magenta     (light magenta) background */
 629                 case 46: /* cyan        (light cyan)    background */
 630                 case 47: /* white       (bright white)  background */
 631                         tem->tvs_bg_color = param - 40;
 632                         tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
 633                         break;
 634 
 635                 case 49:
 636                         /*
 637                          * Reset the background colour and brightness.
 638                          */
 639                         tem->tvs_bg_color = tems.ts_init_color.bg_color;
 640                         if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_BG)
 641                                 tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
 642                         else
 643                                 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
 644                         break;
 645 
 646                 case 90: /* black       (grey)          foreground */
 647                 case 91: /* red         (light red)     foreground */
 648                 case 92: /* green       (light green)   foreground */
 649                 case 93: /* brown       (yellow)        foreground */
 650                 case 94: /* blue        (light blue)    foreground */
 651                 case 95: /* magenta     (light magenta) foreground */
 652                 case 96: /* cyan        (light cyan)    foreground */
 653                 case 97: /* white       (bright white)  foreground */
 654                         tem->tvs_fg_color = param - 90;
 655                         tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
 656                         break;
 657 
 658                 case 100: /* black      (grey)          background */
 659                 case 101: /* red        (light red)     background */
 660                 case 102: /* green      (light green)   background */
 661                 case 103: /* brown      (yellow)        background */
 662                 case 104: /* blue       (light blue)    background */
 663                 case 105: /* magenta    (light magenta) background */
 664                 case 106: /* cyan       (light cyan)    background */
 665                 case 107: /* white      (bright white)  background */
 666                         tem->tvs_bg_color = param - 100;
 667                         tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
 668                         break;
 669 
 670                 default:
 671                         break;
 672                 }
 673                 count++;
 674                 curparam--;
 675 
 676         } while (curparam > 0);
 677 }
 678 
 679 /*
 680  * perform the appropriate action for the escape sequence
 681  *
 682  * General rule:  This code does not validate the arguments passed.
 683  *                It assumes that the next lower level will do so.
 684  */
 685 static void
 686 tem_safe_chkparam(struct tem_vt_state *tem, tem_char_t ch, cred_t *credp,
 687     enum called_from called_from)
 688 {
 689         int     i;
 690         int     row;
 691         int     col;
 692 
 693         ASSERT((called_from == CALLED_FROM_STANDALONE) ||
 694             MUTEX_HELD(&tem->tvs_lock));
 695 
 696         row = tem->tvs_c_cursor.row;
 697         col = tem->tvs_c_cursor.col;
 698 
 699         switch (ch) {
 700 
 701         case 'm': /* select terminal graphics mode */
 702                 tem_safe_send_data(tem, credp, called_from);
 703                 tem_safe_selgraph(tem);
 704                 break;
 705 
 706         case '@':               /* insert char */
 707                 tem_safe_setparam(tem, 1, 1);
 708                 tem_safe_shift(tem, tem->tvs_params[0], TEM_SHIFT_RIGHT,
 709                     credp, called_from);
 710                 break;
 711 
 712         case 'A':               /* cursor up */
 713                 tem_safe_setparam(tem, 1, 1);
 714                 tem_safe_mv_cursor(tem, row - tem->tvs_params[0], col,
 715                     credp, called_from);
 716                 break;
 717 
 718         case 'd':               /* VPA - vertical position absolute */
 719                 tem_safe_setparam(tem, 1, 1);
 720                 tem_safe_mv_cursor(tem, tem->tvs_params[0] - 1, col,
 721                     credp, called_from);
 722                 break;
 723 
 724         case 'e':               /* VPR - vertical position relative */
 725         case 'B':               /* cursor down */
 726                 tem_safe_setparam(tem, 1, 1);
 727                 tem_safe_mv_cursor(tem, row + tem->tvs_params[0], col,
 728                     credp, called_from);
 729                 break;
 730 
 731         case 'a':               /* HPR - horizontal position relative */
 732         case 'C':               /* cursor right */
 733                 tem_safe_setparam(tem, 1, 1);
 734                 tem_safe_mv_cursor(tem, row, col + tem->tvs_params[0],
 735                     credp, called_from);
 736                 break;
 737 
 738         case '`':               /* HPA - horizontal position absolute */
 739                 tem_safe_setparam(tem, 1, 1);
 740                 tem_safe_mv_cursor(tem, row, tem->tvs_params[0] - 1,
 741                     credp, called_from);
 742                 break;
 743 
 744         case 'D':               /* cursor left */
 745                 tem_safe_setparam(tem, 1, 1);
 746                 tem_safe_mv_cursor(tem, row, col - tem->tvs_params[0],
 747                     credp, called_from);
 748                 break;
 749 
 750         case 'E':               /* CNL cursor next line */
 751                 tem_safe_setparam(tem, 1, 1);
 752                 tem_safe_mv_cursor(tem, row + tem->tvs_params[0], 0,
 753                     credp, called_from);
 754                 break;
 755 
 756         case 'F':               /* CPL cursor previous line */
 757                 tem_safe_setparam(tem, 1, 1);
 758                 tem_safe_mv_cursor(tem, row - tem->tvs_params[0], 0,
 759                     credp, called_from);
 760                 break;
 761 
 762         case 'G':               /* cursor horizontal position */
 763                 tem_safe_setparam(tem, 1, 1);
 764                 tem_safe_mv_cursor(tem, row, tem->tvs_params[0] - 1,
 765                     credp, called_from);
 766                 break;
 767 
 768         case 'g':               /* clear tabs */
 769                 tem_safe_setparam(tem, 1, 0);
 770                 tem_safe_clear_tabs(tem, tem->tvs_params[0]);
 771                 break;
 772 
 773         case 'f':               /* HVP Horizontal and Vertical Position */
 774         case 'H':               /* CUP position cursor */
 775                 tem_safe_setparam(tem, 2, 1);
 776                 tem_safe_mv_cursor(tem,
 777                     tem->tvs_params[0] - 1,
 778                     tem->tvs_params[1] - 1,
 779                     credp, called_from);
 780                 break;
 781 
 782         case 'I':               /* CHT - Cursor Horizontal Tab */
 783                 /* Not implemented */
 784                 break;
 785 
 786         case 'J':               /* ED - Erase in Display */
 787                 tem_safe_send_data(tem, credp, called_from);
 788                 tem_safe_setparam(tem, 1, 0);
 789                 switch (tem->tvs_params[0]) {
 790                 case 0:
 791                         /* erase cursor to end of screen */
 792                         /* FIRST erase cursor to end of line */
 793                         tem_safe_clear_chars(tem,
 794                             tems.ts_c_dimension.width -
 795                             tem->tvs_c_cursor.col,
 796                             tem->tvs_c_cursor.row,
 797                             tem->tvs_c_cursor.col, credp, called_from);
 798 
 799                         /* THEN erase lines below the cursor */
 800                         for (row = tem->tvs_c_cursor.row + 1;
 801                             row < tems.ts_c_dimension.height;
 802                             row++) {
 803                                 tem_safe_clear_chars(tem,
 804                                     tems.ts_c_dimension.width,
 805                                     row, 0, credp, called_from);
 806                         }
 807                         break;
 808 
 809                 case 1:
 810                         /* erase beginning of screen to cursor */
 811                         /* FIRST erase lines above the cursor */
 812                         for (row = 0;
 813                             row < tem->tvs_c_cursor.row;
 814                             row++) {
 815                                 tem_safe_clear_chars(tem,
 816                                     tems.ts_c_dimension.width,
 817                                     row, 0, credp, called_from);
 818                         }
 819                         /* THEN erase beginning of line to cursor */
 820                         tem_safe_clear_chars(tem,
 821                             tem->tvs_c_cursor.col + 1,
 822                             tem->tvs_c_cursor.row,
 823                             0, credp, called_from);
 824                         break;
 825 
 826                 case 2:
 827                         /* erase whole screen */
 828                         for (row = 0;
 829                             row < tems.ts_c_dimension.height;
 830                             row++) {
 831                                 tem_safe_clear_chars(tem,
 832                                     tems.ts_c_dimension.width,
 833                                     row, 0, credp, called_from);
 834                         }
 835                         break;
 836                 }
 837                 break;
 838 
 839         case 'K':               /* EL - Erase in Line */
 840                 tem_safe_send_data(tem, credp, called_from);
 841                 tem_safe_setparam(tem, 1, 0);
 842                 switch (tem->tvs_params[0]) {
 843                 case 0:
 844                         /* erase cursor to end of line */
 845                         tem_safe_clear_chars(tem,
 846                             (tems.ts_c_dimension.width -
 847                             tem->tvs_c_cursor.col),
 848                             tem->tvs_c_cursor.row,
 849                             tem->tvs_c_cursor.col,
 850                             credp, called_from);
 851                         break;
 852 
 853                 case 1:
 854                         /* erase beginning of line to cursor */
 855                         tem_safe_clear_chars(tem,
 856                             tem->tvs_c_cursor.col + 1,
 857                             tem->tvs_c_cursor.row,
 858                             0, credp, called_from);
 859                         break;
 860 
 861                 case 2:
 862                         /* erase whole line */
 863                         tem_safe_clear_chars(tem,
 864                             tems.ts_c_dimension.width,
 865                             tem->tvs_c_cursor.row,
 866                             0, credp, called_from);
 867                         break;
 868                 }
 869                 break;
 870 
 871         case 'L':               /* insert line */
 872                 tem_safe_send_data(tem, credp, called_from);
 873                 tem_safe_setparam(tem, 1, 1);
 874                 tem_safe_scroll(tem,
 875                     tem->tvs_c_cursor.row,
 876                     tems.ts_c_dimension.height - 1,
 877                     tem->tvs_params[0], TEM_SCROLL_DOWN,
 878                     credp, called_from);
 879                 break;
 880 
 881         case 'M':               /* delete line */
 882                 tem_safe_send_data(tem, credp, called_from);
 883                 tem_safe_setparam(tem, 1, 1);
 884                 tem_safe_scroll(tem,
 885                     tem->tvs_c_cursor.row,
 886                     tems.ts_c_dimension.height - 1,
 887                     tem->tvs_params[0], TEM_SCROLL_UP,
 888                     credp, called_from);
 889                 break;
 890 
 891         case 'P':               /* DCH - delete char */
 892                 tem_safe_setparam(tem, 1, 1);
 893                 tem_safe_shift(tem, tem->tvs_params[0], TEM_SHIFT_LEFT,
 894                     credp, called_from);
 895                 break;
 896 
 897         case 'S':               /* scroll up */
 898                 tem_safe_send_data(tem, credp, called_from);
 899                 tem_safe_setparam(tem, 1, 1);
 900                 tem_safe_scroll(tem, 0,
 901                     tems.ts_c_dimension.height - 1,
 902                     tem->tvs_params[0], TEM_SCROLL_UP,
 903                     credp, called_from);
 904                 break;
 905 
 906         case 'T':               /* scroll down */
 907                 tem_safe_send_data(tem, credp, called_from);
 908                 tem_safe_setparam(tem, 1, 1);
 909                 tem_safe_scroll(tem, 0,
 910                     tems.ts_c_dimension.height - 1,
 911                     tem->tvs_params[0], TEM_SCROLL_DOWN,
 912                     credp, called_from);
 913                 break;
 914 
 915         case 'X':               /* erase char */
 916                 tem_safe_setparam(tem, 1, 1);
 917                 tem_safe_clear_chars(tem,
 918                     tem->tvs_params[0],
 919                     tem->tvs_c_cursor.row,
 920                     tem->tvs_c_cursor.col,
 921                     credp, called_from);
 922                 break;
 923 
 924         case 'Z':               /* cursor backward tabulation */
 925                 tem_safe_setparam(tem, 1, 1);
 926 
 927                 /*
 928                  * Rule exception - We do sanity checking here.
 929                  *
 930                  * Restrict the count to a sane value to keep from
 931                  * looping for a long time.  There can't be more than one
 932                  * tab stop per column, so use that as a limit.
 933                  */
 934                 if (tem->tvs_params[0] > tems.ts_c_dimension.width)
 935                         tem->tvs_params[0] = tems.ts_c_dimension.width;
 936 
 937                 for (i = 0; i < tem->tvs_params[0]; i++)
 938                         tem_safe_back_tab(tem, credp, called_from);
 939                 break;
 940         }
 941         tem->tvs_state = A_STATE_START;
 942 }
 943 
 944 
 945 /*
 946  * Gather the parameters of an ANSI escape sequence
 947  */
 948 static void
 949 tem_safe_getparams(struct tem_vt_state *tem, tem_char_t ch,
 950     cred_t *credp, enum called_from called_from)
 951 {
 952         ASSERT((called_from == CALLED_FROM_STANDALONE) ||
 953             MUTEX_HELD(&tem->tvs_lock));
 954 
 955         if (ch >= '0' && ch <= '9') {
 956                 tem->tvs_paramval = ((tem->tvs_paramval * 10) + (ch - '0'));
 957                 tem->tvs_gotparam = B_TRUE;  /* Remember got parameter */
 958                 return; /* Return immediately */
 959         } else if (tem->tvs_state == A_STATE_CSI_EQUAL ||
 960             tem->tvs_state == A_STATE_CSI_QMARK) {
 961                 tem->tvs_state = A_STATE_START;
 962         } else {
 963                 if (tem->tvs_curparam < TEM_MAXPARAMS) {
 964                         if (tem->tvs_gotparam) {
 965                                 /* get the parameter value */
 966                                 tem->tvs_params[tem->tvs_curparam] =
 967                                     tem->tvs_paramval;
 968                         }
 969                         tem->tvs_curparam++;
 970                 }
 971 
 972                 if (ch == ';') {
 973                         /* Restart parameter search */
 974                         tem->tvs_gotparam = B_FALSE;
 975                         tem->tvs_paramval = 0; /* No parame value yet */
 976                 } else {
 977                         /* Handle escape sequence */
 978                         tem_safe_chkparam(tem, ch, credp, called_from);
 979                 }
 980         }
 981 }
 982 
 983 /*
 984  * Gather the OSC string.
 985  * OSC Ps ; Pt ST
 986  * OSC Ps ; Pt BEL
 987  * Ps is number sequence identifying the function. We only support
 988  * Ps = 4 and Ps = 104.
 989  * Quite obviously this is nowhere close to be done;)
 990  */
 991 /* ARGSUSED */
 992 static void
 993 tem_safe_get_oscstring(struct tem_vt_state *tem, uchar_t ch,
 994     cred_t *credp __unused, enum called_from called_from)
 995 {
 996         ASSERT((called_from == CALLED_FROM_STANDALONE) ||
 997             MUTEX_HELD(&tem->tvs_lock));
 998 
 999         /* Should we cancel? */
1000         if (ch == A_CAN || ch == A_SUB || ch == A_ESC) {
1001                 tem->tvs_state = A_STATE_START;
1002                 return;
1003         }
1004 
1005         /* following two if statements will read in the numeric parameter */
1006         if (ch >= '0' && ch <= '9') {
1007                 tem->tvs_paramval = ((tem->tvs_paramval * 10) + (ch - '0'));
1008                 tem->tvs_gotparam = B_TRUE;  /* Remember got parameter */
1009                 return; /* Return immediately */
1010         }
1011 
1012         if (tem->tvs_gotparam && ch == ';') {
1013                 /* get the parameter value */
1014                 tem->tvs_params[tem->tvs_curparam] = tem->tvs_paramval;
1015                 tem->tvs_curparam++;
1016                 tem->tvs_gotparam = B_FALSE;
1017                 tem->tvs_paramval = 0;
1018         }
1019 
1020         if (tem->tvs_curparam == 0) {
1021                 /* bad sequence */
1022                 tem->tvs_state = A_STATE_START;
1023                 return;
1024         }
1025         if (tem->tvs_gotparam == B_FALSE && tem->tvs_curparam > 0) {
1026                 if (tem->tvs_params[0] != 4 && tem->tvs_params[0] != 104) {
1027                         /* make sure tvs_params will not get filled up */
1028                         tem->tvs_curparam = 1;
1029                 }
1030         }
1031         if (ch == A_ST || ch == A_BEL) {
1032                 /* done */
1033                 tem->tvs_state = A_STATE_START;
1034                 return;
1035         }
1036 }
1037 
1038 /*
1039  * Add character to internal buffer.
1040  * When its full, send it to the next layer.
1041  */
1042 
1043 static void
1044 tem_safe_outch(struct tem_vt_state *tem, tem_char_t ch,
1045     cred_t *credp, enum called_from called_from)
1046 {
1047         text_color_t fg;
1048         text_color_t bg;
1049         text_attr_t attr;
1050 
1051         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1052             called_from == CALLED_FROM_STANDALONE);
1053 
1054         /* buffer up the character until later */
1055         tem_safe_get_attr(tem, &fg, &bg, &attr, TEM_ATTR_REVERSE);
1056         tem->tvs_outbuf[tem->tvs_outindex].tc_char = ch | TEM_ATTR(attr);
1057         tem->tvs_outbuf[tem->tvs_outindex].tc_fg_color = fg;
1058         tem->tvs_outbuf[tem->tvs_outindex].tc_bg_color = bg;
1059         tem->tvs_outindex++;
1060         tem->tvs_c_cursor.col++;
1061         if (tem->tvs_c_cursor.col >= tems.ts_c_dimension.width) {
1062                 tem_safe_send_data(tem, credp, called_from);
1063                 tem_safe_new_line(tem, credp, called_from);
1064         }
1065 }
1066 
1067 static void
1068 tem_safe_new_line(struct tem_vt_state *tem,
1069     cred_t *credp, enum called_from called_from)
1070 {
1071         tem_safe_cr(tem);
1072         tem_safe_lf(tem, credp, called_from);
1073 }
1074 
1075 static void
1076 tem_safe_cr(struct tem_vt_state *tem)
1077 {
1078         tem->tvs_c_cursor.col = 0;
1079         tem_safe_align_cursor(tem);
1080 }
1081 
1082 static void
1083 tem_safe_lf(struct tem_vt_state *tem,
1084     cred_t *credp, enum called_from called_from)
1085 {
1086         int row;
1087 
1088         ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1089             MUTEX_HELD(&tem->tvs_lock));
1090 
1091         /*
1092          * Sanity checking notes:
1093          * . a_nscroll was validated when it was set.
1094          * . Regardless of that, tem_safe_scroll and tem_safe_mv_cursor
1095          *   will prevent anything bad from happening.
1096          */
1097         row = tem->tvs_c_cursor.row + 1;
1098 
1099         if (row >= tems.ts_c_dimension.height) {
1100                 if (tem->tvs_nscroll != 0) {
1101                         tem_safe_scroll(tem, 0,
1102                             tems.ts_c_dimension.height - 1,
1103                             tem->tvs_nscroll, TEM_SCROLL_UP,
1104                             credp, called_from);
1105                         row = tems.ts_c_dimension.height -
1106                             tem->tvs_nscroll;
1107                 } else {        /* no scroll */
1108                         /*
1109                          * implement Esc[#r when # is zero.  This means no
1110                          * scroll but just return cursor to top of screen,
1111                          * do not clear screen.
1112                          */
1113                         row = 0;
1114                 }
1115         }
1116 
1117         tem_safe_mv_cursor(tem, row, tem->tvs_c_cursor.col,
1118             credp, called_from);
1119 
1120         if (tem->tvs_nscroll == 0) {
1121                 /* erase rest of cursor line */
1122                 tem_safe_clear_chars(tem,
1123                     tems.ts_c_dimension.width -
1124                     tem->tvs_c_cursor.col,
1125                     tem->tvs_c_cursor.row,
1126                     tem->tvs_c_cursor.col,
1127                     credp, called_from);
1128 
1129         }
1130 
1131         tem_safe_align_cursor(tem);
1132 }
1133 
1134 static void
1135 tem_safe_send_data(struct tem_vt_state *tem, cred_t *credp,
1136     enum called_from called_from)
1137 {
1138         ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1139             MUTEX_HELD(&tem->tvs_lock));
1140 
1141         if (tem->tvs_outindex == 0) {
1142                 tem_safe_align_cursor(tem);
1143                 return;
1144         }
1145 
1146         tem_safe_virtual_display(tem,
1147             tem->tvs_outbuf, tem->tvs_outindex,
1148             tem->tvs_s_cursor.row, tem->tvs_s_cursor.col);
1149 
1150         if (tem->tvs_isactive) {
1151                 /*
1152                  * Call the primitive to render this data.
1153                  */
1154                 tem_safe_callback_display(tem,
1155                     tem->tvs_outbuf, tem->tvs_outindex,
1156                     tem->tvs_s_cursor.row, tem->tvs_s_cursor.col,
1157                     credp, called_from);
1158         }
1159 
1160         tem->tvs_outindex = 0;
1161 
1162         tem_safe_align_cursor(tem);
1163 }
1164 
1165 
1166 /*
1167  * We have just done something to the current output point.  Reset the start
1168  * point for the buffered data in a_outbuf.  There shouldn't be any data
1169  * buffered yet.
1170  */
1171 static void
1172 tem_safe_align_cursor(struct tem_vt_state *tem)
1173 {
1174         tem->tvs_s_cursor.row = tem->tvs_c_cursor.row;
1175         tem->tvs_s_cursor.col = tem->tvs_c_cursor.col;
1176 }
1177 
1178 /*
1179  * State machine parser based on the current state and character input
1180  * major terminations are to control character or normal character
1181  */
1182 
1183 static void
1184 tem_safe_parse(struct tem_vt_state *tem, tem_char_t ch,
1185     cred_t *credp, enum called_from called_from)
1186 {
1187         int     i;
1188 
1189         ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1190             MUTEX_HELD(&tem->tvs_lock));
1191 
1192         if (tem->tvs_state == A_STATE_START) {       /* Normal state? */
1193                 if (ch == A_CSI || ch == A_OSC || ch == A_ESC || ch < ' ') {
1194                         /* Control */
1195                         tem_safe_control(tem, ch, credp, called_from);
1196                 } else {
1197                         /* Display */
1198                         tem_safe_outch(tem, ch, credp, called_from);
1199                 }
1200                 return;
1201         }
1202 
1203         /* In <ESC> sequence */
1204         if (tem->tvs_state != A_STATE_ESC) { /* Need to get parameters? */
1205                 if (tem->tvs_state == A_STATE_OSC) {
1206                         tem_safe_get_oscstring(tem, ch, credp, called_from);
1207                         return;
1208                 }
1209 
1210                 if (tem->tvs_state != A_STATE_CSI) {
1211                         tem_safe_getparams(tem, ch, credp, called_from);
1212                         return;
1213                 }
1214 
1215                 switch (ch) {
1216                 case '?':
1217                         tem->tvs_state = A_STATE_CSI_QMARK;
1218                         return;
1219                 case '=':
1220                         tem->tvs_state = A_STATE_CSI_EQUAL;
1221                         return;
1222                 case 's':
1223                         /*
1224                          * As defined below, this sequence
1225                          * saves the cursor.  However, Sun
1226                          * defines ESC[s as reset.  We resolved
1227                          * the conflict by selecting reset as it
1228                          * is exported in the termcap file for
1229                          * sun-mon, while the "save cursor"
1230                          * definition does not exist anywhere in
1231                          * /etc/termcap.
1232                          * However, having no coherent
1233                          * definition of reset, we have not
1234                          * implemented it.
1235                          */
1236 
1237                         /*
1238                          * Original code
1239                          * tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1240                          * tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1241                          * tem->tvs_state = A_STATE_START;
1242                          */
1243 
1244                         tem->tvs_state = A_STATE_START;
1245                         return;
1246                 case 'u':
1247                         tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row,
1248                             tem->tvs_r_cursor.col, credp, called_from);
1249                         tem->tvs_state = A_STATE_START;
1250                         return;
1251                 case 'p':       /* sunbow */
1252                         tem_safe_send_data(tem, credp, called_from);
1253                         /*
1254                          * Don't set anything if we are
1255                          * already 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                         tem_safe_cls(tem, credp, called_from);
1270                         tem->tvs_state = A_STATE_START;
1271                         return;
1272                 case 'q':       /* sunwob */
1273                         tem_safe_send_data(tem, credp, called_from);
1274                         /*
1275                          * Don't set anything if we are
1276                          * already where as we want to be.
1277                          */
1278                         if (!(tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE)) {
1279                                 tem->tvs_flags |= TEM_ATTR_SCREEN_REVERSE;
1280                                 /*
1281                                  * If we have switched the characters to be the
1282                                  * inverse from the screen, then switch them as
1283                                  * well to keep them the inverse of the screen.
1284                                  */
1285                                 if (!(tem->tvs_flags & TEM_ATTR_REVERSE))
1286                                         tem->tvs_flags |= TEM_ATTR_REVERSE;
1287                                 else
1288                                         tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1289                         }
1290 
1291                         tem_safe_cls(tem, credp, called_from);
1292                         tem->tvs_state = A_STATE_START;
1293                         return;
1294                 case 'r':       /* sunscrl */
1295                         /*
1296                          * Rule exception:  check for validity here.
1297                          */
1298                         tem->tvs_nscroll = tem->tvs_paramval;
1299                         if (tem->tvs_nscroll > tems.ts_c_dimension.height)
1300                                 tem->tvs_nscroll = tems.ts_c_dimension.height;
1301                         if (tem->tvs_nscroll < 0)
1302                                 tem->tvs_nscroll = 1;
1303                         tem->tvs_state = A_STATE_START;
1304                         return;
1305                 default:
1306                         tem_safe_getparams(tem, ch, credp, called_from);
1307                         return;
1308                 }
1309         }
1310 
1311         /* Previous char was <ESC> */
1312         if (ch == '[') {
1313                 tem->tvs_curparam = 0;
1314                 tem->tvs_paramval = 0;
1315                 tem->tvs_gotparam = B_FALSE;
1316                 /* clear the parameters */
1317                 for (i = 0; i < TEM_MAXPARAMS; i++)
1318                         tem->tvs_params[i] = -1;
1319                 tem->tvs_state = A_STATE_CSI;
1320         } else if (ch == ']') {
1321                 tem->tvs_curparam = 0;
1322                 tem->tvs_paramval = 0;
1323                 tem->tvs_gotparam = B_FALSE;
1324                 /* clear the parameters */
1325                 for (i = 0; i < TEM_MAXPARAMS; i++)
1326                         tem->tvs_params[i] = -1;
1327                 tem->tvs_state = A_STATE_OSC;
1328         } else if (ch == 'Q') { /* <ESC>Q ? */
1329                 tem->tvs_state = A_STATE_START;
1330         } else if (ch == 'C') { /* <ESC>C ? */
1331                 tem->tvs_state = A_STATE_START;
1332         } else {
1333                 tem->tvs_state = A_STATE_START;
1334                 if (ch == 'c') {
1335                         /* ESC c resets display */
1336                         tem_safe_reset_display(tem, credp, called_from,
1337                             B_TRUE, B_TRUE);
1338                 } else if (ch == 'H') {
1339                         /* ESC H sets a tab */
1340                         tem_safe_set_tab(tem);
1341                 } else if (ch == '7') {
1342                         /* ESC 7 Save Cursor position */
1343                         tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1344                         tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1345                 } else if (ch == '8') {
1346                         /* ESC 8 Restore Cursor position */
1347                         tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row,
1348                             tem->tvs_r_cursor.col, credp, called_from);
1349                 /* check for control chars */
1350                 } else if (ch < ' ') {
1351                         tem_safe_control(tem, ch, credp, called_from);
1352                 } else {
1353                         tem_safe_outch(tem, ch, credp, called_from);
1354                 }
1355         }
1356 }
1357 
1358 /* ARGSUSED */
1359 static void
1360 tem_safe_bell(struct tem_vt_state *tem, enum called_from called_from)
1361 {
1362         if (called_from == CALLED_FROM_STANDALONE)
1363                 (void) beep_polled(BEEP_CONSOLE);
1364         else
1365                 (void) beep(BEEP_CONSOLE);
1366 }
1367 
1368 
1369 static void
1370 tem_safe_scroll(struct tem_vt_state *tem, int start, int end, int count,
1371     int direction, cred_t *credp, enum called_from called_from)
1372 {
1373         int     row;
1374         int     lines_affected;
1375 
1376         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1377             called_from == CALLED_FROM_STANDALONE);
1378 
1379         lines_affected = end - start + 1;
1380         if (count > lines_affected)
1381                 count = lines_affected;
1382         if (count <= 0)
1383                 return;
1384 
1385         switch (direction) {
1386         case TEM_SCROLL_UP:
1387                 if (count < lines_affected) {
1388                         tem_safe_copy_area(tem, 0, start + count,
1389                             tems.ts_c_dimension.width - 1, end,
1390                             0, start, credp, called_from);
1391                 }
1392                 for (row = (end - count) + 1; row <= end; row++) {
1393                         tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1394                             row, 0, credp, called_from);
1395                 }
1396                 break;
1397 
1398         case TEM_SCROLL_DOWN:
1399                 if (count < lines_affected) {
1400                         tem_safe_copy_area(tem, 0, start,
1401                             tems.ts_c_dimension.width - 1,
1402                             end - count, 0, start + count,
1403                             credp, called_from);
1404                 }
1405                 for (row = start; row < start + count; row++) {
1406                         tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1407                             row, 0, credp, called_from);
1408                 }
1409                 break;
1410         }
1411 }
1412 
1413 static void
1414 tem_safe_copy_area(struct tem_vt_state *tem,
1415     screen_pos_t s_col, screen_pos_t s_row,
1416     screen_pos_t e_col, screen_pos_t e_row,
1417     screen_pos_t t_col, screen_pos_t t_row,
1418     cred_t *credp, enum called_from called_from)
1419 {
1420         int rows;
1421         int cols;
1422 
1423         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1424             called_from == CALLED_FROM_STANDALONE);
1425 
1426         if (s_col < 0 || s_row < 0 ||
1427             e_col < 0 || e_row < 0 ||
1428             t_col < 0 || t_row < 0 ||
1429             s_col >= tems.ts_c_dimension.width ||
1430             e_col >= tems.ts_c_dimension.width ||
1431             t_col >= tems.ts_c_dimension.width ||
1432             s_row >= tems.ts_c_dimension.height ||
1433             e_row >= tems.ts_c_dimension.height ||
1434             t_row >= tems.ts_c_dimension.height)
1435                 return;
1436 
1437         if (s_row > e_row || s_col > e_col)
1438                 return;
1439 
1440         rows = e_row - s_row + 1;
1441         cols = e_col - s_col + 1;
1442         if (t_row + rows > tems.ts_c_dimension.height ||
1443             t_col + cols > tems.ts_c_dimension.width)
1444                 return;
1445 
1446         tem_safe_virtual_copy(tem,
1447             s_col, s_row,
1448             e_col, e_row,
1449             t_col, t_row);
1450 
1451         if (!tem->tvs_isactive)
1452                 return;
1453 
1454         tem_safe_callback_copy(tem, s_col, s_row,
1455             e_col, e_row, t_col, t_row, credp, called_from);
1456 }
1457 
1458 static void
1459 tem_safe_clear_chars(struct tem_vt_state *tem, int count, screen_pos_t row,
1460     screen_pos_t col, cred_t *credp, enum called_from called_from)
1461 {
1462         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1463             called_from == CALLED_FROM_STANDALONE);
1464 
1465         if (row < 0 || row >= tems.ts_c_dimension.height ||
1466             col < 0 || col >= tems.ts_c_dimension.width ||
1467             count < 0)
1468                 return;
1469 
1470         /*
1471          * Note that very large values of "count" could cause col+count
1472          * to overflow, so we check "count" independently.
1473          */
1474         if (count > tems.ts_c_dimension.width ||
1475             col + count > tems.ts_c_dimension.width)
1476                 count = tems.ts_c_dimension.width - col;
1477 
1478         tem_safe_virtual_cls(tem, count, row, col);
1479 
1480         if (!tem->tvs_isactive)
1481                 return;
1482 
1483         tem_safe_callback_cls(tem, count, row, col, credp, called_from);
1484 }
1485 
1486 /*ARGSUSED*/
1487 void
1488 tem_safe_text_display(struct tem_vt_state *tem, term_char_t *string,
1489     int count, screen_pos_t row, screen_pos_t col,
1490     cred_t *credp, enum called_from called_from)
1491 {
1492         struct vis_consdisplay da;
1493         int i;
1494         tem_char_t c;
1495 
1496         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1497             called_from == CALLED_FROM_STANDALONE);
1498 
1499         da.data = (uint8_t *)&c;
1500         da.width = 1;
1501         da.row = row;
1502         da.col = col;
1503 
1504         for (i = 0; i < count; i++) {
1505                 tem_safe_get_color(&da.fg_color, &da.bg_color, string[i]);
1506                 c = TEM_CHAR(string[i].tc_char);
1507                 tems_safe_display(&da, credp, called_from);
1508                 da.col++;
1509         }
1510 }
1511 
1512 /*
1513  * This function is used to blit a rectangular color image,
1514  * unperturbed on the underlying framebuffer, to render
1515  * icons and pictures.  The data is a pixel pattern that
1516  * fills a rectangle bounded to the width and height parameters.
1517  * The color pixel data must to be pre-adjusted by the caller
1518  * for the current video depth.
1519  *
1520  * This function is unused now.
1521  */
1522 /*ARGSUSED*/
1523 static void
1524 tem_safe_image_display(struct tem_vt_state *tem, uchar_t *image,
1525     int height, int width, screen_pos_t row, screen_pos_t col,
1526     cred_t *credp, enum called_from called_from)
1527 {
1528         struct vis_consdisplay da;
1529 
1530         mutex_enter(&tems.ts_lock);
1531         mutex_enter(&tem->tvs_lock);
1532 
1533         da.data = image;
1534         da.width = (screen_size_t)width;
1535         da.height = (screen_size_t)height;
1536         da.row = row;
1537         da.col = col;
1538 
1539         tems_safe_display(&da, credp, called_from);
1540 
1541         mutex_exit(&tem->tvs_lock);
1542         mutex_exit(&tems.ts_lock);
1543 }
1544 
1545 /*ARGSUSED*/
1546 void
1547 tem_safe_text_copy(struct tem_vt_state *tem,
1548     screen_pos_t s_col, screen_pos_t s_row,
1549     screen_pos_t e_col, screen_pos_t e_row,
1550     screen_pos_t t_col, screen_pos_t t_row,
1551     cred_t *credp, enum called_from called_from)
1552 {
1553         struct vis_conscopy da;
1554 
1555         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1556             called_from == CALLED_FROM_STANDALONE);
1557 
1558         da.s_row = s_row;
1559         da.s_col = s_col;
1560         da.e_row = e_row;
1561         da.e_col = e_col;
1562         da.t_row = t_row;
1563         da.t_col = t_col;
1564 
1565         tems_safe_copy(&da, credp, called_from);
1566 }
1567 
1568 void
1569 tem_safe_text_cls(struct tem_vt_state *tem,
1570     int count, screen_pos_t row, screen_pos_t col, cred_t *credp,
1571     enum called_from called_from)
1572 {
1573         text_attr_t attr;
1574         term_char_t c;
1575         int i;
1576 
1577         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1578             called_from == CALLED_FROM_STANDALONE);
1579 
1580         tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
1581             TEM_ATTR_SCREEN_REVERSE);
1582         c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
1583 
1584         if (count > tems.ts_c_dimension.width ||
1585             col + count > tems.ts_c_dimension.width)
1586                 count = tems.ts_c_dimension.width - col;
1587 
1588         for (i = 0; i < count; i++)
1589                 tems.ts_blank_line[i] = c;
1590 
1591         tem_safe_text_display(tem, tems.ts_blank_line, count, row, col,
1592                 credp, called_from);
1593 }
1594 
1595 void
1596 tem_safe_pix_display(struct tem_vt_state *tem,
1597     term_char_t *string, int count,
1598     screen_pos_t row, screen_pos_t col,
1599     cred_t *credp, enum called_from called_from)
1600 {
1601         struct vis_consdisplay da;
1602         int     i;
1603 
1604         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1605             called_from == CALLED_FROM_STANDALONE);
1606 
1607         da.data = (uchar_t *)tem->tvs_pix_data;
1608         da.width = (screen_size_t)tems.ts_font.vf_width;
1609         da.height = (screen_size_t)tems.ts_font.vf_height;
1610         da.row = (row * da.height) + tems.ts_p_offset.y;
1611         da.col = (col * da.width) + tems.ts_p_offset.x;
1612 
1613         for (i = 0; i < count; i++) {
1614                 tem_safe_callback_bit2pix(tem, string[i]);
1615                 tems_safe_display(&da, credp, called_from);
1616                 da.col += da.width;
1617         }
1618 }
1619 
1620 void
1621 tem_safe_pix_copy(struct tem_vt_state *tem,
1622     screen_pos_t s_col, screen_pos_t s_row,
1623     screen_pos_t e_col, screen_pos_t e_row,
1624     screen_pos_t t_col, screen_pos_t t_row,
1625     cred_t *credp,
1626     enum called_from called_from)
1627 {
1628         struct vis_conscopy ma;
1629         static boolean_t need_clear = B_TRUE;
1630 
1631         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1632             called_from == CALLED_FROM_STANDALONE);
1633 
1634         if (need_clear && tem->tvs_first_line > 0) {
1635                 /*
1636                  * Clear OBP output above our kernel console term
1637                  * when our kernel console term begins to scroll up,
1638                  * we hope it is user friendly.
1639                  * (Also see comments on tem_safe_pix_clear_prom_output)
1640                  *
1641                  * This is only one time call.
1642                  */
1643                 tem_safe_pix_clear_prom_output(tem, credp, called_from);
1644         }
1645         need_clear = B_FALSE;
1646 
1647         ma.s_row = s_row * tems.ts_font.vf_height + tems.ts_p_offset.y;
1648         ma.e_row = (e_row + 1) * tems.ts_font.vf_height +
1649             tems.ts_p_offset.y - 1;
1650         ma.t_row = t_row * tems.ts_font.vf_height + tems.ts_p_offset.y;
1651 
1652         /*
1653          * Check if we're in process of clearing OBP's columns area,
1654          * which only happens when term scrolls up a whole line.
1655          */
1656         if (tem->tvs_first_line > 0 && t_row < s_row && t_col == 0 &&
1657             e_col == tems.ts_c_dimension.width - 1) {
1658                 /*
1659                  * We need to clear OBP's columns area outside our kernel
1660                  * console term. So that we set ma.e_col to entire row here.
1661                  */
1662                 ma.s_col = s_col * tems.ts_font.vf_width;
1663                 ma.e_col = tems.ts_p_dimension.width - 1;
1664 
1665                 ma.t_col = t_col * tems.ts_font.vf_width;
1666         } else {
1667                 ma.s_col = s_col * tems.ts_font.vf_width + tems.ts_p_offset.x;
1668                 ma.e_col = (e_col + 1) * tems.ts_font.vf_width +
1669                     tems.ts_p_offset.x - 1;
1670                 ma.t_col = t_col * tems.ts_font.vf_width + tems.ts_p_offset.x;
1671         }
1672 
1673         tems_safe_copy(&ma, credp, called_from);
1674 
1675         if (tem->tvs_first_line > 0 && t_row < s_row) {
1676                 /* We have scrolled up (s_row - t_row) rows. */
1677                 tem->tvs_first_line -= (s_row - t_row);
1678                 if (tem->tvs_first_line <= 0) {
1679                         /* All OBP rows have been cleared. */
1680                         tem->tvs_first_line = 0;
1681                 }
1682         }
1683 
1684 }
1685 
1686 void
1687 tem_safe_pix_bit2pix(struct tem_vt_state *tem, term_char_t c)
1688 {
1689         text_color_t fg, bg;
1690         void (*fp)(struct tem_vt_state *, tem_char_t,
1691             unsigned char, unsigned char);
1692 
1693         tem_safe_get_color(&fg, &bg, c);
1694         switch (tems.ts_pdepth) {
1695         case 4:
1696                 fp = bit_to_pix4;
1697                 break;
1698         case 8:
1699                 fp = bit_to_pix8;
1700                 break;
1701         case 15:
1702         case 16:
1703                 fp = bit_to_pix16;
1704                 break;
1705         case 24:
1706                 fp = bit_to_pix24;
1707                 break;
1708         case 32:
1709                 fp = bit_to_pix32;
1710                 break;
1711         default:
1712                 return;
1713         }
1714 
1715         fp(tem, c.tc_char, fg, bg);
1716 }
1717 
1718 
1719 /*
1720  * This function only clears count of columns in one row
1721  */
1722 void
1723 tem_safe_pix_cls(struct tem_vt_state *tem, int count,
1724     screen_pos_t row, screen_pos_t col, cred_t *credp,
1725     enum called_from called_from)
1726 {
1727         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1728             called_from == CALLED_FROM_STANDALONE);
1729 
1730         tem_safe_pix_cls_range(tem, row, 1, tems.ts_p_offset.y,
1731             col, count, tems.ts_p_offset.x, B_FALSE, credp, called_from);
1732 }
1733 
1734 /*
1735  * This function clears OBP output above our kernel console term area
1736  * because OBP's term may have a bigger terminal window than that of
1737  * our kernel console term. So we need to clear OBP output garbage outside
1738  * of our kernel console term at a proper time, which is when the first
1739  * row output of our kernel console term scrolls at the first screen line.
1740  *
1741  *      _________________________________
1742  *      |   _____________________       |  ---> OBP's bigger term window
1743  *      |   |                   |       |
1744  *      |___|                   |       |
1745  *      | | |                   |       |
1746  *      | | |                   |       |
1747  *      |_|_|___________________|_______|
1748  *        | |                   |          ---> first line
1749  *        | |___________________|---> our kernel console term window
1750  *        |
1751  *        |---> columns area to be cleared
1752  *
1753  * This function only takes care of the output above our kernel console term,
1754  * and tem_prom_scroll_up takes care of columns area outside of our kernel
1755  * console term.
1756  */
1757 static void
1758 tem_safe_pix_clear_prom_output(struct tem_vt_state *tem, cred_t *credp,
1759     enum called_from called_from)
1760 {
1761         int     nrows, ncols, width, height, offset;
1762 
1763         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1764             called_from == CALLED_FROM_STANDALONE);
1765 
1766         width = tems.ts_font.vf_width;
1767         height = tems.ts_font.vf_height;
1768         offset = tems.ts_p_offset.y % height;
1769 
1770         nrows = tems.ts_p_offset.y / height;
1771         ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
1772 
1773         if (nrows > 0)
1774                 tem_safe_pix_cls_range(tem, 0, nrows, offset, 0, ncols, 0,
1775                     B_FALSE, credp, called_from);
1776 }
1777 
1778 /*
1779  * clear the whole screen for pixel mode, just clear the
1780  * physical screen.
1781  */
1782 void
1783 tem_safe_pix_clear_entire_screen(struct tem_vt_state *tem, cred_t *credp,
1784     enum called_from called_from)
1785 {
1786         struct vis_consclear cl;
1787         text_color_t fg_color;
1788         text_color_t bg_color;
1789         text_attr_t attr;
1790         term_char_t c;
1791         int nrows, ncols, width, height;
1792 
1793         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1794             called_from == CALLED_FROM_STANDALONE);
1795 
1796         /* call driver first, if error, clear terminal area */
1797         tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
1798             TEM_ATTR_SCREEN_REVERSE);
1799         c.tc_char = TEM_ATTR(attr);
1800 
1801         tem_safe_get_color(&fg_color, &bg_color, c);
1802         cl.bg_color = bg_color;
1803         if (tems_cls_layered(&cl, credp) == 0)
1804                 return;
1805 
1806         width = tems.ts_font.vf_width;
1807         height = tems.ts_font.vf_height;
1808 
1809         nrows = (tems.ts_p_dimension.height + (height - 1))/ height;
1810         ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
1811 
1812         tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y, 0, ncols,
1813             tems.ts_p_offset.x, B_FALSE, credp, called_from);
1814 
1815         /*
1816          * Since the whole screen is cleared, we don't need
1817          * to clear OBP output later.
1818          */
1819         if (tem->tvs_first_line > 0)
1820                 tem->tvs_first_line = 0;
1821 }
1822 
1823 /*
1824  * clear the whole screen, including the virtual screen buffer,
1825  * and reset the cursor to start point.
1826  */
1827 static void
1828 tem_safe_cls(struct tem_vt_state *tem,
1829     cred_t *credp, enum called_from called_from)
1830 {
1831         int     row;
1832 
1833         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1834             called_from == CALLED_FROM_STANDALONE);
1835 
1836         if (tems.ts_display_mode == VIS_TEXT) {
1837                 for (row = 0; row < tems.ts_c_dimension.height; row++) {
1838                         tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1839                             row, 0, credp, called_from);
1840                 }
1841                 tem->tvs_c_cursor.row = 0;
1842                 tem->tvs_c_cursor.col = 0;
1843                 tem_safe_align_cursor(tem);
1844                 return;
1845         }
1846 
1847         ASSERT(tems.ts_display_mode == VIS_PIXEL);
1848 
1849         for (row = 0; row < tems.ts_c_dimension.height; row++) {
1850                 tem_safe_virtual_cls(tem, tems.ts_c_dimension.width, row, 0);
1851         }
1852         tem->tvs_c_cursor.row = 0;
1853         tem->tvs_c_cursor.col = 0;
1854         tem_safe_align_cursor(tem);
1855 
1856         if (!tem->tvs_isactive)
1857                 return;
1858 
1859         tem_safe_pix_clear_entire_screen(tem, credp, called_from);
1860 }
1861 
1862 static void
1863 tem_safe_back_tab(struct tem_vt_state *tem,
1864     cred_t *credp, enum called_from called_from)
1865 {
1866         int     i;
1867         screen_pos_t    tabstop;
1868 
1869         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1870             called_from == CALLED_FROM_STANDALONE);
1871 
1872         tabstop = 0;
1873 
1874         for (i = tem->tvs_ntabs - 1; i >= 0; i--) {
1875                 if (tem->tvs_tabs[i] < tem->tvs_c_cursor.col) {
1876                         tabstop = tem->tvs_tabs[i];
1877                         break;
1878                 }
1879         }
1880 
1881         tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row,
1882             tabstop, credp, called_from);
1883 }
1884 
1885 static void
1886 tem_safe_tab(struct tem_vt_state *tem,
1887     cred_t *credp, enum called_from called_from)
1888 {
1889         int     i;
1890         screen_pos_t    tabstop;
1891 
1892         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1893             called_from == CALLED_FROM_STANDALONE);
1894 
1895         tabstop = tems.ts_c_dimension.width - 1;
1896 
1897         for (i = 0; i < tem->tvs_ntabs; i++) {
1898                 if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
1899                         tabstop = tem->tvs_tabs[i];
1900                         break;
1901                 }
1902         }
1903 
1904         tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row,
1905             tabstop, credp, called_from);
1906 }
1907 
1908 static void
1909 tem_safe_set_tab(struct tem_vt_state *tem)
1910 {
1911         int     i;
1912         int     j;
1913 
1914         if (tem->tvs_ntabs == TEM_MAXTAB)
1915                 return;
1916         if (tem->tvs_ntabs == 0 ||
1917             tem->tvs_tabs[tem->tvs_ntabs] < tem->tvs_c_cursor.col) {
1918                         tem->tvs_tabs[tem->tvs_ntabs++] = tem->tvs_c_cursor.col;
1919                         return;
1920         }
1921         for (i = 0; i < tem->tvs_ntabs; i++) {
1922                 if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col)
1923                         return;
1924                 if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
1925                         for (j = tem->tvs_ntabs - 1; j >= i; j--)
1926                                 tem->tvs_tabs[j+ 1] = tem->tvs_tabs[j];
1927                         tem->tvs_tabs[i] = tem->tvs_c_cursor.col;
1928                         tem->tvs_ntabs++;
1929                         return;
1930                 }
1931         }
1932 }
1933 
1934 static void
1935 tem_safe_clear_tabs(struct tem_vt_state *tem, int action)
1936 {
1937         int     i;
1938         int     j;
1939 
1940         switch (action) {
1941         case 3: /* clear all tabs */
1942                 tem->tvs_ntabs = 0;
1943                 break;
1944         case 0: /* clr tab at cursor */
1945 
1946                 for (i = 0; i < tem->tvs_ntabs; i++) {
1947                         if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) {
1948                                 tem->tvs_ntabs--;
1949                                 for (j = i; j < tem->tvs_ntabs; j++)
1950                                         tem->tvs_tabs[j] = tem->tvs_tabs[j + 1];
1951                                 return;
1952                         }
1953                 }
1954                 break;
1955         }
1956 }
1957 
1958 static void
1959 tem_safe_mv_cursor(struct tem_vt_state *tem, int row, int col,
1960     cred_t *credp, enum called_from called_from)
1961 {
1962         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1963             called_from == CALLED_FROM_STANDALONE);
1964 
1965         /*
1966          * Sanity check and bounds enforcement.  Out of bounds requests are
1967          * clipped to the screen boundaries.  This seems to be what SPARC
1968          * does.
1969          */
1970         if (row < 0)
1971                 row = 0;
1972         if (row >= tems.ts_c_dimension.height)
1973                 row = tems.ts_c_dimension.height - 1;
1974         if (col < 0)
1975                 col = 0;
1976         if (col >= tems.ts_c_dimension.width)
1977                 col = tems.ts_c_dimension.width - 1;
1978 
1979         tem_safe_send_data(tem, credp, called_from);
1980         tem->tvs_c_cursor.row = (screen_pos_t)row;
1981         tem->tvs_c_cursor.col = (screen_pos_t)col;
1982         tem_safe_align_cursor(tem);
1983 }
1984 
1985 /* ARGSUSED */
1986 void
1987 tem_safe_reset_emulator(struct tem_vt_state *tem,
1988     cred_t *credp, enum called_from called_from,
1989     boolean_t init_color)
1990 {
1991         int j;
1992 
1993         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1994             called_from == CALLED_FROM_STANDALONE);
1995 
1996         tem->tvs_c_cursor.row = 0;
1997         tem->tvs_c_cursor.col = 0;
1998         tem->tvs_r_cursor.row = 0;
1999         tem->tvs_r_cursor.col = 0;
2000         tem->tvs_s_cursor.row = 0;
2001         tem->tvs_s_cursor.col = 0;
2002         tem->tvs_outindex = 0;
2003         tem->tvs_state = A_STATE_START;
2004         tem->tvs_gotparam = B_FALSE;
2005         tem->tvs_curparam = 0;
2006         tem->tvs_paramval = 0;
2007         tem->tvs_nscroll = 1;
2008 
2009         if (init_color) {
2010                 /* use initial settings */
2011                 tem->tvs_fg_color = tems.ts_init_color.fg_color;
2012                 tem->tvs_bg_color = tems.ts_init_color.bg_color;
2013                 tem->tvs_flags = tems.ts_init_color.a_flags;
2014         }
2015 
2016         /*
2017          * set up the initial tab stops
2018          */
2019         tem->tvs_ntabs = 0;
2020         for (j = 8; j < tems.ts_c_dimension.width; j += 8)
2021                 tem->tvs_tabs[tem->tvs_ntabs++] = (screen_pos_t)j;
2022 
2023         for (j = 0; j < TEM_MAXPARAMS; j++)
2024                 tem->tvs_params[j] = 0;
2025 }
2026 
2027 void
2028 tem_safe_reset_display(struct tem_vt_state *tem,
2029     cred_t *credp, enum called_from called_from,
2030     boolean_t clear_txt, boolean_t init_color)
2031 {
2032         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2033             called_from == CALLED_FROM_STANDALONE);
2034 
2035         tem_safe_reset_emulator(tem, credp, called_from, init_color);
2036 
2037         if (clear_txt) {
2038                 if (tem->tvs_isactive)
2039                         tem_safe_callback_cursor(tem,
2040                             VIS_HIDE_CURSOR, credp, called_from);
2041 
2042                 tem_safe_cls(tem, credp, called_from);
2043 
2044                 if (tem->tvs_isactive)
2045                         tem_safe_callback_cursor(tem,
2046                             VIS_DISPLAY_CURSOR, credp, called_from);
2047         }
2048 }
2049 
2050 static void
2051 tem_safe_shift(
2052         struct tem_vt_state *tem,
2053         int count,
2054         int direction,
2055         cred_t *credp,
2056         enum called_from called_from)
2057 {
2058         int rest_of_line;
2059 
2060         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2061             called_from == CALLED_FROM_STANDALONE);
2062 
2063         rest_of_line = tems.ts_c_dimension.width - tem->tvs_c_cursor.col;
2064         if (count > rest_of_line)
2065                 count = rest_of_line;
2066 
2067         if (count <= 0)
2068                 return;
2069 
2070         switch (direction) {
2071         case TEM_SHIFT_LEFT:
2072                 if (count < rest_of_line) {
2073                         tem_safe_copy_area(tem,
2074                             tem->tvs_c_cursor.col + count,
2075                             tem->tvs_c_cursor.row,
2076                             tems.ts_c_dimension.width - 1,
2077                             tem->tvs_c_cursor.row,
2078                             tem->tvs_c_cursor.col,
2079                             tem->tvs_c_cursor.row,
2080                             credp, called_from);
2081                 }
2082 
2083                 tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
2084                     (tems.ts_c_dimension.width - count), credp,
2085                     called_from);
2086                 break;
2087         case TEM_SHIFT_RIGHT:
2088                 if (count < rest_of_line) {
2089                         tem_safe_copy_area(tem,
2090                             tem->tvs_c_cursor.col,
2091                             tem->tvs_c_cursor.row,
2092                             tems.ts_c_dimension.width - count - 1,
2093                             tem->tvs_c_cursor.row,
2094                             tem->tvs_c_cursor.col + count,
2095                             tem->tvs_c_cursor.row,
2096                             credp, called_from);
2097                 }
2098 
2099                 tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
2100                     tem->tvs_c_cursor.col, credp, called_from);
2101                 break;
2102         }
2103 }
2104 
2105 void
2106 tem_safe_text_cursor(struct tem_vt_state *tem, short action,
2107     cred_t *credp, enum called_from called_from)
2108 {
2109         struct vis_conscursor   ca;
2110 
2111         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2112             called_from == CALLED_FROM_STANDALONE);
2113 
2114         ca.row = tem->tvs_c_cursor.row;
2115         ca.col = tem->tvs_c_cursor.col;
2116         ca.action = action;
2117 
2118         tems_safe_cursor(&ca, credp, called_from);
2119 
2120         if (action == VIS_GET_CURSOR) {
2121                 tem->tvs_c_cursor.row = ca.row;
2122                 tem->tvs_c_cursor.col = ca.col;
2123         }
2124 }
2125 
2126 void
2127 tem_safe_pix_cursor(struct tem_vt_state *tem, short action,
2128     cred_t *credp, enum called_from called_from)
2129 {
2130         struct vis_conscursor   ca;
2131         uint32_t color;
2132         text_color_t fg, bg;
2133         term_char_t c;
2134         text_attr_t attr;
2135 
2136         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2137             called_from == CALLED_FROM_STANDALONE);
2138 
2139         ca.row = tem->tvs_c_cursor.row * tems.ts_font.vf_height +
2140             tems.ts_p_offset.y;
2141         ca.col = tem->tvs_c_cursor.col * tems.ts_font.vf_width +
2142             tems.ts_p_offset.x;
2143         ca.width = (screen_size_t)tems.ts_font.vf_width;
2144         ca.height = (screen_size_t)tems.ts_font.vf_height;
2145 
2146         tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2147             TEM_ATTR_REVERSE);
2148         c.tc_char = TEM_ATTR(attr);
2149 
2150         tem_safe_get_color(&fg, &bg, c);
2151 
2152         switch (tems.ts_pdepth) {
2153         case 4:
2154                 ca.fg_color.mono = fg;
2155                 ca.bg_color.mono = bg;
2156                 break;
2157         case 8:
2158                 ca.fg_color.mono = tems.ts_color_map(fg);
2159                 ca.bg_color.mono = tems.ts_color_map(bg);
2160                 break;
2161         case 15:
2162         case 16:
2163                 color = tems.ts_color_map(fg);
2164                 ca.fg_color.sixteen[0] = (color >> 8) & 0xFF;
2165                 ca.fg_color.sixteen[1] = color & 0xFF;
2166                 color = tems.ts_color_map(bg);
2167                 ca.bg_color.sixteen[0] = (color >> 8) & 0xFF;
2168                 ca.bg_color.sixteen[1] = color & 0xFF;
2169                 break;
2170         case 24:
2171         case 32:
2172 #ifdef _HAVE_TEM_FIRMWARE
2173                 /* Keeping this block to support old binary only drivers */
2174                 if (tem->tvs_flags & TEM_ATTR_REVERSE) {
2175                         ca.fg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED;
2176                         ca.fg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN;
2177                         ca.fg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE;
2178 
2179                         ca.bg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED;
2180                         ca.bg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN;
2181                         ca.bg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE;
2182                 } else {
2183                         ca.fg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED;
2184                         ca.fg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN;
2185                         ca.fg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE;
2186 
2187                         ca.bg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED;
2188                         ca.bg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN;
2189                         ca.bg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE;
2190                 }
2191 #else
2192                 color = tems.ts_color_map(fg);
2193                 ca.fg_color.twentyfour[0] = (color >> 16) & 0xFF;
2194                 ca.fg_color.twentyfour[1] = (color >> 8) & 0xFF;
2195                 ca.fg_color.twentyfour[2] = color & 0xFF;
2196                 color = tems.ts_color_map(bg);
2197                 ca.bg_color.twentyfour[0] = (color >> 16) & 0xFF;
2198                 ca.bg_color.twentyfour[1] = (color >> 8) & 0xFF;
2199                 ca.bg_color.twentyfour[2] = color & 0xFF;
2200                 break;
2201 #endif
2202         }
2203 
2204         ca.action = action;
2205 
2206         tems_safe_cursor(&ca, credp, called_from);
2207 
2208         if (action == VIS_GET_CURSOR) {
2209                 tem->tvs_c_cursor.row = 0;
2210                 tem->tvs_c_cursor.col = 0;
2211 
2212                 if (ca.row != 0) {
2213                         tem->tvs_c_cursor.row = (ca.row - tems.ts_p_offset.y) /
2214                             tems.ts_font.vf_height;
2215                 }
2216                 if (ca.col != 0) {
2217                         tem->tvs_c_cursor.col = (ca.col - tems.ts_p_offset.x) /
2218                             tems.ts_font.vf_width;
2219                 }
2220         }
2221 }
2222 
2223 static void
2224 bit_to_pix4(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color,
2225     text_color_t bg_color)
2226 {
2227         uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2228         font_bit_to_pix4(&tems.ts_font, dest, c, fg_color, bg_color);
2229 }
2230 
2231 static void
2232 bit_to_pix8(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color,
2233     text_color_t bg_color)
2234 {
2235         uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2236 
2237         fg_color = (text_color_t)tems.ts_color_map(fg_color);
2238         bg_color = (text_color_t)tems.ts_color_map(bg_color);
2239         font_bit_to_pix8(&tems.ts_font, dest, c, fg_color, bg_color);
2240 }
2241 
2242 static void
2243 bit_to_pix16(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color4,
2244     text_color_t bg_color4)
2245 {
2246         uint16_t fg_color16, bg_color16;
2247         uint16_t *dest;
2248 
2249         ASSERT(fg_color4 < 16 && bg_color4 < 16);
2250 
2251         fg_color16 = (uint16_t)tems.ts_color_map(fg_color4);
2252         bg_color16 = (uint16_t)tems.ts_color_map(bg_color4);
2253 
2254         dest = (uint16_t *)tem->tvs_pix_data;
2255         font_bit_to_pix16(&tems.ts_font, dest, c, fg_color16, bg_color16);
2256 }
2257 
2258 static void
2259 bit_to_pix24(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color4,
2260     text_color_t bg_color4)
2261 {
2262         uint32_t fg_color32, bg_color32;
2263         uint8_t *dest;
2264 
2265         ASSERT(fg_color4 < 16 && bg_color4 < 16);
2266 
2267 #ifdef _HAVE_TEM_FIRMWARE
2268         fg_color32 = PIX4TO32(fg_color4);
2269         bg_color32 = PIX4TO32(bg_color4);
2270 #else
2271         fg_color32 = tems.ts_color_map(fg_color4);
2272         bg_color32 = tems.ts_color_map(bg_color4);
2273 #endif
2274 
2275         dest = (uint8_t *)tem->tvs_pix_data;
2276         font_bit_to_pix24(&tems.ts_font, dest, c, fg_color32, bg_color32);
2277 }
2278 
2279 static void
2280 bit_to_pix32(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color4,
2281     text_color_t bg_color4)
2282 {
2283         uint32_t fg_color32, bg_color32, *dest;
2284 
2285         ASSERT(fg_color4 < 16 && bg_color4 < 16);
2286 
2287 #ifdef _HAVE_TEM_FIRMWARE
2288         fg_color32 = PIX4TO32(fg_color4);
2289         bg_color32 = PIX4TO32(bg_color4);
2290 #else
2291         fg_color32 = ((uint32_t)0xFF << 24) | tems.ts_color_map(fg_color4);
2292         bg_color32 = ((uint32_t)0xFF << 24) | tems.ts_color_map(bg_color4);
2293 #endif
2294 
2295         dest = (uint32_t *)tem->tvs_pix_data;
2296         font_bit_to_pix32(&tems.ts_font, dest, c, fg_color32, bg_color32);
2297 }
2298 
2299 /*
2300  * flag: TEM_ATTR_SCREEN_REVERSE or TEM_ATTR_REVERSE
2301  */
2302 void
2303 tem_safe_get_attr(struct tem_vt_state *tem, text_color_t *fg,
2304     text_color_t *bg, text_attr_t *attr, uint8_t flag)
2305 {
2306         if (tem->tvs_flags & flag) {
2307                 *fg = tem->tvs_bg_color;
2308                 *bg = tem->tvs_fg_color;
2309         } else {
2310                 *fg = tem->tvs_fg_color;
2311                 *bg = tem->tvs_bg_color;
2312         }
2313 
2314         if (attr == NULL)
2315                 return;
2316 
2317         *attr = tem->tvs_flags;
2318 }
2319 
2320 static void
2321 tem_safe_get_color(text_color_t *fg, text_color_t *bg, term_char_t c)
2322 {
2323         if (TEM_CHAR_ATTR(c.tc_char) & (TEM_ATTR_BRIGHT_FG | TEM_ATTR_BOLD))
2324                 *fg = brt_xlate[c.tc_fg_color];
2325         else
2326                 *fg = dim_xlate[c.tc_fg_color];
2327 
2328         if (TEM_CHAR_ATTR(c.tc_char) & TEM_ATTR_BRIGHT_BG)
2329                 *bg = brt_xlate[c.tc_bg_color];
2330         else
2331                 *bg = dim_xlate[c.tc_bg_color];
2332 }
2333 
2334 /*
2335  * Clear a rectangle of screen for pixel mode.
2336  *
2337  * arguments:
2338  *    row:      start row#
2339  *    nrows:    the number of rows to clear
2340  *    offset_y: the offset of height in pixels to begin clear
2341  *    col:      start col#
2342  *    ncols:    the number of cols to clear
2343  *    offset_x: the offset of width in pixels to begin clear
2344  *    scroll_up: whether this function is called during sroll up,
2345  *               which is called only once.
2346  */
2347 void
2348 tem_safe_pix_cls_range(struct tem_vt_state *tem,
2349     screen_pos_t row, int nrows, int offset_y,
2350     screen_pos_t col, int ncols, int offset_x,
2351     boolean_t sroll_up, cred_t *credp,
2352     enum called_from called_from)
2353 {
2354         struct vis_consdisplay da;
2355         int     i, j;
2356         int     row_add = 0;
2357         term_char_t c;
2358         text_attr_t attr;
2359 
2360         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2361             called_from == CALLED_FROM_STANDALONE);
2362 
2363         if (sroll_up)
2364                 row_add = tems.ts_c_dimension.height - 1;
2365 
2366         da.width = (screen_size_t)tems.ts_font.vf_width;
2367         da.height = (screen_size_t)tems.ts_font.vf_height;
2368 
2369         tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2370             TEM_ATTR_SCREEN_REVERSE);
2371         /* Make sure we will not draw underlines */
2372         c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
2373 
2374         tem_safe_callback_bit2pix(tem, c);
2375         da.data = (uchar_t *)tem->tvs_pix_data;
2376 
2377         for (i = 0; i < nrows; i++, row++) {
2378                 da.row = (row + row_add) * da.height + offset_y;
2379                 da.col = col * da.width + offset_x;
2380                 for (j = 0; j < ncols; j++) {
2381                         tems_safe_display(&da, credp, called_from);
2382                         da.col += da.width;
2383                 }
2384         }
2385 }
2386 
2387 /*
2388  * virtual screen operations
2389  */
2390 static void
2391 tem_safe_virtual_display(struct tem_vt_state *tem, term_char_t *string,
2392     int count, screen_pos_t row, screen_pos_t col)
2393 {
2394         int i, width;
2395         term_char_t *addr;
2396 
2397         if (row < 0 || row >= tems.ts_c_dimension.height ||
2398             col < 0 || col >= tems.ts_c_dimension.width ||
2399             col + count > tems.ts_c_dimension.width)
2400                 return;
2401 
2402         width = tems.ts_c_dimension.width;
2403         addr = tem->tvs_screen_buf + (row * width + col);
2404         for (i = 0; i < count; i++) {
2405                 *addr++ = string[i];
2406         }
2407 }
2408 
2409 static void
2410 i_virtual_copy_tem_chars(term_char_t *base,
2411     screen_pos_t s_col, screen_pos_t s_row,
2412     screen_pos_t e_col, screen_pos_t e_row,
2413     screen_pos_t t_col, screen_pos_t t_row)
2414 {
2415         term_char_t     *from;
2416         term_char_t     *to;
2417         int             cnt;
2418         screen_size_t chars_per_row;
2419         term_char_t     *to_row_start;
2420         term_char_t     *from_row_start;
2421         screen_size_t   rows_to_move;
2422         int             cols = tems.ts_c_dimension.width;
2423 
2424         chars_per_row = e_col - s_col + 1;
2425         rows_to_move = e_row - s_row + 1;
2426 
2427         to_row_start = base + ((t_row * cols) + t_col);
2428         from_row_start = base + ((s_row * cols) + s_col);
2429 
2430         if (to_row_start < from_row_start) {
2431                 while (rows_to_move-- > 0) {
2432                         to = to_row_start;
2433                         from = from_row_start;
2434                         to_row_start += cols;
2435                         from_row_start += cols;
2436                         for (cnt = chars_per_row; cnt-- > 0; )
2437                                 *to++ = *from++;
2438                 }
2439         } else {
2440                 /*
2441                  * Offset to the end of the region and copy backwards.
2442                  */
2443                 cnt = rows_to_move * cols + chars_per_row;
2444                 to_row_start += cnt;
2445                 from_row_start += cnt;
2446 
2447                 while (rows_to_move-- > 0) {
2448                         to_row_start -= cols;
2449                         from_row_start -= cols;
2450                         to = to_row_start;
2451                         from = from_row_start;
2452                         for (cnt = chars_per_row; cnt-- > 0; )
2453                                 *--to = *--from;
2454                 }
2455         }
2456 }
2457 
2458 static void
2459 tem_safe_virtual_copy(struct tem_vt_state *tem,
2460     screen_pos_t s_col, screen_pos_t s_row,
2461     screen_pos_t e_col, screen_pos_t e_row,
2462     screen_pos_t t_col, screen_pos_t t_row)
2463 {
2464         screen_size_t chars_per_row;
2465         screen_size_t   rows_to_move;
2466         int             rows = tems.ts_c_dimension.height;
2467         int             cols = tems.ts_c_dimension.width;
2468 
2469         if (s_col < 0 || s_col >= cols ||
2470             s_row < 0 || s_row >= rows ||
2471             e_col < 0 || e_col >= cols ||
2472             e_row < 0 || e_row >= rows ||
2473             t_col < 0 || t_col >= cols ||
2474             t_row < 0 || t_row >= rows ||
2475             s_col > e_col ||
2476             s_row > e_row)
2477                 return;
2478 
2479         chars_per_row = e_col - s_col + 1;
2480         rows_to_move = e_row - s_row + 1;
2481 
2482         /* More sanity checks. */
2483         if (t_row + rows_to_move > rows ||
2484             t_col + chars_per_row > cols)
2485                 return;
2486 
2487         i_virtual_copy_tem_chars(tem->tvs_screen_buf, s_col, s_row,
2488             e_col, e_row, t_col, t_row);
2489 }
2490 
2491 static void
2492 tem_safe_virtual_cls(struct tem_vt_state *tem,
2493     int count, screen_pos_t row, screen_pos_t col)
2494 {
2495         int i;
2496         text_attr_t attr;
2497         term_char_t c;
2498 
2499         tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2500             TEM_ATTR_SCREEN_REVERSE);
2501         c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
2502 
2503         for (i = 0; i < tems.ts_c_dimension.width; i++)
2504                 tems.ts_blank_line[i] = c;
2505 
2506         tem_safe_virtual_display(tem, tems.ts_blank_line, count, row, col);
2507 }
2508 
2509 /*
2510  * only blank screen, not clear our screen buffer
2511  */
2512 void
2513 tem_safe_blank_screen(struct tem_vt_state *tem, cred_t *credp,
2514     enum called_from called_from)
2515 {
2516         int     row;
2517 
2518         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2519             called_from == CALLED_FROM_STANDALONE);
2520 
2521         if (tems.ts_display_mode == VIS_PIXEL) {
2522                 tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2523                 return;
2524         }
2525 
2526         for (row = 0; row < tems.ts_c_dimension.height; row++) {
2527                 tem_safe_callback_cls(tem,
2528                     tems.ts_c_dimension.width,
2529                     row, 0, credp, called_from);
2530         }
2531 }
2532 
2533 /*
2534  * unblank screen with associated tem from its screen buffer
2535  */
2536 void
2537 tem_safe_unblank_screen(struct tem_vt_state *tem, cred_t *credp,
2538     enum called_from called_from)
2539 {
2540         int     row;
2541 
2542         ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2543             called_from == CALLED_FROM_STANDALONE);
2544 
2545         if (tems.ts_display_mode == VIS_PIXEL)
2546                 tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2547 
2548         tem_safe_callback_cursor(tem, VIS_HIDE_CURSOR, credp, called_from);
2549 
2550         /*
2551          * Display data in tvs_screen_buf to the actual framebuffer in a
2552          * row by row way.
2553          * When dealing with one row, output data with the same foreground
2554          * and background color all together.
2555          */
2556         for (row = 0; row < tems.ts_c_dimension.height; row++) {
2557                 tem_safe_callback_display(tem, tem->tvs_screen_rows[row],
2558                     tems.ts_c_dimension.width, row, 0, credp, called_from);
2559         }
2560 
2561         tem_safe_callback_cursor(tem, VIS_DISPLAY_CURSOR, credp, called_from);
2562 }