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