From 7810c4556c9f17c3f8bf14947d37b858dbc5f01e Mon Sep 17 00:00:00 2001 From: Stefan Seyfried Date: Wed, 24 Jan 2018 22:36:02 +0100 Subject: [PATCH] yaft: convert into c++ class Origin commit data ------------------ Branch: ni/coolstream Commit: https://github.com/neutrino-images/ni-neutrino/commit/dbc9e6827b330204a666688c2e6fd7f34ad43066 Author: Stefan Seyfried Date: 2018-01-24 (Wed, 24 Jan 2018) ------------------ No further description and justification available within origin commit message! ------------------ This commit was generated by Migit --- src/gui/widget/yaft/ctrlseq/csi.cpp | 415 +++++++++++++ src/gui/widget/yaft/ctrlseq/esc.cpp | 127 ++++ src/gui/widget/yaft/ctrlseq/osc.cpp | 211 +++++++ src/gui/widget/yaft/yaft_priv.cpp | 879 ++++++++++++++++++++++++++++ src/gui/widget/yaft/yaft_priv.h | 260 ++++++++ 5 files changed, 1892 insertions(+) create mode 100644 src/gui/widget/yaft/ctrlseq/csi.cpp create mode 100644 src/gui/widget/yaft/ctrlseq/esc.cpp create mode 100644 src/gui/widget/yaft/ctrlseq/osc.cpp create mode 100644 src/gui/widget/yaft/yaft_priv.cpp create mode 100644 src/gui/widget/yaft/yaft_priv.h diff --git a/src/gui/widget/yaft/ctrlseq/csi.cpp b/src/gui/widget/yaft/ctrlseq/csi.cpp new file mode 100644 index 000000000..eb2b19ed4 --- /dev/null +++ b/src/gui/widget/yaft/ctrlseq/csi.cpp @@ -0,0 +1,415 @@ +/* + * yaft framebuffer terminal as C++ class for embedding in neutrino-MP + * (C) 2018 Stefan Seyfried + * License: GPL-2.0 + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * derived from yaft/ctrlseq/csi.h + * original code + * Copyright (c) 2012 haru + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + */ +#include "yaft_priv.h" +#include + +/* function for csi sequence */ +void YaFT_p::insert_blank(struct parm_t *parm) +{ + int i, num = sum(parm); + + if (num <= 0) + num = 1; + + for (i = cols - 1; cursor.x <= i; i--) { + if (cursor.x <= (i - num)) + copy_cell(cursor.y, i, cursor.y, i - num); + else + erase_cell(cursor.y, i); + } +} + +void YaFT_p::curs_up(struct parm_t *parm) +{ + int num = sum(parm); + + if (num <= 0) + num = 1; + + move_cursor(-num, 0); +} + +void YaFT_p::curs_down(struct parm_t *parm) +{ + int num = sum(parm); + + if (num <= 0) + num = 1; + + move_cursor(num, 0); +} + +void YaFT_p::curs_forward(struct parm_t *parm) +{ + int num = sum(parm); + + if (num <= 0) + num = 1; + + move_cursor(0, num); +} + +void YaFT_p::curs_back(struct parm_t *parm) +{ + int num = sum(parm); + + if (num <= 0) + num = 1; + + move_cursor(0, -num); +} + +void YaFT_p::curs_nl(struct parm_t *parm) +{ + int num = sum(parm); + + if (num <= 0) + num = 1; + + move_cursor(num, 0); + cr(); +} + +void YaFT_p::curs_pl(struct parm_t *parm) +{ + int num = sum(parm); + + if (num <= 0) + num = 1; + + move_cursor(-num, 0); + cr(); +} + +void YaFT_p::curs_col(struct parm_t *parm) +{ + int num; + + num = (parm->argc <= 0) ? 0 : dec2num(parm->argv[parm->argc - 1]) - 1; + set_cursor(cursor.y, num); +} + +void YaFT_p::curs_pos(struct parm_t *parm) +{ + int line, col; + + if (parm->argc <= 0) { + line = col = 0; + } else if (parm->argc == 2) { + line = dec2num(parm->argv[0]) - 1; + col = dec2num(parm->argv[1]) - 1; + } else { + return; + } + + if (line < 0) + line = 0; + if (col < 0) + col = 0; + + set_cursor(line, col); +} + +void YaFT_p::curs_line(struct parm_t *parm) +{ + int num; + + num = (parm->argc <= 0) ? 0 : dec2num(parm->argv[parm->argc - 1]) - 1; + set_cursor(num, cursor.x); +} + +void YaFT_p::erase_display(struct parm_t *parm) +{ + int i, j, pmode; + + pmode = (parm->argc <= 0) ? 0 : dec2num(parm->argv[parm->argc - 1]); + + if (pmode < 0 || 2 < pmode) + return; + + if (pmode == 0) { + for (i = cursor.y; i < lines; i++) + for (j = 0; j < cols; j++) + if (i > cursor.y || (i == cursor.y && j >= cursor.x)) + erase_cell(i, j); + } else if (pmode == 1) { + for (i = 0; i <= cursor.y; i++) + for (j = 0; j < cols; j++) + if (i < cursor.y || (i == cursor.y && j <= cursor.x)) + erase_cell(i, j); + } else if (pmode == 2) { + for (i = 0; i < lines; i++) + for (j = 0; j < cols; j++) + erase_cell(i, j); + } +} + +void YaFT_p::erase_line(struct parm_t *parm) +{ + int i, pmode; + + pmode = (parm->argc <= 0) ? 0 : dec2num(parm->argv[parm->argc - 1]); + + if (pmode < 0 || 2 < pmode) + return; + + if (pmode == 0) { + for (i = cursor.x; i < cols; i++) + erase_cell(cursor.y, i); + } else if (pmode == 1) { + for (i = 0; i <= cursor.x; i++) + erase_cell(cursor.y, i); + } else if (pmode == 2) { + for (i = 0; i < cols; i++) + erase_cell(cursor.y, i); + } +} + +void YaFT_p::insert_line(struct parm_t *parm) +{ + int num = sum(parm); + + if (mode & MODE_ORIGIN) { + if (cursor.y < scrollm.top + || cursor.y > scrollm.bottom) + return; + } + + if (num <= 0) + num = 1; + + scroll(cursor.y, scrollm.bottom, -num); +} + +void YaFT_p::delete_line(struct parm_t *parm) +{ + int num = sum(parm); + + if (mode & MODE_ORIGIN) { + if (cursor.y < scrollm.top + || cursor.y > scrollm.bottom) + return; + } + + if (num <= 0) + num = 1; + + scroll(cursor.y, scrollm.bottom, num); +} + +void YaFT_p::delete_char(struct parm_t *parm) +{ + int i, num = sum(parm); + + if (num <= 0) + num = 1; + + for (i = cursor.x; i < cols; i++) { + if ((i + num) < cols) + copy_cell(cursor.y, i, cursor.y, i + num); + else + erase_cell(cursor.y, i); + } +} + +void YaFT_p::erase_char(struct parm_t *parm) +{ + int i, num = sum(parm); + + if (num <= 0) + num = 1; + else if (num + cursor.x > cols) + num = cols - cursor.x; + + for (i = cursor.x; i < cursor.x + num; i++) + erase_cell(cursor.y, i); +} + +void YaFT_p::set_attr(struct parm_t *parm) +{ + int i, num; + + if (parm->argc <= 0) { + attribute = ATTR_RESET; + color_pair.fg = DEFAULT_FG; + color_pair.bg = DEFAULT_BG; + return; + } + + for (i = 0; i < parm->argc; i++) { + num = dec2num(parm->argv[i]); + + if (num == 0) { /* reset all attribute and color */ + attribute = ATTR_RESET; + color_pair.fg = DEFAULT_FG; + color_pair.bg = DEFAULT_BG; + } else if (1 <= num && num <= 7) { /* set attribute */ + attribute |= attr_mask[num]; + } else if (21 <= num && num <= 27) { /* reset attribute */ + attribute &= ~attr_mask[num - 20]; + } else if (30 <= num && num <= 37) { /* set foreground */ + color_pair.fg = (num - 30); + } else if (num == 38) { /* set 256 color to foreground */ + if ((i + 2) < parm->argc && dec2num(parm->argv[i + 1]) == 5) { + color_pair.fg = dec2num(parm->argv[i + 2]); + i += 2; + } + } else if (num == 39) { /* reset foreground */ + color_pair.fg = DEFAULT_FG; + } else if (40 <= num && num <= 47) { /* set background */ + color_pair.bg = (num - 40); + } else if (num == 48) { /* set 256 color to background */ + if ((i + 2) < parm->argc && dec2num(parm->argv[i + 1]) == 5) { + color_pair.bg = dec2num(parm->argv[i + 2]); + i += 2; + } + } else if (num == 49) { /* reset background */ + color_pair.bg = DEFAULT_BG; + } else if (90 <= num && num <= 97) { /* set bright foreground */ + color_pair.fg = (num - 90) + BRIGHT_INC; + } else if (100 <= num && num <= 107) { /* set bright background */ + color_pair.bg = (num - 100) + BRIGHT_INC; + } + } +} + +void YaFT_p::status_report(struct parm_t *parm) +{ + int i, num; + char buf[BUFSIZE]; + + for (i = 0; i < parm->argc; i++) { + num = dec2num(parm->argv[i]); + if (num == 5) { /* terminal response: ready */ + write(fd, "\033[0n", 4); + } else if (num == 6) { /* cursor position report */ + snprintf(buf, BUFSIZE, "\033[%d;%dR", cursor.y + 1, cursor.x + 1); + write(fd, buf, strlen(buf)); + } else if (num == 15) { /* terminal response: printer not connected */ + write(fd, "\033[?13n", 6); + } + } +} + +void YaFT_p::device_attribute(struct parm_t *parm) +{ + /* TODO: refer VT525 DA */ + (void) parm; + write(fd, "\033[?6c", 5); /* "I am a VT102" */ +} + +void YaFT_p::set_mode(struct parm_t *parm) +{ + int i, pmode; + + for (i = 0; i < parm->argc; i++) { + pmode = dec2num(parm->argv[i]); + if (esc.buf[1] != '?') + continue; /* not supported */ + + if (pmode == 6) { /* private mode */ + mode |= MODE_ORIGIN; + set_cursor(0, 0); + } else if (pmode == 7) { + mode |= MODE_AMRIGHT; + } else if (pmode == 25) { + mode |= MODE_CURSOR; + } else if (pmode == 8901) { + mode |= MODE_VWBS; + } + } + +} + +void YaFT_p::reset_mode(struct parm_t *parm) +{ + int i, pmode; + + for (i = 0; i < parm->argc; i++) { + pmode = dec2num(parm->argv[i]); + if (esc.buf[1] != '?') + continue; /* not supported */ + + if (pmode == 6) { /* private mode */ + mode &= ~MODE_ORIGIN; + set_cursor(0, 0); + } else if (pmode == 7) { + mode &= ~MODE_AMRIGHT; + wrap_occured = false; + } else if (pmode == 25) { + mode &= ~MODE_CURSOR; + } else if (pmode == 8901) { + mode &= ~MODE_VWBS; + } + } + +} + +void YaFT_p::set_margin(struct parm_t *parm) +{ + int top, bottom; + + if (parm->argc <= 0) { /* CSI r */ + top = 0; + bottom = lines - 1; + } else if (parm->argc == 2) { /* CSI ; r -> use default value */ + top = parm->argv[0].empty() ? 0 : dec2num(parm->argv[0]) - 1; + bottom = parm->argv[1].empty() ? lines - 1 : dec2num(parm->argv[1]) - 1; + } else { + return; + } + + if (top < 0 || top >= lines) + top = 0; + if (bottom < 0 || bottom >= lines) + bottom = lines - 1; + + if (top >= bottom) + return; + + scrollm.top = top; + scrollm.bottom = bottom; + + set_cursor(0, 0); /* move cursor to home */ +} + +void YaFT_p::clear_tabstop(struct parm_t *parm) +{ + int i, j, num; + + if (parm->argc <= 0) { + tabstop[cursor.x] = false; + } else { + for (i = 0; i < parm->argc; i++) { + num = dec2num(parm->argv[i]); + if (num == 0) { + tabstop[cursor.x] = false; + } else if (num == 3) { + for (j = 0; j < cols; j++) + tabstop[j] = false; + return; + } + } + } +} diff --git a/src/gui/widget/yaft/ctrlseq/esc.cpp b/src/gui/widget/yaft/ctrlseq/esc.cpp new file mode 100644 index 000000000..ff6baf87b --- /dev/null +++ b/src/gui/widget/yaft/ctrlseq/esc.cpp @@ -0,0 +1,127 @@ +/* + * yaft framebuffer terminal as C++ class for embedding in neutrino-MP + * (C) 2018 Stefan Seyfried + * License: GPL-2.0 + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * derived from yaft/ctrlseq/esc.h + * original code + * Copyright (c) 2012 haru + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + */ + +#include "yaft_priv.h" + +/* function for control character */ +void YaFT_p::bs(void) +{ + if (mode & MODE_VWBS + && cursor.x - 1 >= 0 + && cells[cursor.y][cursor.x - 1].width == NEXT_TO_WIDE) + move_cursor(0, -2); + else + move_cursor(0, -1); +} + +void YaFT_p::tab(void) +{ + int i; + txt.back().append(" "); + for (i = cursor.x + 1; i < cols; i++) { + if (tabstop[i]) { + set_cursor(cursor.y, i); + return; + } + } + set_cursor(cursor.y, cols - 1); +} + +void YaFT_p::nl(void) +{ + nlseen = true; + txt.push(""); + lines_available++; + move_cursor(1, 0); +} + +void YaFT_p::cr(void) +{ + set_cursor(cursor.y, 0); +} + +void YaFT_p::enter_esc(void) +{ + esc.state = STATE_ESC; +} + +/* function for escape sequence */ +void YaFT_p::save_state(void) +{ + state.mode = mode & MODE_ORIGIN; + state.cursor = cursor; + state.attribute = attribute; +} + +void YaFT_p::restore_state(void) +{ + /* restore state */ + if (state.mode & MODE_ORIGIN) + mode |= MODE_ORIGIN; + else + mode &= ~MODE_ORIGIN; + cursor = state.cursor; + attribute = state.attribute; +} + +void YaFT_p::crnl(void) +{ + cr(); + nl(); +} + +void YaFT_p::set_tabstop(void) +{ + tabstop[cursor.x] = true; +} + +void YaFT_p::reverse_nl(void) +{ + move_cursor(-1, 0); +} + +void YaFT_p::identify(void) +{ + write(fd, "\033[?6c", 5); /* "I am a VT102" */ +} + +void YaFT_p::enter_csi(void) +{ + esc.state = STATE_CSI; +} + +void YaFT_p::enter_osc(void) +{ + esc.state = STATE_OSC; +} +#if 0 +void YaFT_p::enter_dcs(void) +{ + esc.state = STATE_DCS; +} +#endif +void YaFT_p::ris(void) +{ + reset(); +} diff --git a/src/gui/widget/yaft/ctrlseq/osc.cpp b/src/gui/widget/yaft/ctrlseq/osc.cpp new file mode 100644 index 000000000..5629e88ec --- /dev/null +++ b/src/gui/widget/yaft/ctrlseq/osc.cpp @@ -0,0 +1,211 @@ +/* + * yaft framebuffer terminal as C++ class for embedding in neutrino-MP + * (C) 2018 Stefan Seyfried + * License: GPL-2.0 + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * derived from yaft/ctrlseq/osc.h, + * original code + * Copyright (c) 2012 haru + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + */ +#include "yaft_priv.h" +#include + +/* function for osc sequence */ +int32_t YaFT_p::parse_color1(std::string &seq) +{ + /* + format + rgb:r/g/b + rgb:rr/gg/bb + rgb:rrr/ggg/bbb + rgb:rrrr/gggg/bbbb + */ + int i, length, value; + int32_t color; + uint32_t rgb[3]; + struct parm_t parm; + + reset_parm(&parm); + parse_arg(seq, &parm, '/', isalnum); + + for (i = 0; i < parm.argc; i++) + logging(DEBUG, "parm.argv[%d]: %s\n", i, parm.argv[i].c_str()); + + if (parm.argc != 3) + return -1; + + length = parm.argv[0].length(); + + for (i = 0; i < 3; i++) { + value = hex2num(parm.argv[i]); + logging(DEBUG, "value:%d\n", value); + + if (length == 1) /* r/g/b/ */ + rgb[i] = 0xFF & (value * 0xFF / 0x0F); + else if (length == 2) /* rr/gg/bb */ + rgb[i] = 0xFF & value; + else if (length == 3) /* rrr/ggg/bbb */ + rgb[i] = 0xFF & (value * 0xFF / 0xFFF); + else if (length == 4) /* rrrr/gggg/bbbb */ + rgb[i] = 0xFF & (value * 0xFF / 0xFFFF); + else + return -1; + } + + color = (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]; + logging(DEBUG, "color:0x%.6X\n", color); + + return color; +} + +int32_t YaFT_p::parse_color2(std::string &seq) +{ + /* + format + #rgb + #rrggbb + #rrrgggbbb + #rrrrggggbbbb + */ + int i, length; + uint32_t rgb[3]; + int32_t color; + char buf[BUFSIZE]; + + length = seq.length(); + memset(buf, '\0', BUFSIZE); + + if (length == 3) { /* rgb */ + for (i = 0; i < 3; i++) { + rgb[i] = 0xFF & hex2num(seq.substr(i, 1)) * 0xFF / 0x0F; + } + } else if (length == 6) { /* rrggbb */ + for (i = 0; i < 3; i++) { /* rrggbb */ + //strncpy(buf, seq + i * 2, 2); + rgb[i] = 0xFF & hex2num(seq.substr(i * 2, 2)); + } + } else if (length == 9) { /* rrrgggbbb */ + for (i = 0; i < 3; i++) { + //strncpy(buf, seq + i * 3, 3); + rgb[i] = 0xFF & hex2num(seq.substr(i * 3, 3)) * 0xFF / 0xFFF; + } + } else if (length == 12) { /* rrrrggggbbbb */ + for (i = 0; i < 3; i++) { + //strncpy(buf, seq + i * 4, 4); + rgb[i] = 0xFF & hex2num(seq.substr(i * 4, 4)) * 0xFF / 0xFFFF; + } + } else { + return -1; + } + + color = (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]; + logging(DEBUG, "color:0x%.6X\n", color); + + return color; +} + +void YaFT_p::set_palette(struct parm_t *pt) +{ + /* + OSC Ps ; Pt ST + ref: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + ref: http://ttssh2.sourceforge.jp/manual/ja/about/ctrlseq.html#OSC + + only recognize change color palette: + Ps: 4 + Pt: c ; spec + c: color index (from 0 to 255) + spec: + rgb:r/g/b + rgb:rr/gg/bb + rgb:rrr/ggg/bbb + rgb:rrrr/gggg/bbbb + #rgb + #rrggbb + #rrrgggbbb + #rrrrggggbbbb + this rgb format is "RGB Device String Specification" + see http://xjman.dsl.gr.jp/X11R6/X11/CH06.html + Pt: c ; ? + response rgb color + OSC 4 ; c ; rgb:rr/gg/bb ST + + TODO: this function only works in 32bpp mode + */ + int i, argc = pt->argc, index; + int32_t color; + uint8_t rgb[3]; + std::string *argv = pt->argv; + char buf[BUFSIZE]; + + if (argc != 3) + return; + + index = dec2num(argv[1]); + if (index < 0 || index >= COLORS) + return; + + if (argv[2].compare(0, 4, "rgb:") == 0) { + std::string tmp = argv[2].substr(4); /* skip "rgb:" */ + if ((color = parse_color1(tmp)) != -1) { + virtual_palette[index] = (uint32_t) color; + palette_modified = true; + } + } else if (argv[2][0] == '#') { + std::string tmp = argv[2].substr(1); /* skip "#" */ + if ((color = parse_color2(tmp)) != -1) { + virtual_palette[index] = (uint32_t) color; + palette_modified = true; + } + } else if (argv[2][0] == '?') { + for (i = 0; i < 3; i++) + rgb[i] = 0xFF & (virtual_palette[index] >> (8 * (2 - i))); + + snprintf(buf, BUFSIZE, "\033]4;%d;rgb:%.2X/%.2X/%.2X\033\\", + index, rgb[0], rgb[1], rgb[2]); + write(fd, buf, strlen(buf)); + } +} + +void YaFT_p::reset_palette(struct parm_t *pt) +{ + /* + reset color c + OSC 104 ; c ST + c: index of color + ST: BEL or ESC \ + reset all color + OSC 104 ST + ST: BEL or ESC \ + + terminfo: oc=\E]104\E\\ + */ + int i, argc = pt->argc, c; + std::string *argv = pt->argv; + + if (argc < 2) { /* reset all color palette */ + for (i = 0; i < COLORS; i++) + virtual_palette[i] = color_list[i]; + palette_modified = true; + } else if (argc == 2) { /* reset color_palette[c] */ + c = dec2num(argv[1]); + if (0 <= c && c < COLORS) { + virtual_palette[c] = color_list[c]; + palette_modified = true; + } + } +} diff --git a/src/gui/widget/yaft/yaft_priv.cpp b/src/gui/widget/yaft/yaft_priv.cpp new file mode 100644 index 000000000..44ab00a10 --- /dev/null +++ b/src/gui/widget/yaft/yaft_priv.cpp @@ -0,0 +1,879 @@ +/* + * yaft framebuffer terminal as C++ class for embedding in neutrino-MP + * (C) 2018 Stefan Seyfried + * License: GPL-2.0 + * + * this file contains the private YaFT_p class with "backend" functionality + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * derived from yaft https://github.com/uobikiemukot/yaft + * original code + * Copyright (c) 2012 haru + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + */ + +#include "yaft_priv.h" +#include "glyph.h" +#include +#include + +/* parse_arg functions */ +void YaFT_p::reset_parm(parm_t *pt) +{ + pt->argc = 0; + for (int i = 0; i < MAX_ARGS; i++) + pt->argv[i].clear(); +} + +void add_parm(struct parm_t *pt, std::string cp) +{ + if (pt->argc >= MAX_ARGS) + return; + + logging(DEBUG, "argv[%d]: %s\n", pt->argc, cp.c_str()); + + pt->argv[pt->argc] = cp; + pt->argc++; +} + +void YaFT_p::parse_arg(std::string &buf, struct parm_t *pt, int delim, int (is_valid)(int c)) +{ + /* + v..........v d v.....v d v.....v ... d + (valid char) (delimiter) + argv[0] argv[1] argv[2] ... argv[argc - 1] + */ + size_t length; + size_t vp = std::string::npos; + int c; + + length = buf.length(); + logging(DEBUG, "parse_arg() length:%u\n", (unsigned) length); + + for (size_t i = 0; i < length; i++) { + c = buf[i]; + + if (vp == std::string::npos && is_valid(c)) + vp = i; + + if (c == delim) { + add_parm(pt, buf.substr(vp, i - vp)); + vp = std::string::npos; + } + + if (i == (length - 1) && (vp != std::string::npos || c == 0)) + add_parm(pt, buf.substr(vp)); + } + + logging(DEBUG, "argc:%d\n", pt->argc); +} + +/* constructor, Paint == false means "quiet mode, just execute + * a command but don't display anything */ +YaFT_p::YaFT_p(bool Paint) +{ + lines_available = 0; + txt.push(""); + nlseen = false; + paint = Paint; + last_paint = 0; +} + +bool YaFT_p::init() +{ + fb.cfb = CFrameBuffer::getInstance(); + fb.buf = (uint32_t *)fb.cfb->getBackBufferPointer(); + fb.width = fb.cfb->getScreenWidth(); + fb.height = fb.cfb->getScreenHeight(); + fb.xstart = fb.cfb->getScreenX(); + fb.ystart = fb.cfb->getScreenY(); + fb.line_length = width * sizeof(fb_pixel_t); + fb.screen_size = fb.line_length * fb.height; + fb.dy_min = fb.height; + fb.dy_max = -1; + screeninfo = fb.cfb->getScreenInfo(); + + return term_init(fb.width, fb.height); +} + +void YaFT_p::erase_cell(int y, int x) +{ + struct cell_t *cellp; + + cellp = &cells[y][x]; + cellp->glyphp = glyph[DEFAULT_CHAR]; + cellp->color_pair = color_pair; /* bce */ + cellp->attribute = ATTR_RESET; + cellp->width = HALF; + line_dirty[y] = true; +} + +void YaFT_p::copy_cell(int dst_y, int dst_x, int src_y, int src_x) +{ + struct cell_t *dst, *src; + + dst = &cells[dst_y][dst_x]; + src = &cells[src_y][src_x]; + + if (src->width == NEXT_TO_WIDE) { + return; + } else if (src->width == WIDE && dst_x == (cols - 1)) { + erase_cell(dst_y, dst_x); + } else { + *dst = *src; + if (src->width == WIDE) { + dst = &cells[dst_y][dst_x + 1]; + *dst = *src; + dst->width = NEXT_TO_WIDE; + } + line_dirty[dst_y] = true; + } +} + +int YaFT_p::set_cell(int y, int x, const struct glyph_t *glyphp) +{ + struct cell_t cell; //, *cellp; + uint8_t color_tmp; + + cell.glyphp = glyphp; + + cell.color_pair.fg = (attribute & attr_mask[ATTR_BOLD] && color_pair.fg <= 7) ? + color_pair.fg + BRIGHT_INC : color_pair.fg; + cell.color_pair.bg = (attribute & attr_mask[ATTR_BLINK] && color_pair.bg <= 7) ? + color_pair.bg + BRIGHT_INC : color_pair.bg; + + if (attribute & attr_mask[ATTR_REVERSE]) { + color_tmp = cell.color_pair.fg; + cell.color_pair.fg = cell.color_pair.bg; + cell.color_pair.bg = color_tmp; + } + + cell.attribute = attribute; + cell.width = (glyph_width)glyphp->width; + + cells[y][x] = cell; + line_dirty[y] = true; + + if (cell.width == WIDE && x + 1 < cols) { + cell.width = NEXT_TO_WIDE; + cells[y][x + 1] = cell; + return WIDE; + } + + if (cell.width == HALF /* isolated NEXT_TO_WIDE cell */ + && x + 1 < cols + && cells[y][x + 1].width == NEXT_TO_WIDE) { + erase_cell(y, x + 1); + } + return HALF; +} + +void YaFT_p::swap_lines(int i, int j) +{ + std::swap(cells[i], cells[j]); +} + +void YaFT_p::scroll(int from, int to, int offset) +{ + int abs_offset, scroll_lines; + + if (offset == 0 || from >= to) + return; + + logging(DEBUG, "scroll from:%d to:%d offset:%d\n", from, to, offset); + + for (int y = from; y <= to; y++) + line_dirty[y] = true; + + abs_offset = abs(offset); + scroll_lines = (to - from + 1) - abs_offset; + + if (offset > 0) { /* scroll down */ + for (int y = from; y < from + scroll_lines; y++) + swap_lines(y, y + offset); + for (int y = (to - offset + 1); y <= to; y++) + for (int x = 0; x < cols; x++) + erase_cell(y, x); + } + else { /* scroll up */ + for (int y = to; y >= from + abs_offset; y--) + swap_lines(y, y - abs_offset); + for (int y = from; y < from + abs_offset; y++) + for (int x = 0; x < cols; x++) + erase_cell(y, x); + } +} + +/* relative movement: cause scrolling */ +void YaFT_p::move_cursor(int y_offset, int x_offset) +{ + int x, y, top, bottom; + + x = cursor.x + x_offset; + y = cursor.y + y_offset; + + top = scrollm.top; + bottom = scrollm.bottom; + + if (x < 0) { + x = 0; + } else if (x >= cols) { + if (mode & MODE_AMRIGHT) + wrap_occured = true; + x = cols - 1; + } + cursor.x = x; + + y = (y < 0) ? 0: + (y >= lines) ? lines - 1: y; + + if (cursor.y == top && y_offset < 0) { + y = top; + scroll(top, bottom, y_offset); + } else if (cursor.y == bottom && y_offset > 0) { + y = bottom; + scroll(top, bottom, y_offset); + } + cursor.y = y; + + if (y_offset > 0 && !nlseen) { + txt.push(""); + lines_available++; + } +} + +/* absolute movement: never scroll */ +void YaFT_p::set_cursor(int y, int x) +{ + int top, bottom; + + if (mode & MODE_ORIGIN) { + top = scrollm.top; + bottom = scrollm.bottom; + y += scrollm.top; + } else { + top = 0; + bottom = lines - 1; + } + + x = (x < 0) ? 0: (x >= cols) ? cols - 1: x; + y = (y < top) ? top: (y > bottom) ? bottom: y; + + if (cursor.y != y && !nlseen) { + txt.push(""); + lines_available++; + } + + cursor.x = x; + cursor.y = y; + wrap_occured = false; +} + +void YaFT_p::addch(uint32_t code) +{ + int _width; + const struct glyph_t *glyphp; + + logging(DEBUG, "addch: U+%.4X\n", code); + + _width = wcwidth(code); + + if (code <= 0xff) { /* non-ascii not supported */ + char c = (char)code; + txt.back().push_back(c); + } + if (_width <= 0) /* zero width: not support comibining character */ + return; + else if (0x100000 <= code && code <= 0x10FFFD) /* unicode private area: plane 16 (DRCSMMv1) */ + glyphp = glyph[SUBSTITUTE_HALF]; + else if (code >= UCS2_CHARS /* yaft support only UCS2 */ + || glyph[code] == NULL /* missing glyph */ + || glyph[code]->width != _width) /* width unmatch */ + glyphp = (_width == 1) ? glyph[SUBSTITUTE_HALF] : glyph[SUBSTITUTE_WIDE]; + else + glyphp = glyph[code]; + + if ((wrap_occured && cursor.x == cols - 1) /* folding */ + || (glyphp->width == WIDE && cursor.x == cols - 1)) { + set_cursor(cursor.y, 0); + move_cursor(1, 0); + } + wrap_occured = false; + + move_cursor(0, set_cell(cursor.y, cursor.x, glyphp)); +} + +void YaFT_p::reset_esc(void) +{ + logging(DEBUG, "*esc reset*\n"); + + esc.buf.clear(); + esc.bp = 0; + esc.state = STATE_RESET; +} + +bool YaFT_p::push_esc(uint8_t ch) +{ + /* ref: http://www.vt100.net/docs/vt102-ug/appendixd.html */ + esc.bp++; + esc.buf.push_back(ch); + if (esc.state == STATE_ESC) { + /* format: + ESC I.......I F + ' ' '/' '0' '~' + 0x1B 0x20-0x2F 0x30-0x7E + */ + if ('0' <= ch && ch <= '~') /* final char */ + return true; + else if (SPACE <= ch && ch <= '/') /* intermediate char */ + return false; + } else if (esc.state == STATE_CSI) { + /* format: + CSI P.......P I.......I F + ESC '[' '0' '?' ' ' '/' '@' '~' + 0x1B 0x5B 0x30-0x3F 0x20-0x2F 0x40-0x7E + */ + if ('@' <= ch && ch <= '~') + return true; + else if (SPACE <= ch && ch <= '?') + return false; + } else { + /* format: + OSC I.....I F + ESC ']' BEL or ESC '\' + 0x1B 0x5D unknown 0x07 or 0x1B 0x5C + DCS I....I F + ESC 'P' BEL or ESC '\' + 0x1B 0x50 unknown 0x07 or 0x1B 0x5C + */ + if (ch == BEL || (ch == BACKSLASH + && esc.bp >= 2 && esc.buf[esc.bp-2] == ESC)) + return true; + else if ((ch == ESC || ch == CR || ch == LF || ch == BS || ch == HT) + || (SPACE <= ch && ch <= '~')) + return false; + } + + /* invalid sequence */ + reset_esc(); + return false; +} + +void YaFT_p::reset_charset(void) +{ + charset.code = charset.count = charset.following_byte = 0; + charset.is_valid = true; +} + +void YaFT_p::reset(void) +{ + mode = MODE_RESET; + mode |= MODE_AMRIGHT; //(MODE_CURSOR | MODE_AMRIGHT); + wrap_occured = false; + + scrollm.top = 0; + scrollm.bottom = lines - 1; + + cursor.x = cursor.y = 0; + + state.mode = mode; + state.cursor = cursor; + state.attribute = ATTR_RESET; + + color_pair.fg = DEFAULT_FG; + color_pair.bg = DEFAULT_BG; + + attribute = ATTR_RESET; + + for (int line = 0; line < lines; line++) { + for (int col = 0; col < cols; col++) { + erase_cell(line, col); + if ((col % TABSTOP) == 0) + tabstop[col] = true; + else + tabstop[col] = false; + } + line_dirty[line] = true; + } + + reset_esc(); + reset_charset(); +} + +void YaFT_p::term_die(void) +{ + line_dirty.clear(); + tabstop.clear(); + esc.buf.clear(); + cells.clear(); +} + +bool YaFT_p::term_init(int w, int h) +{ + const glyph_t *_glyphs; + + width = w; + height = h; + + int j = 0; + do { + _glyphs = glyphs[j]; + CELL_WIDTH = _glyphs[0].code; + CELL_HEIGHT = _glyphs[0].width; + cols = width / CELL_WIDTH; + if (cols > 79) + break; + j++; + } while (glyphs[j]); + + lines = height / CELL_HEIGHT; + + logging(NORMAL, "terminal cols:%d lines:%d\n", cols, lines); + + /* allocate memory */ + line_dirty.reserve(lines); + tabstop.reserve(cols); + esc.buf.reserve(1024); + + cells.clear(); + std::vector line; + line.resize(cols); + for (int i = 0; i < lines; i++) + cells.push_back(line); + line.resize(0); + + /* initialize palette */ + for (int i = 0; i < COLORS; i++) + virtual_palette[i] = color_list[i]; + palette_modified = true; /* first refresh() will initialize real_palette[] */ + + /* initialize glyph map */ + for (uint32_t code = 0; code < UCS2_CHARS; code++) + glyph[code] = NULL; + + for (uint32_t gi = 1; _glyphs[gi].code > 0; gi++) + glyph[_glyphs[gi].code] = &_glyphs[gi]; + + if (!glyph[DEFAULT_CHAR] + || !glyph[SUBSTITUTE_HALF] + || !glyph[SUBSTITUTE_WIDE]) { + logging(NORMAL, "couldn't find essential glyph:\ + DEFAULT_CHAR(U+%.4X):%p SUBSTITUTE_HALF(U+%.4X):%p SUBSTITUTE_WIDE(U+%.4X):%p\n", + DEFAULT_CHAR, glyph[DEFAULT_CHAR], + SUBSTITUTE_HALF, glyph[SUBSTITUTE_HALF], + SUBSTITUTE_WIDE, glyph[SUBSTITUTE_WIDE]); + return false; + } + + /* reset terminal */ + reset(); + + return true; +} + +void YaFT_p::parse(uint8_t *buf, int size) +{ + /* + CTRL CHARS : 0x00 ~ 0x1F + ASCII(printable): 0x20 ~ 0x7E + CTRL CHARS(DEL) : 0x7F + UTF-8 : 0x80 ~ 0xFF + */ + uint8_t ch; + + for (int i = 0; i < size; i++) { + ch = buf[i]; + if (esc.state == STATE_RESET) { + /* interrupted by illegal byte */ + if (charset.following_byte > 0 && (ch < 0x80 || ch > 0xBF)) { + addch(REPLACEMENT_CHAR); + reset_charset(); + } + + if (ch <= 0x1F) + control_character(ch); + else if (ch <= 0x7F) + addch(ch); + else + utf8_charset(ch); + } else if (esc.state == STATE_ESC) { + if (push_esc(ch)) + esc_sequence(ch); + } else if (esc.state == STATE_CSI) { + if (push_esc(ch)) + csi_sequence(ch); + } else if (esc.state == STATE_OSC) { + if (push_esc(ch)) + osc_sequence(ch); + } + } +} + +/* ctr char/esc sequence/charset function */ +void YaFT_p::control_character(uint8_t ch) +{ + static const char *ctrl_char[] = { + "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", + "BS ", "HT ", "LF ", "VT ", "FF ", "CR ", "SO ", "SI ", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", + "CAN", "EM ", "SUB", "ESC", "FS ", "GS ", "RS ", "US ", + }; + + logging(DEBUG, "ctl: %s\n", ctrl_char[ch]); + + switch(ch) { + case BS: bs(); break; + case HT: tab(); break; + case LF: nl(); break; + case VT: nl(); break; + case FF: nl(); break; + case CR: cr(); break; + case ESC: enter_esc(); break; + default: break; + } +} + +void YaFT_p::esc_sequence(uint8_t ch) +{ + logging(DEBUG, "esc: ESC %s\n", esc.buf.c_str()); + + if (esc.bp == 1) + { + switch(ch) { + case '7': save_state(); break; + case '8': restore_state(); break; + case 'D': nl(); break; + case 'E': crnl(); break; + case 'H': set_tabstop(); break; + case 'M': reverse_nl(); break; + case 'Z': identify(); break; + case '[': enter_csi(); break; + case ']': enter_osc(); break; + case 'c': ris(); break; + default: break; + } + } + + /* not reset if csi/osc/dcs seqence */ + if (ch == '[' || ch == ']' || ch == 'P') + return; + + reset_esc(); +} + +void YaFT_p::csi_sequence(uint8_t ch) +{ + struct parm_t parm; + + esc.buf.resize(esc.bp - 1); + std::string csi = esc.buf.substr(1); + logging(DEBUG, "csi: CSI %s\n", csi.c_str()); + + reset_parm(&parm); + parse_arg(csi, &parm, ';', isdigit); /* skip '[' */ + + switch (ch) { + case '@': insert_blank(&parm); break; + case 'A': curs_up(&parm); break; + case 'B': curs_down(&parm); break; + case 'C': curs_forward(&parm); break; + case 'D': curs_back(&parm); break; + case 'E': curs_nl(&parm); break; + case 'F': curs_pl(&parm); break; + case 'G': curs_col(&parm); break; + case 'H': curs_pos(&parm); break; + case 'J': erase_display(&parm); break; + case 'K': erase_line(&parm); break; + case 'L': insert_line(&parm); break; + case 'M': delete_line(&parm); break; + case 'P': delete_char(&parm); break; + case 'X': erase_char(&parm); break; + case 'a': curs_forward(&parm); break; + case 'c': device_attribute(&parm);break; + case 'd': curs_line(&parm); break; + case 'e': curs_down(&parm); break; + case 'f': curs_pos(&parm); break; + case 'g': clear_tabstop(&parm); break; + case 'h': set_mode(&parm); break; + case 'l': reset_mode(&parm); break; + case 'm': set_attr(&parm); break; + case 'n': status_report(&parm); break; + case 'r': set_margin(&parm); break; + /* XXX: not implemented because these sequences conflict DECSLRM/DECSHTS + case 's': sco_save_state(&parm);break; + case 'u': sco_restore_state(&parm); break; + */ + case '`': curs_col(&parm); break; + default: break; + } + + reset_esc(); +} + +static int is_osc_parm(int c) +{ + if (isdigit(c) || isalpha(c) || + c == '?' || c == ':' || c == '/' || c == '#') + return true; + else + return false; +} + +#if 0 +static void omit_string_terminator(char *bp, uint8_t ch) +{ + if (ch == BACKSLASH) /* ST: ESC BACKSLASH */ + *(bp - 2) = '\0'; + else /* ST: BEL */ + *(bp - 1) = '\0'; +} +#endif + +void YaFT_p::osc_sequence(uint8_t ch) +{ + int osc_mode; + struct parm_t parm; + + if (ch == BACKSLASH) /* ST: ESC BACKSLASH */ + esc.buf.resize(esc.bp - 2); + else /* ST: BEL */ + esc.buf.resize(esc.bp - 1); + // omit_string_terminator(esc.bp, ch); + + logging(DEBUG, "osc: OSC %s\n", esc.buf.c_str()); + + std::string osc = esc.buf.substr(1); + reset_parm(&parm); + parse_arg(osc, &parm, ';', is_osc_parm); /* skip ']' */ + + if (parm.argc > 0) { + osc_mode = dec2num(parm.argv[0]); + logging(DEBUG, "osc_mode:%d\n", osc_mode); + + /* XXX: disable because this functions only work 24/32bpp + -> support other bpp (including pseudo color) */ + if (osc_mode == 4) + set_palette(&parm); + else if (osc_mode == 104) + reset_palette(&parm); +#if 0 + if (osc_mode == 8900) + glyph_width_report(&parm); +#endif + } + reset_esc(); +} + +void YaFT_p::utf8_charset(uint8_t ch) +{ + if (0x80 <= ch && ch <= 0xBF) { + /* check illegal UTF-8 sequence + * ? byte sequence: first byte must be between 0xC2 ~ 0xFD + * 2 byte sequence: first byte must be between 0xC2 ~ 0xDF + * 3 byte sequence: second byte following 0xE0 must be between 0xA0 ~ 0xBF + * 4 byte sequence: second byte following 0xF0 must be between 0x90 ~ 0xBF + * 5 byte sequence: second byte following 0xF8 must be between 0x88 ~ 0xBF + * 6 byte sequence: second byte following 0xFC must be between 0x84 ~ 0xBF + */ + if ((charset.following_byte == 0) + || (charset.following_byte == 1 && charset.count == 0 && charset.code <= 1) + || (charset.following_byte == 2 && charset.count == 0 && charset.code == 0 && ch < 0xA0) + || (charset.following_byte == 3 && charset.count == 0 && charset.code == 0 && ch < 0x90) + || (charset.following_byte == 4 && charset.count == 0 && charset.code == 0 && ch < 0x88) + || (charset.following_byte == 5 && charset.count == 0 && charset.code == 0 && ch < 0x84)) + charset.is_valid = false; + + charset.code <<= 6; + charset.code += ch & 0x3F; + charset.count++; + } else if (0xC0 <= ch && ch <= 0xDF) { + charset.code = ch & 0x1F; + charset.following_byte = 1; + charset.count = 0; + return; + } else if (0xE0 <= ch && ch <= 0xEF) { + charset.code = ch & 0x0F; + charset.following_byte = 2; + charset.count = 0; + return; + } else if (0xF0 <= ch && ch <= 0xF7) { + charset.code = ch & 0x07; + charset.following_byte = 3; + charset.count = 0; + return; + } else if (0xF8 <= ch && ch <= 0xFB) { + charset.code = ch & 0x03; + charset.following_byte = 4; + charset.count = 0; + return; + } else if (0xFC <= ch && ch <= 0xFD) { + charset.code = ch & 0x01; + charset.following_byte = 5; + charset.count = 0; + return; + } else { /* 0xFE - 0xFF: not used in UTF-8 */ + addch(REPLACEMENT_CHAR); + reset_charset(); + return; + } + + if (charset.count >= charset.following_byte) { + /* illegal code point (ref: http://www.unicode.org/reports/tr27/tr27-4.html) + 0xD800 ~ 0xDFFF : surrogate pair + 0xFDD0 ~ 0xFDEF : noncharacter + 0xnFFFE ~ 0xnFFFF: noncharacter (n: 0x00 ~ 0x10) + 0x110000 ~ : invalid (unicode U+0000 ~ U+10FFFF) + */ + if (!charset.is_valid + || (0xD800 <= charset.code && charset.code <= 0xDFFF) + || (0xFDD0 <= charset.code && charset.code <= 0xFDEF) + || ((charset.code & 0xFFFF) == 0xFFFE || (charset.code & 0xFFFF) == 0xFFFF) + || (charset.code > 0x10FFFF)) + addch(REPLACEMENT_CHAR); + else + addch(charset.code); + + reset_charset(); + } +} + +int YaFT_p::sum(struct parm_t *parm) +{ + int s = 0; + for (int i = 0; i < parm->argc; i++) + s += dec2num(parm->argv[i]); + return s; +} + +static int my_ceil(int val, int div) +{ + if (div == 0) + return 0; + else + return (val + div - 1) / div; +} + +void YaFT_p::draw_line(int line) +{ + int pos, bdf_padding, glyph_w, margin_right; + int col, w, h; + uint32_t pixel; + struct color_pair_t col_pair; + struct cell_t *cellp; + + if (fb.dy_min > line * CELL_HEIGHT) + fb.dy_min = line * CELL_HEIGHT; + if (fb.dy_max < (line+1) * CELL_HEIGHT - 1) + fb.dy_max = (line+1) * CELL_HEIGHT - 1; + + //std::string s = ""; + for (col = cols - 1; col >= 0; col--) { + margin_right = (cols - 1 - col) * CELL_WIDTH; + + /* target cell */ + cellp = &cells[line][col]; + + /* copy current color_pair (maybe changed) */ + col_pair = cellp->color_pair; + + /* check wide character or not */ + glyph_w = (cellp->width == HALF) ? CELL_WIDTH: CELL_WIDTH * 2; + bdf_padding = my_ceil(glyph_w, BITS_PER_BYTE) * BITS_PER_BYTE - glyph_w; + if (cellp->width == WIDE) + bdf_padding += CELL_WIDTH; + + /* check cursor positon */ + if ((mode & MODE_CURSOR && line == cursor.y) + && (col == cursor.x + || (cellp->width == WIDE && (col + 1) == cursor.x) + || (cellp->width == NEXT_TO_WIDE && (col - 1) == cursor.x))) { + col_pair.fg = DEFAULT_BG; + col_pair.bg = ACTIVE_CURSOR_COLOR; + } + + for (h = 0; h < CELL_HEIGHT; h++) { + /* if UNDERLINE attribute on, swap bg/fg */ + if ((h == (CELL_HEIGHT - 1)) && (cellp->attribute & attr_mask[ATTR_UNDERLINE])) + col_pair.bg = col_pair.fg; + + pos = (width - 1 - margin_right/* - w*/) + + (line * CELL_HEIGHT + h) * fb.width; + + for (w = 0; w < CELL_WIDTH; w++) { + /* set color palette */ + if (cellp->glyphp->bitmap[h] & (0x01 << (bdf_padding + w))) + pixel = fb.real_palette[col_pair.fg]; + else + pixel = fb.real_palette[col_pair.bg]; + + /* update copy buffer only */ + //memcpy(fb.buf + pos, &pixel, fb.info.bytes_per_pixel); + fb.buf[pos] = pixel; + pos--; + } + } + //s.insert(s.begin(), cellp->glyphp->code); + } + //printf("draw_line: %02d ", line);puts(s.c_str()); + + line_dirty[line] = ((mode & MODE_CURSOR) && cursor.y == line) ? true: false; +} + +static inline int color2pixel(uint32_t color, struct fb_var_screeninfo *var) +{ + uint32_t r, g, b; + r = (color >> 16) & 0x000000ff; + g = (color >> 8) & 0x000000ff; + b = (color >> 0) & 0x000000ff; + return (0xff << var->transp.offset) | + (r << var->red.offset) | + (g << var->green.offset) | + (b << var->blue.offset); +} + +/* actually update the framebuffer contents */ +void YaFT_p::refresh() +{ + if (! paint) + return; + + if (palette_modified) { + palette_modified = false; + for (int i = 0; i < COLORS; i++) + fb.real_palette[i] = color2pixel(virtual_palette[i], screeninfo); + } + + logging(DEBUG,"%s: mode %x cur: %x cursor.y %d\n", __func__, mode, MODE_CURSOR, cursor.y); + if (mode & MODE_CURSOR) + line_dirty[cursor.y] = true; + + for (int line = 0; line < lines; line++) { + if (line_dirty[line]) { + draw_line(line); + } + } +#if 1 + if (fb.dy_max != -1) { + int blit_height = fb.dy_max - fb.dy_min; + uint32_t *blit_start = fb.buf + (fb.dy_min * fb.width); + fb.cfb->blit2FB(blit_start, fb.width, blit_height, fb.xstart, fb.ystart+fb.dy_min, 0, 0); + } +#else + fb.cfb->blit2FB(fb.buf, fb.width, fb.height, fb.xstart, fb.ystart, 0, 0); +#endif + fb.dy_min = fb.height; + fb.dy_max = -1; + last_paint = time_monotonic_ms(); +} diff --git a/src/gui/widget/yaft/yaft_priv.h b/src/gui/widget/yaft/yaft_priv.h new file mode 100644 index 000000000..58a7e7ce2 --- /dev/null +++ b/src/gui/widget/yaft/yaft_priv.h @@ -0,0 +1,260 @@ +/* + * yaft framebuffer terminal as C++ class for embedding in neutrino-MP + * (C) 2018 Stefan Seyfried + * License: GPL-2.0 + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * private "backend" functions + * + * original code https://github.com/uobikiemukot/yaft + * Copyright (c) 2012 haru + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + */ + +#include +#include +#include +#include +#include +#include /* atoi(), strtol() */ +#include /* write() */ +#include +//#include "glyph.h" + +#include "color.h" + +#include +#define logging(a, message...) dprintf(DEBUG_ ## a, "YaFT: " message) + +const uint8_t attr_mask[] = { + 0x00, 0x01, 0x00, 0x00, /* 0:none 1:bold 2:none 3:none */ + 0x02, 0x04, 0x00, 0x08, /* 4:underline 5:blink 6:none 7:reverse */ +}; + +#define BUFSIZE 1024 +#define MAX_ARGS 16 +struct parm_t { /* for parse_arg() */ + int argc; + std::string argv[MAX_ARGS]; +}; + +enum char_code { + /* 7 bit */ + BEL = 0x07, BS = 0x08, HT = 0x09, + LF = 0x0A, VT = 0x0B, FF = 0x0C, + CR = 0x0D, ESC = 0x1B, DEL = 0x7F, + /* others */ + SPACE = 0x20, + BACKSLASH = 0x5C, +}; + +class CFrameBuffer; +class YaFT_p +{ + /* color: index number of color_palette[] (see color.h) */ + enum { + DEFAULT_FG = 7, + DEFAULT_BG = 0, + ACTIVE_CURSOR_COLOR = 2, + PASSIVE_CURSOR_COLOR = 1, + }; + + /* misc */ + enum { + TABSTOP = 8, /* hardware tabstop */ + SUBSTITUTE_HALF = 0x0020, /* used for missing glyph(single width): U+0020 (SPACE) */ + SUBSTITUTE_WIDE = 0x3000, /* used for missing glyph(double width): U+3000 (IDEOGRAPHIC SPACE) */ + REPLACEMENT_CHAR = 0x003F, /* used for malformed UTF-8 sequence : U+003F (QUESTION MARK) */ + }; + + enum misc { + BITS_PER_BYTE = 8, /* bits per byte */ + UCS2_CHARS = 0x10000, /* number of UCS2 glyphs */ + DEFAULT_CHAR = SPACE, /* used for erase char */ + BRIGHT_INC = 8, /* value used for brightening color */ + }; + + struct margin_t { uint16_t top, bottom; }; + struct point_t { uint16_t x, y; }; + struct color_pair_t { uint8_t fg, bg; }; + + struct state_t { /* for save, restore state */ + struct point_t cursor; + int mode; + int attribute; + }; + + struct charset_t { + uint32_t code; /* UCS4 code point: yaft only prints UCS2 and DRCSMMv1 */ + int following_byte, count; + bool is_valid; + }; + + enum esc_state { + STATE_RESET = 0x00, + STATE_ESC = 0x01, /* 0x1B, \033, ESC */ + STATE_CSI = 0x02, /* ESC [ */ + STATE_OSC = 0x04, /* ESC ] */ + STATE_DCS = 0x08, /* ESC P */ + }; + + enum char_attr { + ATTR_RESET = 0, + ATTR_BOLD = 1, /* brighten foreground */ + ATTR_UNDERLINE = 4, + ATTR_BLINK = 5, /* brighten background */ + ATTR_REVERSE = 7, + }; + + enum term_mode { + MODE_RESET = 0x00, + MODE_ORIGIN = 0x01, /* origin mode: DECOM */ + MODE_CURSOR = 0x02, /* cursor visible: DECTCEM */ + MODE_AMRIGHT = 0x04, /* auto wrap: DECAWM */ + MODE_VWBS = 0x08, /* variable-width backspace */ + }; + + struct esc_t { + std::string buf; + int bp; /* buffer position */ + enum esc_state state; + }; + + enum glyph_width { + NEXT_TO_WIDE = 0, + HALF, + WIDE, + }; + + struct cell_t { + const struct glyph_t *glyphp; /* pointer to glyph */ + struct color_pair_t color_pair; /* color (fg, bg) */ + int attribute; /* bold, underscore, etc... */ + enum glyph_width width; /* wide char flag: WIDE, NEXT_TO_WIDE, HALF */ + }; + + struct framebuffer_t { + int fd; /* file descriptor of framebuffer */ + uint32_t *buf; /* copy of framebuffer */ + uint32_t real_palette[COLORS]; /* hardware specific color palette */ + int width, height; /* display resolution */ + int xstart, ystart; /* position of the window in the framebuffer */ + long screen_size; /* screen data size (byte) */ + int line_length; /* line length (byte) */ + int dy_min, dy_max; /* dirty region for blit */ + CFrameBuffer *cfb; + }; + + private: + int width, height; /* terminal size (pixel) */ + std::vector > cells; + struct margin_t scrollm; /* scroll margin */ + struct point_t cursor; /* cursor pos (x, y) */ + std::vector line_dirty; /* dirty flag */ + std::vector tabstop; /* tabstop flag */ + int mode; /* for set/reset mode */ + bool wrap_occured; /* whether auto wrap occured or not */ + struct state_t state; /* for restore */ + struct color_pair_t color_pair; /* color (fg, bg) */ + int attribute; /* bold, underscore, etc... */ + struct charset_t charset; /* store UTF-8 byte stream */ + struct esc_t esc; /* store escape sequence */ + uint32_t virtual_palette[COLORS]; /* virtual color palette: always 32bpp */ + bool palette_modified; /* true if palette changed by OSC 4/104 */ + const struct glyph_t *glyph[UCS2_CHARS]; /* array of pointer to glyphs[] */ + bool nlseen; + int CELL_WIDTH, CELL_HEIGHT; + struct framebuffer_t fb; + struct fb_var_screeninfo *screeninfo; + bool paint; + public: + int fd; /* master of pseudo terminal */ + int cols, lines; /* terminal size (cell) */ + time_t last_paint; + std::queue txt; /* contains "sanitized" (without control chars) output text */ + int lines_available; /* lines available in txt */ + + YaFT_p(bool paint = true); + bool init(); + void parse(uint8_t *buf, int size); + void refresh(void); + private: + void draw_line(int line); + void erase_cell(int y, int x); + void copy_cell(int dst_y, int dst_x, int src_y, int src_x); + int set_cell(int y, int x, const struct glyph_t *glyphp); + void swap_lines(int i, int j); + void scroll(int from, int to, int offset); + void move_cursor(int y_offset, int x_offset); + void set_cursor(int y, int x); + void addch(uint32_t code); + bool push_esc(uint8_t ch); + void reset_esc(void); + void reset_charset(void); + void reset(void); + void term_die(void); + bool term_init(int width, int height); + void utf8_charset(uint8_t ch); + void control_character(uint8_t ch); + void esc_sequence(uint8_t ch); + void csi_sequence(uint8_t ch); + void osc_sequence(uint8_t ch); + void bs(void); + void nl(void); + void cr(void); + void ris(void); + void crnl(void); + void curs_up(parm_t *parm); + void curs_down(parm_t *parm); + void curs_back(parm_t *parm); + void curs_forward(parm_t *parm); + void curs_nl(parm_t *parm); + void curs_pl(parm_t *parm); + void curs_col(parm_t *parm); + void curs_pos(parm_t *parm); + void curs_line(parm_t *parm); + void reverse_nl(void); + void insert_blank(parm_t *parm); + void erase_line(parm_t *parm); + void insert_line(parm_t *parm); + void delete_line(parm_t *parm); + void erase_char(parm_t *parm); + void delete_char(parm_t *parm); + void device_attribute(parm_t *parm); + void erase_display(parm_t *parm); + void identify(void); + void set_tabstop(void); + void clear_tabstop(parm_t *parm); + void set_mode(parm_t *parm); + void reset_mode(parm_t *parm); + void set_palette(parm_t *parm); + void reset_palette(parm_t *parm); + void set_attr(parm_t *parm); + void status_report(parm_t *parm); + void set_margin(parm_t *parm); + void enter_esc(void); + void enter_csi(void); + void enter_osc(void); + void save_state(void); + void restore_state(void); + void tab(void); + int dec2num(std::string &s) { return atoi(s.c_str()); }; + int hex2num(std::string s) { return strtol(s.c_str(), NULL, 16); }; + int sum(parm_t *param); + void parse_arg(std::string &buf, parm_t *pt, int delim, int (is_valid)(int c)); + void reset_parm(parm_t *parm); + int32_t parse_color1(std::string &seq); + int32_t parse_color2(std::string &seq); +};