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