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