summaryrefslogtreecommitdiff
path: root/usr/src/boot/common/tem.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/boot/common/tem.c')
-rw-r--r--usr/src/boot/common/tem.c2952
1 files changed, 2952 insertions, 0 deletions
diff --git a/usr/src/boot/common/tem.c b/usr/src/boot/common/tem.c
new file mode 100644
index 0000000000..2798f2883d
--- /dev/null
+++ b/usr/src/boot/common/tem.c
@@ -0,0 +1,2952 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ * Copyright 2021 Toomas Soome <tsoome@me.com>
+ */
+
+/*
+ * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and
+ * the like.
+ *
+ * How Virtual Terminal Emulator Works:
+ *
+ * Every virtual terminal is associated with a tem_vt_state structure
+ * and maintains a virtual screen buffer in tvs_screen_buf, which contains
+ * all the characters which should be shown on the physical screen when
+ * the terminal is activated.
+ *
+ * Data written to a virtual terminal is composed of characters which
+ * should be displayed on the screen when this virtual terminal is
+ * activated, fg/bg colors of these characters, and other control
+ * information (escape sequence, etc).
+ *
+ * When data is passed to a virtual terminal it first is parsed for
+ * control information by tem_parse(). Subsequently the character
+ * and color data are written to tvs_screen_buf.
+ * They are saved in buffer in order to refresh the screen when this
+ * terminal is activated. If the terminal is currently active, the data
+ * (characters and colors) are also written to the physical screen by
+ * invoking a callback function, tem_text_callbacks() or tem_pix_callbacks().
+ *
+ * When rendering data to the framebuffer, if the framebuffer is in
+ * VIS_PIXEL mode, the character data will first be converted to pixel
+ * data using tem_pix_bit2pix(), and then the pixels get displayed
+ * on the physical screen. We only store the character and color data in
+ * tem_vt_state since the bit2pix conversion only happens when actually
+ * rendering to the physical framebuffer.
+ *
+ * Color support:
+ * Text mode can only support standard system colors, 4-bit [0-15] indexed.
+ * On framebuffer devices, we can aditionally use [16-255] or truecolor.
+ * Additional colors can be used via CSI 38 and CSI 48 sequences.
+ * CSI 38/48;5 is using indexed colors [0-255], CSI 38/48;2 does
+ * specify color by RGB triple.
+ *
+ * While sending glyphs to display, we need to process glyph attributes:
+ * TEM_ATTR_BOLD will cause BOLD font to be used (or BRIGHT color if we
+ * we use indexed color [0-7]).
+ * We ignore TEM_ATTR_BRIGHT_FG/TEM_ATTR_BRIGHT_BG with RGB colors.
+ * TEM_ATTR_REVERSE and TEM_ATTR_SCREEN_REVERSE will cause fg and bg to be
+ * swapped.
+ */
+
+#include <stand.h>
+#include <sys/ascii.h>
+#include <sys/errno.h>
+#include <sys/tem_impl.h>
+#ifdef _HAVE_TEM_FIRMWARE
+#include <sys/promif.h>
+#endif /* _HAVE_TEM_FIRMWARE */
+#include <sys/consplat.h>
+#include <sys/kd.h>
+#include <stdbool.h>
+
+/* Terminal emulator internal helper functions */
+static void tems_setup_terminal(struct vis_devinit *, size_t, size_t);
+static void tems_modechange_callback(struct vis_modechg_arg *,
+ struct vis_devinit *);
+
+static void tems_reset_colormap(void);
+
+static void tem_free_buf(struct tem_vt_state *);
+static void tem_internal_init(struct tem_vt_state *, boolean_t, boolean_t);
+static void tems_get_initial_color(tem_color_t *pcolor);
+
+static void tem_control(struct tem_vt_state *, uint8_t);
+static void tem_setparam(struct tem_vt_state *, int, int);
+static void tem_selgraph(struct tem_vt_state *);
+static void tem_chkparam(struct tem_vt_state *, uint8_t);
+static void tem_getparams(struct tem_vt_state *, uint8_t);
+static void tem_outch(struct tem_vt_state *, tem_char_t);
+static void tem_parse(struct tem_vt_state *, tem_char_t);
+
+static void tem_new_line(struct tem_vt_state *);
+static void tem_cr(struct tem_vt_state *);
+static void tem_lf(struct tem_vt_state *);
+static void tem_send_data(struct tem_vt_state *);
+static void tem_cls(struct tem_vt_state *);
+static void tem_tab(struct tem_vt_state *);
+static void tem_back_tab(struct tem_vt_state *);
+static void tem_clear_tabs(struct tem_vt_state *, int);
+static void tem_set_tab(struct tem_vt_state *);
+static void tem_mv_cursor(struct tem_vt_state *, int, int);
+static void tem_shift(struct tem_vt_state *, int, int);
+static void tem_scroll(struct tem_vt_state *, int, int, int, int);
+static void tem_clear_chars(struct tem_vt_state *tem,
+ int count, screen_pos_t row, screen_pos_t col);
+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);
+static void tem_bell(struct tem_vt_state *tem);
+static void tem_pix_clear_prom_output(struct tem_vt_state *tem);
+
+static void tem_virtual_cls(struct tem_vt_state *, size_t, screen_pos_t,
+ screen_pos_t);
+static void tem_virtual_display(struct tem_vt_state *, term_char_t *,
+ size_t, screen_pos_t, screen_pos_t);
+static void tem_align_cursor(struct tem_vt_state *tem);
+
+static void tem_check_first_time(struct tem_vt_state *tem);
+static void tem_reset_display(struct tem_vt_state *, boolean_t, boolean_t);
+static void tem_terminal_emulate(struct tem_vt_state *, uint8_t *, int);
+static void tem_text_cursor(struct tem_vt_state *, short);
+static void tem_text_cls(struct tem_vt_state *,
+ int count, screen_pos_t row, screen_pos_t col);
+static void tem_pix_display(struct tem_vt_state *, term_char_t *,
+ int, screen_pos_t, screen_pos_t);
+static void tem_pix_copy(struct tem_vt_state *,
+ screen_pos_t, screen_pos_t,
+ screen_pos_t, screen_pos_t,
+ screen_pos_t, screen_pos_t);
+static void tem_pix_cursor(struct tem_vt_state *, short);
+static void tem_get_attr(struct tem_vt_state *, text_color_t *,
+ text_color_t *, text_attr_t *, uint8_t);
+static void tem_get_color(struct tem_vt_state *,
+ text_color_t *, text_color_t *, term_char_t *);
+static void tem_set_color(text_color_t *, color_t *);
+static void tem_pix_align(struct tem_vt_state *);
+static void tem_text_display(struct tem_vt_state *, term_char_t *, int,
+ screen_pos_t, screen_pos_t);
+static void tem_text_copy(struct tem_vt_state *,
+ screen_pos_t, screen_pos_t, screen_pos_t, screen_pos_t,
+ screen_pos_t, screen_pos_t);
+static void tem_pix_bit2pix(struct tem_vt_state *, term_char_t *);
+static void tem_pix_cls_range(struct tem_vt_state *, screen_pos_t, int,
+ int, screen_pos_t, int, int, boolean_t);
+static void tem_pix_cls(struct tem_vt_state *, int,
+ screen_pos_t, screen_pos_t);
+
+static void bit_to_pix32(struct tem_vt_state *tem, tem_char_t c,
+ text_color_t fg_color, text_color_t bg_color);
+
+/*
+ * Globals
+ */
+tem_state_t tems; /* common term info */
+
+tem_callbacks_t tem_text_callbacks = {
+ .tsc_display = &tem_text_display,
+ .tsc_copy = &tem_text_copy,
+ .tsc_cursor = &tem_text_cursor,
+ .tsc_bit2pix = NULL,
+ .tsc_cls = &tem_text_cls
+};
+tem_callbacks_t tem_pix_callbacks = {
+ .tsc_display = &tem_pix_display,
+ .tsc_copy = &tem_pix_copy,
+ .tsc_cursor = &tem_pix_cursor,
+ .tsc_bit2pix = &tem_pix_bit2pix,
+ .tsc_cls = &tem_pix_cls
+};
+
+#define tem_callback_display (*tems.ts_callbacks->tsc_display)
+#define tem_callback_copy (*tems.ts_callbacks->tsc_copy)
+#define tem_callback_cursor (*tems.ts_callbacks->tsc_cursor)
+#define tem_callback_cls (*tems.ts_callbacks->tsc_cls)
+#define tem_callback_bit2pix (*tems.ts_callbacks->tsc_bit2pix)
+
+static void
+tem_add(struct tem_vt_state *tem)
+{
+ list_insert_head(&tems.ts_list, tem);
+}
+
+/*
+ * This is the main entry point to the module. It handles output requests
+ * during normal system operation, when (e.g.) mutexes are available.
+ */
+void
+tem_write(tem_vt_state_t tem_arg, uint8_t *buf, ssize_t len)
+{
+ struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
+
+ if (tems.ts_initialized == 0 || tem->tvs_initialized == 0) {
+ return;
+ }
+
+ tem_check_first_time(tem);
+ tem_terminal_emulate(tem, buf, len);
+}
+
+static void
+tem_internal_init(struct tem_vt_state *ptem,
+ boolean_t init_color, boolean_t clear_screen)
+{
+ size_t size, width, height;
+
+ if (tems.ts_display_mode == VIS_PIXEL) {
+ ptem->tvs_pix_data_size = tems.ts_pix_data_size;
+ ptem->tvs_pix_data = malloc(ptem->tvs_pix_data_size);
+ }
+
+ width = tems.ts_c_dimension.width;
+ height = tems.ts_c_dimension.height;
+
+ size = width * sizeof (tem_char_t);
+ ptem->tvs_outbuf = malloc(size);
+ if (ptem->tvs_outbuf == NULL)
+ panic("out of memory in tem_internal_init()\n");
+
+ ptem->tvs_maxtab = width / 8;
+ ptem->tvs_tabs = calloc(ptem->tvs_maxtab, sizeof (*ptem->tvs_tabs));
+ if (ptem->tvs_tabs == NULL)
+ panic("out of memory in tem_internal_init()\n");
+
+ tem_reset_display(ptem, clear_screen, init_color);
+
+ ptem->tvs_utf8_left = 0;
+ ptem->tvs_utf8_partial = 0;
+
+ ptem->tvs_initialized = true;
+
+ /*
+ * Out of memory is not fatal there, without the screen history,
+ * we can not optimize the screen copy.
+ */
+ size = width * height * sizeof (term_char_t);
+ ptem->tvs_screen_buf = malloc(size);
+ tem_virtual_cls(ptem, width * height, 0, 0);
+}
+
+int
+tem_initialized(tem_vt_state_t tem_arg)
+{
+ struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg;
+
+ return (ptem->tvs_initialized);
+}
+
+tem_vt_state_t
+tem_init(void)
+{
+ struct tem_vt_state *ptem;
+
+ ptem = calloc(1, sizeof (struct tem_vt_state));
+ if (ptem == NULL)
+ return ((tem_vt_state_t)ptem);
+
+ ptem->tvs_isactive = false;
+ ptem->tvs_fbmode = KD_TEXT;
+
+ /*
+ * A tem is regarded as initialized only after tem_internal_init(),
+ * will be set at the end of tem_internal_init().
+ */
+ ptem->tvs_initialized = 0;
+
+ if (!tems.ts_initialized) {
+ /*
+ * Only happens during early console configuration.
+ */
+ tem_add(ptem);
+ return ((tem_vt_state_t)ptem);
+ }
+
+ tem_internal_init(ptem, B_TRUE, B_FALSE);
+ tem_add(ptem);
+
+ return ((tem_vt_state_t)ptem);
+}
+
+/*
+ * re-init the tem after video mode has changed and tems_info has
+ * been re-inited.
+ */
+static void
+tem_reinit(struct tem_vt_state *tem, boolean_t reset_display)
+{
+ tem_free_buf(tem); /* only free virtual buffers */
+
+ /* reserve color */
+ tem_internal_init(tem, B_FALSE, reset_display);
+}
+
+static void
+tem_free_buf(struct tem_vt_state *tem)
+{
+ free(tem->tvs_outbuf);
+ tem->tvs_outbuf = NULL;
+
+ free(tem->tvs_pix_data);
+ tem->tvs_pix_data = NULL;
+
+ free(tem->tvs_screen_buf);
+ tem->tvs_screen_buf = NULL;
+
+ free(tem->tvs_tabs);
+ tem->tvs_tabs = NULL;
+}
+
+static int
+tems_failed(boolean_t finish_ioctl)
+{
+ if (finish_ioctl && tems.ts_hdl != NULL)
+ (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_DEVFINI, NULL);
+
+ tems.ts_hdl = NULL;
+ return (ENXIO);
+}
+
+/*
+ * Only called once during boot
+ */
+int
+tem_info_init(struct console *cp)
+{
+ int ret;
+ struct vis_devinit temargs;
+ size_t height = 0;
+ size_t width = 0;
+ struct tem_vt_state *p;
+
+ if (tems.ts_initialized) {
+ return (0);
+ }
+
+ list_create(&tems.ts_list, sizeof (struct tem_vt_state),
+ __offsetof(struct tem_vt_state, tvs_list_node));
+ tems.ts_active = NULL;
+
+ tems.ts_hdl = cp;
+ bzero(&temargs, sizeof (temargs));
+ temargs.modechg_cb = (vis_modechg_cb_t)tems_modechange_callback;
+ temargs.modechg_arg = NULL;
+
+ /*
+ * Initialize the console and get the device parameters
+ */
+ if (cp->c_ioctl(cp, VIS_DEVINIT, &temargs) != 0) {
+ printf("terminal emulator: Compatible fb not found\n");
+ ret = tems_failed(B_FALSE);
+ return (ret);
+ }
+
+ /* Make sure the fb driver and terminal emulator versions match */
+ if (temargs.version != VIS_CONS_REV) {
+ printf(
+ "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
+ "of console fb driver not supported\n", temargs.version);
+ ret = tems_failed(B_TRUE);
+ return (ret);
+ }
+
+ /* other sanity checks */
+ if (!((temargs.depth == 4) || (temargs.depth == 8) ||
+ (temargs.depth == 15) || (temargs.depth == 16) ||
+ (temargs.depth == 24) || (temargs.depth == 32))) {
+ printf("terminal emulator: unsupported depth\n");
+ ret = tems_failed(B_TRUE);
+ return (ret);
+ }
+
+ if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) {
+ printf("terminal emulator: unsupported mode\n");
+ ret = tems_failed(B_TRUE);
+ return (ret);
+ }
+
+ plat_tem_get_prom_size(&height, &width);
+
+ /*
+ * Initialize the common terminal emulator info
+ */
+ tems_setup_terminal(&temargs, height, width);
+
+ tems_reset_colormap();
+ tems_get_initial_color(&tems.ts_init_color);
+
+ tems.ts_initialized = 1; /* initialization flag */
+
+ for (p = list_head(&tems.ts_list); p != NULL;
+ p = list_next(&tems.ts_list, p)) {
+ tem_internal_init(p, B_TRUE, B_FALSE);
+ if (temargs.mode == VIS_PIXEL)
+ tem_pix_align(p);
+ }
+
+ return (0);
+}
+
+#define TEMS_DEPTH_DIFF 0x01
+#define TEMS_DIMENSION_DIFF 0x02
+
+static uint8_t
+tems_check_videomode(struct vis_devinit *tp)
+{
+ uint8_t result = 0;
+
+ if (tems.ts_pdepth != tp->depth)
+ result |= TEMS_DEPTH_DIFF;
+
+ if (tp->mode == VIS_TEXT) {
+ if (tems.ts_c_dimension.width != tp->width ||
+ tems.ts_c_dimension.height != tp->height)
+ result |= TEMS_DIMENSION_DIFF;
+ } else {
+ if (tems.ts_p_dimension.width != tp->width ||
+ tems.ts_p_dimension.height != tp->height)
+ result |= TEMS_DIMENSION_DIFF;
+ }
+ if (tems.update_font == true)
+ result |= TEMS_DIMENSION_DIFF;
+
+ return (result);
+}
+
+static int
+env_screen_nounset(struct env_var *ev __unused)
+{
+ if (tems.ts_p_dimension.width == 0 &&
+ tems.ts_p_dimension.height == 0)
+ return (0);
+ return (EPERM);
+}
+
+static void
+tems_setup_font(screen_size_t height, screen_size_t width)
+{
+ bitmap_data_t *font_data;
+
+ /*
+ * set_font() will select an appropriate sized font for
+ * the number of rows and columns selected. If we don't
+ * have a font that will fit, then it will use the
+ * default builtin font and adjust the rows and columns
+ * to fit on the screen.
+ */
+ font_data = set_font(&tems.ts_c_dimension.height,
+ &tems.ts_c_dimension.width, height, width);
+
+ if (font_data == NULL)
+ panic("out of memory");
+
+ /*
+ * To use loaded font, we assign the loaded font data to tems.ts_font.
+ * In case of next load, the previously loaded data is freed
+ * when loading the new font.
+ */
+ for (int i = 0; i < VFNT_MAPS; i++) {
+ tems.ts_font.vf_map[i] =
+ font_data->font->vf_map[i];
+ tems.ts_font.vf_map_count[i] =
+ font_data->font->vf_map_count[i];
+ }
+
+ tems.ts_font.vf_bytes = font_data->font->vf_bytes;
+ tems.ts_font.vf_width = font_data->font->vf_width;
+ tems.ts_font.vf_height = font_data->font->vf_height;
+}
+
+static void
+tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width)
+{
+ char env[8];
+
+ tems.ts_pdepth = tp->depth;
+ tems.ts_linebytes = tp->linebytes;
+ tems.ts_display_mode = tp->mode;
+ tems.ts_color_map = tp->color_map;
+
+ switch (tp->mode) {
+ case VIS_TEXT:
+ /* Set fake pixel dimensions to assist set_font() */
+ tems.ts_p_dimension.width = 0;
+ tems.ts_p_dimension.height = 0;
+ tems.ts_c_dimension.width = tp->width;
+ tems.ts_c_dimension.height = tp->height;
+ tems.ts_callbacks = &tem_text_callbacks;
+
+ tems_setup_font(16 * tp->height + BORDER_PIXELS,
+ 8 * tp->width + BORDER_PIXELS);
+
+ /* ensure the following are not set for text mode */
+ unsetenv("screen-height");
+ unsetenv("screen-width");
+ break;
+
+ case VIS_PIXEL:
+ /*
+ * First check to see if the user has specified a screen size.
+ * If so, use those values. Else use 34x80 as the default.
+ */
+ if (width == 0) {
+ width = TEM_DEFAULT_COLS;
+ height = TEM_DEFAULT_ROWS;
+ }
+ tems.ts_c_dimension.height = (screen_size_t)height;
+ tems.ts_c_dimension.width = (screen_size_t)width;
+ tems.ts_p_dimension.height = tp->height;
+ tems.ts_p_dimension.width = tp->width;
+ tems.ts_callbacks = &tem_pix_callbacks;
+
+ tems_setup_font(tp->height, tp->width);
+
+ snprintf(env, sizeof (env), "%d", tems.ts_p_dimension.height);
+ env_setenv("screen-height", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_screen_nounset);
+ snprintf(env, sizeof (env), "%d", tems.ts_p_dimension.width);
+ env_setenv("screen-width", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_screen_nounset);
+
+ tems.ts_p_offset.y = (tems.ts_p_dimension.height -
+ (tems.ts_c_dimension.height * tems.ts_font.vf_height)) / 2;
+ tems.ts_p_offset.x = (tems.ts_p_dimension.width -
+ (tems.ts_c_dimension.width * tems.ts_font.vf_width)) / 2;
+ tems.ts_pix_data_size =
+ tems.ts_font.vf_width * tems.ts_font.vf_height;
+ tems.ts_pix_data_size *= 4;
+ tems.ts_pdepth = tp->depth;
+
+ break;
+ }
+
+ tems.update_font = false;
+
+ snprintf(env, sizeof (env), "%d", tems.ts_c_dimension.height);
+ env_setenv("screen-#rows", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_nounset);
+ snprintf(env, sizeof (env), "%d", tems.ts_c_dimension.width);
+ env_setenv("screen-#cols", EV_VOLATILE | EV_NOHOOK, env,
+ env_noset, env_nounset);
+
+ snprintf(env, sizeof (env), "%dx%d", tems.ts_font.vf_width,
+ tems.ts_font.vf_height);
+ env_setenv("screen-font", EV_VOLATILE | EV_NOHOOK, env, NULL,
+ NULL);
+}
+
+/*
+ * This is a callback function that we register with the frame
+ * buffer driver layered underneath. It gets invoked from
+ * the underlying frame buffer driver to reconfigure the terminal
+ * emulator to a new screen size and depth in conjunction with
+ * framebuffer videomode changes.
+ * Here we keep the foreground/background color and attributes,
+ * which may be different with the initial settings, so that
+ * the color won't change while the framebuffer videomode changes.
+ * And we also reset the kernel terminal emulator and clear the
+ * whole screen.
+ */
+/* ARGSUSED */
+void
+tems_modechange_callback(struct vis_modechg_arg *arg __unused,
+ struct vis_devinit *devinit)
+{
+ uint8_t diff;
+ struct tem_vt_state *p;
+ tem_modechg_cb_t cb;
+ tem_modechg_cb_arg_t cb_arg;
+ size_t height = 0;
+ size_t width = 0;
+ int state;
+
+ diff = tems_check_videomode(devinit);
+ if (diff == 0) {
+ /*
+ * This is color related change, reset color and redraw the
+ * screen. Only need to reinit the active tem.
+ */
+ struct tem_vt_state *active = tems.ts_active;
+ tems_get_initial_color(&tems.ts_init_color);
+ active->tvs_fg_color = tems.ts_init_color.fg_color;
+ active->tvs_bg_color = tems.ts_init_color.bg_color;
+ active->tvs_flags = tems.ts_init_color.a_flags;
+ tem_reinit(active, B_TRUE);
+ return;
+ }
+
+ diff = diff & TEMS_DIMENSION_DIFF;
+
+ if (diff == 0) {
+ /*
+ * Only need to reinit the active tem.
+ */
+ struct tem_vt_state *active = tems.ts_active;
+ tems.ts_pdepth = devinit->depth;
+ /* color depth did change, reset colors */
+ tems_reset_colormap();
+ tems_get_initial_color(&tems.ts_init_color);
+ tem_reinit(active, B_TRUE);
+
+ return;
+ }
+
+ plat_tem_get_prom_size(&height, &width);
+
+ state = tems.ts_initialized;
+ tems.ts_initialized = 0; /* stop all output */
+ tems_setup_terminal(devinit, height, width);
+
+ tems_reset_colormap();
+ tems_get_initial_color(&tems.ts_init_color);
+ tems.ts_initialized = state; /* restore state */
+
+ for (p = list_head(&tems.ts_list); p != NULL;
+ p = list_next(&tems.ts_list, p)) {
+ tem_reinit(p, p->tvs_isactive);
+ }
+
+
+ if (tems.ts_modechg_cb == NULL) {
+ return;
+ }
+
+ cb = tems.ts_modechg_cb;
+ cb_arg = tems.ts_modechg_arg;
+
+ cb(cb_arg);
+}
+
+/*
+ * This function is used to clear entire screen via the underlying framebuffer
+ * driver.
+ */
+int
+tems_cls(struct vis_consclear *pda)
+{
+ if (tems.ts_hdl == NULL)
+ return (1);
+ return (tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCLEAR, pda));
+}
+
+/*
+ * This function is used to display a rectangular blit of data
+ * of a given size and location via the underlying framebuffer driver.
+ * The blit can be as small as a pixel or as large as the screen.
+ */
+void
+tems_display(struct vis_consdisplay *pda)
+{
+ if (tems.ts_hdl != NULL)
+ (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSDISPLAY, pda);
+}
+
+/*
+ * This function is used to invoke a block copy operation in the
+ * underlying framebuffer driver. Rectangle copies are how scrolling
+ * is implemented, as well as horizontal text shifting escape seqs.
+ * such as from vi when deleting characters and words.
+ */
+void
+tems_copy(struct vis_conscopy *pma)
+{
+ if (tems.ts_hdl != NULL)
+ (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCOPY, pma);
+}
+
+/*
+ * This function is used to show or hide a rectangluar monochrom
+ * pixel inverting, text block cursor via the underlying framebuffer.
+ */
+void
+tems_cursor(struct vis_conscursor *pca)
+{
+ if (tems.ts_hdl != NULL)
+ (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCURSOR, pca);
+}
+
+static void
+tem_kdsetmode(int mode)
+{
+ if (tems.ts_hdl != NULL) {
+ (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, KDSETMODE,
+ (void *)(intptr_t)mode);
+ }
+}
+
+static void
+tems_reset_colormap(void)
+{
+ struct vis_cmap cm;
+
+ switch (tems.ts_pdepth) {
+ case 8:
+ cm.index = 0;
+ cm.count = 16;
+ /* 8-bits (1/3 of TrueColor 24) */
+ cm.red = (uint8_t *)cmap4_to_24.red;
+ /* 8-bits (1/3 of TrueColor 24) */
+ cm.blue = (uint8_t *)cmap4_to_24.blue;
+ /* 8-bits (1/3 of TrueColor 24) */
+ cm.green = (uint8_t *)cmap4_to_24.green;
+ if (tems.ts_hdl != NULL)
+ (void) tems.ts_hdl->c_ioctl(tems.ts_hdl,
+ VIS_PUTCMAP, &cm);
+ break;
+ }
+}
+
+void
+tem_get_size(uint16_t *r, uint16_t *c, uint16_t *x, uint16_t *y)
+{
+ *r = (uint16_t)tems.ts_c_dimension.height;
+ *c = (uint16_t)tems.ts_c_dimension.width;
+ *x = (uint16_t)tems.ts_p_dimension.width;
+ *y = (uint16_t)tems.ts_p_dimension.height;
+}
+
+/*
+ * Loader extension. Store important data in environment. Intended to be used
+ * just before booting the OS to make the data available in kernel
+ * environment module.
+ */
+void
+tem_save_state(void)
+{
+ struct tem_vt_state *active = tems.ts_active;
+ char buf[80];
+
+ /*
+ * We already have in environment:
+ * tem.inverse, tem.inverse_screen
+ * tem.fg_color, tem.bg_color.
+ * So we only need to add the position of the cursor.
+ */
+
+ if (active != NULL) {
+ snprintf(buf, sizeof (buf), "%d", active->tvs_c_cursor.col);
+ setenv("tem.cursor.col", buf, 1);
+ snprintf(buf, sizeof (buf), "%d", active->tvs_c_cursor.row);
+ setenv("tem.cursor.row", buf, 1);
+ }
+}
+
+void
+tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg)
+{
+ tems.ts_modechg_cb = func;
+ tems.ts_modechg_arg = arg;
+}
+
+/*
+ * This function is to scroll up the OBP output, which has
+ * different screen height and width with our kernel console.
+ */
+static void
+tem_prom_scroll_up(struct tem_vt_state *tem, int nrows)
+{
+ struct vis_conscopy ma;
+ int ncols, width;
+
+ /* copy */
+ ma.s_row = nrows * tems.ts_font.vf_height;
+ ma.e_row = tems.ts_p_dimension.height - 1;
+ ma.t_row = 0;
+
+ ma.s_col = 0;
+ ma.e_col = tems.ts_p_dimension.width - 1;
+ ma.t_col = 0;
+
+ tems_copy(&ma);
+
+ /* clear */
+ width = tems.ts_font.vf_width;
+ ncols = (tems.ts_p_dimension.width + (width - 1)) / width;
+
+ tem_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y,
+ 0, ncols, 0, B_TRUE);
+}
+
+/*
+ * This function is to compute the starting row of the console, according to
+ * PROM cursor's position. Here we have to take different fonts into account.
+ */
+static int
+tem_adjust_row(struct tem_vt_state *tem, int prom_row)
+{
+ int tem_row;
+ int tem_y;
+ int prom_charheight = 0;
+ int prom_window_top = 0;
+ int scroll_up_lines;
+
+ plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top);
+ if (prom_charheight == 0)
+ prom_charheight = tems.ts_font.vf_height;
+
+ tem_y = (prom_row + 1) * prom_charheight + prom_window_top -
+ tems.ts_p_offset.y;
+ tem_row = (tem_y + tems.ts_font.vf_height - 1) /
+ tems.ts_font.vf_height - 1;
+
+ if (tem_row < 0) {
+ tem_row = 0;
+ } else if (tem_row >= (tems.ts_c_dimension.height - 1)) {
+ /*
+ * Scroll up the prom outputs if the PROM cursor's position is
+ * below our tem's lower boundary.
+ */
+ scroll_up_lines = tem_row -
+ (tems.ts_c_dimension.height - 1);
+ tem_prom_scroll_up(tem, scroll_up_lines);
+ tem_row = tems.ts_c_dimension.height - 1;
+ }
+
+ return (tem_row);
+}
+
+static void
+tem_pix_align(struct tem_vt_state *tem)
+{
+ uint32_t row = 0;
+ uint32_t col = 0;
+
+ if (plat_stdout_is_framebuffer()) {
+ plat_tem_hide_prom_cursor();
+
+ /*
+ * We are getting the current cursor position in pixel
+ * mode so that we don't over-write the console output
+ * during boot.
+ */
+ plat_tem_get_prom_pos(&row, &col);
+
+ /*
+ * Adjust the row if necessary when the font of our
+ * kernel console tem is different with that of prom
+ * tem.
+ */
+ row = tem_adjust_row(tem, row);
+
+ /* first line of our kernel console output */
+ tem->tvs_first_line = row + 1;
+
+ /* re-set and align cursor position */
+ tem->tvs_s_cursor.row = tem->tvs_c_cursor.row =
+ (screen_pos_t)row;
+ tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0;
+ } else {
+ tem_reset_display(tem, B_TRUE, B_TRUE);
+ }
+}
+
+static void
+tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen)
+{
+ int i_inverse = 0;
+ int i_inverse_screen = 0;
+
+ plat_tem_get_inverses(&i_inverse, &i_inverse_screen);
+
+ *p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE;
+ *p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE;
+}
+
+/*
+ * Get the foreground/background color and attributes from environment.
+ */
+static void
+tems_get_initial_color(tem_color_t *pcolor)
+{
+ boolean_t inverse, inverse_screen;
+ unsigned short flags = 0;
+ uint8_t fg, bg;
+
+ fg = DEFAULT_ANSI_FOREGROUND;
+ bg = DEFAULT_ANSI_BACKGROUND;
+ plat_tem_get_colors(&fg, &bg);
+ pcolor->fg_color.n = fg;
+ pcolor->bg_color.n = bg;
+
+ tems_get_inverses(&inverse, &inverse_screen);
+ if (inverse)
+ flags |= TEM_ATTR_REVERSE;
+ if (inverse_screen)
+ flags |= TEM_ATTR_SCREEN_REVERSE;
+
+ if (flags != 0) {
+ /*
+ * The reverse attribute is set.
+ * In case of black on white we want bright white for BG.
+ */
+ if (pcolor->fg_color.n == ANSI_COLOR_WHITE)
+ flags |= TEM_ATTR_BRIGHT_BG;
+
+ /*
+ * For white on black, unset the bright attribute we
+ * had set to have bright white background.
+ */
+ if (pcolor->fg_color.n == ANSI_COLOR_BLACK)
+ flags &= ~TEM_ATTR_BRIGHT_BG;
+ } else {
+ /*
+ * In case of black on white we want bright white for BG.
+ */
+ if (pcolor->bg_color.n == ANSI_COLOR_WHITE)
+ flags |= TEM_ATTR_BRIGHT_BG;
+ }
+
+ pcolor->a_flags = flags;
+}
+
+void
+tem_activate(tem_vt_state_t tem_arg, boolean_t unblank)
+{
+ struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
+
+ tems.ts_active = tem;
+ tem->tvs_isactive = true;
+
+ tem_kdsetmode(tem->tvs_fbmode);
+
+ if (unblank)
+ tem_cls(tem);
+}
+
+static void
+tem_check_first_time(struct tem_vt_state *tem)
+{
+ static int first_time = 1;
+
+ /*
+ * Realign the console cursor. We did this in tem_init().
+ * However, drivers in the console stream may emit additional
+ * messages before we are ready. This causes text overwrite
+ * on the screen. This is a workaround.
+ */
+ if (!first_time)
+ return;
+
+ first_time = 0;
+ if (tems.ts_display_mode == VIS_TEXT)
+ tem_text_cursor(tem, VIS_GET_CURSOR);
+ else
+ tem_pix_cursor(tem, VIS_GET_CURSOR);
+ tem_align_cursor(tem);
+}
+
+/* Process partial UTF-8 sequence. */
+static void
+tem_input_partial(struct tem_vt_state *tem)
+{
+ unsigned i;
+ tem_char_t c;
+
+ if (tem->tvs_utf8_left == 0)
+ return;
+
+ for (i = 0; i < sizeof (tem->tvs_utf8_partial); i++) {
+ c = (tem->tvs_utf8_partial >> (24 - (i << 3))) & 0xff;
+ if (c != 0) {
+ tem_parse(tem, c);
+ }
+ }
+ tem->tvs_utf8_left = 0;
+ tem->tvs_utf8_partial = 0;
+}
+
+/*
+ * Handle UTF-8 sequences.
+ */
+static void
+tem_input_byte(struct tem_vt_state *tem, uint8_t c)
+{
+ /*
+ * Check for UTF-8 code points. In case of error fall back to
+ * 8-bit code. As we only have 8859-1 fonts for console, this will set
+ * the limits on what chars we actually can display, therefore we
+ * have to return to this code once we have solved the font issue.
+ */
+ if ((c & 0x80) == 0x00) {
+ /* One-byte sequence. */
+ tem_input_partial(tem);
+ tem_parse(tem, c);
+ return;
+ }
+ if ((c & 0xe0) == 0xc0) {
+ /* Two-byte sequence. */
+ tem_input_partial(tem);
+ tem->tvs_utf8_left = 1;
+ tem->tvs_utf8_partial = c;
+ return;
+ }
+ if ((c & 0xf0) == 0xe0) {
+ /* Three-byte sequence. */
+ tem_input_partial(tem);
+ tem->tvs_utf8_left = 2;
+ tem->tvs_utf8_partial = c;
+ return;
+ }
+ if ((c & 0xf8) == 0xf0) {
+ /* Four-byte sequence. */
+ tem_input_partial(tem);
+ tem->tvs_utf8_left = 3;
+ tem->tvs_utf8_partial = c;
+ return;
+ }
+ if ((c & 0xc0) == 0x80) {
+ /* Invalid state? */
+ if (tem->tvs_utf8_left == 0) {
+ tem_parse(tem, c);
+ return;
+ }
+ tem->tvs_utf8_left--;
+ tem->tvs_utf8_partial = (tem->tvs_utf8_partial << 8) | c;
+ if (tem->tvs_utf8_left == 0) {
+ tem_char_t v, u;
+ uint8_t b;
+
+ /*
+ * Transform the sequence of 2 to 4 bytes to
+ * unicode number.
+ */
+ v = 0;
+ u = tem->tvs_utf8_partial;
+ b = (u >> 24) & 0xff;
+ if (b != 0) { /* Four-byte sequence */
+ v = b & 0x07;
+ b = (u >> 16) & 0xff;
+ v = (v << 6) | (b & 0x3f);
+ b = (u >> 8) & 0xff;
+ v = (v << 6) | (b & 0x3f);
+ b = u & 0xff;
+ v = (v << 6) | (b & 0x3f);
+ } else if ((b = (u >> 16) & 0xff) != 0) {
+ v = b & 0x0f; /* Three-byte sequence */
+ b = (u >> 8) & 0xff;
+ v = (v << 6) | (b & 0x3f);
+ b = u & 0xff;
+ v = (v << 6) | (b & 0x3f);
+ } else if ((b = (u >> 8) & 0xff) != 0) {
+ v = b & 0x1f; /* Two-byte sequence */
+ b = u & 0xff;
+ v = (v << 6) | (b & 0x3f);
+ }
+
+ tem_parse(tem, v);
+ tem->tvs_utf8_partial = 0;
+ }
+ return;
+ }
+ /* Anything left is illegal in UTF-8 sequence. */
+ tem_input_partial(tem);
+ tem_parse(tem, c);
+}
+
+/*
+ * This is the main entry point into the terminal emulator.
+ *
+ * For each data message coming downstream, ANSI assumes that it is composed
+ * of ASCII characters, which are treated as a byte-stream input to the
+ * parsing state machine. All data is parsed immediately -- there is
+ * no enqueing.
+ */
+static void
+tem_terminal_emulate(struct tem_vt_state *tem, uint8_t *buf, int len)
+{
+ if (tem->tvs_isactive)
+ tem_callback_cursor(tem, VIS_HIDE_CURSOR);
+
+ for (; len > 0; len--, buf++)
+ tem_input_byte(tem, *buf);
+
+ /*
+ * Send the data we just got to the framebuffer.
+ */
+ tem_send_data(tem);
+
+ if (tem->tvs_isactive)
+ tem_callback_cursor(tem, VIS_DISPLAY_CURSOR);
+}
+
+/*
+ * send the appropriate control message or set state based on the
+ * value of the control character ch
+ */
+
+static void
+tem_control(struct tem_vt_state *tem, uint8_t ch)
+{
+ tem->tvs_state = A_STATE_START;
+ switch (ch) {
+ case A_BEL:
+ tem_bell(tem);
+ break;
+
+ case A_BS:
+ tem_mv_cursor(tem,
+ tem->tvs_c_cursor.row,
+ tem->tvs_c_cursor.col - 1);
+ break;
+
+ case A_HT:
+ tem_tab(tem);
+ break;
+
+ case A_NL:
+ /*
+ * tem_send_data(tem, credp, called_from);
+ * tem_new_line(tem, credp, called_from);
+ * break;
+ */
+
+ case A_VT:
+ tem_send_data(tem);
+ tem_lf(tem);
+ break;
+
+ case A_FF:
+ tem_send_data(tem);
+ tem_cls(tem);
+ break;
+
+ case A_CR:
+ tem_send_data(tem);
+ tem_cr(tem);
+ break;
+
+ case A_ESC:
+ tem->tvs_state = A_STATE_ESC;
+ break;
+
+ case A_CSI:
+ tem->tvs_curparam = 0;
+ tem->tvs_paramval = 0;
+ tem->tvs_gotparam = B_FALSE;
+ /* clear the parameters */
+ for (int i = 0; i < TEM_MAXPARAMS; i++)
+ tem->tvs_params[i] = -1;
+ tem->tvs_state = A_STATE_CSI;
+ break;
+
+ case A_GS:
+ tem_back_tab(tem);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/*
+ * if parameters [0..count - 1] are not set, set them to the value
+ * of newparam.
+ */
+
+static void
+tem_setparam(struct tem_vt_state *tem, int count, int newparam)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (tem->tvs_params[i] == -1)
+ tem->tvs_params[i] = newparam;
+ }
+}
+
+/*
+ * For colors 0-15 the tem is using color code translation
+ * from sun colors to vga (dim_xlate and brt_xlate tables, see tem_get_color).
+ * Colors 16-255 are used without translation.
+ */
+static void
+tem_select_color(struct tem_vt_state *tem, int color, bool fg)
+{
+ if (color < 0 || color > 255)
+ return;
+
+ /* VGA text mode only does support 16 colors. */
+ if (tems.ts_display_mode == VIS_TEXT && color > 15)
+ return;
+
+ /* Switch to use indexed colors. */
+ if (fg == true) {
+ tem->tvs_flags &= ~TEM_ATTR_RGB_FG;
+ tem->tvs_fg_color.n = color;
+ } else {
+ tem->tvs_flags &= ~TEM_ATTR_RGB_BG;
+ tem->tvs_bg_color.n = color;
+ }
+
+ /*
+ * For colors 0-7, make sure the BRIGHT attribute is not set.
+ */
+ if (color < 8) {
+ if (fg == true)
+ tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
+ else
+ tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
+ return;
+ }
+
+ /*
+ * For colors 8-15, we use color codes 0-7 and set BRIGHT attribute.
+ */
+ if (color < 16) {
+ if (fg == true) {
+ tem->tvs_fg_color.n -= 8;
+ tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
+ } else {
+ tem->tvs_bg_color.n -= 8;
+ tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
+ }
+ }
+}
+
+/*
+ * select graphics mode based on the param vals stored in a_params
+ */
+static void
+tem_selgraph(struct tem_vt_state *tem)
+{
+ int curparam;
+ int count = 0;
+ int param;
+ int r, g, b;
+
+ tem->tvs_state = A_STATE_START;
+
+ curparam = tem->tvs_curparam;
+ do {
+ param = tem->tvs_params[count];
+
+ switch (param) {
+ case -1:
+ case 0:
+ /* reset to initial normal settings */
+ tem->tvs_fg_color = tems.ts_init_color.fg_color;
+ tem->tvs_bg_color = tems.ts_init_color.bg_color;
+ tem->tvs_flags = tems.ts_init_color.a_flags;
+ break;
+
+ case 1: /* Bold Intense */
+ tem->tvs_flags |= TEM_ATTR_BOLD;
+ break;
+
+ case 2: /* Faint Intense */
+ tem->tvs_flags &= ~TEM_ATTR_BOLD;
+ break;
+
+ case 4: /* Underline */
+ tem->tvs_flags |= TEM_ATTR_UNDERLINE;
+ break;
+
+ case 5: /* Blink */
+ tem->tvs_flags |= TEM_ATTR_BLINK;
+ break;
+
+ case 7: /* Reverse video */
+ if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
+ tem->tvs_flags &= ~TEM_ATTR_REVERSE;
+ } else {
+ tem->tvs_flags |= TEM_ATTR_REVERSE;
+ }
+ break;
+
+ case 22: /* Remove Bold */
+ tem->tvs_flags &= ~TEM_ATTR_BOLD;
+ break;
+
+ case 24: /* Remove Underline */
+ tem->tvs_flags &= ~TEM_ATTR_UNDERLINE;
+ break;
+
+ case 25: /* Remove Blink */
+ tem->tvs_flags &= ~TEM_ATTR_BLINK;
+ break;
+
+ case 27: /* Remove Reverse */
+ if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
+ tem->tvs_flags |= TEM_ATTR_REVERSE;
+ } else {
+ tem->tvs_flags &= ~TEM_ATTR_REVERSE;
+ }
+ break;
+
+ case 30: /* black (grey) foreground */
+ case 31: /* red (light red) foreground */
+ case 32: /* green (light green) foreground */
+ case 33: /* brown (yellow) foreground */
+ case 34: /* blue (light blue) foreground */
+ case 35: /* magenta (light magenta) foreground */
+ case 36: /* cyan (light cyan) foreground */
+ case 37: /* white (bright white) foreground */
+ tem->tvs_fg_color.n = param - 30;
+ tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
+ tem->tvs_flags &= ~TEM_ATTR_RGB_FG;
+ break;
+
+ case 38:
+ /*
+ * We should have 3 parameters for 256 colors and
+ * 5 parameters for 24-bit colors.
+ */
+ if (curparam < 3) {
+ curparam = 0;
+ break;
+ }
+
+ /*
+ * 256 and truecolor needs depth > 8, but
+ * we still need to process the sequence.
+ */
+ count++;
+ curparam--;
+ param = tem->tvs_params[count];
+ switch (param) {
+ case 2: /* RGB colors */
+ if (curparam < 4) {
+ curparam = 0;
+ break;
+ }
+ r = tem->tvs_params[++count];
+ g = tem->tvs_params[++count];
+ b = tem->tvs_params[++count];
+ curparam -= 3;
+ if (r < 0 || r > 255 || g < 0 || g > 255 ||
+ b < 0 || b > 255)
+ break;
+
+ if (tems.ts_display_mode == VIS_PIXEL &&
+ tems.ts_pdepth > 8) {
+ tem->tvs_flags |= TEM_ATTR_RGB_FG;
+ tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
+ tem->tvs_fg_color.rgb.a =
+ tem->tvs_alpha;
+ tem->tvs_fg_color.rgb.r = r;
+ tem->tvs_fg_color.rgb.g = g;
+ tem->tvs_fg_color.rgb.b = b;
+ }
+ break;
+ case 5: /* 256 colors */
+ count++;
+ curparam--;
+ tem_select_color(tem, tem->tvs_params[count],
+ true);
+ break;
+ default:
+ curparam = 0;
+ break;
+ }
+ break;
+
+ case 39:
+ /*
+ * Reset the foreground colour and brightness.
+ */
+ tem->tvs_fg_color = tems.ts_init_color.fg_color;
+ tem->tvs_flags &= ~TEM_ATTR_RGB_FG;
+ if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_FG)
+ tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
+ else
+ tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
+ break;
+
+ case 40: /* black (grey) background */
+ case 41: /* red (light red) background */
+ case 42: /* green (light green) background */
+ case 43: /* brown (yellow) background */
+ case 44: /* blue (light blue) background */
+ case 45: /* magenta (light magenta) background */
+ case 46: /* cyan (light cyan) background */
+ case 47: /* white (bright white) background */
+ tem->tvs_bg_color.n = param - 40;
+ tem->tvs_flags &= ~TEM_ATTR_RGB_BG;
+ tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
+ break;
+
+ case 48:
+ /*
+ * We should have 3 parameters for 256 colors and
+ * 5 parameters for 24-bit colors.
+ */
+ /* We should have at least 3 parameters */
+ if (curparam < 3) {
+ curparam = 0;
+ break;
+ }
+
+ /*
+ * 256 and truecolor needs depth > 8, but
+ * we still need to process the sequence.
+ */
+ count++;
+ curparam--;
+ param = tem->tvs_params[count];
+ switch (param) {
+ case 2: /* RGB colors */
+ if (curparam < 4) {
+ curparam = 0;
+ break;
+ }
+ r = tem->tvs_params[++count];
+ g = tem->tvs_params[++count];
+ b = tem->tvs_params[++count];
+ curparam -= 3;
+ if (r < 0 || r > 255 || g < 0 || g > 255 ||
+ b < 0 || b > 255)
+ break;
+
+ if (tems.ts_display_mode == VIS_PIXEL &&
+ tems.ts_pdepth > 8) {
+ tem->tvs_flags |= TEM_ATTR_RGB_BG;
+ tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
+ tem->tvs_bg_color.rgb.a =
+ tem->tvs_alpha;
+ tem->tvs_bg_color.rgb.r = r;
+ tem->tvs_bg_color.rgb.g = g;
+ tem->tvs_bg_color.rgb.b = b;
+ }
+ break;
+ case 5: /* 256 colors */
+ count++;
+ curparam--;
+ tem_select_color(tem, tem->tvs_params[count],
+ false);
+ break;
+ default:
+ curparam = 0;
+ break;
+ }
+ break;
+
+ case 49:
+ /*
+ * Reset the background colour and brightness.
+ */
+ tem->tvs_bg_color = tems.ts_init_color.bg_color;
+ tem->tvs_flags &= ~TEM_ATTR_RGB_BG;
+ if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_BG)
+ tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
+ else
+ tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
+ break;
+
+ case 90: /* black (grey) foreground */
+ case 91: /* red (light red) foreground */
+ case 92: /* green (light green) foreground */
+ case 93: /* brown (yellow) foreground */
+ case 94: /* blue (light blue) foreground */
+ case 95: /* magenta (light magenta) foreground */
+ case 96: /* cyan (light cyan) foreground */
+ case 97: /* white (bright white) foreground */
+ tem->tvs_fg_color.n = param - 90;
+ tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
+ tem->tvs_flags &= ~TEM_ATTR_RGB_FG;
+ break;
+
+ case 100: /* black (grey) background */
+ case 101: /* red (light red) background */
+ case 102: /* green (light green) background */
+ case 103: /* brown (yellow) background */
+ case 104: /* blue (light blue) background */
+ case 105: /* magenta (light magenta) background */
+ case 106: /* cyan (light cyan) background */
+ case 107: /* white (bright white) background */
+ tem->tvs_bg_color.n = param - 100;
+ tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
+ tem->tvs_flags &= ~TEM_ATTR_RGB_BG;
+ break;
+
+ default:
+ break;
+ }
+ count++;
+ curparam--;
+
+ } while (curparam > 0);
+}
+
+/*
+ * perform the appropriate action for the escape sequence
+ *
+ * General rule: This code does not validate the arguments passed.
+ * It assumes that the next lower level will do so.
+ */
+static void
+tem_chkparam(struct tem_vt_state *tem, uint8_t ch)
+{
+ int i;
+ int row;
+ int col;
+
+ row = tem->tvs_c_cursor.row;
+ col = tem->tvs_c_cursor.col;
+
+ switch (ch) {
+
+ case 'm': /* select terminal graphics mode */
+ tem_send_data(tem);
+ tem_selgraph(tem);
+ break;
+
+ case '@': /* insert char */
+ tem_setparam(tem, 1, 1);
+ tem_shift(tem, tem->tvs_params[0], TEM_SHIFT_RIGHT);
+ break;
+
+ case 'A': /* cursor up */
+ tem_setparam(tem, 1, 1);
+ tem_mv_cursor(tem, row - tem->tvs_params[0], col);
+ break;
+
+ case 'd': /* VPA - vertical position absolute */
+ tem_setparam(tem, 1, 1);
+ tem_mv_cursor(tem, tem->tvs_params[0] - 1, col);
+ break;
+
+ case 'e': /* VPR - vertical position relative */
+ case 'B': /* cursor down */
+ tem_setparam(tem, 1, 1);
+ tem_mv_cursor(tem, row + tem->tvs_params[0], col);
+ break;
+
+ case 'a': /* HPR - horizontal position relative */
+ case 'C': /* cursor right */
+ tem_setparam(tem, 1, 1);
+ tem_mv_cursor(tem, row, col + tem->tvs_params[0]);
+ break;
+
+ case '`': /* HPA - horizontal position absolute */
+ tem_setparam(tem, 1, 1);
+ tem_mv_cursor(tem, row, tem->tvs_params[0] - 1);
+ break;
+
+ case 'D': /* cursor left */
+ tem_setparam(tem, 1, 1);
+ tem_mv_cursor(tem, row, col - tem->tvs_params[0]);
+ break;
+
+ case 'E': /* CNL cursor next line */
+ tem_setparam(tem, 1, 1);
+ tem_mv_cursor(tem, row + tem->tvs_params[0], 0);
+ break;
+
+ case 'F': /* CPL cursor previous line */
+ tem_setparam(tem, 1, 1);
+ tem_mv_cursor(tem, row - tem->tvs_params[0], 0);
+ break;
+
+ case 'G': /* cursor horizontal position */
+ tem_setparam(tem, 1, 1);
+ tem_mv_cursor(tem, row, tem->tvs_params[0] - 1);
+ break;
+
+ case 'g': /* clear tabs */
+ tem_setparam(tem, 1, 0);
+ tem_clear_tabs(tem, tem->tvs_params[0]);
+ break;
+
+ case 'f': /* HVP Horizontal and Vertical Position */
+ case 'H': /* CUP position cursor */
+ tem_setparam(tem, 2, 1);
+ tem_mv_cursor(tem,
+ tem->tvs_params[0] - 1, tem->tvs_params[1] - 1);
+ break;
+
+ case 'I': /* CHT - Cursor Horizontal Tab */
+ /* Not implemented */
+ break;
+
+ case 'J': /* ED - Erase in Display */
+ tem_send_data(tem);
+ tem_setparam(tem, 1, 0);
+ switch (tem->tvs_params[0]) {
+ case 0:
+ /* erase cursor to end of screen */
+ /* FIRST erase cursor to end of line */
+ tem_clear_chars(tem,
+ tems.ts_c_dimension.width -
+ tem->tvs_c_cursor.col,
+ tem->tvs_c_cursor.row,
+ tem->tvs_c_cursor.col);
+
+ /* THEN erase lines below the cursor */
+ for (row = tem->tvs_c_cursor.row + 1;
+ row < tems.ts_c_dimension.height;
+ row++) {
+ tem_clear_chars(tem,
+ tems.ts_c_dimension.width, row, 0);
+ }
+ break;
+
+ case 1:
+ /* erase beginning of screen to cursor */
+ /* FIRST erase lines above the cursor */
+ for (row = 0;
+ row < tem->tvs_c_cursor.row;
+ row++) {
+ tem_clear_chars(tem,
+ tems.ts_c_dimension.width, row, 0);
+ }
+ /* THEN erase beginning of line to cursor */
+ tem_clear_chars(tem,
+ tem->tvs_c_cursor.col + 1,
+ tem->tvs_c_cursor.row, 0);
+ break;
+
+ case 2:
+ /* erase whole screen */
+ for (row = 0;
+ row < tems.ts_c_dimension.height;
+ row++) {
+ tem_clear_chars(tem,
+ tems.ts_c_dimension.width, row, 0);
+ }
+ break;
+ }
+ break;
+
+ case 'K': /* EL - Erase in Line */
+ tem_send_data(tem);
+ tem_setparam(tem, 1, 0);
+ switch (tem->tvs_params[0]) {
+ case 0:
+ /* erase cursor to end of line */
+ tem_clear_chars(tem,
+ (tems.ts_c_dimension.width -
+ tem->tvs_c_cursor.col),
+ tem->tvs_c_cursor.row,
+ tem->tvs_c_cursor.col);
+ break;
+
+ case 1:
+ /* erase beginning of line to cursor */
+ tem_clear_chars(tem,
+ tem->tvs_c_cursor.col + 1,
+ tem->tvs_c_cursor.row, 0);
+ break;
+
+ case 2:
+ /* erase whole line */
+ tem_clear_chars(tem,
+ tems.ts_c_dimension.width,
+ tem->tvs_c_cursor.row, 0);
+ break;
+ }
+ break;
+
+ case 'L': /* insert line */
+ tem_send_data(tem);
+ tem_setparam(tem, 1, 1);
+ tem_scroll(tem,
+ tem->tvs_c_cursor.row,
+ tems.ts_c_dimension.height - 1,
+ tem->tvs_params[0], TEM_SCROLL_DOWN);
+ break;
+
+ case 'M': /* delete line */
+ tem_send_data(tem);
+ tem_setparam(tem, 1, 1);
+ tem_scroll(tem,
+ tem->tvs_c_cursor.row,
+ tems.ts_c_dimension.height - 1,
+ tem->tvs_params[0], TEM_SCROLL_UP);
+ break;
+
+ case 'P': /* DCH - delete char */
+ tem_setparam(tem, 1, 1);
+ tem_shift(tem, tem->tvs_params[0], TEM_SHIFT_LEFT);
+ break;
+
+ case 'S': /* scroll up */
+ tem_send_data(tem);
+ tem_setparam(tem, 1, 1);
+ tem_scroll(tem, 0,
+ tems.ts_c_dimension.height - 1,
+ tem->tvs_params[0], TEM_SCROLL_UP);
+ break;
+
+ case 'T': /* scroll down */
+ tem_send_data(tem);
+ tem_setparam(tem, 1, 1);
+ tem_scroll(tem, 0,
+ tems.ts_c_dimension.height - 1,
+ tem->tvs_params[0], TEM_SCROLL_DOWN);
+ break;
+
+ case 'X': /* erase char */
+ tem_setparam(tem, 1, 1);
+ tem_clear_chars(tem,
+ tem->tvs_params[0],
+ tem->tvs_c_cursor.row,
+ tem->tvs_c_cursor.col);
+ break;
+
+ case 'Z': /* cursor backward tabulation */
+ tem_setparam(tem, 1, 1);
+
+ /*
+ * Rule exception - We do sanity checking here.
+ *
+ * Restrict the count to a sane value to keep from
+ * looping for a long time. There can't be more than one
+ * tab stop per column, so use that as a limit.
+ */
+ if (tem->tvs_params[0] > tems.ts_c_dimension.width)
+ tem->tvs_params[0] = tems.ts_c_dimension.width;
+
+ for (i = 0; i < tem->tvs_params[0]; i++)
+ tem_back_tab(tem);
+ break;
+ }
+ tem->tvs_state = A_STATE_START;
+}
+
+
+/*
+ * Gather the parameters of an ANSI escape sequence
+ */
+static void
+tem_getparams(struct tem_vt_state *tem, uint8_t ch)
+{
+ if (isdigit(ch)) {
+ tem->tvs_paramval = ((tem->tvs_paramval * 10) + (ch - '0'));
+ tem->tvs_gotparam = B_TRUE; /* Remember got parameter */
+ return; /* Return immediately */
+ } else if (tem->tvs_state == A_STATE_CSI_EQUAL ||
+ tem->tvs_state == A_STATE_CSI_QMARK) {
+ tem->tvs_state = A_STATE_START;
+ } else {
+ if (tem->tvs_curparam < TEM_MAXPARAMS) {
+ if (tem->tvs_gotparam) {
+ /* get the parameter value */
+ tem->tvs_params[tem->tvs_curparam] =
+ tem->tvs_paramval;
+ }
+ tem->tvs_curparam++;
+ }
+
+ if (ch == ';') {
+ /* Restart parameter search */
+ tem->tvs_gotparam = B_FALSE;
+ tem->tvs_paramval = 0; /* No parame value yet */
+ } else {
+ /* Handle escape sequence */
+ tem_chkparam(tem, ch);
+ }
+ }
+}
+
+/*
+ * Add character to internal buffer.
+ * When its full, send it to the next layer.
+ */
+static void
+tem_outch(struct tem_vt_state *tem, tem_char_t ch)
+{
+ text_color_t fg;
+ text_color_t bg;
+ text_attr_t attr;
+
+ /* buffer up the character until later */
+ tem_get_attr(tem, &fg, &bg, &attr, TEM_ATTR_REVERSE);
+ tem->tvs_outbuf[tem->tvs_outindex].tc_char = ch | TEM_ATTR(attr);
+ tem->tvs_outbuf[tem->tvs_outindex].tc_fg_color = fg;
+ tem->tvs_outbuf[tem->tvs_outindex].tc_bg_color = bg;
+ tem->tvs_outindex++;
+ tem->tvs_c_cursor.col++;
+ if (tem->tvs_c_cursor.col >= tems.ts_c_dimension.width) {
+ tem_send_data(tem);
+ tem_new_line(tem);
+ }
+}
+
+static void
+tem_new_line(struct tem_vt_state *tem)
+{
+ tem_cr(tem);
+ tem_lf(tem);
+}
+
+static void
+tem_cr(struct tem_vt_state *tem)
+{
+ tem->tvs_c_cursor.col = 0;
+ tem_align_cursor(tem);
+}
+
+static void
+tem_lf(struct tem_vt_state *tem)
+{
+ int row;
+
+ /*
+ * Sanity checking notes:
+ * . a_nscroll was validated when it was set.
+ * . Regardless of that, tem_scroll and tem_mv_cursor
+ * will prevent anything bad from happening.
+ */
+ row = tem->tvs_c_cursor.row + 1;
+
+ if (row >= tems.ts_c_dimension.height) {
+ if (tem->tvs_nscroll != 0) {
+ tem_scroll(tem, 0,
+ tems.ts_c_dimension.height - 1,
+ tem->tvs_nscroll, TEM_SCROLL_UP);
+ row = tems.ts_c_dimension.height -
+ tem->tvs_nscroll;
+ } else { /* no scroll */
+ /*
+ * implement Esc[#r when # is zero. This means no
+ * scroll but just return cursor to top of screen,
+ * do not clear screen.
+ */
+ row = 0;
+ }
+ }
+
+ tem_mv_cursor(tem, row, tem->tvs_c_cursor.col);
+
+ if (tem->tvs_nscroll == 0) {
+ /* erase rest of cursor line */
+ tem_clear_chars(tem,
+ tems.ts_c_dimension.width -
+ tem->tvs_c_cursor.col,
+ tem->tvs_c_cursor.row,
+ tem->tvs_c_cursor.col);
+
+ }
+
+ tem_align_cursor(tem);
+}
+
+static void
+tem_send_data(struct tem_vt_state *tem)
+{
+ if (tem->tvs_outindex == 0) {
+ tem_align_cursor(tem);
+ return;
+ }
+
+ tem_virtual_display(tem, tem->tvs_outbuf, tem->tvs_outindex,
+ tem->tvs_s_cursor.row, tem->tvs_s_cursor.col);
+
+ if (tem->tvs_isactive) {
+ /*
+ * Call the primitive to render this data.
+ */
+ tem_callback_display(tem,
+ tem->tvs_outbuf, tem->tvs_outindex,
+ tem->tvs_s_cursor.row, tem->tvs_s_cursor.col);
+ }
+
+ tem->tvs_outindex = 0;
+
+ tem_align_cursor(tem);
+}
+
+
+/*
+ * We have just done something to the current output point. Reset the start
+ * point for the buffered data in a_outbuf. There shouldn't be any data
+ * buffered yet.
+ */
+static void
+tem_align_cursor(struct tem_vt_state *tem)
+{
+ tem->tvs_s_cursor.row = tem->tvs_c_cursor.row;
+ tem->tvs_s_cursor.col = tem->tvs_c_cursor.col;
+}
+
+/*
+ * State machine parser based on the current state and character input
+ * major terminations are to control character or normal character
+ */
+
+static void
+tem_parse(struct tem_vt_state *tem, tem_char_t ch)
+{
+ int i;
+
+ if (tem->tvs_state == A_STATE_START) { /* Normal state? */
+ if (ch == A_CSI || ch == A_ESC || ch < ' ') {
+ /* Control */
+ tem_control(tem, ch);
+ } else {
+ /* Display */
+ tem_outch(tem, ch);
+ }
+ return;
+ }
+
+ /* In <ESC> sequence */
+ if (tem->tvs_state != A_STATE_ESC) { /* Need to get parameters? */
+ if (tem->tvs_state != A_STATE_CSI) {
+ tem_getparams(tem, ch);
+ return;
+ }
+
+ switch (ch) {
+ case '?':
+ tem->tvs_state = A_STATE_CSI_QMARK;
+ return;
+ case '=':
+ tem->tvs_state = A_STATE_CSI_EQUAL;
+ return;
+ case 's':
+ /*
+ * As defined below, this sequence
+ * saves the cursor. However, Sun
+ * defines ESC[s as reset. We resolved
+ * the conflict by selecting reset as it
+ * is exported in the termcap file for
+ * sun-mon, while the "save cursor"
+ * definition does not exist anywhere in
+ * /etc/termcap.
+ * However, having no coherent
+ * definition of reset, we have not
+ * implemented it.
+ */
+
+ /*
+ * Original code
+ * tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
+ * tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
+ * tem->tvs_state = A_STATE_START;
+ */
+
+ tem->tvs_state = A_STATE_START;
+ return;
+ case 'u':
+ tem_mv_cursor(tem, tem->tvs_r_cursor.row,
+ tem->tvs_r_cursor.col);
+ tem->tvs_state = A_STATE_START;
+ return;
+ case 'p': /* sunbow */
+ tem_send_data(tem);
+ /*
+ * Don't set anything if we are
+ * already as we want to be.
+ */
+ if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
+ tem->tvs_flags &= ~TEM_ATTR_SCREEN_REVERSE;
+ /*
+ * If we have switched the characters to be the
+ * inverse from the screen, then switch them as
+ * well to keep them the inverse of the screen.
+ */
+ if (tem->tvs_flags & TEM_ATTR_REVERSE)
+ tem->tvs_flags &= ~TEM_ATTR_REVERSE;
+ else
+ tem->tvs_flags |= TEM_ATTR_REVERSE;
+ }
+ tem_cls(tem);
+ tem->tvs_state = A_STATE_START;
+ return;
+ case 'q': /* sunwob */
+ tem_send_data(tem);
+ /*
+ * Don't set anything if we are
+ * already where as we want to be.
+ */
+ if (!(tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE)) {
+ tem->tvs_flags |= TEM_ATTR_SCREEN_REVERSE;
+ /*
+ * If we have switched the characters to be the
+ * inverse from the screen, then switch them as
+ * well to keep them the inverse of the screen.
+ */
+ if (!(tem->tvs_flags & TEM_ATTR_REVERSE))
+ tem->tvs_flags |= TEM_ATTR_REVERSE;
+ else
+ tem->tvs_flags &= ~TEM_ATTR_REVERSE;
+ }
+
+ tem_cls(tem);
+ tem->tvs_state = A_STATE_START;
+ return;
+ case 'r': /* sunscrl */
+ /*
+ * Rule exception: check for validity here.
+ */
+ tem->tvs_nscroll = tem->tvs_paramval;
+ if (tem->tvs_nscroll > tems.ts_c_dimension.height)
+ tem->tvs_nscroll = tems.ts_c_dimension.height;
+ if (tem->tvs_nscroll < 0)
+ tem->tvs_nscroll = 1;
+ tem->tvs_state = A_STATE_START;
+ return;
+ default:
+ tem_getparams(tem, ch);
+ return;
+ }
+ }
+
+ /* Previous char was <ESC> */
+ if (ch == '[') {
+ tem->tvs_curparam = 0;
+ tem->tvs_paramval = 0;
+ tem->tvs_gotparam = B_FALSE;
+ /* clear the parameters */
+ for (i = 0; i < TEM_MAXPARAMS; i++)
+ tem->tvs_params[i] = -1;
+ tem->tvs_state = A_STATE_CSI;
+ } else if (ch == 'Q') { /* <ESC>Q ? */
+ tem->tvs_state = A_STATE_START;
+ } else if (ch == 'C') { /* <ESC>C ? */
+ tem->tvs_state = A_STATE_START;
+ } else {
+ tem->tvs_state = A_STATE_START;
+ if (ch == 'c') {
+ /* ESC c resets display */
+ tem_reset_display(tem, B_TRUE, B_TRUE);
+ } else if (ch == 'H') {
+ /* ESC H sets a tab */
+ tem_set_tab(tem);
+ } else if (ch == '7') {
+ /* ESC 7 Save Cursor position */
+ tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
+ tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
+ } else if (ch == '8') {
+ /* ESC 8 Restore Cursor position */
+ tem_mv_cursor(tem, tem->tvs_r_cursor.row,
+ tem->tvs_r_cursor.col);
+ /* check for control chars */
+ } else if (ch < ' ') {
+ tem_control(tem, ch);
+ } else {
+ tem_outch(tem, ch);
+ }
+ }
+}
+
+/* ARGSUSED */
+static void
+tem_bell(struct tem_vt_state *tem __unused)
+{
+ /* (void) beep(BEEP_CONSOLE); */
+}
+
+
+static void
+tem_scroll(struct tem_vt_state *tem, int start, int end, int count,
+ int direction)
+{
+ int row;
+ int lines_affected;
+
+ lines_affected = end - start + 1;
+ if (count > lines_affected)
+ count = lines_affected;
+ if (count <= 0)
+ return;
+
+ switch (direction) {
+ case TEM_SCROLL_UP:
+ if (count < lines_affected) {
+ tem_copy_area(tem, 0, start + count,
+ tems.ts_c_dimension.width - 1, end, 0, start);
+ }
+ for (row = (end - count) + 1; row <= end; row++) {
+ tem_clear_chars(tem, tems.ts_c_dimension.width, row, 0);
+ }
+ break;
+
+ case TEM_SCROLL_DOWN:
+ if (count < lines_affected) {
+ tem_copy_area(tem, 0, start,
+ tems.ts_c_dimension.width - 1,
+ end - count, 0, start + count);
+ }
+ for (row = start; row < start + count; row++) {
+ tem_clear_chars(tem, tems.ts_c_dimension.width, row, 0);
+ }
+ break;
+ }
+}
+
+static int
+tem_copy_width(term_char_t *src, term_char_t *dst, int cols)
+{
+ int width = cols - 1;
+
+ while (width >= 0) {
+ /* We do not have image bits to compare, stop there. */
+ if (TEM_CHAR_ATTR(src[width].tc_char) == TEM_ATTR_IMAGE ||
+ TEM_CHAR_ATTR(dst[width].tc_char) == TEM_ATTR_IMAGE)
+ break;
+
+ /*
+ * Find difference on line, compare char with its attributes
+ * and colors.
+ */
+ if (src[width].tc_char != dst[width].tc_char ||
+ src[width].tc_fg_color.n != dst[width].tc_fg_color.n ||
+ src[width].tc_bg_color.n != dst[width].tc_bg_color.n) {
+ break;
+ }
+ width--;
+ }
+ return (width + 1);
+}
+
+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)
+{
+ size_t soffset, toffset;
+ term_char_t *src, *dst;
+ int rows;
+ int cols;
+
+ if (s_col < 0 || s_row < 0 ||
+ e_col < 0 || e_row < 0 ||
+ t_col < 0 || t_row < 0 ||
+ s_col >= tems.ts_c_dimension.width ||
+ e_col >= tems.ts_c_dimension.width ||
+ t_col >= tems.ts_c_dimension.width ||
+ s_row >= tems.ts_c_dimension.height ||
+ e_row >= tems.ts_c_dimension.height ||
+ t_row >= tems.ts_c_dimension.height)
+ return;
+
+ if (s_row > e_row || s_col > e_col)
+ return;
+
+ rows = e_row - s_row + 1;
+ cols = e_col - s_col + 1;
+ if (t_row + rows > tems.ts_c_dimension.height ||
+ t_col + cols > tems.ts_c_dimension.width)
+ return;
+
+ if (tem->tvs_screen_buf == NULL) {
+ if (tem->tvs_isactive) {
+ tem_callback_copy(tem, s_col, s_row,
+ e_col, e_row, t_col, t_row);
+ }
+ return;
+ }
+
+ soffset = s_col + s_row * tems.ts_c_dimension.width;
+ toffset = t_col + t_row * tems.ts_c_dimension.width;
+ src = tem->tvs_screen_buf + soffset;
+ dst = tem->tvs_screen_buf + toffset;
+
+ /*
+ * Copy line by line. We determine the length by comparing the
+ * screen content from cached text in tvs_screen_buf.
+ */
+ if (toffset <= soffset) {
+ for (int i = 0; i < rows; i++) {
+ int increment = i * tems.ts_c_dimension.width;
+ int width;
+
+ width = tem_copy_width(src + increment,
+ dst + increment, cols);
+ memmove(dst + increment, src + increment,
+ width * sizeof (term_char_t));
+ if (tem->tvs_isactive) {
+ tem_callback_copy(tem, s_col, s_row + i,
+ e_col - cols + width, s_row + i,
+ t_col, t_row + i);
+ }
+ }
+ } else {
+ for (int i = rows - 1; i >= 0; i--) {
+ int increment = i * tems.ts_c_dimension.width;
+ int width;
+
+ width = tem_copy_width(src + increment,
+ dst + increment, cols);
+ memmove(dst + increment, src + increment,
+ width * sizeof (term_char_t));
+ if (tem->tvs_isactive) {
+ tem_callback_copy(tem, s_col, s_row + i,
+ e_col - cols + width, s_row + i,
+ t_col, t_row + i);
+ }
+ }
+ }
+}
+
+static void
+tem_clear_chars(struct tem_vt_state *tem, int count, screen_pos_t row,
+ screen_pos_t col)
+{
+ if (row < 0 || row >= tems.ts_c_dimension.height ||
+ col < 0 || col >= tems.ts_c_dimension.width ||
+ count < 0)
+ return;
+
+ /*
+ * Note that very large values of "count" could cause col+count
+ * to overflow, so we check "count" independently.
+ */
+ if (count > tems.ts_c_dimension.width ||
+ col + count > tems.ts_c_dimension.width)
+ count = tems.ts_c_dimension.width - col;
+
+ tem_virtual_cls(tem, count, row, col);
+
+ if (!tem->tvs_isactive)
+ return;
+
+ tem_callback_cls(tem, count, row, col);
+}
+
+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)
+{
+ struct vis_consdisplay da;
+ int i;
+ tem_char_t c;
+ text_color_t bg, fg;
+
+ if (count == 0)
+ return;
+
+ da.data = (unsigned char *)&c;
+ da.width = 1;
+ da.row = row;
+ da.col = col;
+
+ for (i = 0; i < count; i++) {
+ tem_get_color(tem, &fg, &bg, &string[i]);
+ tem_set_color(&fg, &da.fg_color);
+ tem_set_color(&bg, &da.bg_color);
+ c = TEM_CHAR(string[i].tc_char);
+ tems_display(&da);
+ da.col++;
+ }
+}
+
+/*
+ * This function is used to mark a rectangular image area so the scrolling
+ * will know we need to copy the data from there.
+ */
+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)
+{
+ screen_pos_t i, j;
+ term_char_t c;
+
+ c.tc_char = TEM_ATTR(TEM_ATTR_IMAGE);
+
+ for (i = s_row; i <= e_row; i++) {
+ for (j = s_col; j <= e_col; j++) {
+ tem_virtual_display(tem, &c, 1, i, j);
+ }
+ }
+}
+
+/*ARGSUSED*/
+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)
+{
+ struct vis_conscopy da;
+
+ da.s_row = s_row;
+ da.s_col = s_col;
+ da.e_row = e_row;
+ da.e_col = e_col;
+ da.t_row = t_row;
+ da.t_col = t_col;
+ tems_copy(&da);
+}
+
+static void
+tem_text_cls(struct tem_vt_state *tem,
+ int count, screen_pos_t row, screen_pos_t col)
+{
+ text_attr_t attr;
+ term_char_t c;
+ int i;
+
+ tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
+ TEM_ATTR_SCREEN_REVERSE);
+ c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
+
+ if (count > tems.ts_c_dimension.width ||
+ col + count > tems.ts_c_dimension.width)
+ count = tems.ts_c_dimension.width - col;
+
+ for (i = 0; i < count; i++)
+ tem_text_display(tem, &c, 1, row, col++);
+
+}
+
+static void
+tem_pix_display(struct tem_vt_state *tem,
+ term_char_t *string, int count,
+ screen_pos_t row, screen_pos_t col)
+{
+ struct vis_consdisplay da;
+ int i;
+
+ da.data = (uint8_t *)tem->tvs_pix_data;
+ da.width = tems.ts_font.vf_width;
+ da.height = tems.ts_font.vf_height;
+ da.row = (row * da.height) + tems.ts_p_offset.y;
+ da.col = (col * da.width) + tems.ts_p_offset.x;
+
+ for (i = 0; i < count; i++) {
+ tem_callback_bit2pix(tem, &string[i]);
+ tems_display(&da);
+ da.col += da.width;
+ }
+}
+
+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)
+{
+ struct vis_conscopy ma;
+ static boolean_t need_clear = B_TRUE;
+
+ if (need_clear && tem->tvs_first_line > 0) {
+ /*
+ * Clear OBP output above our kernel console term
+ * when our kernel console term begins to scroll up,
+ * we hope it is user friendly.
+ * (Also see comments on tem_pix_clear_prom_output)
+ *
+ * This is only one time call.
+ */
+ tem_pix_clear_prom_output(tem);
+ }
+ need_clear = B_FALSE;
+
+ ma.s_row = s_row * tems.ts_font.vf_height + tems.ts_p_offset.y;
+ ma.e_row = (e_row + 1) * tems.ts_font.vf_height +
+ tems.ts_p_offset.y - 1;
+ ma.t_row = t_row * tems.ts_font.vf_height + tems.ts_p_offset.y;
+
+ /*
+ * Check if we're in process of clearing OBP's columns area,
+ * which only happens when term scrolls up a whole line.
+ */
+ if (tem->tvs_first_line > 0 && t_row < s_row && t_col == 0 &&
+ e_col == tems.ts_c_dimension.width - 1) {
+ /*
+ * We need to clear OBP's columns area outside our kernel
+ * console term. So that we set ma.e_col to entire row here.
+ */
+ ma.s_col = s_col * tems.ts_font.vf_width;
+ ma.e_col = tems.ts_p_dimension.width - 1;
+
+ ma.t_col = t_col * tems.ts_font.vf_width;
+ } else {
+ ma.s_col = s_col * tems.ts_font.vf_width + tems.ts_p_offset.x;
+ ma.e_col = (e_col + 1) * tems.ts_font.vf_width +
+ tems.ts_p_offset.x - 1;
+ ma.t_col = t_col * tems.ts_font.vf_width + tems.ts_p_offset.x;
+ }
+
+ tems_copy(&ma);
+
+ if (tem->tvs_first_line > 0 && t_row < s_row) {
+ /* We have scrolled up (s_row - t_row) rows. */
+ tem->tvs_first_line -= (s_row - t_row);
+ if (tem->tvs_first_line <= 0) {
+ /* All OBP rows have been cleared. */
+ tem->tvs_first_line = 0;
+ }
+ }
+}
+
+static void
+tem_pix_bit2pix(struct tem_vt_state *tem, term_char_t *c)
+{
+ text_color_t fg, bg;
+
+ tem_get_color(tem, &fg, &bg, c);
+ bit_to_pix32(tem, c->tc_char, fg, bg);
+}
+
+
+/*
+ * This function only clears count of columns in one row
+ */
+static void
+tem_pix_cls(struct tem_vt_state *tem, int count,
+ screen_pos_t row, screen_pos_t col)
+{
+ tem_pix_cls_range(tem, row, 1, tems.ts_p_offset.y,
+ col, count, tems.ts_p_offset.x, B_FALSE);
+}
+
+/*
+ * This function clears OBP output above our kernel console term area
+ * because OBP's term may have a bigger terminal window than that of
+ * our kernel console term. So we need to clear OBP output garbage outside
+ * of our kernel console term at a proper time, which is when the first
+ * row output of our kernel console term scrolls at the first screen line.
+ *
+ * _________________________________
+ * | _____________________ | ---> OBP's bigger term window
+ * | | | |
+ * |___| | |
+ * | | | | |
+ * | | | | |
+ * |_|_|___________________|_______|
+ * | | | ---> first line
+ * | |___________________|---> our kernel console term window
+ * |
+ * |---> columns area to be cleared
+ *
+ * This function only takes care of the output above our kernel console term,
+ * and tem_prom_scroll_up takes care of columns area outside of our kernel
+ * console term.
+ */
+static void
+tem_pix_clear_prom_output(struct tem_vt_state *tem)
+{
+ int nrows, ncols, width, height, offset;
+
+ width = tems.ts_font.vf_width;
+ height = tems.ts_font.vf_height;
+ offset = tems.ts_p_offset.y % height;
+
+ nrows = tems.ts_p_offset.y / height;
+ ncols = (tems.ts_p_dimension.width + (width - 1)) / width;
+
+ if (nrows > 0)
+ tem_pix_cls_range(tem, 0, nrows, offset, 0, ncols, 0,
+ B_FALSE);
+}
+
+/*
+ * Clear the whole screen and reset the cursor to start point.
+ */
+static void
+tem_cls(struct tem_vt_state *tem)
+{
+ struct vis_consclear cl;
+ text_color_t fg_color;
+ text_color_t bg_color;
+ text_attr_t attr;
+ term_char_t c;
+ int row;
+
+ for (row = 0; row < tems.ts_c_dimension.height; row++) {
+ tem_virtual_cls(tem, tems.ts_c_dimension.width, row, 0);
+ }
+
+ if (!tem->tvs_isactive)
+ return;
+
+ tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
+ TEM_ATTR_SCREEN_REVERSE);
+ c.tc_char = TEM_ATTR(attr);
+
+ tem_get_color(tem, &fg_color, &bg_color, &c);
+ tem_set_color(&bg_color, &cl.bg_color);
+ (void) tems_cls(&cl);
+
+ tem->tvs_c_cursor.row = 0;
+ tem->tvs_c_cursor.col = 0;
+ tem_align_cursor(tem);
+}
+
+static void
+tem_back_tab(struct tem_vt_state *tem)
+{
+ int i;
+ screen_pos_t tabstop;
+
+ tabstop = 0;
+
+ for (i = tem->tvs_ntabs - 1; i >= 0; i--) {
+ if (tem->tvs_tabs[i] < tem->tvs_c_cursor.col) {
+ tabstop = tem->tvs_tabs[i];
+ break;
+ }
+ }
+
+ tem_mv_cursor(tem, tem->tvs_c_cursor.row, tabstop);
+}
+
+static void
+tem_tab(struct tem_vt_state *tem)
+{
+ size_t i;
+ screen_pos_t tabstop;
+
+ tabstop = tems.ts_c_dimension.width - 1;
+
+ for (i = 0; i < tem->tvs_ntabs; i++) {
+ if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
+ tabstop = tem->tvs_tabs[i];
+ break;
+ }
+ }
+
+ tem_mv_cursor(tem, tem->tvs_c_cursor.row, tabstop);
+}
+
+static void
+tem_set_tab(struct tem_vt_state *tem)
+{
+ size_t i, j;
+
+ if (tem->tvs_ntabs == tem->tvs_maxtab)
+ return;
+ if (tem->tvs_ntabs == 0 ||
+ tem->tvs_tabs[tem->tvs_ntabs] < tem->tvs_c_cursor.col) {
+ tem->tvs_tabs[tem->tvs_ntabs++] = tem->tvs_c_cursor.col;
+ return;
+ }
+ for (i = 0; i < tem->tvs_ntabs; i++) {
+ if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col)
+ return;
+ if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
+ for (j = tem->tvs_ntabs - 1; j >= i; j--)
+ tem->tvs_tabs[j+ 1] = tem->tvs_tabs[j];
+ tem->tvs_tabs[i] = tem->tvs_c_cursor.col;
+ tem->tvs_ntabs++;
+ return;
+ }
+ }
+}
+
+static void
+tem_clear_tabs(struct tem_vt_state *tem, int action)
+{
+ size_t i, j;
+
+ switch (action) {
+ case 3: /* clear all tabs */
+ tem->tvs_ntabs = 0;
+ break;
+ case 0: /* clr tab at cursor */
+
+ for (i = 0; i < tem->tvs_ntabs; i++) {
+ if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) {
+ tem->tvs_ntabs--;
+ for (j = i; j < tem->tvs_ntabs; j++)
+ tem->tvs_tabs[j] = tem->tvs_tabs[j + 1];
+ return;
+ }
+ }
+ break;
+ }
+}
+
+static void
+tem_mv_cursor(struct tem_vt_state *tem, int row, int col)
+{
+ /*
+ * Sanity check and bounds enforcement. Out of bounds requests are
+ * clipped to the screen boundaries. This seems to be what SPARC
+ * does.
+ */
+ if (row < 0)
+ row = 0;
+ if (row >= tems.ts_c_dimension.height)
+ row = tems.ts_c_dimension.height - 1;
+ if (col < 0)
+ col = 0;
+ if (col >= tems.ts_c_dimension.width)
+ col = tems.ts_c_dimension.width - 1;
+
+ tem_send_data(tem);
+ tem->tvs_c_cursor.row = (screen_pos_t)row;
+ tem->tvs_c_cursor.col = (screen_pos_t)col;
+ tem_align_cursor(tem);
+}
+
+/* ARGSUSED */
+static void
+tem_reset_emulator(struct tem_vt_state *tem, boolean_t init_color)
+{
+ int j;
+
+ tem->tvs_c_cursor.row = 0;
+ tem->tvs_c_cursor.col = 0;
+ tem->tvs_r_cursor.row = 0;
+ tem->tvs_r_cursor.col = 0;
+ tem->tvs_s_cursor.row = 0;
+ tem->tvs_s_cursor.col = 0;
+ tem->tvs_outindex = 0;
+ tem->tvs_state = A_STATE_START;
+ tem->tvs_gotparam = B_FALSE;
+ tem->tvs_curparam = 0;
+ tem->tvs_paramval = 0;
+ tem->tvs_nscroll = 1;
+
+ if (init_color) {
+ /* use initial settings */
+ tem->tvs_alpha = 0xff;
+ tem->tvs_fg_color = tems.ts_init_color.fg_color;
+ tem->tvs_bg_color = tems.ts_init_color.bg_color;
+ tem->tvs_flags = tems.ts_init_color.a_flags;
+ }
+
+ /*
+ * set up the initial tab stops
+ */
+ tem->tvs_ntabs = 0;
+ for (j = 8; j < tems.ts_c_dimension.width; j += 8)
+ tem->tvs_tabs[tem->tvs_ntabs++] = (screen_pos_t)j;
+
+ for (j = 0; j < TEM_MAXPARAMS; j++)
+ tem->tvs_params[j] = 0;
+}
+
+static void
+tem_reset_display(struct tem_vt_state *tem,
+ boolean_t clear_txt, boolean_t init_color)
+{
+ tem_reset_emulator(tem, init_color);
+
+ if (clear_txt) {
+ if (tem->tvs_isactive)
+ tem_callback_cursor(tem, VIS_HIDE_CURSOR);
+
+ tem_cls(tem);
+
+ if (tem->tvs_isactive)
+ tem_callback_cursor(tem, VIS_DISPLAY_CURSOR);
+ }
+}
+
+static void
+tem_shift(struct tem_vt_state *tem, int count, int direction)
+{
+ int rest_of_line;
+
+ rest_of_line = tems.ts_c_dimension.width - tem->tvs_c_cursor.col;
+ if (count > rest_of_line)
+ count = rest_of_line;
+
+ if (count <= 0)
+ return;
+
+ switch (direction) {
+ case TEM_SHIFT_LEFT:
+ if (count < rest_of_line) {
+ tem_copy_area(tem,
+ tem->tvs_c_cursor.col + count,
+ tem->tvs_c_cursor.row,
+ tems.ts_c_dimension.width - 1,
+ tem->tvs_c_cursor.row,
+ tem->tvs_c_cursor.col,
+ tem->tvs_c_cursor.row);
+ }
+
+ tem_clear_chars(tem, count, tem->tvs_c_cursor.row,
+ (tems.ts_c_dimension.width - count));
+ break;
+ case TEM_SHIFT_RIGHT:
+ if (count < rest_of_line) {
+ tem_copy_area(tem,
+ tem->tvs_c_cursor.col,
+ tem->tvs_c_cursor.row,
+ tems.ts_c_dimension.width - count - 1,
+ tem->tvs_c_cursor.row,
+ tem->tvs_c_cursor.col + count,
+ tem->tvs_c_cursor.row);
+ }
+
+ tem_clear_chars(tem, count, tem->tvs_c_cursor.row,
+ tem->tvs_c_cursor.col);
+ break;
+ }
+}
+
+static void
+tem_text_cursor(struct tem_vt_state *tem, short action)
+{
+ struct vis_conscursor ca;
+
+ ca.row = tem->tvs_c_cursor.row;
+ ca.col = tem->tvs_c_cursor.col;
+ ca.action = action;
+
+ tems_cursor(&ca);
+
+ if (action == VIS_GET_CURSOR) {
+ tem->tvs_c_cursor.row = ca.row;
+ tem->tvs_c_cursor.col = ca.col;
+ }
+}
+
+static void
+tem_pix_cursor(struct tem_vt_state *tem, short action)
+{
+ struct vis_conscursor ca;
+ text_color_t fg, bg;
+ term_char_t c;
+ text_attr_t attr;
+
+ ca.row = tem->tvs_c_cursor.row * tems.ts_font.vf_height +
+ tems.ts_p_offset.y;
+ ca.col = tem->tvs_c_cursor.col * tems.ts_font.vf_width +
+ tems.ts_p_offset.x;
+ ca.width = tems.ts_font.vf_width;
+ ca.height = tems.ts_font.vf_height;
+
+ tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
+ TEM_ATTR_REVERSE);
+ c.tc_char = TEM_ATTR(attr);
+
+ tem_get_color(tem, &fg, &bg, &c);
+ tem_set_color(&fg, &ca.fg_color);
+ tem_set_color(&bg, &ca.bg_color);
+
+ ca.action = action;
+
+ tems_cursor(&ca);
+
+ if (action == VIS_GET_CURSOR) {
+ tem->tvs_c_cursor.row = 0;
+ tem->tvs_c_cursor.col = 0;
+
+ if (ca.row != 0) {
+ tem->tvs_c_cursor.row = (ca.row - tems.ts_p_offset.y) /
+ tems.ts_font.vf_height;
+ }
+ if (ca.col != 0) {
+ tem->tvs_c_cursor.col = (ca.col - tems.ts_p_offset.x) /
+ tems.ts_font.vf_width;
+ }
+ }
+}
+
+static void
+bit_to_pix32(struct tem_vt_state *tem,
+ tem_char_t c, text_color_t fg, text_color_t bg)
+{
+ uint32_t *dest;
+
+ dest = (uint32_t *)tem->tvs_pix_data;
+ font_bit_to_pix32(&tems.ts_font, dest, c, fg.n, bg.n);
+}
+
+/*
+ * flag: TEM_ATTR_SCREEN_REVERSE or TEM_ATTR_REVERSE
+ */
+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)
+{
+ if (tem->tvs_flags & flag) {
+ *fg = tem->tvs_bg_color;
+ *bg = tem->tvs_fg_color;
+ } else {
+ *fg = tem->tvs_fg_color;
+ *bg = tem->tvs_bg_color;
+ }
+
+ if (attr != NULL)
+ *attr = tem->tvs_flags;
+}
+
+static void
+tem_get_color(struct tem_vt_state *tem, text_color_t *fg, text_color_t *bg,
+ term_char_t *c)
+{
+ bool bold_font;
+
+ *fg = c->tc_fg_color;
+ *bg = c->tc_bg_color;
+
+ bold_font = tems.ts_font.vf_map_count[VFNT_MAP_BOLD] != 0;
+
+ /*
+ * If we have both normal and bold font components,
+ * we use bold font for TEM_ATTR_BOLD.
+ * The bright color is traditionally used with TEM_ATTR_BOLD,
+ * in case there is no bold font.
+ */
+ if (!TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_FG) &&
+ c->tc_fg_color.n < XLATE_NCOLORS) {
+ if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_BRIGHT_FG) ||
+ (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_BOLD) && !bold_font))
+ fg->n = brt_xlate[c->tc_fg_color.n];
+ else
+ fg->n = dim_xlate[c->tc_fg_color.n];
+ }
+
+ if (!TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_BG) &&
+ c->tc_bg_color.n < XLATE_NCOLORS) {
+ if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_BRIGHT_BG))
+ bg->n = brt_xlate[c->tc_bg_color.n];
+ else
+ bg->n = dim_xlate[c->tc_bg_color.n];
+ }
+
+ if (tems.ts_display_mode == VIS_TEXT)
+ return;
+
+ /*
+ * Translate fg and bg to RGB colors.
+ */
+ if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_FG)) {
+ fg->n = rgb_to_color(&rgb_info,
+ fg->rgb.a, fg->rgb.r, fg->rgb.g, fg->rgb.b);
+ } else {
+ fg->n = rgb_color_map(&rgb_info, fg->n, tem->tvs_alpha);
+ }
+
+ if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_BG)) {
+ bg->n = rgb_to_color(&rgb_info,
+ bg->rgb.a, bg->rgb.r, bg->rgb.g, bg->rgb.b);
+ } else {
+ bg->n = rgb_color_map(&rgb_info, bg->n, tem->tvs_alpha);
+ }
+}
+
+static void
+tem_set_color(text_color_t *t, color_t *c)
+{
+ switch (tems.ts_pdepth) {
+ case 4:
+ c->four = t->n & 0xFF;
+ break;
+ default:
+ /* gfx module is expecting all pixel data in 32-bit colors */
+ *(uint32_t *)c = t->n;
+ break;
+ }
+}
+
+void
+tem_get_colors(tem_vt_state_t tem_arg, text_color_t *fg, text_color_t *bg)
+{
+ struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
+ text_attr_t attr;
+ term_char_t c;
+
+ tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
+ TEM_ATTR_REVERSE);
+ c.tc_char = TEM_ATTR(attr);
+ tem_get_color(tem, fg, bg, &c);
+}
+
+/*
+ * Clear a rectangle of screen for pixel mode.
+ *
+ * arguments:
+ * row: start row#
+ * nrows: the number of rows to clear
+ * offset_y: the offset of height in pixels to begin clear
+ * col: start col#
+ * ncols: the number of cols to clear
+ * offset_x: the offset of width in pixels to begin clear
+ * scroll_up: whether this function is called during sroll up,
+ * which is called only once.
+ */
+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)
+{
+ struct vis_consdisplay da;
+ int i, j;
+ int row_add = 0;
+ term_char_t c;
+ text_attr_t attr;
+
+ if (sroll_up)
+ row_add = tems.ts_c_dimension.height - 1;
+
+ da.width = tems.ts_font.vf_width;
+ da.height = tems.ts_font.vf_height;
+
+ tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
+ TEM_ATTR_SCREEN_REVERSE);
+ /* Make sure we will not draw underlines */
+ c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
+
+ tem_callback_bit2pix(tem, &c);
+ da.data = (uint8_t *)tem->tvs_pix_data;
+
+ for (i = 0; i < nrows; i++, row++) {
+ da.row = (row + row_add) * da.height + offset_y;
+ da.col = col * da.width + offset_x;
+ for (j = 0; j < ncols; j++) {
+ tems_display(&da);
+ da.col += da.width;
+ }
+ }
+}
+
+/*
+ * virtual screen operations
+ */
+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)
+{
+ size_t i, width;
+ term_char_t *addr;
+
+ if (tem->tvs_screen_buf == NULL)
+ return;
+
+ if (row < 0 || row >= tems.ts_c_dimension.height ||
+ col < 0 || col >= tems.ts_c_dimension.width ||
+ col + count > (size_t)tems.ts_c_dimension.width)
+ return;
+
+ width = tems.ts_c_dimension.width;
+ addr = tem->tvs_screen_buf + (row * width + col);
+ for (i = 0; i < count; i++) {
+ *addr++ = string[i];
+ }
+}
+
+static void
+tem_virtual_cls(struct tem_vt_state *tem, size_t count,
+ screen_pos_t row, screen_pos_t col)
+{
+ term_char_t c;
+ text_attr_t attr;
+
+ tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
+ TEM_ATTR_SCREEN_REVERSE);
+ /* Make sure we will not draw underlines */
+ c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
+
+ while (count > 0) {
+ tem_virtual_display(tem, &c, 1, row, col);
+ col++;
+ count--;
+ }
+}