#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
#include <sys/consplat.h>
#include <sys/kd.h>
#include <stdbool.h>
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 *, bool, bool);
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 *, bool, bool);
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, bool);
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);
tem_state_t tems;
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);
}
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,
bool init_color, bool 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);
}
ptem->tvs_stateflags = TVS_AUTOWRAP;
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;
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;
ptem->tvs_initialized = 0;
if (!tems.ts_initialized) {
tem_add(ptem);
return ((tem_vt_state_t)ptem);
}
tem_internal_init(ptem, true, false);
tem_add(ptem);
return ((tem_vt_state_t)ptem);
}
static void
tem_reinit(struct tem_vt_state *tem, bool reset_display)
{
tem_free_buf(tem);
tem_internal_init(tem, 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(bool 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);
}
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;
if (cp->c_ioctl(cp, VIS_DEVINIT, &temargs) != 0) {
printf("terminal emulator: Compatible fb not found\n");
ret = tems_failed(false);
return (ret);
}
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(true);
return (ret);
}
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(true);
return (ret);
}
if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) {
printf("terminal emulator: unsupported mode\n");
ret = tems_failed(true);
return (ret);
}
plat_tem_get_prom_size(&height, &width);
tems_setup_terminal(&temargs, height, width);
tems_reset_colormap();
tems_get_initial_color(&tems.ts_init_color);
tems.ts_initialized = 1;
for (p = list_head(&tems.ts_list); p != NULL;
p = list_next(&tems.ts_list, p)) {
tem_internal_init(p, true, 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;
font_data = set_font(&tems.ts_c_dimension.height,
&tems.ts_c_dimension.width, height, width);
if (font_data == NULL)
panic("out of memory");
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:
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);
unsetenv("screen-height");
unsetenv("screen-width");
break;
case VIS_PIXEL:
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);
}
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) {
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, true);
return;
}
diff = diff & TEMS_DIMENSION_DIFF;
if (diff == 0) {
struct tem_vt_state *active = tems.ts_active;
tems.ts_pdepth = devinit->depth;
tems_reset_colormap();
tems_get_initial_color(&tems.ts_init_color);
tem_reinit(active, true);
return;
}
plat_tem_get_prom_size(&height, &width);
state = tems.ts_initialized;
tems.ts_initialized = 0;
tems_setup_terminal(devinit, height, width);
tems_reset_colormap();
tems_get_initial_color(&tems.ts_init_color);
tems.ts_initialized = 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);
}
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));
}
void
tems_display(struct vis_consdisplay *pda)
{
if (tems.ts_hdl != NULL)
(void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSDISPLAY, pda);
}
void
tems_copy(struct vis_conscopy *pma)
{
if (tems.ts_hdl != NULL)
(void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCOPY, pma);
}
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;
cm.red = (uint8_t *)cmap4_to_24.red;
cm.blue = (uint8_t *)cmap4_to_24.blue;
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;
}
void
tem_save_state(void)
{
struct tem_vt_state *active = tems.ts_active;
char buf[80];
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;
}
static void
tem_prom_scroll_up(struct tem_vt_state *tem, int nrows)
{
struct vis_conscopy ma;
int ncols, width;
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);
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, true);
}
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_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();
plat_tem_get_prom_pos(&row, &col);
row = tem_adjust_row(tem, row);
tem->tvs_first_line = row + 1;
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, true, true);
}
}
static void
tems_get_inverses(bool *p_inverse, bool *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;
*p_inverse_screen = i_inverse_screen != 0;
}
static void
tems_get_initial_color(tem_color_t *pcolor)
{
bool 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) {
if (pcolor->fg_color.n == ANSI_COLOR_WHITE)
flags |= TEM_ATTR_BRIGHT_BG;
if (pcolor->fg_color.n == ANSI_COLOR_BLACK)
flags &= ~TEM_ATTR_BRIGHT_BG;
} else {
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, bool 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;
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);
}
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;
}
static void
tem_input_byte(struct tem_vt_state *tem, uint8_t c)
{
if ((c & 0x80) == 0x00) {
tem_input_partial(tem);
tem_parse(tem, c);
return;
}
if ((c & 0xe0) == 0xc0) {
tem_input_partial(tem);
tem->tvs_utf8_left = 1;
tem->tvs_utf8_partial = c;
return;
}
if ((c & 0xf0) == 0xe0) {
tem_input_partial(tem);
tem->tvs_utf8_left = 2;
tem->tvs_utf8_partial = c;
return;
}
if ((c & 0xf8) == 0xf0) {
tem_input_partial(tem);
tem->tvs_utf8_left = 3;
tem->tvs_utf8_partial = c;
return;
}
if ((c & 0xc0) == 0x80) {
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;
v = 0;
u = tem->tvs_utf8_partial;
b = (u >> 24) & 0xff;
if (b != 0) {
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;
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;
b = u & 0xff;
v = (v << 6) | (b & 0x3f);
}
tem_parse(tem, v);
tem->tvs_utf8_partial = 0;
}
return;
}
tem_input_partial(tem);
tem_parse(tem, c);
}
static void
tem_terminal_emulate(struct tem_vt_state *tem, uint8_t *buf, int len)
{
if (tem->tvs_isactive && !tem->tvs_cursor_hidden)
tem_callback_cursor(tem, VIS_HIDE_CURSOR);
for (; len > 0; len--, buf++)
tem_input_byte(tem, *buf);
tem_send_data(tem);
if (tem->tvs_isactive && !tem->tvs_cursor_hidden)
tem_callback_cursor(tem, VIS_DISPLAY_CURSOR);
}
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:
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 = false;
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;
}
}
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;
}
}
static void
tem_select_color(struct tem_vt_state *tem, int color, bool fg)
{
if (color < 0 || color > 255)
return;
if (tems.ts_display_mode == VIS_TEXT && color > 15)
return;
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;
}
if (color < 8) {
if (fg == true)
tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
else
tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
return;
}
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;
}
}
}
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:
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:
tem->tvs_flags |= TEM_ATTR_BOLD;
break;
case 2:
tem->tvs_flags &= ~TEM_ATTR_BOLD;
break;
case 4:
tem->tvs_flags |= TEM_ATTR_UNDERLINE;
break;
case 5:
tem->tvs_flags |= TEM_ATTR_BLINK;
break;
case 7:
if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
tem->tvs_flags &= ~TEM_ATTR_REVERSE;
} else {
tem->tvs_flags |= TEM_ATTR_REVERSE;
}
break;
case 22:
tem->tvs_flags &= ~TEM_ATTR_BOLD;
break;
case 24:
tem->tvs_flags &= ~TEM_ATTR_UNDERLINE;
break;
case 25:
tem->tvs_flags &= ~TEM_ATTR_BLINK;
break;
case 27:
if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
tem->tvs_flags |= TEM_ATTR_REVERSE;
} else {
tem->tvs_flags &= ~TEM_ATTR_REVERSE;
}
break;
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
tem->tvs_fg_color.n = param - 30;
tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
tem->tvs_flags &= ~TEM_ATTR_RGB_FG;
break;
case 38:
if (curparam < 3) {
curparam = 0;
break;
}
count++;
curparam--;
param = tem->tvs_params[count];
switch (param) {
case 2:
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:
count++;
curparam--;
tem_select_color(tem, tem->tvs_params[count],
true);
break;
default:
curparam = 0;
break;
}
break;
case 39:
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:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
tem->tvs_bg_color.n = param - 40;
tem->tvs_flags &= ~TEM_ATTR_RGB_BG;
tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
break;
case 48:
if (curparam < 3) {
curparam = 0;
break;
}
count++;
curparam--;
param = tem->tvs_params[count];
switch (param) {
case 2:
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:
count++;
curparam--;
tem_select_color(tem, tem->tvs_params[count],
false);
break;
default:
curparam = 0;
break;
}
break;
case 49:
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:
case 91:
case 92:
case 93:
case 94:
case 95:
case 96:
case 97:
tem->tvs_fg_color.n = param - 90;
tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
tem->tvs_flags &= ~TEM_ATTR_RGB_FG;
break;
case 100:
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
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);
}
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':
tem_send_data(tem);
tem_selgraph(tem);
break;
case '@':
tem_setparam(tem, 1, 1);
tem_shift(tem, tem->tvs_params[0], TEM_SHIFT_RIGHT);
break;
case 'A':
tem_setparam(tem, 1, 1);
tem_mv_cursor(tem, row - tem->tvs_params[0], col);
break;
case 'd':
tem_setparam(tem, 1, 1);
tem_mv_cursor(tem, tem->tvs_params[0] - 1, col);
break;
case 'e':
case 'B':
tem_setparam(tem, 1, 1);
tem_mv_cursor(tem, row + tem->tvs_params[0], col);
break;
case 'a':
case 'C':
tem_setparam(tem, 1, 1);
tem_mv_cursor(tem, row, col + tem->tvs_params[0]);
break;
case '`':
tem_setparam(tem, 1, 1);
tem_mv_cursor(tem, row, tem->tvs_params[0] - 1);
break;
case 'D':
tem_setparam(tem, 1, 1);
tem_mv_cursor(tem, row, col - tem->tvs_params[0]);
break;
case 'E':
tem_setparam(tem, 1, 1);
tem_mv_cursor(tem, row + tem->tvs_params[0], 0);
break;
case 'F':
tem_setparam(tem, 1, 1);
tem_mv_cursor(tem, row - tem->tvs_params[0], 0);
break;
case 'G':
tem_setparam(tem, 1, 1);
tem_mv_cursor(tem, row, tem->tvs_params[0] - 1);
break;
case 'g':
tem_setparam(tem, 1, 0);
tem_clear_tabs(tem, tem->tvs_params[0]);
break;
case 'f':
case 'H':
tem_setparam(tem, 2, 1);
tem_mv_cursor(tem,
tem->tvs_params[0] - 1, tem->tvs_params[1] - 1);
break;
case 'I':
break;
case 'J':
tem_send_data(tem);
tem_setparam(tem, 1, 0);
switch (tem->tvs_params[0]) {
case 0:
tem_clear_chars(tem,
tems.ts_c_dimension.width -
tem->tvs_c_cursor.col,
tem->tvs_c_cursor.row,
tem->tvs_c_cursor.col);
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:
for (row = 0;
row < tem->tvs_c_cursor.row;
row++) {
tem_clear_chars(tem,
tems.ts_c_dimension.width, row, 0);
}
tem_clear_chars(tem,
tem->tvs_c_cursor.col + 1,
tem->tvs_c_cursor.row, 0);
break;
case 2:
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':
tem_send_data(tem);
tem_setparam(tem, 1, 0);
switch (tem->tvs_params[0]) {
case 0:
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:
tem_clear_chars(tem,
tem->tvs_c_cursor.col + 1,
tem->tvs_c_cursor.row, 0);
break;
case 2:
tem_clear_chars(tem,
tems.ts_c_dimension.width,
tem->tvs_c_cursor.row, 0);
break;
}
break;
case 'L':
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':
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':
tem_setparam(tem, 1, 1);
tem_shift(tem, tem->tvs_params[0], TEM_SHIFT_LEFT);
break;
case 'S':
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':
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':
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':
tem_setparam(tem, 1, 1);
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;
}
static void
tem_chkparam_qmark(struct tem_vt_state *tem, tem_char_t ch)
{
switch (ch) {
case 'h':
tem_setparam(tem, 1, 1);
switch (tem->tvs_params[0]) {
case 7:
tem->tvs_stateflags |= TVS_AUTOWRAP;
break;
case 25:
tem_send_data(tem);
tem->tvs_cursor_hidden = false;
break;
}
break;
case 'l':
tem_setparam(tem, 1, 1);
switch (tem->tvs_params[0]) {
case 7:
tem->tvs_stateflags &= ~TVS_AUTOWRAP;
break;
case 25:
tem_send_data(tem);
tem->tvs_cursor_hidden = true;
break;
}
break;
}
tem->tvs_state = A_STATE_START;
}
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 = true;
return;
} else if (tem->tvs_state == A_STATE_CSI_EQUAL) {
tem->tvs_state = A_STATE_START;
} else if (tem->tvs_state == A_STATE_CSI_QMARK) {
if (tem->tvs_curparam < TEM_MAXPARAMS) {
if (tem->tvs_gotparam) {
tem->tvs_params[tem->tvs_curparam] =
tem->tvs_paramval;
}
tem->tvs_curparam++;
}
if (ch == ';') {
tem->tvs_gotparam = false;
tem->tvs_paramval = 0;
} else {
tem_chkparam_qmark(tem, ch);
}
} else {
if (tem->tvs_curparam < TEM_MAXPARAMS) {
if (tem->tvs_gotparam) {
tem->tvs_params[tem->tvs_curparam] =
tem->tvs_paramval;
}
tem->tvs_curparam++;
}
if (ch == ';') {
tem->tvs_gotparam = false;
tem->tvs_paramval = 0;
} else {
tem_chkparam(tem, ch);
}
}
}
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;
if ((tem->tvs_stateflags & (TVS_AUTOWRAP | TVS_WRAPPED)) ==
(TVS_AUTOWRAP | TVS_WRAPPED)) {
tem_new_line(tem);
}
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->tvs_stateflags |= TVS_WRAPPED;
tem->tvs_c_cursor.col--;
tem_send_data(tem);
} else {
tem->tvs_stateflags &= ~TVS_WRAPPED;
}
}
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->tvs_stateflags &= ~TVS_WRAPPED;
tem_align_cursor(tem);
}
static void
tem_lf(struct tem_vt_state *tem)
{
int row;
tem->tvs_stateflags &= ~TVS_WRAPPED;
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 {
row = 0;
}
}
tem_mv_cursor(tem, row, tem->tvs_c_cursor.col);
if (tem->tvs_nscroll == 0) {
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) {
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);
}
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;
}
static void
tem_parse(struct tem_vt_state *tem, tem_char_t ch)
{
int i;
if (tem->tvs_state == A_STATE_START) {
if (ch == A_CSI || ch == A_ESC || ch < ' ') {
tem_control(tem, ch);
} else {
tem_outch(tem, ch);
}
return;
}
if (tem->tvs_state != A_STATE_ESC) {
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':
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':
tem_send_data(tem);
if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
tem->tvs_flags &= ~TEM_ATTR_SCREEN_REVERSE;
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':
tem_send_data(tem);
if (!(tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE)) {
tem->tvs_flags |= TEM_ATTR_SCREEN_REVERSE;
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':
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;
}
}
if (ch == '[') {
tem->tvs_curparam = 0;
tem->tvs_paramval = 0;
tem->tvs_gotparam = false;
for (i = 0; i < TEM_MAXPARAMS; i++)
tem->tvs_params[i] = -1;
tem->tvs_state = A_STATE_CSI;
} else if (ch == 'Q') {
tem->tvs_state = A_STATE_START;
} else if (ch == 'C') {
tem->tvs_state = A_STATE_START;
} else {
tem->tvs_state = A_STATE_START;
if (ch == 'c') {
tem_reset_display(tem, true, true);
} else if (ch == 'H') {
tem_set_tab(tem);
} else if (ch == '7') {
tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
} else if (ch == '8') {
tem_mv_cursor(tem, tem->tvs_r_cursor.row,
tem->tvs_r_cursor.col);
} else if (ch < ' ') {
tem_control(tem, ch);
} else {
tem_outch(tem, ch);
}
}
}
static void
tem_bell(struct tem_vt_state *tem __unused)
{
}
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) {
if (TEM_CHAR_ATTR(src[width].tc_char) == TEM_ATTR_IMAGE ||
TEM_CHAR_ATTR(dst[width].tc_char) == TEM_ATTR_IMAGE)
break;
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;
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;
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++;
}
}
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);
}
}
}
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 bool need_clear = true;
if (need_clear && tem->tvs_first_line > 0) {
tem_pix_clear_prom_output(tem);
}
need_clear = 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;
if (tem->tvs_first_line > 0 && t_row < s_row && t_col == 0 &&
e_col == tems.ts_c_dimension.width - 1) {
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) {
tem->tvs_first_line -= (s_row - t_row);
if (tem->tvs_first_line <= 0) {
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);
}
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, false);
}
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, false);
}
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:
tem->tvs_ntabs = 0;
break;
case 0:
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)
{
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) {
tem->tvs_stateflags |= TVS_WRAPPED;
col = tems.ts_c_dimension.width - 1;
} else {
tem->tvs_stateflags &= ~TVS_WRAPPED;
}
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);
}
static void
tem_reset_emulator(struct tem_vt_state *tem, bool 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 = false;
tem->tvs_curparam = 0;
tem->tvs_paramval = 0;
tem->tvs_nscroll = 1;
if (init_color) {
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;
}
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, bool clear_txt, bool 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);
}
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 (!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;
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:
*(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);
}
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)
{
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);
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;
}
}
}
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);
c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
while (count > 0) {
tem_virtual_display(tem, &c, 1, row, col);
col++;
count--;
}
}