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