From 1f877e6f07a48bc78f0b38d4fba4306d3a4688a2 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 9 Aug 2007 19:18:54 +0200 Subject: [PATCH] many many updates Signed-off-by: Pierre Habouzit --- .gitignore | 10 +- Makefile | 2 +- am/footer.mk | 4 +- demo/.gitignore | 1 + demo/Makefile | 2 +- demo/boxshell.c | 53 ++- madtty/.gitignore | 5 +- madtty/Makefile.in | 4 +- madtty/inject.c | 657 --------------------------------- madtty/madtty.c | 841 +++++++++++++++++++++++++++++++++++-------- madtty/madtty.h | 60 +-- madtty/madtty_priv.h | 50 --- 12 files changed, 761 insertions(+), 928 deletions(-) create mode 100644 demo/.gitignore delete mode 100644 madtty/inject.c delete mode 100644 madtty/madtty_priv.h diff --git a/.gitignore b/.gitignore index a48f4ca..9de549a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ -config.* -.depends -autom4te.cache -configure +/config.* +/autom4te.cache +/configure +/.depends +.*.sw? +.objs* diff --git a/Makefile b/Makefile index e36fd9f..09f278f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ DEPTH=. include $(DEPTH)/am/header.mk -SUBDIRS = madtty +SUBDIRS = madtty demo include $(DEPTH)/am/footer.mk # vim:set ft=automake: diff --git a/am/footer.mk b/am/footer.mk index 315e3f9..07511c8 100644 --- a/am/footer.mk +++ b/am/footer.mk @@ -42,7 +42,7 @@ define CLASS_PROGRAMS_tpl $1: $$(call deps_create,$$($1_SOURCES),$(_cobjs)/$1) $(CC) $$(CFLAGS) $$($1_CPPFLAGS) -o $$@ $$(filter %.o,$$^) \ - $$(LDFLAGS) $$($1_LDFLAGS) $$($1_LIBADD) + $$(LDFLAGS) $$($1_LDFLAGS) $$($1_LDADD) $$(eval $$(call RULE_C_tpl,$1,)) @@ -68,7 +68,7 @@ define CLASS_LIBRARIES_tpl $1.so$3: $$(call deps_create,$$($1_SOURCES),$(_cobjs)/$1) $(CC) $$(CFLAGS) $$($1_CPPFLAGS) -fPIC -shared -o $$@ $$(filter %.o,$$^) \ - $$(LDFLAGS) $$($1_LDFLAGS) $$($1_LIBADD) + $$(LDFLAGS) $$($1_LDFLAGS) $$($1_LDADD) $$(if $$(word 2,$$(subst ., ,$3)),ln -sf $$@ $1.so.$$(word 1,$$(subst ., ,$3))) $$(if $$($1_VERSION), ln -sf $$@ $1.so) diff --git a/demo/.gitignore b/demo/.gitignore new file mode 100644 index 0000000..f56a3ea --- /dev/null +++ b/demo/.gitignore @@ -0,0 +1 @@ +/boxshell diff --git a/demo/Makefile b/demo/Makefile index 0783b00..7e218c7 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -4,7 +4,7 @@ include $(DEPTH)/am/header.mk bin_PROGRAMS = boxshell boxshell_SOURCES = boxshell.c -boxshell_LDFLAGS = -L../madtty -lmadtty -lncurses +boxshell_LDFLAGS = -L../madtty -lmadtty boxshell_CPPFLAGS = -Wno-error include $(DEPTH)/am/footer.mk diff --git a/demo/boxshell.c b/demo/boxshell.c index 43c007a..a1d746e 100644 --- a/demo/boxshell.c +++ b/demo/boxshell.c @@ -6,9 +6,12 @@ */ #include +#include #include #include #include +#include +#include #include @@ -23,13 +26,15 @@ void sigchld(int signo __attribute__((unused))) int main(int argc, char *argv[]) { + struct timeval next = { 0, 0 }; RoteTerm *rt; - int i, j, ch, w, h; + int i, j, ch, w, h, pos; + char buf[BUFSIZ]; signal(SIGCHLD, sigchld); w = 80; - h = 50; + h = 40; if (argc > 1) { char *p = argv[1]; w = strtol(p, &p, 10); @@ -37,6 +42,7 @@ int main(int argc, char *argv[]) h = strtol(p, &p, 10); } + setlocale(LC_ALL, ""); initscr(); noecho(); start_color(); @@ -70,17 +76,15 @@ int main(int argc, char *argv[]) refresh(); /* create a window with a frame */ - term_win = newwin(h + 2, w + 2, 1, 2); - wattrset(term_win, COLOR_PAIR(7*8+7-0)); /* black over white */ + term_win = newwin(h + 2, w + 2, 2, 3); wborder(term_win, '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'); mvwprintw(term_win, 0, 27, " Term In a Box "); wrefresh(term_win); - /* create the terminal and have it run bash */ rt = rote_vt_create(h, w); { - const char *path = "/bin/bash"; - const char *args[] = {"/bin/bash", "--login", NULL}; + const char *path = getenv("SHELL") ?: "/bin/sh"; + const char *args[] = { path, "--login", NULL}; rote_vt_forkpty(rt, path, args); } @@ -88,29 +92,44 @@ int main(int argc, char *argv[]) /* keep reading keypresses from the user and passing them to the terminal; * also, redraw the terminal to the window at each iteration */ ch = '\0'; + pos = 0; while (!getout) { fd_set rfds; - struct timeval tv = { 0 , 1000 }; + struct timeval tv = { 0 , 1000 }, t; FD_ZERO(&rfds); FD_SET(rt->pty, &rfds); if (select(rt->pty + 1, &rfds, NULL, NULL, &tv) > 0) { - char buf[512]; - int nbread; - - nbread = rote_vt_read(rt, buf, sizeof(buf)); - if (nbread > 0) - rote_vt_inject(rt, buf, nbread); + int nb; + + nb = rote_vt_read(rt, buf + pos, sizeof(buf) - pos); + if (nb <= 0) + continue; + pos += nb; + + nb = rote_vt_inject(rt, buf, pos); + if (nb <= 0) + continue; + memmove(buf, buf + nb, pos - nb); + pos -= nb; } - while ((ch = getch()) != ERR) { rote_vt_keypress(rt, ch); /* pass the keypress for handling */ } - rote_vt_draw(rt, term_win, 1, 1, NULL); - wrefresh(term_win); + gettimeofday(&t, NULL); + if (timercmp(&t, &next, >=)) { + rote_vt_draw(rt, term_win, 1, 1); + wrefresh(term_win); + gettimeofday(&next, NULL); + next.tv_usec += 1000 * 1000 / 100; + if (next.tv_usec > 1000 * 1000) { + next.tv_usec -= 1000 * 1000; + next.tv_sec++; + } + } } endwin(); diff --git a/madtty/.gitignore b/madtty/.gitignore index 2f037b1..45e50cd 100644 --- a/madtty/.gitignore +++ b/madtty/.gitignore @@ -1,2 +1,3 @@ -Makefile -madtty.pc +/Makefile +/madtty.pc +/libmadtty.so* diff --git a/madtty/Makefile.in b/madtty/Makefile.in index 19662ea..e535f05 100644 --- a/madtty/Makefile.in +++ b/madtty/Makefile.in @@ -7,9 +7,9 @@ DISTCLEANFILES = madtty.pc include_HEADERS = madtty.h lib_LIBRARIES = libmadtty -libmadtty_SOURCES = madtty.h madtty_priv.h \ - madtty.c inject.c +libmadtty_SOURCES = madtty.h madtty.c libmadtty_CPPFLAGS = -Wno-error +libmadtty_LDADD = -lutil -lncursesw libmadtty_VERSION = @PACKAGE_VERSION@ pkgconfigdir=$(libdir)/pkgconfig diff --git a/madtty/inject.c b/madtty/inject.c deleted file mode 100644 index 29755fb..0000000 --- a/madtty/inject.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - LICENSE INFORMATION: - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License (LGPL) as published by the Free Software Foundation. - - Please refer to the COPYING file for more information. - - 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. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Copyright © 2004 Bruno T. C. de Oliveira - Copyright © 2006 Pierre Habouzit - */ - -#include -#include -#include - -#include "madtty.h" -#include "madtty_priv.h" - -#define MAX_CSI_ES_PARAMS 32 - -static inline void clamp_cursor_to_bounds(RoteTerm *rt) -{ - if (rt->crow < 0) { - rt->curpos_dirty = true; - rt->crow = 0; - } - if (rt->ccol < 0) { - rt->curpos_dirty = true; - rt->ccol = 0; - } - - if (rt->crow >= rt->rows) { - rt->curpos_dirty = true; - rt->crow = rt->rows - 1; - } - if (rt->ccol >= rt->cols) { - rt->curpos_dirty = true; - rt->ccol = rt->cols - 1; - } -} - -static void cursor_line_down(RoteTerm *rt) -{ - int i; - - rt->crow++; - rt->curpos_dirty = true; - if (rt->crow <= rt->pd->scrollbottom) - return; - - /* must scroll the scrolling region up by 1 line, and put cursor on - * last line of it */ - rt->crow = rt->pd->scrollbottom; - - for (i = rt->pd->scrolltop; i < rt->pd->scrollbottom; i++) { - rt->line_dirty[i] = true; - memcpy(rt->cells[i], rt->cells[i+1], sizeof(RoteCell) * rt->cols); - } - - rt->line_dirty[rt->pd->scrollbottom] = true; - - /* clear last row of the scrolling region */ - for (i = 0; i < rt->cols; i++) { - rt->cells[rt->pd->scrollbottom][i].ch = 0x20; - rt->cells[rt->pd->scrollbottom][i].attr = 0x70; - } -} - -static void cursor_line_up(RoteTerm *rt) -{ - int i; - - rt->crow--; - rt->curpos_dirty = true; - if (rt->crow >= rt->pd->scrolltop) - return; - - /* must scroll the scrolling region up by 1 line, and put cursor on - * first line of it */ - rt->crow = rt->pd->scrolltop; - - for (i = rt->pd->scrollbottom; i > rt->pd->scrolltop; i--) { - rt->line_dirty[i] = true; - memcpy(rt->cells[i], rt->cells[i-1], sizeof(RoteCell) * rt->cols); - } - - rt->line_dirty[rt->pd->scrolltop] = true; - - /* clear first row of the scrolling region */ - for (i = 0; i < rt->cols; i++) { - rt->cells[rt->pd->scrolltop][i].ch = 0x20; - rt->cells[rt->pd->scrolltop][i].attr = 0x70; - } -} - -static inline void put_normal_char(RoteTerm *rt, int c) -{ - if (rt->ccol >= rt->cols) { - rt->ccol = 0; - cursor_line_down(rt); - } - - if (rt->insert) { - int i; - - for(i = rt->cols - 1; i >= rt->ccol+1; i--) { - rt->cells[rt->crow][i] = rt->cells[rt->crow][i-1]; - } - } - - rt->cells[rt->crow][rt->ccol].ch = c; - rt->cells[rt->crow][rt->ccol].attr = rt->curattr; - rt->ccol++; - - rt->line_dirty[rt->crow] = true; - rt->curpos_dirty = true; -} - -static inline void put_graphmode_char(RoteTerm *rt, int c) -{ - char nc; - /* do some very pitiful translation to regular ascii chars */ - switch (c) { - case 'j': case 'k': case 'l': case 'm': case 'n': case 't': - case 'u': case 'v': case 'w': - nc = '+'; break; - case 'x': - nc = '|'; break; - default: - nc = '%'; - } - - put_normal_char(rt, nc); -} - -static inline void new_escape_sequence(RoteTerm *rt) -{ - rt->pd->escaped = true; - rt->pd->esbuf_len = 0; - rt->pd->esbuf[0] = '\0'; -} - -static inline void cancel_escape_sequence(RoteTerm *rt) -{ - rt->pd->escaped = false; - rt->pd->esbuf_len = 0; - rt->pd->esbuf[0] = '\0'; -} - -static void handle_control_char(RoteTerm *rt, int c) -{ - switch (c) { - case '\r': /* carriage return */ - rt->ccol = 0; - break; - - case '\n': /* line feed */ - rt->ccol = 0; - cursor_line_down(rt); - rt->curpos_dirty = true; - break; - - case '\b': /* backspace */ - if (rt->ccol > 0) - rt->ccol--; - rt->curpos_dirty = true; - break; - - case '\t': /* tab */ - rt->ccol = (rt->ccol + 8) & ~7; - clamp_cursor_to_bounds(rt); - break; - - case '\x1b': /* begin escape sequence (aborting previous one if any) */ - new_escape_sequence(rt); - break; - - case '\x0e': /* enter graphical character mode */ - rt->pd->graphmode = true; - break; - - case '\x0f': /* exit graphical character mode */ - rt->pd->graphmode = false; - break; - - case '\x9b': /* CSI character. Equivalent to ESC [ */ - new_escape_sequence(rt); - rt->pd->esbuf[rt->pd->esbuf_len++] = '['; - break; - - case '\x18': - case '\x1a': /* these interrupt escape sequences */ - cancel_escape_sequence(rt); - break; - - case '\a': /* bell */ - /* do nothing for now... maybe a visual bell would be nice? */ - break; - } -} - -static inline bool is_valid_csi_ender(int c) -{ - return (c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c == '@' || c == '`'); -} - -static void try_interpret_escape_seq(RoteTerm *rt) -{ - char firstchar = rt->pd->esbuf[0]; - char lastchar = rt->pd->esbuf[rt->pd->esbuf_len-1]; - - if (!firstchar) - return; /* too early to do anything */ - - /* interpret ESC-M as reverse line-feed */ - if (firstchar == 'M') { - cursor_line_up(rt); - cancel_escape_sequence(rt); - return; - } - - if (firstchar != '[' && firstchar != ']') { - /* unrecognized escape sequence. Let's forget about it. */ - cancel_escape_sequence(rt); - return; - } - - if (firstchar == '[' && is_valid_csi_ender(lastchar)) { - /* we have a csi escape sequence: interpret it */ - rote_es_interpret_csi(rt); - cancel_escape_sequence(rt); - } else if (firstchar == ']' && lastchar == '\a') { - /* we have an xterm escape sequence: interpret it */ - - /* rote_es_interpret_xterm_es(rt); -- TODO!*/ - cancel_escape_sequence(rt); - } - - /* if the escape sequence took up all available space and could - * not yet be parsed, abort it */ - if (rt->pd->esbuf_len + 1 >= ESEQ_BUF_SIZE) - cancel_escape_sequence(rt); -} - -int rote_vt_inject(RoteTerm *rt, const char *data, int len) -{ - for (; len-- > 0; data++) { - if ((unsigned char)*data <= 31) { - handle_control_char(rt, *data); - continue; - } - - if (rt->pd->escaped && rt->pd->esbuf_len < ESEQ_BUF_SIZE) { - /* append character to ongoing escape sequence */ - rt->pd->esbuf[rt->pd->esbuf_len] = *data; - rt->pd->esbuf[++rt->pd->esbuf_len] = 0; - - try_interpret_escape_seq(rt); - } else - if (rt->pd->graphmode) { - put_graphmode_char(rt, *data); - } else { - put_normal_char(rt, *data); - } - } - - return 0; -} - -/****************************************************************************/ -/* CSI things */ -/****************************************************************************/ - -/* interprets a 'set attribute' (SGR) CSI escape sequence */ -static void interpret_csi_SGR(RoteTerm *rt, int param[], int pcount) -{ - int i; - - if (pcount == 0) { - /* special case: reset attributes */ - rt->curattr = 0x70; - return; - } - - for (i = 0; i < pcount; i++) { - - // From http://vt100.net/docs/vt510-rm/SGR table 5-16 - // 0 All attributes off - // 1 Bold - // 4 Underline - // 5 Blinking - // 7 Negative image - // 8 Invisible image - // 10 The ASCII character set is the current 7-bit - // display character set (default) - SCO Console only. - // 11 Map Hex 00-7F of the PC character set codes - // to the current 7-bit display character set - // - SCO Console only. - // 12 Map Hex 80-FF of the current character set to - // the current 7-bit display character set - SCO - // Console only. - // 22 Bold off - // 24 Underline off - // 25 Blinking off - // 27 Negative image off - // 28 Invisible image off - - if (param[i] == 0) rt->curattr = 0x70; - else if (param[i] == 1 || param[i] == 2 || param[i] == 4) /* set bold */ - ROTE_ATTR_MOD_BOLD(rt->curattr,1); - else if (param[i] == 5) /* set blink */ - ROTE_ATTR_MOD_BLINK(rt->curattr,1); - else if (param[i] == 7 || param[i] == 27) { /* reverse video */ - int fg = ROTE_ATTR_FG(rt->curattr); - int bg = ROTE_ATTR_BG(rt->curattr); - ROTE_ATTR_MOD_FG(rt->curattr, bg); - ROTE_ATTR_MOD_BG(rt->curattr, fg); - } - else if (param[i] == 8) rt->curattr = 0x0; /* invisible */ - else if (param[i] == 22 || param[i] == 24) /* bold off */ - ROTE_ATTR_MOD_BOLD(rt->curattr,0); - else if (param[i] == 25) /* blink off */ - ROTE_ATTR_MOD_BLINK(rt->curattr,0); - else if (param[i] == 28) /* invisible off */ - rt->curattr = 0x70; - else if (param[i] >= 30 && param[i] <= 37) /* set fg */ - ROTE_ATTR_MOD_FG(rt->curattr, param[i] - 30); - else if (param[i] >= 40 && param[i] <= 47) /* set bg */ - ROTE_ATTR_MOD_BG(rt->curattr, param[i] - 40); - else if (param[i] == 39) /* reset foreground to default */ - ROTE_ATTR_MOD_FG(rt->curattr, 7); - else if (param[i] == 49) /* reset background to default */ - ROTE_ATTR_MOD_BG(rt->curattr, 0); - } -} - -/* interprets an 'erase display' (ED) escape sequence */ -static void interpret_csi_ED(RoteTerm *rt, int param[], int pcount) -{ - int r, c; - int start_row, start_col, end_row, end_col; - - /* decide range */ - if (pcount && param[0] == 2) { - start_row = 0; - start_col = 0; - end_row = rt->rows - 1; - end_col = rt->cols - 1; - } else - if (pcount && param[0] == 1) { - start_row = 0; - start_col = 0; - end_row = rt->crow; - end_col = rt->ccol; - } else { - start_row = rt->crow; - start_col = rt->ccol; - end_row = rt->rows - 1; - end_col = rt->cols - 1; - } - - /* clean range */ - for (r = start_row; r <= end_row; r++) { - rt->line_dirty[r] = true; - - for (c = (r == start_row ? start_col : 0); - c <= (r == end_row ? end_col : rt->cols - 1); - c++) - { - rt->cells[r][c].ch = 0x20; - rt->cells[r][c].attr = rt->curattr; - } - } -} - -/* interprets a 'move cursor' (CUP) escape sequence */ -static void interpret_csi_CUP(RoteTerm *rt, int param[], int pcount) -{ - if (pcount == 0) { - /* special case */ - rt->crow = rt->ccol = 0; - return; - } else - if (pcount < 2) { - return; /* malformed */ - } - - rt->crow = param[0] - 1; /* convert from 1-based to 0-based */ - rt->ccol = param[1] - 1; /* convert from 1-based to 0-based */ - - rt->curpos_dirty = true; - - clamp_cursor_to_bounds(rt); -} - -/* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL, - * CPL, CHA, HPR, VPA, VPR, HPA */ -static void interpret_csi_C(RoteTerm *rt, char verb, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - - switch (verb) { - case 'A': rt->crow -= n; break; - case 'B': case 'e': rt->crow += n; break; - case 'C': case 'a': rt->ccol += n; break; - case 'D': rt->ccol -= n; break; - case 'E': rt->crow += n; rt->ccol = 0; break; - case 'F': rt->crow -= n; rt->ccol = 0; break; - case 'G': case '`': rt->ccol = param[0] - 1; break; - case 'd': rt->crow = param[0] - 1; break; - } - - rt->curpos_dirty = true; - clamp_cursor_to_bounds(rt); -} - -/* Interpret the 'erase line' escape sequence */ -static void interpret_csi_EL(RoteTerm *rt, int param[], int pcount) -{ - int erase_start, erase_end, i; - int cmd = pcount ? param[0] : 0; - - switch (cmd) { - case 1: erase_start = 0; erase_end = rt->ccol; break; - case 2: erase_start = 0; erase_end = rt->cols - 1; break; - default: erase_start = rt->ccol; erase_end = rt->cols - 1; break; - } - - for (i = erase_start; i <= erase_end; i++) { - rt->cells[rt->crow][i].ch = 0x20; - rt->cells[rt->crow][i].attr = rt->curattr; - } - - rt->line_dirty[rt->crow] = true; -} - -/* Interpret the 'insert blanks' sequence (ICH) */ -static void interpret_csi_ICH(RoteTerm *rt, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - int i; - - for (i = rt->cols - 1; i >= rt->ccol + n; i--) { - rt->cells[rt->crow][i] = rt->cells[rt->crow][i - n]; - } - - for (i = rt->ccol; i < rt->ccol + n; i++) { - rt->cells[rt->crow][i].ch = 0x20; - rt->cells[rt->crow][i].attr = rt->curattr; - } - - rt->line_dirty[rt->crow] = true; -} - -/* Interpret the 'delete chars' sequence (DCH) */ -static void interpret_csi_DCH(RoteTerm *rt, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - int i; - - for (i = rt->ccol; i < rt->cols; i++) { - if (i + n < rt->cols) { - rt->cells[rt->crow][i] = rt->cells[rt->crow][i + n]; - } else { - rt->cells[rt->crow][i].ch = 0x20; - rt->cells[rt->crow][i].attr = rt->curattr; - } - } - - rt->line_dirty[rt->crow] = true; -} - -/* Interpret an 'insert line' sequence (IL) */ -static void interpret_csi_IL(RoteTerm *rt, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - int i, j; - - for (i = rt->pd->scrollbottom; i >= rt->crow + n; i--) { - memcpy(rt->cells[i], rt->cells[i - n], sizeof(RoteCell) * rt->cols); - } - - for (i = rt->crow; i < rt->crow + n && i <= rt->pd->scrollbottom; i++) { - rt->line_dirty[i] = true; - for (j = 0; j < rt->cols; j++) { - rt->cells[i][j].ch = 0x20; - rt->cells[i][j].attr = rt->curattr; - } - } - -} - -/* Interpret a 'delete line' sequence (DL) */ -static void interpret_csi_DL(RoteTerm *rt, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - int i, j; - - for (i = rt->crow; i <= rt->pd->scrollbottom; i++) { - rt->line_dirty[i] = true; - if (i + n <= rt->pd->scrollbottom) { - memcpy(rt->cells[i], rt->cells[i+n], sizeof(RoteCell) * rt->cols); - } else { - for (j = 0; j < rt->cols; j++) { - rt->cells[i][j].ch = 0x20; - rt->cells[i][j].attr = rt->curattr; - } - } - } -} - -/* Interpret an 'erase characters' (ECH) sequence */ -static void interpret_csi_ECH(RoteTerm *rt, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - int i; - - for (i = rt->ccol; i < rt->ccol + n && i < rt->cols; i++) { - rt->cells[rt->crow][i].ch = 0x20; - rt->cells[rt->crow][i].attr = rt->curattr; - } - - rt->line_dirty[rt->crow] = true; -} - -/* Interpret a 'set scrolling region' (DECSTBM) sequence */ -static void interpret_csi_DECSTBM(RoteTerm *rt, int param[], int pcount) -{ - int newtop, newbottom; - - if (!pcount) { - newtop = 0; - newbottom = rt->rows - 1; - } else - if (pcount < 2) { - return; /* malformed */ - } - - newtop = param[0] - 1; - newbottom = param[1] - 1; - - /* clamp to bounds */ - if (newtop < 0) - newtop = 0; - if (newtop >= rt->rows) - newtop = rt->rows - 1; - if (newbottom < 0) - newbottom = 0; - if (newbottom >= rt->rows) - newbottom = rt->rows - 1; - - /* check for range validity */ - if (newtop > newbottom) - return; - rt->pd->scrolltop = newtop; - rt->pd->scrollbottom = newbottom; -} - -static void -interpret_csi_SAVECUR(RoteTerm *rt, - int param[] __attribute__((unused)), - int pcount __attribute__((unused))) -{ - rt->pd->saved_x = rt->ccol; - rt->pd->saved_y = rt->crow; -} - -static void -interpret_csi_RESTORECUR(RoteTerm *rt, - int param[] __attribute__((unused)), - int pcount __attribute__((unused))) -{ - rt->ccol = rt->pd->saved_x; - rt->crow = rt->pd->saved_y; - rt->curpos_dirty = true; -} - -void rote_es_interpret_csi(RoteTerm *rt) -{ - static int csiparam[MAX_CSI_ES_PARAMS]; - int param_count = 0; - const char *p = rt->pd->esbuf + 1; - char verb = rt->pd->esbuf[rt->pd->esbuf_len - 1]; - - if (!strncmp(rt->pd->esbuf, "[?", 2)) { /* private-mode CSI, ignore */ - return; - } - - /* parse numeric parameters */ - while (isdigit((unsigned char)*p) || *p == ';') { - if (*p == ';') { - if (param_count >= MAX_CSI_ES_PARAMS) return; /* too long! */ - csiparam[param_count++] = 0; - } else { - if (param_count == 0) csiparam[param_count++] = 0; - csiparam[param_count - 1] *= 10; - csiparam[param_count - 1] += *p - '0'; - } - - p++; - } - - /* delegate handling depending on command character (verb) */ - switch (verb) { - case 'h': - if (param_count == 1 && csiparam[0] == 4) /* insert mode */ - rt->insert = true; - break; - case 'l': - if (param_count == 1 && csiparam[0] == 4) /* replace mode */ - rt->insert = false; - break; - case 'm': /* it's a 'set attribute' sequence */ - interpret_csi_SGR(rt, csiparam, param_count); break; - case 'J': /* it's an 'erase display' sequence */ - interpret_csi_ED(rt, csiparam, param_count); break; - case 'H': case 'f': /* it's a 'move cursor' sequence */ - interpret_csi_CUP(rt, csiparam, param_count); break; - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': - case 'e': case 'a': case 'd': case '`': - /* it is a 'relative move' */ - interpret_csi_C(rt, verb, csiparam, param_count); break; - case 'K': /* erase line */ - interpret_csi_EL(rt, csiparam, param_count); break; - case '@': /* insert characters */ - interpret_csi_ICH(rt, csiparam, param_count); break; - case 'P': /* delete characters */ - interpret_csi_DCH(rt, csiparam, param_count); break; - case 'L': /* insert lines */ - interpret_csi_IL(rt, csiparam, param_count); break; - case 'M': /* delete lines */ - interpret_csi_DL(rt, csiparam, param_count); break; - case 'X': /* erase chars */ - interpret_csi_ECH(rt, csiparam, param_count); break; - case 'r': /* set scrolling region */ - interpret_csi_DECSTBM(rt, csiparam, param_count); break; - case 's': /* save cursor location */ - interpret_csi_SAVECUR(rt, csiparam, param_count); break; - case 'u': /* restore cursor location */ - interpret_csi_RESTORECUR(rt, csiparam, param_count); break; - default: - break; - } -} - diff --git a/madtty/madtty.c b/madtty/madtty.c index da2f3a9..d10b917 100644 --- a/madtty/madtty.c +++ b/madtty/madtty.c @@ -19,20 +19,689 @@ Copyright © 2006 Pierre Habouzit */ +#include #include -#include #include +#include #include +#include #include #include #include "madtty.h" -#include "madtty_priv.h" + +#define MAX_CSI_ES_PARAMS 32 +#define ESEQ_BUF_SIZE 128 /* size of escape sequence buffer */ + +/* Terminal private data */ +struct RoteTermPrivate_ { + unsigned escaped : 1; /* whether we are currently reading an + * escape sequence */ + + unsigned graphmode : 1; /* whether terminal is in graphical + * character mode or not */ + + int scrolltop, scrollbottom; /* current scrolling region of terminal */ + int saved_x, saved_y; /* saved cursor position */ + + char esbuf[ESEQ_BUF_SIZE]; /* 0-terminated string. Does NOT include + * the initial escape (\x1B) character. */ + int esbuf_len; /* length of buffer. The following property + * is always kept: esbuf[esbuf_len] == '\0' */ +}; + +static void clamp_cursor_to_bounds(RoteTerm *rt) +{ + if (rt->crow < 0) { + rt->curpos_dirty = true; + rt->crow = 0; + } + if (rt->ccol < 0) { + rt->curpos_dirty = true; + rt->ccol = 0; + } + + if (rt->crow >= rt->rows) { + rt->curpos_dirty = true; + rt->crow = rt->rows - 1; + } + if (rt->ccol >= rt->cols) { + rt->curpos_dirty = true; + rt->ccol = rt->cols - 1; + } +} + +static void cursor_line_down(RoteTerm *rt) +{ + int i; + + rt->crow++; + rt->curpos_dirty = true; + if (rt->crow <= rt->pd->scrollbottom) + return; + + /* must scroll the scrolling region up by 1 line, and put cursor on + * last line of it */ + rt->crow = rt->pd->scrollbottom; + + for (i = rt->pd->scrolltop; i < rt->pd->scrollbottom; i++) { + rt->line_dirty[i] = true; + memcpy(rt->cells[i], rt->cells[i+1], sizeof(RoteCell) * rt->cols); + } + + rt->line_dirty[rt->pd->scrollbottom] = true; + + /* clear last row of the scrolling region */ + for (i = 0; i < rt->cols; i++) { + rt->cells[rt->pd->scrollbottom][i].s[0] = 0x20; + rt->cells[rt->pd->scrollbottom][i].len = 1; + rt->cells[rt->pd->scrollbottom][i].attr = 0x70; + } +} + +static void cursor_line_up(RoteTerm *rt) +{ + int i; + + rt->crow--; + rt->curpos_dirty = true; + if (rt->crow >= rt->pd->scrolltop) + return; + + /* must scroll the scrolling region up by 1 line, and put cursor on + * first line of it */ + rt->crow = rt->pd->scrolltop; + + for (i = rt->pd->scrollbottom; i > rt->pd->scrolltop; i--) { + rt->line_dirty[i] = true; + memcpy(rt->cells[i], rt->cells[i-1], sizeof(RoteCell) * rt->cols); + } + + rt->line_dirty[rt->pd->scrolltop] = true; + + /* clear first row of the scrolling region */ + for (i = 0; i < rt->cols; i++) { + rt->cells[rt->pd->scrolltop][i].s[0] = 0x20; + rt->cells[rt->pd->scrolltop][i].len = 1; + rt->cells[rt->pd->scrolltop][i].attr = 0x70; + } +} + +static void put_normal_char(RoteTerm *rt, const char *s, int len) +{ + if (rt->ccol >= rt->cols) { + rt->ccol = 0; + cursor_line_down(rt); + } + + if (rt->insert) { + int i; + + for (i = rt->cols - 1; i >= rt->ccol+1; i--) { + rt->cells[rt->crow][i] = rt->cells[rt->crow][i-1]; + } + } + + memcpy(rt->cells[rt->crow][rt->ccol].s, s, len); + rt->cells[rt->crow][rt->ccol].len = len; + rt->cells[rt->crow][rt->ccol].attr = rt->curattr; + rt->ccol++; + + rt->line_dirty[rt->crow] = true; + rt->curpos_dirty = true; +} + +static void put_graphmode_char(RoteTerm *rt, int c) +{ + char nc; + /* do some very pitiful translation to regular ascii chars */ + switch (c) { + case 'j': case 'k': case 'l': case 'm': case 'n': case 't': + case 'u': case 'v': case 'w': + nc = '+'; break; + case 'x': + nc = '|'; break; + default: + nc = '%'; + } + + put_normal_char(rt, &nc, 1); +} + +static void new_escape_sequence(RoteTerm *rt) +{ + rt->pd->escaped = true; + rt->pd->esbuf_len = 0; + rt->pd->esbuf[0] = '\0'; +} + +static void cancel_escape_sequence(RoteTerm *rt) +{ + rt->pd->escaped = false; + rt->pd->esbuf_len = 0; + rt->pd->esbuf[0] = '\0'; +} + +static void handle_control_char(RoteTerm *rt, int c) +{ + switch (c) { + case '\r': /* carriage return */ + rt->ccol = 0; + break; + + case '\n': /* line feed */ + rt->ccol = 0; + cursor_line_down(rt); + rt->curpos_dirty = true; + break; + + case '\b': /* backspace */ + if (rt->ccol > 0) + rt->ccol--; + rt->curpos_dirty = true; + break; + + case '\t': /* tab */ + rt->ccol = (rt->ccol + 8) & ~7; + clamp_cursor_to_bounds(rt); + break; + + case '\x1b': /* begin escape sequence (aborting previous one if any) */ + new_escape_sequence(rt); + break; + + case '\x0e': /* enter graphical character mode */ + rt->pd->graphmode = true; + break; + + case '\x0f': /* exit graphical character mode */ + rt->pd->graphmode = false; + break; + + case '\x9b': /* CSI character. Equivalent to ESC [ */ + new_escape_sequence(rt); + rt->pd->esbuf[rt->pd->esbuf_len++] = '['; + break; + + case '\x18': + case '\x1a': /* these interrupt escape sequences */ + cancel_escape_sequence(rt); + break; + + case '\a': /* bell */ + /* do nothing for now... maybe a visual bell would be nice? */ + break; + } +} + +static bool is_valid_csi_ender(int c) +{ + return (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c == '@' || c == '`'); +} + +static void rote_es_interpret_csi(RoteTerm *rt); + +static void try_interpret_escape_seq(RoteTerm *rt) +{ + char firstchar = rt->pd->esbuf[0]; + char lastchar = rt->pd->esbuf[rt->pd->esbuf_len-1]; + + if (!firstchar) + return; /* too early to do anything */ + + /* interpret ESC-M as reverse line-feed */ + if (firstchar == 'M') { + cursor_line_up(rt); + cancel_escape_sequence(rt); + return; + } + + if (firstchar != '[' && firstchar != ']') { + /* unrecognized escape sequence. Let's forget about it. */ + cancel_escape_sequence(rt); + return; + } + + if (firstchar == '[' && is_valid_csi_ender(lastchar)) { + /* we have a csi escape sequence: interpret it */ + rote_es_interpret_csi(rt); + cancel_escape_sequence(rt); + } else if (firstchar == ']' && lastchar == '\a') { + /* we have an xterm escape sequence: interpret it */ + + /* rote_es_interpret_xterm_es(rt); -- TODO!*/ + cancel_escape_sequence(rt); + } + + /* if the escape sequence took up all available space and could + * not yet be parsed, abort it */ + if (rt->pd->esbuf_len + 1 >= ESEQ_BUF_SIZE) + cancel_escape_sequence(rt); +} + +int rote_vt_inject(RoteTerm *rt, const char *data, int len) +{ + int pos; + + for (pos = 0; pos < len; pos++) { + if ((unsigned char)data[pos] <= 31) { + handle_control_char(rt, data[pos]); + continue; + } + + if (rt->pd->escaped && rt->pd->esbuf_len < ESEQ_BUF_SIZE) { + /* append character to ongoing escape sequence */ + rt->pd->esbuf[rt->pd->esbuf_len] = data[pos]; + rt->pd->esbuf[++rt->pd->esbuf_len] = 0; + + try_interpret_escape_seq(rt); + } else + if (rt->pd->graphmode) { + put_graphmode_char(rt, data[pos]); + } else { + static int8_t const lens[5] = { 1, -1, 2, 3, 4 }; + int bsf = __builtin_clz(~((unsigned char)data[pos] << 24)); + + if (pos + lens[bsf] > len) + return pos; + + put_normal_char(rt, data + pos, lens[bsf]); + pos += lens[bsf] - 1; + } + } + + return len; +} + +/****************************************************************************/ +/* CSI things */ +/****************************************************************************/ + +/* interprets a 'set attribute' (SGR) CSI escape sequence */ +static void interpret_csi_SGR(RoteTerm *rt, int param[], int pcount) +{ + int i; + + if (pcount == 0) { + /* special case: reset attributes */ + rt->curattr = 0x70; + return; + } + + for (i = 0; i < pcount; i++) { + + // From http://vt100.net/docs/vt510-rm/SGR table 5-16 + // 0 All attributes off + // 1 Bold + // 4 Underline + // 5 Blinking + // 7 Negative image + // 8 Invisible image + // 10 The ASCII character set is the current 7-bit + // display character set (default) - SCO Console only. + // 11 Map Hex 00-7F of the PC character set codes + // to the current 7-bit display character set + // - SCO Console only. + // 12 Map Hex 80-FF of the current character set to + // the current 7-bit display character set - SCO + // Console only. + // 22 Bold off + // 24 Underline off + // 25 Blinking off + // 27 Negative image off + // 28 Invisible image off + + if (param[i] == 0) rt->curattr = 0x70; + else if (param[i] == 1 || param[i] == 2 || param[i] == 4) /* set bold */ + ROTE_ATTR_MOD_BOLD(rt->curattr,1); + else if (param[i] == 5) /* set blink */ + ROTE_ATTR_MOD_BLINK(rt->curattr,1); + else if (param[i] == 7 || param[i] == 27) { /* reverse video */ + int fg = ROTE_ATTR_FG(rt->curattr); + int bg = ROTE_ATTR_BG(rt->curattr); + ROTE_ATTR_MOD_FG(rt->curattr, bg); + ROTE_ATTR_MOD_BG(rt->curattr, fg); + } + else if (param[i] == 8) rt->curattr = 0x0; /* invisible */ + else if (param[i] == 22 || param[i] == 24) /* bold off */ + ROTE_ATTR_MOD_BOLD(rt->curattr,0); + else if (param[i] == 25) /* blink off */ + ROTE_ATTR_MOD_BLINK(rt->curattr,0); + else if (param[i] == 28) /* invisible off */ + rt->curattr = 0x70; + else if (param[i] >= 30 && param[i] <= 37) /* set fg */ + ROTE_ATTR_MOD_FG(rt->curattr, param[i] - 30); + else if (param[i] >= 40 && param[i] <= 47) /* set bg */ + ROTE_ATTR_MOD_BG(rt->curattr, param[i] - 40); + else if (param[i] == 39) /* reset foreground to default */ + ROTE_ATTR_MOD_FG(rt->curattr, 7); + else if (param[i] == 49) /* reset background to default */ + ROTE_ATTR_MOD_BG(rt->curattr, 0); + } +} + +/* interprets an 'erase display' (ED) escape sequence */ +static void interpret_csi_ED(RoteTerm *rt, int param[], int pcount) +{ + int r, c; + int start_row, start_col, end_row, end_col; + + /* decide range */ + if (pcount && param[0] == 2) { + start_row = 0; + start_col = 0; + end_row = rt->rows - 1; + end_col = rt->cols - 1; + } else + if (pcount && param[0] == 1) { + start_row = 0; + start_col = 0; + end_row = rt->crow; + end_col = rt->ccol; + } else { + start_row = rt->crow; + start_col = rt->ccol; + end_row = rt->rows - 1; + end_col = rt->cols - 1; + } + + /* clean range */ + for (r = start_row; r <= end_row; r++) { + rt->line_dirty[r] = true; + + for (c = (r == start_row ? start_col : 0); + c <= (r == end_row ? end_col : rt->cols - 1); + c++) + { + rt->cells[r][c].s[0] = 0x20; + rt->cells[r][c].len = 1; + rt->cells[r][c].attr = rt->curattr; + } + } +} + +/* interprets a 'move cursor' (CUP) escape sequence */ +static void interpret_csi_CUP(RoteTerm *rt, int param[], int pcount) +{ + if (pcount == 0) { + /* special case */ + rt->crow = rt->ccol = 0; + return; + } else + if (pcount < 2) { + return; /* malformed */ + } + + rt->crow = param[0] - 1; /* convert from 1-based to 0-based */ + rt->ccol = param[1] - 1; /* convert from 1-based to 0-based */ + + rt->curpos_dirty = true; + + clamp_cursor_to_bounds(rt); +} + +/* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL, + * CPL, CHA, HPR, VPA, VPR, HPA */ +static void interpret_csi_C(RoteTerm *rt, char verb, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + + switch (verb) { + case 'A': rt->crow -= n; break; + case 'B': case 'e': rt->crow += n; break; + case 'C': case 'a': rt->ccol += n; break; + case 'D': rt->ccol -= n; break; + case 'E': rt->crow += n; rt->ccol = 0; break; + case 'F': rt->crow -= n; rt->ccol = 0; break; + case 'G': case '`': rt->ccol = param[0] - 1; break; + case 'd': rt->crow = param[0] - 1; break; + } + + rt->curpos_dirty = true; + clamp_cursor_to_bounds(rt); +} + +/* Interpret the 'erase line' escape sequence */ +static void interpret_csi_EL(RoteTerm *rt, int param[], int pcount) +{ + int erase_start, erase_end, i; + int cmd = pcount ? param[0] : 0; + + switch (cmd) { + case 1: erase_start = 0; erase_end = rt->ccol; break; + case 2: erase_start = 0; erase_end = rt->cols - 1; break; + default: erase_start = rt->ccol; erase_end = rt->cols - 1; break; + } + + for (i = erase_start; i <= erase_end; i++) { + rt->cells[rt->crow][i].s[0] = 0x20; + rt->cells[rt->crow][i].len = 1; + rt->cells[rt->crow][i].attr = rt->curattr; + } + + rt->line_dirty[rt->crow] = true; +} + +/* Interpret the 'insert blanks' sequence (ICH) */ +static void interpret_csi_ICH(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i; + + for (i = rt->cols - 1; i >= rt->ccol + n; i--) { + rt->cells[rt->crow][i] = rt->cells[rt->crow][i - n]; + } + + for (i = rt->ccol; i < rt->ccol + n; i++) { + rt->cells[rt->crow][i].s[0] = 0x20; + rt->cells[rt->crow][i].len = 1; + rt->cells[rt->crow][i].attr = rt->curattr; + } + + rt->line_dirty[rt->crow] = true; +} + +/* Interpret the 'delete chars' sequence (DCH) */ +static void interpret_csi_DCH(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i; + + for (i = rt->ccol; i < rt->cols; i++) { + if (i + n < rt->cols) { + rt->cells[rt->crow][i] = rt->cells[rt->crow][i + n]; + } else { + rt->cells[rt->crow][i].s[0] = 0x20; + rt->cells[rt->crow][i].len = 1; + rt->cells[rt->crow][i].attr = rt->curattr; + } + } + + rt->line_dirty[rt->crow] = true; +} + +/* Interpret an 'insert line' sequence (IL) */ +static void interpret_csi_IL(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i, j; + + for (i = rt->pd->scrollbottom; i >= rt->crow + n; i--) { + memcpy(rt->cells[i], rt->cells[i - n], sizeof(RoteCell) * rt->cols); + } + + for (i = rt->crow; i < rt->crow + n && i <= rt->pd->scrollbottom; i++) { + rt->line_dirty[i] = true; + for (j = 0; j < rt->cols; j++) { + rt->cells[i][j].s[0] = 0x20; + rt->cells[i][j].len = 1; + rt->cells[i][j].attr = rt->curattr; + } + } + +} + +/* Interpret a 'delete line' sequence (DL) */ +static void interpret_csi_DL(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i, j; + + for (i = rt->crow; i <= rt->pd->scrollbottom; i++) { + rt->line_dirty[i] = true; + if (i + n <= rt->pd->scrollbottom) { + memcpy(rt->cells[i], rt->cells[i+n], sizeof(RoteCell) * rt->cols); + } else { + for (j = 0; j < rt->cols; j++) { + rt->cells[i][j].s[0] = 0x20; + rt->cells[i][j].len = 1; + rt->cells[i][j].attr = rt->curattr; + } + } + } +} + +/* Interpret an 'erase characters' (ECH) sequence */ +static void interpret_csi_ECH(RoteTerm *rt, int param[], int pcount) +{ + int n = (pcount && param[0] > 0) ? param[0] : 1; + int i; + + for (i = rt->ccol; i < rt->ccol + n && i < rt->cols; i++) { + rt->cells[rt->crow][i].s[0] = 0x20; + rt->cells[rt->crow][i].len = 1; + rt->cells[rt->crow][i].attr = rt->curattr; + } + + rt->line_dirty[rt->crow] = true; +} + +/* Interpret a 'set scrolling region' (DECSTBM) sequence */ +static void interpret_csi_DECSTBM(RoteTerm *rt, int param[], int pcount) +{ + int newtop, newbottom; + + if (!pcount) { + newtop = 0; + newbottom = rt->rows - 1; + } else + if (pcount < 2) { + return; /* malformed */ + } + + newtop = param[0] - 1; + newbottom = param[1] - 1; + + /* clamp to bounds */ + if (newtop < 0) + newtop = 0; + if (newtop >= rt->rows) + newtop = rt->rows - 1; + if (newbottom < 0) + newbottom = 0; + if (newbottom >= rt->rows) + newbottom = rt->rows - 1; + + /* check for range validity */ + if (newtop > newbottom) + return; + rt->pd->scrolltop = newtop; + rt->pd->scrollbottom = newbottom; +} + +static void +interpret_csi_SAVECUR(RoteTerm *rt, + int param[] __attribute__((unused)), + int pcount __attribute__((unused))) +{ + rt->pd->saved_x = rt->ccol; + rt->pd->saved_y = rt->crow; +} + +static void +interpret_csi_RESTORECUR(RoteTerm *rt, + int param[] __attribute__((unused)), + int pcount __attribute__((unused))) +{ + rt->ccol = rt->pd->saved_x; + rt->crow = rt->pd->saved_y; + rt->curpos_dirty = true; +} + +static void rote_es_interpret_csi(RoteTerm *rt) +{ + static int csiparam[MAX_CSI_ES_PARAMS]; + int param_count = 0; + const char *p = rt->pd->esbuf + 1; + char verb = rt->pd->esbuf[rt->pd->esbuf_len - 1]; + + if (!strncmp(rt->pd->esbuf, "[?", 2)) { /* private-mode CSI, ignore */ + return; + } + + /* parse numeric parameters */ + while (isdigit((unsigned char)*p) || *p == ';') { + if (*p == ';') { + if (param_count >= MAX_CSI_ES_PARAMS) return; /* too long! */ + csiparam[param_count++] = 0; + } else { + if (param_count == 0) csiparam[param_count++] = 0; + csiparam[param_count - 1] *= 10; + csiparam[param_count - 1] += *p - '0'; + } + + p++; + } + + /* delegate handling depending on command character (verb) */ + switch (verb) { + case 'h': + if (param_count == 1 && csiparam[0] == 4) /* insert mode */ + rt->insert = true; + break; + case 'l': + if (param_count == 1 && csiparam[0] == 4) /* replace mode */ + rt->insert = false; + break; + case 'm': /* it's a 'set attribute' sequence */ + interpret_csi_SGR(rt, csiparam, param_count); break; + case 'J': /* it's an 'erase display' sequence */ + interpret_csi_ED(rt, csiparam, param_count); break; + case 'H': case 'f': /* it's a 'move cursor' sequence */ + interpret_csi_CUP(rt, csiparam, param_count); break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'e': case 'a': case 'd': case '`': + /* it is a 'relative move' */ + interpret_csi_C(rt, verb, csiparam, param_count); break; + case 'K': /* erase line */ + interpret_csi_EL(rt, csiparam, param_count); break; + case '@': /* insert characters */ + interpret_csi_ICH(rt, csiparam, param_count); break; + case 'P': /* delete characters */ + interpret_csi_DCH(rt, csiparam, param_count); break; + case 'L': /* insert lines */ + interpret_csi_IL(rt, csiparam, param_count); break; + case 'M': /* delete lines */ + interpret_csi_DL(rt, csiparam, param_count); break; + case 'X': /* erase chars */ + interpret_csi_ECH(rt, csiparam, param_count); break; + case 'r': /* set scrolling region */ + interpret_csi_DECSTBM(rt, csiparam, param_count); break; + case 's': /* save cursor location */ + interpret_csi_SAVECUR(rt, csiparam, param_count); break; + case 'u': /* restore cursor location */ + interpret_csi_RESTORECUR(rt, csiparam, param_count); break; + default: + break; + } +} RoteTerm *rote_vt_create(int rows, int cols) { RoteTerm *rt; - int i, j; + int i; if (rows <= 0 || cols <= 0) return NULL; @@ -49,16 +718,9 @@ RoteTerm *rote_vt_create(int rows, int cols) rt->insert = false; /* create the cell matrix */ - rt->cells = (RoteCell**) malloc(sizeof(RoteCell*) * rt->rows); + rt->cells = (RoteCell**)calloc(sizeof(RoteCell*), rt->rows); for (i = 0; i < rt->rows; i++) { - /* create row */ - rt->cells[i] = (RoteCell*) malloc(sizeof(RoteCell) * rt->cols); - - /* fill row with spaces */ - for (j = 0; j < rt->cols; j++) { - rt->cells[i][j].ch = 0x20; /* a space */ - rt->cells[i][j].attr = 0x70; /* white text, black background */ - } + rt->cells[i] = (RoteCell*)calloc(sizeof(RoteCell), rt->cols); } /* allocate dirtiness array */ @@ -95,108 +757,57 @@ void rote_vt_destroy(RoteTerm *rt) free(rt); } -#ifdef USE_NCURSES - -static void default_cur_set_attr(WINDOW *win, unsigned char attr) +static void cur_set_attr(WINDOW *win, uint8_t attr) { - int cp = ROTE_ATTR_BG(attr) * 8 + 7 - ROTE_ATTR_FG(attr); - if (!cp) wattrset(win, A_NORMAL); - else wattrset(win, COLOR_PAIR(cp)); + unsigned cp = ROTE_ATTR_BG(attr) * 8 + 7 - ROTE_ATTR_FG(attr); + wattrset(win, cp ? COLOR_PAIR(cp) : A_NORMAL); - if (ROTE_ATTR_BOLD(attr)) wattron(win, A_BOLD); - if (ROTE_ATTR_BLINK(attr)) wattron(win, A_BLINK); + if (ROTE_ATTR_BOLD(attr)) + wattron(win, A_BOLD); + if (ROTE_ATTR_BLINK(attr)) + wattron(win, A_BLINK); } -static inline unsigned char ensure_printable(unsigned int ch) +static unsigned ensure_printable(unsigned ch) { return ch >= 32 ? ch : 32; } -void rote_vt_draw(RoteTerm *rt, WINDOW *win, int srow, int scol, - void (*cur_set_attr)(WINDOW*,unsigned char)) { - +void rote_vt_draw(RoteTerm *rt, WINDOW *win, int srow, int scol) +{ int i, j; - if (!cur_set_attr) - cur_set_attr = default_cur_set_attr; - for (i = 0; i < rt->rows; i++) { wmove(win, srow + i, scol); for (j = 0; j < rt->cols; j++) { - (*cur_set_attr)(win, rt->cells[i][j].attr); - waddch(win, ensure_printable(rt->cells[i][j].ch)); + cur_set_attr(win, rt->cells[i][j].attr); + if (rt->cells[i][j].len && rt->cells[i][j].s[0] >= ' ') { + waddnstr(win, rt->cells[i][j].s, rt->cells[i][j].len); + } else { + waddch(win, ' '); + } } } wmove(win, srow + rt->crow, scol + rt->ccol); } -#endif - /******************************************************/ -#define PTYCHAR1 "pqrstuvwxyz" -#define PTYCHAR2 "0123456789abcdef" - -/* allocate one pty/tty pair */ -static int get_pty(char *tty_str) -{ - int fd; - char ptydev[] = "/dev/pty??"; - char ttydev[] = "/dev/tty??"; - int len = strlen(ttydev); - const char *c1, *c2; - - for (c1 = PTYCHAR1; *c1; c1++) { - ptydev[len-2] = ttydev[len-2] = *c1; - for (c2 = PTYCHAR2; *c2; c2++) { - ptydev[len-1] = ttydev[len-1] = *c2; - if ((fd = open(ptydev, O_RDWR)) >= 0) { - if (access(ttydev, R_OK|W_OK) == 0) { - strcpy(tty_str, ttydev); - return fd; - } - close(fd); - } - } - } - return -1; -} - -static int -run_process(const char *path, const char **argv, int *fd_ptr, int *pid_ptr) -{ - int pty_fd, pid, i, nb_fds; - char tty_name[32]; +pid_t rote_vt_forkpty(RoteTerm *rt, const char *path, const char *argv[]) +{ struct winsize ws; + pid_t pid; - pty_fd = get_pty(tty_name); - if (pty_fd < 0) - return -1; - - fcntl(pty_fd, F_SETFL, O_NONBLOCK); - - /* set dummy screen size */ - ws.ws_col = 80; - ws.ws_row = 25; - ws.ws_xpixel = ws.ws_col; - ws.ws_ypixel = ws.ws_row; - ioctl(pty_fd, TIOCSWINSZ, &ws); + ws.ws_row = rt->rows; + ws.ws_col = rt->cols; + ws.ws_xpixel = ws.ws_ypixel = 0; - pid = fork(); + pid = forkpty(&rt->pty, NULL, NULL, &ws); if (pid < 0) return -1; if (pid == 0) { - /* child process */ - nb_fds = getdtablesize(); - for (i = 0; i < nb_fds; i++) - close(i); - /* open pseudo tty for standard i/o */ - open(tty_name, O_RDWR); - dup(0); - dup(0); - setsid(); setenv("TERM", "linux", 1); @@ -204,34 +815,8 @@ run_process(const char *path, const char **argv, int *fd_ptr, int *pid_ptr) fprintf(stderr, "\nexecv() failed.\nCommand: '%s'\n", argv[0]); exit(1); } - /* return file info */ - *fd_ptr = pty_fd; - *pid_ptr = pid; - return 0; -} - -pid_t rote_vt_forkpty(RoteTerm *rt, const char *path, const char *argv[]) -{ - struct winsize ws; - - ws.ws_row = rt->rows; - ws.ws_col = rt->cols; - ws.ws_xpixel = ws.ws_ypixel = 0; - - if (run_process(path, argv, &rt->pty, &rt->childpid)) { - return -1; - } - - ioctl(rt->pty, TIOCSWINSZ, &ws); - return rt->childpid; -} -void rote_vt_forsake_child(RoteTerm *rt) -{ - if (rt->pty >= 0) - close(rt->pty); - rt->pty = -1; - rt->childpid = 0; + return rt->childpid = pid; } int rote_vt_read(RoteTerm *rt, char *buf, int buflen) @@ -253,7 +838,7 @@ int rote_vt_write(RoteTerm *rt, const char *data, int len) return -1; } - again: +again: res = write(rt->pty, data, len); if (res < 0) { if (errno == EINTR || errno == EAGAIN) @@ -263,34 +848,6 @@ int rote_vt_write(RoteTerm *rt, const char *data, int len) return res; } -void *rote_vt_take_snapshot(RoteTerm *rt) -{ - const int bytes_per_row = sizeof(RoteCell) * rt->cols; - void *buf = malloc(bytes_per_row * rt->rows); - void *ptr = buf; - int i; - - for (i = 0; i < rt->rows; i++) { - memcpy(ptr, rt->cells[i], bytes_per_row); - ptr = (char *)ptr + bytes_per_row; - } - - return buf; -} - -void rote_vt_restore_snapshot(RoteTerm *rt, void *snapbuf) -{ - const int bytes_per_row = sizeof(RoteCell) * rt->cols; - - int i; - - for (i = 0; i < rt->rows; i++) { - rt->line_dirty[i] = true; - memcpy(rt->cells[i], snapbuf, bytes_per_row); - snapbuf = (char *)snapbuf + bytes_per_row; - } -} - static const char *keytable[KEY_MAX+1]; static void keytable_init() diff --git a/madtty/madtty.h b/madtty/madtty.h index e8123db..7424429 100644 --- a/madtty/madtty.h +++ b/madtty/madtty.h @@ -22,14 +22,12 @@ #ifndef MADTTY_MADTTY_H #define MADTTY_MADTTY_H -#ifdef USE_NCURSES -#include -#else +#include #include -#endif +#include +#include #include #include -#include /* Color codes: 0 = black, 1 = red, 2 = green, 3 = yellow, 4 = blue, * 5 = magenta, 6 = cyan, 7 = white. @@ -85,10 +83,9 @@ /* Represents each of the text cells in the terminal screen */ typedef struct RoteCell_ { - unsigned int ch; /* >= 32, that is, control characters are not - * allowed to be on the virtual screen */ - - unsigned char attr; /* a color attribute, as described previously */ + char s[4]; + char len; + uint8_t attr; /* a color attribute, as described previously */ } RoteCell; /* Declaration of opaque rote_Term_Private structure */ @@ -114,7 +111,7 @@ typedef struct RoteTerm_ { int crow, ccol; /* cursor coordinates. READ-ONLY. */ - unsigned char curattr; /* current attribute, that is the attribute + uint8_t curattr; /* current attribute, that is the attribute * that will be used for newly inserted * characters */ @@ -124,12 +121,12 @@ typedef struct RoteTerm_ { RoteTermPrivate *pd; /* private state data */ - bool insert; /* insert or replace mode */ + unsigned insert : 1; /* insert or replace mode */ /* --- dirtiness flags: the following flags will be raised when the * corresponding items are modified. They can only be unset by YOU * (when, for example, you redraw the term or something) --- */ - bool curpos_dirty; /* whether cursor location has changed */ + unsigned curpos_dirty : 1; /* whether cursor location has changed */ bool *line_dirty; /* whether each row is dirty */ /* --- end dirtiness flags */ } RoteTerm; @@ -170,12 +167,6 @@ void rote_vt_destroy(RoteTerm *rt); */ pid_t rote_vt_forkpty(RoteTerm *rt, const char *path, const char *argv[]); -/* Disconnects the RoteTerm from its forked child process. This function - * should be called when the child process dies or something of the sort. - * It is not strictly necessary to call this function, but it is - * certainly tidy. */ -void rote_vt_forsake_child(RoteTerm *rt); - int rote_vt_read(RoteTerm *rt, char *buf, int buflen); int rote_vt_write(RoteTerm *rt, const char *data, int length); @@ -187,28 +178,8 @@ int rote_vt_write(RoteTerm *rt, const char *data, int length); * to use rote_vt_write. */ int rote_vt_inject(RoteTerm *rt, const char *data, int length); +void rote_vt_draw(RoteTerm *rt, WINDOW *win, int startrow, int startcol); -#ifdef USE_NCURSES -/* Paints the virtual terminal screen on the given window, putting - * the top-left corner at the given position. The cur_set_attr - * function must set the curses attributes given a Rote attribute - * byte. It should, for example, do wattrset(win, COLOR_PAIR(n)) where - * n is the colorpair appropriate for the attribute and such. - * - * If you pass NULL for cur_set_attr, the default implementation will - * set the color pair given by (bg * 8 + 7 - fg), which seems to be - * a common mapping, and the bold and blink attributes will be mapped - * to A_BOLD and A_BLINK. - * - * At the end of the function, the cursor will be left where the virtual - * cursor of the terminal is supposed to be. - * - * This function does not call wrefresh(win); you have to do that yourself. - */ -void rote_vt_draw(RoteTerm *rt, WINDOW *win, int startrow, int startcol, - void (*cur_set_attr)(WINDOW *win, unsigned char attr)); - -#endif /* Indicates to the terminal that the given key has been pressed. * This will cause the terminal to rote_vt_write() the appropriate * escape sequence for that key (that is, the escape sequence @@ -217,15 +188,4 @@ void rote_vt_draw(RoteTerm *rt, WINDOW *win, int startrow, int startcol, * when you use keypad(somewin, TRUE) (see man page). */ void rote_vt_keypress(RoteTerm *rt, int keycode); -/* Takes a snapshot of the current contents of the terminal and - * saves them to a dynamically allocated buffer. Returns a pointer - * to the newly created buffer, which you can pass to - * rote_vt_restore_snapshot. Caller is responsible for free()'ing when - * the snapshot is no longer needed. */ -void *rote_vt_take_snapshot(RoteTerm *rt); - -/* Restores a snapshot previously taken with rote_vt_take_snapshot. - * This function does NOT free() the passed buffer */ -void rote_vt_restore_snapshot(RoteTerm *rt, void *snapbuf); - #endif /* MADTTY_MADTTY_H */ diff --git a/madtty/madtty_priv.h b/madtty/madtty_priv.h deleted file mode 100644 index e33902b..0000000 --- a/madtty/madtty_priv.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - LICENSE INFORMATION: - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License (LGPL) as published by the Free Software Foundation. - - Please refer to the COPYING file for more information. - - 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. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Copyright © 2004 Bruno T. C. de Oliveira - Copyright © 2006 Pierre Habouzit - */ - -#ifndef MADTTY_MADTTY_PRIV_H -#define MADTTY_MADTTY_PRIV_H - -#define ESEQ_BUF_SIZE 128 /* size of escape sequence buffer */ - -/* Terminal private data */ -struct RoteTermPrivate_ { - unsigned escaped : 1; /* whether we are currently reading an - * escape sequence */ - - unsigned graphmode : 1; /* whether terminal is in graphical - * character mode or not */ - - int scrolltop, scrollbottom; /* current scrolling region of terminal */ - int saved_x, saved_y; /* saved cursor position */ - - char esbuf[ESEQ_BUF_SIZE]; /* 0-terminated string. Does NOT include - * the initial escape (\x1B) character. */ - int esbuf_len; /* length of buffer. The following property - * is always kept: esbuf[esbuf_len] == '\0' */ -}; - -/* Interprets a CSI escape sequence stored in rt->pd->esbuf, - * changing rt to reflect the effect of the sequence. This function - * will not change rt->pd->esbuf, rt->pd->escaped or other escape-sequence - * related fields in it */ -void rote_es_interpret_csi(RoteTerm *rt); - -#endif /* MADTTY_MADTTY_PRIV_H */ -- 2.20.1