diff -Nru dvtm-0.6/bstack.c dvtm-0.12/bstack.c --- dvtm-0.6/bstack.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/bstack.c 2014-07-05 07:32:09.000000000 +0000 @@ -1,57 +1,57 @@ -static void -bstack(void) { +static void bstack(void) +{ unsigned int i, m, n, nx, ny, nw, nh, mh, tw; Client *c; - for(n = 0, m = 0, c = clients; c; c = c->next, n++) - if(c->minimized) + for (n = 0, m = 0, c = clients; c; c = c->next, n++) + if (c->minimized) m++; - if(n == 1) + if (n == 1) mh = wah; - else if(n - 1 == m) + else if (n - 1 == m) mh = wah - m; else - mh = mwfact * (wah - m); + mh = screen.mfact * (wah - m); /* true if there are at least 2 non minimized clients */ - if(n - 1 > m) + if (n - 1 > m) tw = waw / (n - m - 1); nx = wax; ny = way; - for(i = 0, c = clients; c; c = c->next, i++){ - if(i == 0){ /* master */ + for (i = 0, c = clients; c; c = c->next, i++) { + if (i == 0) { /* master */ nh = mh; nw = waw; - } else { /* tile window */ - if(i == 1){ + } else { /* tile window */ + if (i == 1) { nx = wax; ny += mh; nh = (way + wah - m) - ny; } - if(i == n - m - 1){ /* last not minimized client */ + if (i == n - m - 1) { /* last not minimized client */ nw = (wax + waw) - nx; - } else if(i == n - m){ /* first minimized client */ + } else if (i == n - m) { /* first minimized client */ ny += nh; nx = wax; nw = waw; nh = 1; - } else if(c->minimized) { /* minimized window */ + } else if (c->minimized) { /* minimized window */ nw = waw; nh = 1; ny++; - } else /* normal non minimized tile window */ + } else /* normal non minimized tile window */ nw = tw; - if(i > 1 && !c->minimized){ + if (i > 1 && !c->minimized) { mvvline(ny, nx, ACS_VLINE, nh); mvaddch(ny, nx, ACS_TTEE); nx++, nw--; } } - resize(c,nx,ny,nw,nh); + resize(c, nx, ny, nw, nh); - if(n > 1 && i < n - m - 1) + if (n > 1 && i < n - m - 1) nx += nw; } } diff -Nru dvtm-0.6/cmdfifo.c dvtm-0.12/cmdfifo.c --- dvtm-0.6/cmdfifo.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/cmdfifo.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ -static int cmdfd = -1; -static unsigned short int client_id = 0; -static const char *cmdpath = NULL; - -/* glibc has a non-standard realpath(3) implementation which allocates - * the destination buffer, other C libraries may have a broken implementation - * which expect an already allocated destination buffer. - */ - -#ifndef __GLIBC__ -# include -# ifndef PATH_MAX -# define PATH_MAX 1024 -# endif -#endif - -static char *get_realpath(const char *path) { -#ifdef __GLIBC__ - return realpath(path, NULL); -#else - static char buf[PATH_MAX]; - return realpath(path, buf); -#endif -} - -static Cmd * -get_cmd_by_name(const char *name) { - for (int i = 0; i < countof(commands); i++) { - if (!strcmp(name, commands[i].name)) - return &commands[i]; - } - return NULL; -} - -static void -handle_cmdfifo() { - int r; - char *p, *s, cmdbuf[512], c; - Cmd *cmd; - switch (r = read(cmdfd, cmdbuf, sizeof cmdbuf - 1)) { - case -1: - case 0: - cmdfd = -1; - break; - default: - cmdbuf[r] = '\0'; - p = cmdbuf; - while (*p) { - /* find the command name */ - for (; *p == ' ' || *p == '\n'; p++); - for (s = p; *p && *p != ' ' && *p != '\n'; p++); - if ((c = *p)) - *p++ = '\0'; - if (*s && (cmd = get_cmd_by_name(s)) != NULL) { - bool quote = false; - int argc = 0; - /* XXX: initializer assumes MAX_ARGS == 2 use a initialization loop? */ - const char *args[MAX_ARGS] = { NULL, NULL}, *arg; - /* if arguments were specified in config.h ignore the one given via - * the named pipe and thus skip everything until we find a new line - */ - if (cmd->action.args[0] || c == '\n') { - debug("execute %s", s); - cmd->action.cmd(cmd->action.args); - while (*p && *p != '\n') - p++; - continue; - } - /* no arguments were given in config.h so we parse the command line */ - while (*p == ' ') - p++; - arg = p; - for (; (c = *p); p++) { - switch (*p) { - case '\\': - /* remove the escape character '\\' move every - * following character to the left by one position - */ - switch (*(++p)) { - case '\\': - case '\'': - case '\"': { - char *t = p; - for (;;) { - *(t - 1) = *t; - if (*t++ == '\0') - break; - } - p -= 2; - } - } - break; - case '\'': - case '\"': - quote = !quote; - break; - case ' ': - if (!quote) { - case '\n': - /* remove trailing quote if there is one */ - if (*(p - 1) == '\'' || *(p - 1) == '\"') - *(p - 1) = '\0'; - *p++ = '\0'; - /* remove leading quote if there is one */ - if (*arg == '\'' || *arg == '\"') - arg++; - if (argc < MAX_ARGS) - args[argc++] = arg; - - while (*p == ' ') - ++p; - arg = p; - } - break; - } - - if (c == '\n' || *p == '\n') { - debug("execute %s", s); - for(int i = 0; i < argc; i++) - debug(" %s", args[i]); - debug("\n"); - cmd->action.cmd(args); - break; - } - } - } - } - } -} diff -Nru dvtm-0.6/config.def.h dvtm-0.12/config.def.h --- dvtm-0.6/config.def.h 1970-01-01 00:00:00.000000000 +0000 +++ dvtm-0.12/config.def.h 2014-07-05 07:32:09.000000000 +0000 @@ -0,0 +1,159 @@ +/* valid curses attributes are listed below they can be ORed + * + * A_NORMAL Normal display (no highlight) + * A_STANDOUT Best highlighting mode of the terminal. + * A_UNDERLINE Underlining + * A_REVERSE Reverse video + * A_BLINK Blinking + * A_DIM Half bright + * A_BOLD Extra bright or bold + * A_PROTECT Protected mode + * A_INVIS Invisible or blank mode + */ +#define BLUE (COLORS==256 ? 68 : COLOR_BLUE) +/* curses attributes for the currently focused window */ +#define SELECTED_ATTR COLOR(BLUE, -1) | A_NORMAL +/* curses attributes for normal (not selected) windows */ +#define NORMAL_ATTR COLOR(-1, -1) | A_NORMAL +/* curses attributes for the status bar */ +#define BAR_ATTR COLOR(BLUE, -1) | A_NORMAL +/* status bar (command line option -s) position */ +#define BAR_POS BAR_TOP /* BAR_BOTTOM, BAR_OFF */ +/* determines whether the statusbar text should be right or left aligned */ +#define BAR_ALIGN ALIGN_RIGHT +/* separator between window title and window number */ +#define SEPARATOR " | " +/* printf format string for the window title, first %s + * is replaced by the title, second %s is replaced by + * the SEPARATOR, %d stands for the window number */ +#define TITLE "[%s%s#%d]" +/* master width factor [0.1 .. 0.9] */ +#define MFACT 0.5 +/* scroll back buffer size in lines */ +#define SCROLL_HISTORY 500 + +#include "tile.c" +#include "grid.c" +#include "bstack.c" +#include "fullscreen.c" + +/* by default the first layout entry is used */ +static Layout layouts[] = { + { "[]=", tile }, + { "+++", grid }, + { "TTT", bstack }, + { "[ ]", fullscreen }, +}; + +#define MOD CTRL('g') + +/* you can at most specifiy MAX_ARGS (3) number of arguments */ +static Key keys[] = { + { MOD, 'c', { create, { NULL } } }, + { MOD, 'C', { create, { NULL, NULL, "$CWD" } } }, + { MOD, 'x', { killclient, { NULL } } }, + { MOD, 'j', { focusnext, { NULL } } }, + { MOD, 'u', { focusnextnm, { NULL } } }, + { MOD, 'i', { focusprevnm, { NULL } } }, + { MOD, 'k', { focusprev, { NULL } } }, + { MOD, 't', { setlayout, { "[]=" } } }, + { MOD, 'g', { setlayout, { "+++" } } }, + { MOD, 'b', { setlayout, { "TTT" } } }, + { MOD, 'm', { setlayout, { "[ ]" } } }, + { MOD, ' ', { setlayout, { NULL } } }, + { MOD, 'h', { setmfact, { "-0.05" } } }, + { MOD, 'l', { setmfact, { "+0.05" } } }, + { MOD, '.', { toggleminimize, { NULL } } }, + { MOD, 's', { togglebar, { NULL } } }, + { MOD, 'M', { togglemouse, { NULL } } }, + { MOD, '\n', { zoom , { NULL } } }, + { MOD, '1', { focusn, { "1" } } }, + { MOD, '2', { focusn, { "2" } } }, + { MOD, '3', { focusn, { "3" } } }, + { MOD, '4', { focusn, { "4" } } }, + { MOD, '5', { focusn, { "5" } } }, + { MOD, '6', { focusn, { "6" } } }, + { MOD, '7', { focusn, { "7" } } }, + { MOD, '8', { focusn, { "8" } } }, + { MOD, '9', { focusn, { "9" } } }, + { MOD, '\t', { focuslast, { NULL } } }, + { MOD, 'q', { quit, { NULL } } }, + { MOD, 'a', { togglerunall, { NULL } } }, + { MOD, 'r', { redraw, { NULL } } }, + { MOD, 'B', { togglebell, { NULL } } }, + { MOD, 'v', { copymode, { NULL } } }, + { MOD, '/', { copymode, { "/" } } }, + { MOD, '?', { copymode, { "?" } } }, + { MOD, 'p', { paste, { NULL } } }, + { MOD, KEY_PPAGE, { scrollback, { "-1" } } }, + { MOD, KEY_NPAGE, { scrollback, { "1" } } }, + { MOD, KEY_F(1), { create, { "man dvtm", "dvtm help" } } }, + { MOD, MOD, { send, { (const char []){MOD, 0} } } }, + { NOMOD, KEY_SPREVIOUS, { scrollback, { "-1" } } }, + { NOMOD, KEY_SNEXT, { scrollback, { "1" } } }, +}; + +static const ColorRule colorrules[] = { + { "", A_NORMAL, -1, -1 }, /* default */ +#if 0 + /* title attrs fgcolor bgcolor */ + { "ssh", A_NORMAL, COLOR_BLACK, 224 }, +#endif +}; + +/* possible values for the mouse buttons are listed below: + * + * BUTTON1_PRESSED mouse button 1 down + * BUTTON1_RELEASED mouse button 1 up + * BUTTON1_CLICKED mouse button 1 clicked + * BUTTON1_DOUBLE_CLICKED mouse button 1 double clicked + * BUTTON1_TRIPLE_CLICKED mouse button 1 triple clicked + * BUTTON2_PRESSED mouse button 2 down + * BUTTON2_RELEASED mouse button 2 up + * BUTTON2_CLICKED mouse button 2 clicked + * BUTTON2_DOUBLE_CLICKED mouse button 2 double clicked + * BUTTON2_TRIPLE_CLICKED mouse button 2 triple clicked + * BUTTON3_PRESSED mouse button 3 down + * BUTTON3_RELEASED mouse button 3 up + * BUTTON3_CLICKED mouse button 3 clicked + * BUTTON3_DOUBLE_CLICKED mouse button 3 double clicked + * BUTTON3_TRIPLE_CLICKED mouse button 3 triple clicked + * BUTTON4_PRESSED mouse button 4 down + * BUTTON4_RELEASED mouse button 4 up + * BUTTON4_CLICKED mouse button 4 clicked + * BUTTON4_DOUBLE_CLICKED mouse button 4 double clicked + * BUTTON4_TRIPLE_CLICKED mouse button 4 triple clicked + * BUTTON_SHIFT shift was down during button state change + * BUTTON_CTRL control was down during button state change + * BUTTON_ALT alt was down during button state change + * ALL_MOUSE_EVENTS report all button state changes + * REPORT_MOUSE_POSITION report mouse movement + */ + +#ifdef NCURSES_MOUSE_VERSION +# define CONFIG_MOUSE /* compile in mouse support if we build against ncurses */ +#endif + +#define ENABLE_MOUSE true /* whether to enable mouse events by default */ + +#ifdef CONFIG_MOUSE +static Button buttons[] = { + { BUTTON1_CLICKED, { mouse_focus, { NULL } } }, + { BUTTON1_DOUBLE_CLICKED, { mouse_fullscreen, { "[ ]" } } }, + { BUTTON2_CLICKED, { mouse_zoom, { NULL } } }, + { BUTTON3_CLICKED, { mouse_minimize, { NULL } } }, +}; +#endif /* CONFIG_MOUSE */ + +static Cmd commands[] = { + { "create", { create, { NULL } } }, +}; + +/* gets executed when dvtm is started */ +static Action actions[] = { + { create, { NULL } }, +}; + +static char const * const keytable[] = { + /* add your custom key escape sequences */ +}; diff -Nru dvtm-0.6/config.h dvtm-0.12/config.h --- dvtm-0.6/config.h 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/config.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -/* curses attributes for the currently focused window */ -/* valid curses attributes are listed below they can be ORed - * - * A_NORMAL Normal display (no highlight) - * A_STANDOUT Best highlighting mode of the terminal. - * A_UNDERLINE Underlining - * A_REVERSE Reverse video - * A_BLINK Blinking - * A_DIM Half bright - * A_BOLD Extra bright or bold - * A_PROTECT Protected mode - * A_INVIS Invisible or blank mode - * - */ -#define BLUE (COLORS==256 ? 68 : COLOR_BLUE) - -#define SELECTED_ATTR A_NORMAL -#define SELECTED_FG BLUE -#define SELECTED_BG -1 -/* curses attributes for normal (not selected) windows */ -#define NORMAL_ATTR A_NORMAL -#define NORMAL_FG -1 -#define NORMAL_BG -1 -/* status bar (command line option -s) position */ -#define BARPOS BarTop /* BarBot, BarOff */ -/* curses attributes for the status bar */ -#define BAR_ATTR A_NORMAL -#define BAR_FG BLUE -#define BAR_BG -1 -/* determines whether the statusbar text should be right or left aligned */ -#define BAR_ALIGN ALIGN_RIGHT -/* separator between window title and window number */ -#define SEPARATOR " | " -/* printf format string for the window title, first %s - * is replaced by the title, second %s is replaced by - * the SEPARATOR, %d stands for the window number */ -#define TITLE "[%s%s#%d]" -/* master width factor [0.1 .. 0.9] */ -#define MWFACT 0.5 -/* scroll back buffer size in lines */ -#define SCROLL_BUF_SIZE 500 - -#include "tile.c" -#include "grid.c" -#include "bstack.c" -#include "fullscreen.c" - -/* by default the first layout entry is used */ -Layout layouts[] = { - { "[]=", tile }, - { "+++", grid }, - { "TTT", bstack }, - { "[ ]", fullscreen }, -}; - -#define MOD CTRL('g') - -/* you can at most specifiy MAX_ARGS (2) number of arguments */ -Key keys[] = { - { MOD, 'c', { create, { NULL } } }, - { MOD, 'x', { killclient, { NULL } } }, - { MOD, 'j', { focusnext, { NULL } } }, - { MOD, 'u', { focusnextnm, { NULL } } }, - { MOD, 'i', { focusprevnm, { NULL } } }, - { MOD, 'k', { focusprev, { NULL } } }, - { MOD, 't', { setlayout, { "[]=" } } }, - { MOD, 'g', { setlayout, { "+++" } } }, - { MOD, 'b', { setlayout, { "TTT" } } }, - { MOD, 'm', { setlayout, { "[ ]" } } }, - { MOD, ' ', { setlayout, { NULL } } }, - { MOD, 'h', { setmwfact, { "-0.05" } } }, - { MOD, 'l', { setmwfact, { "+0.05" } } }, - { MOD, '.', { toggleminimize, { NULL } } }, -#ifdef CONFIG_STATUSBAR - { MOD, 's', { togglebar, { NULL } } }, -#endif -#ifdef CONFIG_MOUSE - { MOD, 'M', { mouse_toggle, { NULL } } }, -#endif - { MOD, '\n', { zoom , { NULL } } }, - { MOD, '1', { focusn, { "1" } } }, - { MOD, '2', { focusn, { "2" } } }, - { MOD, '3', { focusn, { "3" } } }, - { MOD, '4', { focusn, { "4" } } }, - { MOD, '5', { focusn, { "5" } } }, - { MOD, '6', { focusn, { "6" } } }, - { MOD, '7', { focusn, { "7" } } }, - { MOD, '8', { focusn, { "8" } } }, - { MOD, '9', { focusn, { "9" } } }, - { MOD, 'q', { quit, { NULL } } }, - { MOD, 'G', { escapekey, { NULL } } }, - { MOD, 'a', { togglerunall, { NULL } } }, - { MOD, 'r', { redraw, { NULL } } }, - { MOD, 'X', { lock, { NULL } } }, - { MOD, 'B', { togglebell, { NULL } } }, - { MOD, KEY_PPAGE, { scrollback, { "-1" } } }, - { MOD, KEY_NPAGE, { scrollback, { "1" } } }, - { MOD, '?', { create, { "man dvtm", "dvtm help" } } }, -}; - -/* possible values for the mouse buttons are listed below: - * - * BUTTON1_PRESSED mouse button 1 down - * BUTTON1_RELEASED mouse button 1 up - * BUTTON1_CLICKED mouse button 1 clicked - * BUTTON1_DOUBLE_CLICKED mouse button 1 double clicked - * BUTTON1_TRIPLE_CLICKED mouse button 1 triple clicked - * BUTTON2_PRESSED mouse button 2 down - * BUTTON2_RELEASED mouse button 2 up - * BUTTON2_CLICKED mouse button 2 clicked - * BUTTON2_DOUBLE_CLICKED mouse button 2 double clicked - * BUTTON2_TRIPLE_CLICKED mouse button 2 triple clicked - * BUTTON3_PRESSED mouse button 3 down - * BUTTON3_RELEASED mouse button 3 up - * BUTTON3_CLICKED mouse button 3 clicked - * BUTTON3_DOUBLE_CLICKED mouse button 3 double clicked - * BUTTON3_TRIPLE_CLICKED mouse button 3 triple clicked - * BUTTON4_PRESSED mouse button 4 down - * BUTTON4_RELEASED mouse button 4 up - * BUTTON4_CLICKED mouse button 4 clicked - * BUTTON4_DOUBLE_CLICKED mouse button 4 double clicked - * BUTTON4_TRIPLE_CLICKED mouse button 4 triple clicked - * BUTTON_SHIFT shift was down during button state change - * BUTTON_CTRL control was down during button state change - * BUTTON_ALT alt was down during button state change - * ALL_MOUSE_EVENTS report all button state changes - * REPORT_MOUSE_POSITION report mouse movement - */ - -#ifdef CONFIG_MOUSE -Button buttons[] = { - { BUTTON1_CLICKED, { mouse_focus, { NULL } } }, - { BUTTON1_DOUBLE_CLICKED, { mouse_fullscreen, { "[ ]" } } }, - { BUTTON2_CLICKED, { mouse_zoom, { NULL } } }, - { BUTTON3_CLICKED, { mouse_minimize, { NULL } } }, -}; -#endif /* CONFIG_MOUSE */ - -#ifdef CONFIG_CMDFIFO -Cmd commands[] = { - { "create", { create, { NULL } } }, -}; -#endif /* CONFIG_CMDFIFO */ - -/* gets executed when dvtm is started */ -Action actions[] = { - { create, { NULL } }, -}; diff -Nru dvtm-0.6/config.mk dvtm-0.12/config.mk --- dvtm-0.6/config.mk 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/config.mk 2014-07-05 07:32:09.000000000 +0000 @@ -1,23 +1,23 @@ # dvtm version -VERSION = 0.6 +VERSION = 0.12 # Customize below to fit your system PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man -INCS = -I. -I/usr/include -I/usr/local/include +INCS = -I. LIBS = -lc -lutil -lncursesw -#LIBS = -lc -lutil -lncurses +# NetBSD +#LIBS = -lc -lutil -lcurses +# AIX +#LIBS = -lc -lncursesw +# Cygwin +#INCS += -I/usr/include/ncurses CFLAGS += -std=c99 -Os ${INCS} -DVERSION=\"${VERSION}\" -DNDEBUG -LDFLAGS += -L/usr/lib -L/usr/local/lib ${LIBS} +LDFLAGS += ${LIBS} -# Mouse handling -CFLAGS += -DCONFIG_MOUSE -#CFLAGS += -DCONFIG_CMDFIFO -CFLAGS += -DCONFIG_STATUSBAR - -DEBUG_CFLAGS = ${CFLAGS} -UNDEBUG -O0 -g -ggdb -Wall +DEBUG_CFLAGS = ${CFLAGS} -UNDEBUG -O0 -g -ggdb -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter CC = cc diff -Nru dvtm-0.6/debian/changelog dvtm-0.12/debian/changelog --- dvtm-0.6/debian/changelog 2010-10-18 15:14:36.000000000 +0000 +++ dvtm-0.12/debian/changelog 2014-09-07 14:09:09.000000000 +0000 @@ -1,3 +1,14 @@ +dvtm (0.12-1) unstable; urgency=medium + + * New upstream release (Closes: #760607) + * Switch to debhelper compat level 9 + * cflags_and_strip_fixes.diff: refresh following upstream changes + * debian/control: + - Update Standards-Version to 3.9.5, no change required + - Update email address from albin.tonnerre@gmail.com to lutin@debian.org + + -- Albin Tonnerre Sun, 07 Sep 2014 13:10:51 +0200 + dvtm (0.6-1) unstable; urgency=low * New upstream release diff -Nru dvtm-0.6/debian/compat dvtm-0.12/debian/compat --- dvtm-0.6/debian/compat 2010-10-18 15:14:36.000000000 +0000 +++ dvtm-0.12/debian/compat 2014-09-07 14:09:09.000000000 +0000 @@ -1 +1 @@ -7 +9 diff -Nru dvtm-0.6/debian/control dvtm-0.12/debian/control --- dvtm-0.6/debian/control 2010-10-18 15:14:36.000000000 +0000 +++ dvtm-0.12/debian/control 2014-09-07 14:09:09.000000000 +0000 @@ -1,10 +1,10 @@ Source: dvtm Section: utils Priority: extra -Maintainer: Albin Tonnerre +Maintainer: Albin Tonnerre Uploaders: Xavier Oswald -Build-Depends: debhelper (>= 7.0.50~), libncursesw5-dev -Standards-Version: 3.9.1 +Build-Depends: debhelper (>= 9), libncursesw5-dev +Standards-Version: 3.9.5 Homepage: http://www.brain-dump.org/projects/dvtm Package: dvtm diff -Nru dvtm-0.6/debian/patches/cflags_and_strip_fixes.diff dvtm-0.12/debian/patches/cflags_and_strip_fixes.diff --- dvtm-0.6/debian/patches/cflags_and_strip_fixes.diff 2010-10-18 15:14:36.000000000 +0000 +++ dvtm-0.12/debian/patches/cflags_and_strip_fixes.diff 2014-09-07 14:07:42.000000000 +0000 @@ -1,42 +1,35 @@ -# Patch from Albin Tonnerre -# Explicitely define LDFLAGS = , not +=, as this leads to have both -# -lncurses and -lncursesw appended when you call 'make unicode' -Index: dvtm-0.5.1/Makefile -=================================================================== ---- dvtm-0.5.1.orig/Makefile 2009-02-17 21:42:13.000000000 +0100 -+++ dvtm-0.5.1/Makefile 2009-02-17 21:52:20.000000000 +0100 -@@ -40,8 +40,8 @@ +diff --git a/Makefile b/Makefile +index abef9a9..2ecd2fc 100644 +--- a/Makefile ++++ b/Makefile +@@ -43,8 +43,6 @@ dist: clean @rm -rf dvtm-${VERSION} install: dvtm - @echo stripping executable - @strip -s dvtm -+# @echo stripping executable -+# @strip -s dvtm @echo installing executable file to ${DESTDIR}${PREFIX}/bin @mkdir -p ${DESTDIR}${PREFIX}/bin @cp -f dvtm ${DESTDIR}${PREFIX}/bin -Index: dvtm-0.5.1/config.mk -=================================================================== ---- dvtm-0.5.1.orig/config.mk 2009-02-17 21:42:13.000000000 +0100 -+++ dvtm-0.5.1/config.mk 2009-02-17 21:52:11.000000000 +0100 -@@ -6,16 +6,16 @@ - PREFIX = /usr/local - MANPREFIX = ${PREFIX}/share/man +diff --git a/config.mk b/config.mk +index 05106e5..e957080 100644 +--- a/config.mk ++++ b/config.mk +@@ -3,7 +3,7 @@ VERSION = 0.12 --INCS = -I. -I/usr/include -I/usr/local/include -+INCS = -I. -I/usr/include/ncursesw -I/usr/local/include/ncursesw - LIBS = -lc -lutil -lncursesw - #LIBS = -lc -lutil -lncurses + # Customize below to fit your system --CFLAGS += -std=c99 -Os ${INCS} -DVERSION=\"${VERSION}\" -DNDEBUG -+CFLAGS += -std=c99 ${BUILD_OPTS} ${INCS} -DVERSION=\"${VERSION}\" -DNDEBUG - LDFLAGS += -L/usr/lib -L/usr/local/lib ${LIBS} +-PREFIX = /usr/local ++PREFIX = /usr + MANPREFIX = ${PREFIX}/share/man - # Mouse handling - CFLAGS += -DCONFIG_MOUSE --#CFLAGS += -DCONFIG_CMDFIFO -+CFLAGS += -DCONFIG_CMDFIFO - CFLAGS += -DCONFIG_STATUSBAR + INCS = -I. +@@ -13,7 +13,7 @@ LIBS = -lc -lutil -lncursesw + # AIX + #LIBS = -lc -lncursesw + # Cygwin +-#INCS += -I/usr/include/ncurses ++INCS += -I/usr/include/ncursesw - DEBUG_CFLAGS = ${CFLAGS} -UNDEBUG -O0 -g -ggdb -Wall + CFLAGS += -std=c99 -Os ${INCS} -DVERSION=\"${VERSION}\" -DNDEBUG + LDFLAGS += ${LIBS} diff -Nru dvtm-0.6/debian/rules dvtm-0.12/debian/rules --- dvtm-0.6/debian/rules 2010-10-18 15:14:36.000000000 +0000 +++ dvtm-0.12/debian/rules 2014-09-07 11:11:54.000000000 +0000 @@ -1,7 +1,6 @@ #!/usr/bin/make -f - -%: +%: dh $@ override_dh_auto_install: diff -Nru dvtm-0.6/dvtm.1 dvtm-0.12/dvtm.1 --- dvtm-0.6/dvtm.1 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/dvtm.1 2014-07-05 07:32:09.000000000 +0000 @@ -1,9 +1,24 @@ .TH DVTM 1 dvtm\-VERSION +.nh .SH NAME dvtm \- dynamic virtual terminal manager .SH SYNOPSIS .B dvtm -.RB [ \-v ] \ [ \-m \ mod ] \ [ \-s \ status-fifo ] \ [cmd...] +.RB [ \-v ] +.RB [ \-M ] +.RB [ \-m +.IR modifier ] +.RB [ \-d +.IR delay ] +.RB [ \-h +.IR lines ] +.RB [ \-t +.IR title ] +.RB [ \-s +.IR status-fifo ] +.RB [ \-c +.IR cmd-fifo ] +.RI [ command \ ... "" ] .SH DESCRIPTION dvtm is a dynamic tiling window manager for the console. As a console window manager it tries to make it easy to work with multiple @@ -11,30 +26,55 @@ .SH OPTIONS .TP .B \-v -prints version information to standard output, then exits. +Print version information to standard output and exit. .TP -.B \-m mod -set default modifier at runtime. -.TP -.B \-d escdelay -set the delay ncurses waits before deciding if a character that might be part -of an escape sequence is actually part of an escape sequence. -.TP -.B \-h nnn -set the scrollback history buffer size at runtime. -.TP -.B \-s status-fifo -if status-fifo is a named pipe it's content is read and displayed. See the dvtm-status script -for an usage example. -.TP -.B [cmd...] -Execute cmd after dvtm is started. +.B \-M +Toggle default mouse grabbing upon startup. Use this to allow normal mouse operation +under X. +.TP +.BI \-m \ modifier +Set command modifier at runtime. +.TP +.BI \-d \ delay +Set the delay ncurses waits before deciding if a character that might be +part of an escape sequence is actually part of an escape sequence. +.TP +.BI \-h \ lines +Set the scrollback history buffer size at runtime. +.TP +.BI \-t \ title +Set a static terminal +.I title +and don't change it to the one of the currently focused window. +.TP +.BI \-s \ status-fifo +Open or create the named pipe +.I status-fifo +read its content and display it in the statusbar. See the +.I dvtm-status +script for an usage example. +.TP +.BI \-c \ cmd-fifo +Open or create the named pipe +.I cmd-fifo +and look for commands to execute which were defined in +.IR config.h . +.TP +.IR command \ ... +Execute +.IR command (s), +each in a separate window. .SH USAGE .SS Keyboard commands .TP .B Mod -Each keybinding begins with Mod which defaults to ^g but can be changed in config.h or with -the -m command line option. +Each keybinding begins with Mod which defaults to +.BR ^g , +but can be changed in +.I config.h +or with the +.B \-m +command line option. .TP .B Mod\-c Create a new shell window. @@ -43,10 +83,12 @@ Close focused window. .TP .B Mod\-l -Increases the master area width about 5% (all except grid and fullscreen layout). +Increases the master area width about 5% (all except grid and +fullscreen layout). .TP .B Mod\-h -Decreases the master area width about 5% (all except grid and fullscreen layout). +Decreases the master area width about 5% (all except grid and +fullscreen layout). .TP .B Mod\-j Focus next window. @@ -54,11 +96,14 @@ .B Mod\-k Focus previous window. .TP -.B Mod\-[1..n] +.BI Mod\- n Focus the -.BR nth +.IR n \-th window. .TP +.B Mod\-Tab +Focus previously selected window. +.TP .B Mod\-. Toggle minimization of current window. .TP @@ -66,14 +111,18 @@ Focus next non minimized window. .TP .B Mod\-i -Focus prev non minimized window. +Focus previous non minimized window. .TP .B Mod\-m Maximize current window (change to fullscreen layout). .TP +.B Shift\-PageUp +.TQ .B Mod\-PageUp Scroll up. .TP +.B Shift\-PageDown +.TQ .B Mod\-PageDown Scroll down. .TP @@ -93,19 +142,14 @@ Change to grid layout. .TP .B Mod\-s -Shows/hides the status bar. +Show/hide the status bar. .TP .B Mod\-r Redraw whole screen. .TP -.B Mod\-G -Escape the next typed key. -.TP .B Mod\-a -Toggle keyboard multiplexing mode, if activated keypresses are sent to all non minimized windows. -.TP -.B Mod\-X -Lock screen. +Toggle keyboard multiplexing mode, if activated keypresses are sent to all +visible windows. .TP .B Mod\-B Toggle bell (off by default). @@ -113,35 +157,122 @@ .B Mod\-M Toggle dvtm mouse grabbing. .TP +.B Mod\-v +Enter copy mode (see section below for navigation commands). +.TP +.B Mod\-/ +Enter copy mode and start searching forward. +.TP +.B Mod\-? +Enter copy mode and start searching backwards. +.TP +.B Mod\-p +Paste last copied text from copy mode at current cursor position. +.TP +.B Mod\-F1 +Show this manual page. +.TP +.B Mod\-Mod +Send the Mod key. +.TP .B Mod\-q Quit dvtm. .SS Mouse commands .TP .B Copy and Paste -By default dvtm captures mouse events to provide the actions listed below. Unfortunately -this interferes with the standard X copy and paste mechanism. To work around this -you need to hold down SHIFT while selecting or pasting text. Alternatively you can -disable mouse support at compile time, or use Mod\-M to toggle mouse support dynamically. +By default dvtm captures mouse events to provide the actions listed below. +Unfortunately this interferes with the standard X copy and paste mechanism. +To work around this you need to hold down +.B Shift +while selecting or pasting text. +Alternatively you can disable mouse support at compile time, start dvtm with the +.B -M +flag or toggle mouse support during runtime with +.BR Mod\-M . .TP .B Button1 click -Select window. +Focus window. .TP .B Button1 double click -Select window and toggle maximization. +Focus window and toggle maximization. .TP .B Button2 click -Zooms/cycles current window to/from master area. +Zoom/cycle current window to/from master area. .TP .B Button3 click Toggle minimization of current window. - +.SS Copy mode +Copy mode gives easy access to past output. +The commands use vi style keybindings and support number prefixes as +command multipliers. +.TP +.B Entering +Copy mode can be entered with +.BR Mod\-v . +.TP +.B Navigation +Once in, navigation works with vi style keybindings +.RB ( h , j , k , l , ^ , $ , g , H , M , L , G ) +as well as with the +.BR Arrows / Home / End / Page-Down / Page-Up +keys. +.BR CTRL+u +can be used as a synonym for +.BR Page-Up +and similarly +.BR CTRL+d +corresponds to a +.BR Page-Down . +.TP +.B Searching +Search forward with +.B / +and backwards with +.BR ? . +Jump forward to next match with +.BR n . +Jump backwards to next match with +.BR N . +.TP +.B Selecting +To start making a selection press +.B v +(similar to visual mode in vi). +.TP +.B Copying +To copy the current selection use +.BR y . +If you haven't made a selection the current line is copied. +Add a number prefix to copy n lines starting from the current line. +This command leaves the copy mode. +.TP +.B Pasting +The previously copied text can be pasted at the current cursor position +with +.BR Mod\-p . +.TP +.B Leaving +Copy mode is automatically left upon copying something. To manually +exit at any time press either +.BR ESC , +.BR CTRL+c +or +.BR q . +.SH ENVIRONMENT VARIABLES +By default dvtm uses its own terminfo file and therefore sets +.BR TERM=dvtm +within the client windows. This can be overridden by setting the +.BR DVTM_TERM +environment variable to a valid terminal name before launching dvtm. .SH EXAMPLE -.TP -See the dvtm-status script as an example. - +See the +.I dvtm-status +script as an example of how to display text in the +status bar. .SH CUSTOMIZATION -dvtm is customized by creating a custom config.h and (re)compiling the source -code. This keeps it fast, secure and simple. - +dvtm is customized by creating a custom +.I config.h +and (re)compiling the source code. +This keeps it fast, secure and simple. .SH AUTHOR -dvtm is written by Marc Andre Tanner +dvtm is written by Marc André Tanner diff -Nru dvtm-0.6/dvtm.c dvtm-0.12/dvtm.c --- dvtm-0.6/dvtm.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/dvtm.c 2014-07-05 07:32:09.000000000 +0000 @@ -1,11 +1,12 @@ /* * The initial "port" of dwm to curses was done by - * (c) 2007-2009 Marc Andre Tanner + * + * © 2007-2014 Marc André Tanner * * It is highly inspired by the original X11 dwm and * reuses some code of it which is mostly * - * (c) 2006-2007 Anselm R. Garbe + * © 2006-2007 Anselm R. Garbe * * See LICENSE for details. */ @@ -13,12 +14,13 @@ #define _GNU_SOURCE #include #include -#include #include #include #include -#include +#include +#include #include +#include #include #include #include @@ -28,7 +30,23 @@ #ifdef __CYGWIN__ # include #endif -#include "madtty.h" +#include "vt.h" + +#ifdef PDCURSES +int ESCDELAY; +#endif + +#ifndef NCURSES_REENTRANT +# define set_escdelay(d) (ESCDELAY = (d)) +#endif + +typedef struct { + float mfact; + int history; + int w; + int h; + bool need_resize; +} Screen; typedef struct { const char *symbol; @@ -38,38 +56,47 @@ typedef struct Client Client; struct Client { WINDOW *window; - madtty_t *term; + Vt *term; const char *cmd; - char title[256]; - uint8_t order; + char title[255]; + int order; pid_t pid; int pty; -#ifdef CONFIG_CMDFIFO unsigned short int id; -#endif - short int x; - short int y; - short int w; - short int h; + unsigned short int x; + unsigned short int y; + unsigned short int w; + unsigned short int h; + bool has_title_line; bool minimized; bool died; Client *next; Client *prev; }; +typedef struct { + const char *title; + unsigned attrs; + short fg; + short bg; +} ColorRule; + #define ALT(k) ((k) + (161 - 'a')) +#if defined CTRL && defined _AIX + #undef CTRL +#endif #ifndef CTRL #define CTRL(k) ((k) & 0x1F) #endif #define CTRL_ALT(k) ((k) + (129 - 'a')) -#define MAX_ARGS 2 +#define MAX_ARGS 3 typedef struct { void (*cmd)(const char *args[]); /* needed to avoid an error about initialization * of nested flexible array members */ - const char *args[MAX_ARGS + 1]; + const char *args[MAX_ARGS]; } Action; typedef struct { @@ -78,23 +105,33 @@ Action action; } Key; -#ifdef CONFIG_MOUSE typedef struct { mmask_t mask; Action action; } Button; -#endif -#ifdef CONFIG_CMDFIFO typedef struct { const char *name; Action action; } Cmd; -#endif -#ifdef CONFIG_STATUSBAR -enum { BarTop, BarBot, BarOff }; -#endif +enum { BAR_TOP, BAR_BOTTOM, BAR_OFF }; +enum { ALIGN_LEFT, ALIGN_RIGHT }; + +typedef struct { + int fd; + int pos; + unsigned short int h; + unsigned short int y; + char text[512]; + const char *file; +} StatusBar; + +typedef struct { + int fd; + const char *file; + unsigned short int id; +} CmdFifo; #define countof(arr) (sizeof(arr) / sizeof((arr)[0])) #define sstrlen(str) (sizeof(str) - 1) @@ -107,78 +144,61 @@ #endif /* commands for use by keybindings */ -static void quit(const char *args[]); static void create(const char *args[]); -static void startup(const char *args[]); -static void escapekey(const char *args[]); -static void killclient(const char *args[]); +static void copymode(const char *args[]); static void focusn(const char *args[]); static void focusnext(const char *args[]); static void focusnextnm(const char *args[]); static void focusprev(const char *args[]); static void focusprevnm(const char *args[]); +static void focuslast(const char *args[]); +static void killclient(const char *args[]); +static void paste(const char *args[]); +static void quit(const char *args[]); +static void redraw(const char *args[]); +static void scrollback(const char *args[]); +static void send(const char *args[]); +static void setlayout(const char *args[]); +static void setmfact(const char *args[]); +static void startup(const char *args[]); +static void togglebar(const char *args[]); static void togglebell(const char *key[]); static void toggleminimize(const char *args[]); -static void setmwfact(const char *args[]); -static void setlayout(const char *args[]); -static void scrollback(const char *args[]); -static void redraw(const char *args[]); -static void zoom(const char *args[]); -static void lock(const char *key[]); +static void togglemouse(const char *args[]); static void togglerunall(const char *args[]); +static void zoom(const char *args[]); -#ifdef CONFIG_STATUSBAR -enum { ALIGN_LEFT, ALIGN_RIGHT }; -static void togglebar(const char *args[]); -#endif - -#ifdef CONFIG_MOUSE +/* commands for use by mouse bindings */ static void mouse_focus(const char *args[]); static void mouse_fullscreen(const char *args[]); static void mouse_minimize(const char *args[]); static void mouse_zoom(const char *args[]); -static void mouse_toggle(); -#endif -static void clear_workspace(); -static void draw(Client *c); -static void draw_all(bool border); -static void draw_border(Client *c); +/* functions and variables available to layouts via config.h */ static void resize(Client *c, int x, int y, int w, int h); -static void resize_screen(); -static void eprint(const char *errstr, ...); -static bool isarrange(void (*func)()); -static void arrange(); -static void focus(Client *c); -static void keypress(int code); - +extern Screen screen; static unsigned int waw, wah, wax, way; static Client *clients = NULL; -extern double mwfact; +static char *title; +#define COLOR(fg, bg) COLOR_PAIR(vt_color_reserve(fg, bg)) +#define NOMOD ERR #include "config.h" +/* global variables */ +Screen screen = { MFACT, SCROLL_HISTORY }; static Client *sel = NULL; -double mwfact = MWFACT; +static Client *lastsel = NULL; +static Client *msel = NULL; +static bool mouse_events_enabled = ENABLE_MOUSE; static Layout *layout = layouts; +static StatusBar bar = { -1, BAR_POS, 1 }; +static CmdFifo cmdfifo = { -1 }; static const char *shell; -static bool need_screen_resize; -static int width, height, scroll_buf_size = SCROLL_BUF_SIZE; -static bool running = true; +static char *copybuf; +static volatile sig_atomic_t running = true; static bool runinall = false; -#ifdef CONFIG_MOUSE -# include "mouse.c" -#endif - -#ifdef CONFIG_CMDFIFO -# include "cmdfifo.c" -#endif - -#ifdef CONFIG_STATUSBAR -# include "statusbar.c" -#endif - static void eprint(const char *errstr, ...) { va_list ap; @@ -196,21 +216,136 @@ exit(EXIT_FAILURE); } +static bool +isarrange(void (*func)()) { + return func == layout->arrange; +} + +static bool +is_content_visible(Client *c) { + if (!c) + return false; + if (isarrange(fullscreen)) + return sel == c; + return !c->minimized; +} + +static void +drawbar() { + wchar_t wbuf[sizeof bar.text]; + int x, y, w, maxwidth = screen.w - 2; + if (bar.pos == BAR_OFF) + return; + getyx(stdscr, y, x); + attrset(BAR_ATTR); + mvaddch(bar.y, 0, '['); + if (mbstowcs(wbuf, bar.text, sizeof bar.text) == (size_t)-1) + return; + if ((w = wcswidth(wbuf, maxwidth)) == -1) + return; + if (BAR_ALIGN == ALIGN_RIGHT) { + for (int i = 0; i + w < maxwidth; i++) + addch(' '); + } + addstr(bar.text); + if (BAR_ALIGN == ALIGN_LEFT) { + for (; w < maxwidth; w++) + addch(' '); + } + mvaddch(bar.y, screen.w - 1, ']'); + attrset(NORMAL_ATTR); + move(y, x); + wnoutrefresh(stdscr); +} + +static int +show_border() { + return (bar.fd != -1 && bar.pos != BAR_OFF) || (clients && clients->next); +} + +static void +draw_border(Client *c) { + if (!show_border()) + return; + char t = '\0'; + int x, y, maxlen; + + wattrset(c->window, (sel == c || (runinall && !c->minimized)) ? SELECTED_ATTR : NORMAL_ATTR); + getyx(c->window, y, x); + mvwhline(c->window, 0, 0, ACS_HLINE, c->w); + maxlen = c->w - (2 + sstrlen(TITLE) - sstrlen("%s%sd") + sstrlen(SEPARATOR) + 2); + if (maxlen < 0) + maxlen = 0; + if ((size_t)maxlen < sizeof(c->title)) { + t = c->title[maxlen]; + c->title[maxlen] = '\0'; + } + + mvwprintw(c->window, 0, 2, TITLE, + *c->title ? c->title : "", + *c->title ? SEPARATOR : "", + c->order); + if (t) + c->title[maxlen] = t; + wmove(c->window, y, x); +} + +static void +draw_content(Client *c) { + vt_draw(c->term, c->window, c->has_title_line, 0); +} + +static void +draw(Client *c) { + if (is_content_visible(c)) { + redrawwin(c->window); + draw_content(c); + } + if (!isarrange(fullscreen) || sel == c) + draw_border(c); + wnoutrefresh(c->window); +} + +static void +draw_all() { + if (!isarrange(fullscreen)) { + for (Client *c = clients; c; c = c->next) { + if (c == sel) + continue; + draw(c); + } + } + /* as a last step the selected window is redrawn, + * this has the effect that the cursor position is + * accurate + */ + if (sel) + draw(sel); +} + +static void +arrange() { + erase(); + drawbar(); + attrset(NORMAL_ATTR); + layout->arrange(); + wnoutrefresh(stdscr); + draw_all(); +} + static void attach(Client *c) { - uint8_t order; if (clients) clients->prev = c; c->next = clients; c->prev = NULL; clients = c; - for (order = 1; c; c = c->next, order++) - c->order = order; + for (int o = 1; c; c = c->next, o++) + c->order = o; } static void attachafter(Client *c, Client *a) { /* attach c after a */ - uint8_t o; if (c == a) return; if (!a) @@ -222,7 +357,7 @@ c->next = a->next; c->prev = a; a->next = c; - for (o = a->order; c; c = c->next) + for (int o = a->order; c; c = c->next) c->order = ++o; } } @@ -243,16 +378,12 @@ } static void -arrange() { - clear_workspace(); - layout->arrange(); - wnoutrefresh(stdscr); - draw_all(true); -} - -static bool -isarrange(void (*func)()) { - return func == layout->arrange; +settitle(Client *c) { + char *term, *t = title; + if (!t && sel == c && *c->title) + t = c->title; + if (t && (term = getenv("TERM")) && !strstr(term, "linux")) + printf("\033]0;%s\007", t); } static void @@ -260,639 +391,846 @@ Client *tmp = sel; if (sel == c) return; + lastsel = sel; sel = c; - if (tmp) { + settitle(c); + if (tmp && !isarrange(fullscreen)) { draw_border(tmp); - wrefresh(tmp->window); + wnoutrefresh(tmp->window); } - if (isarrange(fullscreen)) - redrawwin(c->window); - draw_border(c); - wrefresh(c->window); + if (isarrange(fullscreen)) { + draw(c); + } else { + draw_border(c); + wnoutrefresh(c->window); + } + curs_set(!c->minimized && vt_cursor(c->term)); } static void -focusn(const char *args[]) { - Client *c; +applycolorrules(Client *c) { + const ColorRule *r = colorrules; + short fg = r->fg, bg = r->bg; + unsigned attrs = r->attrs; - for (c = clients; c; c = c->next) { - if (c->order == atoi(args[0])) { - focus(c); - if (c->minimized) - toggleminimize(NULL); - return; + for (unsigned int i = 1; i < countof(colorrules); i++) { + r = &colorrules[i]; + if (strstr(c->title, r->title)) { + attrs = r->attrs; + fg = r->fg; + bg = r->bg; + break; } } + + vt_set_default_colors(c->term, attrs, fg, bg); } static void -focusnext(const char *args[]) { - Client *c; +term_event_handler(Vt *term, int event, void *event_data) { + Client *c = (Client *)vt_get_data(term); + switch (event) { + case VT_EVENT_TITLE: + if (event_data) + strncpy(c->title, event_data, sizeof(c->title) - 1); + c->title[event_data ? sizeof(c->title) - 1 : 0] = '\0'; + settitle(c); + if (!isarrange(fullscreen) || sel == c) + draw_border(c); + applycolorrules(c); + break; + case VT_EVENT_COPY_TEXT: + if (event_data) { + free(copybuf); + copybuf = event_data; + } + break; + } +} - if (!sel) +static void +move_client(Client *c, int x, int y) { + if (c->x == x && c->y == y) return; - - c = sel->next; - if (!c) - c = clients; - if (c) - focus(c); + debug("moving, x: %d y: %d\n", x, y); + if (mvwin(c->window, y, x) == ERR) { + eprint("error moving, x: %d y: %d\n", x, y); + } else { + c->x = x; + c->y = y; + } } static void -focusnextnm(const char *args[]) { - Client *c; - - if (!sel) - return; - c = sel; - do { - c = c->next; - if (!c) - c = clients; - } while (c->minimized && c != sel); - focus(c); +resize_client(Client *c, int w, int h) { + bool has_title_line = show_border(); + bool resize_window = c->w != w || c->h != h; + if (resize_window) { + debug("resizing, w: %d h: %d\n", w, h); + if (wresize(c->window, h, w) == ERR) { + eprint("error resizing, w: %d h: %d\n", w, h); + } else { + c->w = w; + c->h = h; + } + } + if (resize_window || c->has_title_line != has_title_line) { + c->has_title_line = has_title_line; + vt_resize(c->term, h - has_title_line, w); + } } static void -focusprev(const char *args[]) { - Client *c; +resize(Client *c, int x, int y, int w, int h) { + resize_client(c, w, h); + move_client(c, x, y); +} - if (!sel) - return; - c = sel->prev; - if (!c) - for (c = clients; c && c->next; c = c->next); - if (c) - focus(c); +static Client* +get_client_by_pid(pid_t pid) { + for (Client *c = clients; c; c = c->next) { + if (c->pid == pid) + return c; + } + return NULL; +} + +static Client* +get_client_by_coord(unsigned int x, unsigned int y) { + if (y < way || y >= wah) + return NULL; + if (isarrange(fullscreen)) + return sel; + for (Client *c = clients; c; c = c->next) { + if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) { + debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order); + return c; + } + } + return NULL; } static void -focusprevnm(const char *args[]) { - Client *c; +sigchld_handler(int sig) { + int errsv = errno; + int status; + pid_t pid; - if (!sel) - return; - c = sel; - do { - c = c->prev; - if (!c) - for (c = clients; c && c->next; c = c->next); - } while (c->minimized && c != sel); - focus(c); + while ((pid = waitpid(-1, &status, WNOHANG)) != 0) { + if (pid == -1) { + if (errno == ECHILD) { + /* no more child processes */ + break; + } + eprint("waitpid: %s\n", strerror(errno)); + break; + } + debug("child with pid %d died\n", pid); + Client *c = get_client_by_pid(pid); + if (c) + c->died = true; + } + + errno = errsv; } static void -zoom(const char *args[]) { - Client *c; - - if (!sel) - return; - if ((c = sel) == clients) - if (!(c = c->next)) - return; - detach(c); - attach(c); - focus(c); - if (c->minimized) - toggleminimize(NULL); - arrange(); +sigwinch_handler(int sig) { + screen.need_resize = true; } static void -togglebell(const char *args[]) { - madtty_togglebell(sel->term); +sigterm_handler(int sig) { + running = false; } static void -toggleminimize(const char *args[]) { - Client *c, *m; - unsigned int n; - if (!sel) +updatebarpos(void) { + bar.y = 0; + wax = 0; + way = 0; + wah = screen.h; + if (bar.fd == -1) return; - /* the last window can't be minimized */ - if (!sel->minimized) { - for (n = 0, c = clients; c; c = c->next) - if (!c->minimized) - n++; - if (n == 1) - return; + if (bar.pos == BAR_TOP) { + wah -= bar.h; + way += bar.h; + } else if (bar.pos == BAR_BOTTOM) { + wah -= bar.h; + bar.y = wah; } - sel->minimized = !sel->minimized; - m = sel; - /* check whether the master client was minimized */ - if (sel == clients && sel->minimized) { - c = sel->next; - detach(c); - attach(c); - focus(c); - detach(m); - for (; c && c->next && !c->next->minimized; c = c->next); - attachafter(m, c); - } else if (m->minimized) { - /* non master window got minimized move it above all other - * minimized ones */ - focusnextnm(NULL); - detach(m); - for (c = clients; c && c->next && !c->next->minimized; c = c->next); - attachafter(m, c); - } else { /* window is no longer minimized, move it to the master area */ - madtty_dirty(m->term); - detach(m); - attach(m); +} + +static void +resize_screen() { + struct winsize ws; + + if (ioctl(0, TIOCGWINSZ, &ws) == -1) { + getmaxyx(stdscr, screen.h, screen.w); + } else { + screen.w = ws.ws_col; + screen.h = ws.ws_row; } + + debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h); + + resizeterm(screen.h, screen.w); + wresize(stdscr, screen.h, screen.w); + + waw = screen.w; + wah = screen.h; + updatebarpos(); + clear(); arrange(); } +static bool +is_modifier(unsigned int mod) { + for (unsigned int i = 0; i < countof(keys); i++) { + if (keys[i].mod == mod) + return true; + } + return false; +} + +static Key* +keybinding(unsigned int mod, unsigned int code) { + for (unsigned int i = 0; i < countof(keys); i++) { + if (keys[i].mod == mod && keys[i].code == code) + return &keys[i]; + } + return NULL; +} + static void -setlayout(const char *args[]) { - unsigned int i; +keypress(int code) { + unsigned int len = 1; + char buf[8] = { '\e' }; - if (!args || !args[0]) { - if (++layout == &layouts[countof(layouts)]) - layout = &layouts[0]; - } else { - for (i = 0; i < countof(layouts); i++) - if (!strcmp(args[0], layouts[i].symbol)) - break; - if (i == countof(layouts)) - return; - layout = &layouts[i]; + if (code == '\e') { + /* pass characters following escape to the underlying app */ + nodelay(stdscr, TRUE); + for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++) + buf[len] = t; + nodelay(stdscr, FALSE); + } + + for (Client *c = runinall ? clients : sel; c; c = c->next) { + if (is_content_visible(c)) { + if (code == '\e') + vt_write(c->term, buf, len); + else + vt_keypress(c->term, code); + } + if (!runinall) + break; + } +} + +static void +mouse_setup() { +#ifdef CONFIG_MOUSE + mmask_t mask = 0; + + if (mouse_events_enabled) { + mask = BUTTON1_CLICKED | BUTTON2_CLICKED; + for (unsigned int i = 0; i < countof(buttons); i++) + mask |= buttons[i].mask; } + mousemask(mask, NULL); +#endif /* CONFIG_MOUSE */ +} + +static void +setup() { + if (!(shell = getenv("SHELL"))) + shell = "/bin/sh"; + setlocale(LC_CTYPE, ""); + initscr(); + start_color(); + noecho(); + keypad(stdscr, TRUE); + mouse_setup(); + raw(); + vt_init(); + vt_set_keytable(keytable, countof(keytable)); + resize_screen(); + struct sigaction sa; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigwinch_handler; + sigaction(SIGWINCH, &sa, NULL); + sa.sa_handler = sigchld_handler; + sigaction(SIGCHLD, &sa, NULL); + sa.sa_handler = sigterm_handler; + sigaction(SIGTERM, &sa, NULL); +} + +static void +destroy(Client *c) { + if (sel == c) + focusnextnm(NULL); + detach(c); + if (sel == c) { + if (clients) { + focus(clients); + toggleminimize(NULL); + } else { + sel = NULL; + } + } + if (lastsel == c) + lastsel = NULL; + werase(c->window); + wnoutrefresh(c->window); + vt_destroy(c->term); + delwin(c->window); + if (!clients && countof(actions)) { + if (!strcmp(c->cmd, shell)) + quit(NULL); + else + create(NULL); + } + free(c); arrange(); } static void -setmwfact(const char *args[]) { - double delta; +cleanup() { + while (clients) + destroy(clients); + vt_shutdown(); + endwin(); + free(copybuf); + if (bar.fd > 0) + close(bar.fd); + if (bar.file) + unlink(bar.file); + if (cmdfifo.fd > 0) + close(cmdfifo.fd); + if (cmdfifo.file) + unlink(cmdfifo.file); +} - if (isarrange(fullscreen) || isarrange(grid)) +static char *getcwd_by_pid(Client *c) { + if (!c) + return NULL; + char buf[32]; + snprintf(buf, sizeof buf, "/proc/%d/cwd", c->pid); + return realpath(buf, NULL); +} + +/* commands for use by keybindings */ +static void +create(const char *args[]) { + Client *c = calloc(1, sizeof(Client)); + if (!c) + return; + const char *cmd = (args && args[0]) ? args[0] : shell; + const char *pargs[] = { "/bin/sh", "-c", cmd, NULL }; + c->id = ++cmdfifo.id; + char buf[8], *cwd = NULL; + snprintf(buf, sizeof buf, "%d", c->id); + const char *env[] = { + "DVTM", VERSION, + "DVTM_WINDOW_ID", buf, + NULL + }; + + if (!(c->window = newwin(wah, waw, way, wax))) { + free(c); return; - /* arg handling, manipulate mwfact */ - if (args[0] == NULL) - mwfact = MWFACT; - else if (1 == sscanf(args[0], "%lf", &delta)) { - if (args[0][0] == '+' || args[0][0] == '-') - mwfact += delta; - else - mwfact = delta; - if (mwfact < 0.1) - mwfact = 0.1; - else if (mwfact > 0.9) - mwfact = 0.9; } + + c->has_title_line = show_border(); + if (!(c->term = vt_create(screen.h - c->has_title_line, screen.w, screen.history))) { + delwin(c->window); + free(c); + return; + } + + c->cmd = cmd; + if (args && args[1]) { + strncpy(c->title, args[1], sizeof(c->title) - 1); + c->title[sizeof(c->title) - 1] = '\0'; + } + if (args && args[2]) + cwd = !strcmp(args[2], "$CWD") ? getcwd_by_pid(sel) : (char*)args[2]; + c->pid = vt_forkpty(c->term, "/bin/sh", pargs, cwd, env, &c->pty); + if (args && args[2] && !strcmp(args[2], "$CWD")) + free(cwd); + vt_set_data(c->term, c); + vt_set_event_handler(c->term, term_event_handler); + c->w = screen.w; + c->h = screen.h; + c->x = wax; + c->y = way; + c->order = 0; + c->minimized = false; + debug("client with pid %d forked\n", c->pid); + attach(c); + focus(c); arrange(); } static void +copymode(const char *args[]) { + if (!sel) + return; + vt_copymode_enter(sel->term); + if (args[0]) { + vt_copymode_keypress(sel->term, args[0][0]); + draw(sel); + } +} + +static void +focusn(const char *args[]) { + for (Client *c = clients; c; c = c->next) { + if (c->order == atoi(args[0])) { + focus(c); + if (c->minimized) + toggleminimize(NULL); + return; + } + } +} + +static void +focusnext(const char *args[]) { + if (!sel) + return; + Client *c = sel->next; + if (!c) + c = clients; + if (c) + focus(c); +} + +static void +focusnextnm(const char *args[]) { + if (!sel) + return; + Client *c = sel; + do { + c = c->next; + if (!c) + c = clients; + } while (c->minimized && c != sel); + focus(c); +} + +static void +focusprev(const char *args[]) { + if (!sel) + return; + Client *c = sel->prev; + if (!c) + for (c = clients; c && c->next; c = c->next); + if (c) + focus(c); +} + +static void +focusprevnm(const char *args[]) { + if (!sel) + return; + Client *c = sel; + do { + c = c->prev; + if (!c) + for (c = clients; c && c->next; c = c->next); + } while (c->minimized && c != sel); + focus(c); +} + +static void +focuslast(const char *args[]) { + if (lastsel) + focus(lastsel); +} + +static void +killclient(const char *args[]) { + if (!sel) + return; + debug("killing client with pid: %d\n", sel->pid); + kill(-sel->pid, SIGKILL); +} + +static void +paste(const char *args[]) { + if (sel && copybuf) + vt_write(sel->term, copybuf, strlen(copybuf)); +} + +static void +quit(const char *args[]) { + cleanup(); + exit(EXIT_SUCCESS); +} + +static void +redraw(const char *args[]) { + for (Client *c = clients; c; c = c->next) { + if (!c->minimized) { + vt_dirty(c->term); + wclear(c->window); + wnoutrefresh(c->window); + } + } + resize_screen(); +} + +static void scrollback(const char *args[]) { - if (!sel) return; + if (!is_content_visible(sel)) + return; if (!args[0] || atoi(args[0]) < 0) - madtty_scroll(sel->term, -sel->h/2); + vt_scroll(sel->term, -sel->h/2); else - madtty_scroll(sel->term, sel->h/2); + vt_scroll(sel->term, sel->h/2); draw(sel); + curs_set(vt_cursor(sel->term)); } static void -redraw(const char *args[]) { - wrefresh(curscr); - resize_screen(); - draw_all(true); +send(const char *args[]) { + if (sel && args && args[0]) + vt_write(sel->term, args[0], strlen(args[0])); } static void -draw_border(Client *c) { - char *s, t = '\0'; - int x, y, o; - if (sel == c) { - wattrset(c->window, SELECTED_ATTR); - madtty_color_set(c->window, SELECTED_FG, SELECTED_BG); +setlayout(const char *args[]) { + unsigned int i; + + if (!args || !args[0]) { + if (++layout == &layouts[countof(layouts)]) + layout = &layouts[0]; } else { - wattrset(c->window, NORMAL_ATTR); - madtty_color_set(c->window, NORMAL_FG, NORMAL_BG); - } - getyx(c->window, y, x); - curs_set(0); - mvwhline(c->window, 0, 0, ACS_HLINE, c->w); - o = c->w - (4 + sstrlen(TITLE) - 5 + sstrlen(SEPARATOR)); - if (o < 0) - o = 0; - if (o < sizeof(c->title)) { - t = *(s = &c->title[o]); - *s = '\0'; + for (i = 0; i < countof(layouts); i++) + if (!strcmp(args[0], layouts[i].symbol)) + break; + if (i == countof(layouts)) + return; + layout = &layouts[i]; } - mvwprintw(c->window, 0, 2, TITLE, - *c->title ? c->title : "", - *c->title ? SEPARATOR : "", - c->order); - if (t) - *s = t; - wmove(c->window, y, x); - if (!c->minimized) - curs_set(madtty_cursor(c->term)); + arrange(); } static void -draw_content(Client *c) { - if (!c->minimized || isarrange(fullscreen)) { - madtty_draw(c->term, c->window, 1, 0); - if (c != sel) - curs_set(0); +setmfact(const char *args[]) { + float delta; + + if (isarrange(fullscreen) || isarrange(grid)) + return; + /* arg handling, manipulate mfact */ + if (args[0] == NULL) { + screen.mfact = MFACT; + } else if (1 == sscanf(args[0], "%f", &delta)) { + if (args[0][0] == '+' || args[0][0] == '-') + screen.mfact += delta; + else + screen.mfact = delta; + if (screen.mfact < 0.1) + screen.mfact = 0.1; + else if (screen.mfact > 0.9) + screen.mfact = 0.9; } + arrange(); } static void -draw(Client *c) { - draw_content(c); - draw_border(c); - wrefresh(c->window); +startup(const char *args[]) { + for (unsigned int i = 0; i < countof(actions); i++) + actions[i].cmd(actions[i].args); } static void -clear_workspace() { - unsigned int y; - for (y = 0; y < wah; y++) - mvhline(way + y, 0, ' ', waw); - wnoutrefresh(stdscr); +togglebar(const char *args[]) { + if (bar.pos == BAR_OFF) + bar.pos = (BAR_POS == BAR_OFF) ? BAR_TOP : BAR_POS; + else + bar.pos = BAR_OFF; + updatebarpos(); + redraw(NULL); } static void -draw_all(bool border) { - Client *c; - curs_set(0); - for (c = clients; c; c = c->next) { - redrawwin(c->window); - if (c == sel) - continue; - draw_content(c); - if (border) - draw_border(c); - wnoutrefresh(c->window); - } - /* as a last step the selected window is redrawn, - * this has the effect that the cursor position is - * accurate - */ - refresh(); - if (sel) { - draw_content(sel); - if (border) - draw_border(sel); - wrefresh(sel->window); - } +togglebell(const char *args[]) { + vt_togglebell(sel->term); } static void -escapekey(const char *args[]) { - int key; - if ((key = getch()) >= 0) { - debug("escaping key `%c'\n", key); - keypress(CTRL(key)); +toggleminimize(const char *args[]) { + Client *c, *m; + unsigned int n; + if (!sel) + return; + /* the last window can't be minimized */ + if (!sel->minimized) { + for (n = 0, c = clients; c; c = c->next) + if (!c->minimized) + n++; + if (n == 1) + return; + } + sel->minimized = !sel->minimized; + m = sel; + /* check whether the master client was minimized */ + if (sel == clients && sel->minimized) { + c = sel->next; + detach(c); + attach(c); + focus(c); + detach(m); + for (; c && c->next && !c->next->minimized; c = c->next); + attachafter(m, c); + } else if (m->minimized) { + /* non master window got minimized move it above all other + * minimized ones */ + focusnextnm(NULL); + detach(m); + for (c = clients; c && c->next && !c->next->minimized; c = c->next); + attachafter(m, c); + } else { /* window is no longer minimized, move it to the master area */ + vt_dirty(m->term); + detach(m); + attach(m); } + arrange(); } -/* - * Lock the screen until the correct password is entered. - * The password can either be specified in config.h which is - * not recommended because `strings dvtm` will contain it. If - * no password is specified in the configuration file it is read - * from the keyboard before the screen is locked. - * - * NOTE: this function doesn't handle the input from clients. All - * foreground operations are temporarily suspended since the - * function doesn't return. - */ static void -lock(const char *args[]) { - size_t len = 0, i = 0; - char buf[16], *pass = buf, c; - - erase(); - curs_set(0); - - if (args && args[0]) { - len = strlen(args[0]); - pass = (char *)args[0]; - } else { - mvprintw(LINES / 2, COLS / 2 - 7, "Enter password"); - while (len < sizeof buf && (c = getch()) != '\n') - if (c != ERR) - buf[len++] = c; - } - - mvprintw(LINES / 2, COLS / 2 - 7, "Screen locked!"); - - while (i != len) { - for(i = 0; i < len; i++) { - if (getch() != pass[i]) - break; - } - } - - arrange(); +togglemouse(const char *args[]) { + mouse_events_enabled = !mouse_events_enabled; + mouse_setup(); } static void togglerunall(const char *args[]) { runinall = !runinall; + draw_all(); } static void -killclient(const char *args[]) { - if (!sel) - return; - debug("killing client with pid: %d\n", sel->pid); - kill(-sel->pid, SIGKILL); -} - -static int -title_escape_seq_handler(madtty_t *term, char *es) { +zoom(const char *args[]) { Client *c; - unsigned int l; - if (es[0] != ']' || (es[1] && (es[1] < '0' || es[1] > '9')) || (es[2] && es[2] != ';')) - return MADTTY_HANDLER_NOWAY; - if ((l = strlen(es)) < 3 || es[l - 1] != '\07') - return MADTTY_HANDLER_NOTYET; - es[l - 1] = '\0'; - c = (Client *)madtty_get_data(term); - strncpy(c->title, es + 3, sizeof(c->title)); - draw_border(c); - debug("window title: %s\n", c->title); - return MADTTY_HANDLER_OK; -} -static void -create(const char *args[]) { - Client *c = calloc(sizeof(Client), 1); - if (!c) + if (!sel) return; - const char *cmd = (args && args[0]) ? args[0] : shell; - const char *pargs[] = { "/bin/sh", "-c", cmd, NULL }; -#ifdef CONFIG_CMDFIFO - c->id = ++client_id; - char buf[8]; - snprintf(buf, sizeof buf, "%d", c->id); -#endif - const char *env[] = { - "DVTM", VERSION, -#ifdef CONFIG_CMDFIFO - "DVTM_WINDOW_ID", buf, -#endif - NULL - }; - - c->window = newwin(wah, waw, way, wax); - c->term = madtty_create(height - 1, width, scroll_buf_size); - c->cmd = cmd; - if (args && args[1]) - strncpy(c->title, args[1], sizeof(c->title)); - c->pid = madtty_forkpty(c->term, "/bin/sh", pargs, env, &c->pty); - madtty_set_data(c->term, c); - madtty_set_handler(c->term, title_escape_seq_handler); - c->w = width; - c->h = height; - c->x = wax; - c->y = way; - c->order = 0; - c->minimized = false; - debug("client with pid %d forked\n", c->pid); + if (args && args[0]) + focusn(args); + if ((c = sel) == clients) + if (!(c = c->next)) + return; + detach(c); attach(c); focus(c); + if (c->minimized) + toggleminimize(NULL); arrange(); } +/* commands for use by mouse bindings */ static void -destroy(Client *c) { - if (sel == c) - focusnextnm(NULL); - detach(c); - if (sel == c) { - if (clients) { - focus(clients); - toggleminimize(NULL); - } else - sel = NULL; - } - werase(c->window); - wrefresh(c->window); - madtty_destroy(c->term); - delwin(c->window); - if (!clients && countof(actions)) { - if (!strcmp(c->cmd, shell)) - quit(NULL); - else - create(NULL); - } - free(c); - arrange(); +mouse_focus(const char *args[]) { + focus(msel); + if (msel->minimized) + toggleminimize(NULL); } static void -move_client(Client *c, int x, int y) { - if (c->x == x && c->y == y) - return; - debug("moving, x: %d y: %d\n", x, y); - if (mvwin(c->window, y, x) == ERR) - eprint("error moving, x: %d y: %d\n", x, y); - else { - c->x = x; - c->y = y; - } +mouse_fullscreen(const char *args[]) { + mouse_focus(NULL); + if (isarrange(fullscreen)) + setlayout(NULL); + else + setlayout(args); } static void -resize_client(Client *c, int w, int h) { - if (c->w == w && c->h == h) - return; - debug("resizing, w: %d h: %d\n", w, h); - if (wresize(c->window, h, w) == ERR) - eprint("error resizing, w: %d h: %d\n", w, h); - else { - c->w = w; - c->h = h; - } - madtty_resize(c->term, h - 1, w); +mouse_minimize(const char *args[]) { + focus(msel); + toggleminimize(NULL); } static void -resize(Client *c, int x, int y, int w, int h) { - resize_client(c, w, h); - move_client(c, x, y); -} - -static bool -is_modifier(unsigned int mod) { - unsigned int i; - for (i = 0; i < countof(keys); i++) { - if (keys[i].mod == mod) - return true; - } - return false; -} - -static Key* -keybinding(unsigned int mod, unsigned int code) { - unsigned int i; - for (i = 0; i < countof(keys); i++) { - if (keys[i].mod == mod && keys[i].code == code) - return &keys[i]; - } - return NULL; +mouse_zoom(const char *args[]) { + focus(msel); + zoom(NULL); } -static Client* -get_client_by_pid(pid_t pid) { - Client *c; - for (c = clients; c; c = c->next) { - if (c->pid == pid) - return c; +static Cmd * +get_cmd_by_name(const char *name) { + for (unsigned int i = 0; i < countof(commands); i++) { + if (!strcmp(name, commands[i].name)) + return &commands[i]; } return NULL; } static void -sigchld_handler(int sig) { - int errsv = errno; - int status; - pid_t pid; - Client *c; - - signal(SIGCHLD, sigchld_handler); +handle_cmdfifo() { + int r; + char *p, *s, cmdbuf[512], c; + Cmd *cmd; + switch (r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1)) { + case -1: + case 0: + cmdfifo.fd = -1; + break; + default: + cmdbuf[r] = '\0'; + p = cmdbuf; + while (*p) { + /* find the command name */ + for (; *p == ' ' || *p == '\n'; p++); + for (s = p; *p && *p != ' ' && *p != '\n'; p++); + if ((c = *p)) + *p++ = '\0'; + if (*s && (cmd = get_cmd_by_name(s)) != NULL) { + bool quote = false; + int argc = 0; + /* XXX: initializer assumes MAX_ARGS == 2 use a initialization loop? */ + const char *args[MAX_ARGS] = { NULL, NULL, NULL}, *arg; + /* if arguments were specified in config.h ignore the one given via + * the named pipe and thus skip everything until we find a new line + */ + if (cmd->action.args[0] || c == '\n') { + debug("execute %s", s); + cmd->action.cmd(cmd->action.args); + while (*p && *p != '\n') + p++; + continue; + } + /* no arguments were given in config.h so we parse the command line */ + while (*p == ' ') + p++; + arg = p; + for (; (c = *p); p++) { + switch (*p) { + case '\\': + /* remove the escape character '\\' move every + * following character to the left by one position + */ + switch (p[1]) { + case '\\': + case '\'': + case '\"': { + char *t = p+1; + do { + t[-1] = *t; + } while (*t++); + } + } + break; + case '\'': + case '\"': + quote = !quote; + break; + case ' ': + if (!quote) { + case '\n': + /* remove trailing quote if there is one */ + if (*(p - 1) == '\'' || *(p - 1) == '\"') + *(p - 1) = '\0'; + *p++ = '\0'; + /* remove leading quote if there is one */ + if (*arg == '\'' || *arg == '\"') + arg++; + if (argc < MAX_ARGS) + args[argc++] = arg; + + while (*p == ' ') + ++p; + arg = p--; + } + break; + } - while ((pid = waitpid(-1, &status, WNOHANG)) != 0) { - if (pid == -1) { - if (errno == ECHILD) { - /* no more child processes */ - break; + if (c == '\n' || *p == '\n') { + if (!*p) + p++; + debug("execute %s", s); + for(int i = 0; i < argc; i++) + debug(" %s", args[i]); + debug("\n"); + cmd->action.cmd(args); + break; + } + } } - eprint("waitpid: %s\n", strerror(errno)); - break; } - debug("child with pid %d died\n", pid); - if ((c = get_client_by_pid(pid))) - c->died = true; } - - errno = errsv; -} - -static void -sigwinch_handler(int sig) { - signal(SIGWINCH, sigwinch_handler); - need_screen_resize = true; -} - -static void -sigterm_handler(int sig) { - running = false; } static void -resize_screen() { - struct winsize ws; - - if (ioctl(0, TIOCGWINSZ, &ws) == -1) +handle_mouse() { +#ifdef CONFIG_MOUSE + MEVENT event; + unsigned int i; + if (getmouse(&event) != OK) return; + msel = get_client_by_coord(event.x, event.y); - width = ws.ws_col; - height = ws.ws_row; - - debug("resize_screen(), w: %d h: %d\n", width, height); - -#if defined(__OpenBSD__) || defined(__NetBSD__) - resizeterm(height, width); -#else - resize_term(height, width); -#endif - wresize(stdscr, height, width); - wrefresh(curscr); - refresh(); - - waw = width; - wah = height; -#ifdef CONFIG_STATUSBAR - updatebarpos(); - drawbar(); -#endif - arrange(); -} + if (!msel) + return; -static void -startup(const char *args[]) { - for (int i = 0; i < countof(actions); i++) - actions[i].cmd(actions[i].args); -} + debug("mouse x:%d y:%d cx:%d cy:%d mask:%d\n", event.x, event.y, event.x - msel->x, event.y - msel->y, event.bstate); -static void -setup() { - if (!(shell = getenv("SHELL"))) - shell = "/bin/sh"; - setlocale(LC_CTYPE, ""); - initscr(); - start_color(); - noecho(); - keypad(stdscr, TRUE); -#ifdef CONFIG_MOUSE - mouse_setup(); -#endif - raw(); - madtty_init_colors(); - madtty_init_vt100_graphics(); - getmaxyx(stdscr, height, width); - resize_screen(); - signal(SIGWINCH, sigwinch_handler); - signal(SIGCHLD, sigchld_handler); - signal(SIGTERM, sigterm_handler); -} + vt_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate); -static void -cleanup() { - endwin(); -#ifdef CONFIG_STATUSBAR - if (statusfd > 0) - close(statusfd); -#endif -#ifdef CONFIG_CMDFIFO - if (cmdfd > 0) - close(cmdfd); - if (cmdpath) - unlink(cmdpath); -#endif -} + for (i = 0; i < countof(buttons); i++) { + if (event.bstate & buttons[i].mask) + buttons[i].action.cmd(buttons[i].action.args); + } -static void -quit(const char *args[]) { - cleanup(); - exit(EXIT_SUCCESS); + msel = NULL; +#endif /* CONFIG_MOUSE */ } static void -usage() { - cleanup(); - eprint("usage: dvtm [-v] [-m mod] [-d escdelay] [-h n] " -#ifdef CONFIG_STATUSBAR - "[-s status-fifo] " -#endif -#ifdef CONFIG_CMDFIFO - "[-c cmd-fifo] " -#endif - "[cmd...]\n"); - exit(EXIT_FAILURE); +handle_statusbar() { + char *p; + int r; + switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) { + case -1: + strncpy(bar.text, strerror(errno), sizeof bar.text - 1); + bar.text[sizeof bar.text - 1] = '\0'; + bar.fd = -1; + break; + case 0: + bar.fd = -1; + break; + default: + bar.text[r] = '\0'; + p = bar.text + r - 1; + for (; p >= bar.text && *p == '\n'; *p-- = '\0'); + for (; p >= bar.text && *p != '\n'; --p); + if (p >= bar.text) + memmove(bar.text, p + 1, strlen(p)); + drawbar(); + } } static int -open_or_create_fifo(const char *name) { +open_or_create_fifo(const char *name, const char **name_created) { struct stat info; int fd; -open: - if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) { - if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) - goto open; - error("%s\n", strerror(errno)); - } + + do { + if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) { + if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) { + *name_created = name; + continue; + } + error("%s\n", strerror(errno)); + } + } while (fd == -1); + if (fstat(fd, &info) == -1) error("%s\n", strerror(errno)); if (!S_ISFIFO(info.st_mode)) @@ -900,16 +1238,23 @@ return fd; } +static void +usage() { + cleanup(); + eprint("usage: dvtm [-v] [-M] [-m mod] [-d delay] [-h lines] [-t title] " + "[-s status-fifo] [-c cmd-fifo] [cmd...]\n"); + exit(EXIT_FAILURE); +} + static bool parse_args(int argc, char *argv[]) { - int arg; bool init = false; if (!getenv("ESCDELAY")) - ESCDELAY = 100; - for (arg = 1; arg < argc; arg++) { + set_escdelay(100); + for (int arg = 1; arg < argc; arg++) { if (argv[arg][0] != '-') { - const char *args[] = { argv[arg], NULL }; + const char *args[] = { argv[arg], NULL, NULL }; if (!init) { setup(); init = true; @@ -917,44 +1262,48 @@ create(args); continue; } - if (argv[arg][1] != 'v' && (arg + 1) >= argc) + if (argv[arg][1] != 'v' && argv[arg][1] != 'M' && (arg + 1) >= argc) usage(); switch (argv[arg][1]) { case 'v': - puts("dvtm-"VERSION" (c) 2007-2009 Marc Andre Tanner"); + puts("dvtm-"VERSION" © 2007-2014 Marc André Tanner"); exit(EXIT_SUCCESS); + case 'M': + mouse_events_enabled = !mouse_events_enabled; + break; case 'm': { char *mod = argv[++arg]; if (mod[0] == '^' && mod[1]) *mod = CTRL(mod[1]); - for (int i = 0; i < countof(keys); i++) + for (unsigned int i = 0; i < countof(keys); i++) keys[i].mod = *mod; break; } case 'd': - ESCDELAY = atoi(argv[++arg]); + set_escdelay(atoi(argv[++arg])); if (ESCDELAY < 50) - ESCDELAY = 50; + set_escdelay(50); else if (ESCDELAY > 1000) - ESCDELAY = 1000; + set_escdelay(1000); break; case 'h': - scroll_buf_size = atoi(argv[++arg]); + screen.history = atoi(argv[++arg]); + break; + case 't': + title = argv[++arg]; break; -#ifdef CONFIG_STATUSBAR case 's': - statusfd = open_or_create_fifo(argv[++arg]); + bar.fd = open_or_create_fifo(argv[++arg], &bar.file); updatebarpos(); break; -#endif -#ifdef CONFIG_CMDFIFO - case 'c': - cmdfd = open_or_create_fifo(argv[++arg]); - if (!(cmdpath = get_realpath(argv[arg]))) + case 'c': { + const char *fifo; + cmdfifo.fd = open_or_create_fifo(argv[++arg], &cmdfifo.file); + if (!(fifo = realpath(argv[arg], NULL))) error("%s\n", strerror(errno)); - setenv("DVTM_CMD_FIFO", cmdpath, 1); + setenv("DVTM_CMD_FIFO", fifo, 1); break; -#endif + } default: usage(); } @@ -962,68 +1311,40 @@ return init; } -void -keypress(int code) { - Client *c; - int len = 1; - char buf[8] = { '\e' }; - - if (code == '\e') { - /* pass characters following escape to the underlying app */ - nodelay(stdscr, TRUE); - while (len < sizeof(buf) - 1 && (buf[len] = getch()) != ERR) - len++; - buf[len] = '\0'; - nodelay(stdscr, FALSE); - } - - for (c = runinall ? clients : sel; c; c = c->next) { - if (!c->minimized || isarrange(fullscreen)) { - if (code == '\e') - madtty_keypress_sequence(c->term, buf); - else - madtty_keypress(c->term, code); - } - if (!runinall) - break; - } -} - int main(int argc, char *argv[]) { + int mod = ERR; + if (!parse_args(argc, argv)) { setup(); startup(NULL); } while (running) { - Client *c, *t; int r, nfds = 0; fd_set rd; - if (need_screen_resize) { + if (screen.need_resize) { resize_screen(); - need_screen_resize = false; + screen.need_resize = false; } FD_ZERO(&rd); FD_SET(STDIN_FILENO, &rd); -#ifdef CONFIG_CMDFIFO - if (cmdfd != -1) { - FD_SET(cmdfd, &rd); - nfds = cmdfd; + if (cmdfifo.fd != -1) { + FD_SET(cmdfifo.fd, &rd); + nfds = cmdfifo.fd; } -#endif -#ifdef CONFIG_STATUSBAR - if (statusfd != -1) { - FD_SET(statusfd, &rd); - nfds = max(nfds, statusfd); + + if (bar.fd != -1) { + FD_SET(bar.fd, &rd); + nfds = max(nfds, bar.fd); } -#endif - for (c = clients; c; ) { + + for (Client *c = clients; c; ) { if (c->died) { - t = c->next; + Client *t = c->next; destroy(c); c = t; continue; @@ -1032,6 +1353,8 @@ nfds = max(nfds, c->pty); c = c->next; } + + doupdate(); r = select(nfds + 1, &rd, NULL, NULL, NULL); if (r == -1 && errno == EINTR) @@ -1046,22 +1369,19 @@ int code = getch(); Key *key; if (code >= 0) { -#ifdef CONFIG_MOUSE - if (code == KEY_MOUSE) { + if (mod >= 0) { + if ((key = keybinding(mod, code))) + key->action.cmd(key->action.args); + mod = ERR; + } else if (code == KEY_MOUSE) { handle_mouse(); - } else -#endif /* CONFIG_MOUSE */ - if (is_modifier(code)) { - int mod = code; - code = getch(); - if (code >= 0) { - if (code == mod) - keypress(code); - else if ((key = keybinding(mod, code))) - key->action.cmd(key->action.args); - } - } else if ((key = keybinding(0, code))) { + } else if (is_modifier(code)) { + mod = code; + } else if ((key = keybinding(ERR, code))) { key->action.cmd(key->action.args); + } else if (sel && vt_copymode(sel->term)) { + vt_copymode_keypress(sel->term, code); + draw(sel); } else { keypress(code); } @@ -1070,38 +1390,34 @@ continue; } -#ifdef CONFIG_CMDFIFO - if (cmdfd != -1 && FD_ISSET(cmdfd, &rd)) + if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd)) handle_cmdfifo(); -#endif -#ifdef CONFIG_STATUSBAR - if (statusfd != -1 && FD_ISSET(statusfd, &rd)) + + if (bar.fd != -1 && FD_ISSET(bar.fd, &rd)) handle_statusbar(); -#endif - for (c = clients; c; ) { - if (FD_ISSET(c->pty, &rd)) { - if (madtty_process(c->term) < 0 && errno == EIO) { + for (Client *c = clients; c; ) { + if (FD_ISSET(c->pty, &rd) && !vt_copymode(c->term)) { + if (vt_process(c->term) < 0 && errno == EIO) { /* client probably terminated */ - t = c->next; + Client *t = c->next; destroy(c); c = t; continue; } - if (c != sel) { + if (c != sel && is_content_visible(c)) { draw_content(c); - if (!isarrange(fullscreen)) - wnoutrefresh(c->window); + wnoutrefresh(c->window); } } c = c->next; } - if (sel) { + if (is_content_visible(sel)) { draw_content(sel); + curs_set(vt_cursor(sel->term)); wnoutrefresh(sel->window); } - doupdate(); } cleanup(); diff -Nru dvtm-0.6/dvtm.info dvtm-0.12/dvtm.info --- dvtm-0.6/dvtm.info 1970-01-01 00:00:00.000000000 +0000 +++ dvtm-0.12/dvtm.info 2014-07-05 07:32:09.000000000 +0000 @@ -0,0 +1,134 @@ +dvtm|dynamic virtual terminal manager, + am, + eo, + mir, + msgr, + xenl, + colors#8, + cols#80, + it#8, + lines#24, + ncv@, + pairs#64, + acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + bel=^G, + blink=\E[5m, + bold=\E[1m, + civis=\E[?25l, + clear=\E[H\E[2J, + cnorm=\E[?25h, + cr=^M, + csr=\E[%i%p1%d;%p2%dr, + cub=\E[%p1%dD, + cub1=^H, + cud=\E[%p1%dB, + cud1=^J, + cuf=\E[%p1%dC, + cuf1=\E[C, + cup=\E[%i%p1%d;%p2%dH, + cuu=\E[%p1%dA, + cuu1=\E[A, + dl=\E[%p1%dM, + dl1=\E[M, + ed=\E[J, + el=\E[K, + el1=\E[1K, + enacs=\E(B\E)0, + home=\E[H, + hpa=\E[%i%p1%dG, + ht=^I, + hts=\EH, + ich=\E[%p1%d@, + ich1=\E[@, + il=\E[%p1%dL, + il1=\E[L, + ind=^J, + is1=\E[?47l\E=\E[?1l, + is2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l, + kDC=\E[3$, + kEND=\E[8$, + kHOM=\E[7$, + kIC=\E[2$, + kLFT=\E[d, + kNXT=\E[6$, + kPRV=\E[5$, + kRIT=\E[c, + ka1=\EOw, + ka3=\EOy, + kb2=\EOu, + kbs=\177, + kc1=\EOq, + kc3=\EOs, + kcbt=\E[Z, + kcub1=\E[D, + kcud1=\E[B, + kcuf1=\E[C, + kcuu1=\E[A, + kdch1=\E[3~, + kel=\E[8\^, + kend=\E[8~, + kent=\EOM, + kf0=\E[21~, + kf1=\E[11~, + kf2=\E[12~, + kf3=\E[13~, + kf4=\E[14~, + kf5=\E[15~, + kf6=\E[17~, + kf7=\E[18~, + kf8=\E[19~, + kf9=\E[20~, + kf10=\E[21~, + kf11=\E[23~, + kf12=\E[24~, + kf13=\E[25~, + kf14=\E[26~, + kf15=\E[28~, + kf16=\E[29~, + kf17=\E[31~, + kf18=\E[32~, + kf19=\E[33~, + kf20=\E[34~, + kf21=\E[23$, + kf22=\E[24$ + kfnd=\E[1~, + khome=\E[7~, + kich1=\E[2~, + kind=\E[a, + kmous=\E[M, + knp=\E[6~, + kpp=\E[5~, + kri=\E[b, + kslt=\E[4~, + op=\E[39;49m, + rc=\E8, + rev=\E[7m, + ri=\EM, + rmacs=^O, + rmcup=\E[2J\E[?47l\E8, + rmir=\E[4l, + rmso=\E[27m, + rmul=\E[24m, + rs1=\E>\E[?1;3;4;5;6l\E[?7h\E[m\E[r\E[2J\E[H, + rs2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l\E>\E[?1000l\E[?25h, + s0ds=\E(B, + s1ds=\E(0, + sc=\E7, + setab=\E[4%p1%dm, + setaf=\E[3%p1%dm, + sgr=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;, + sgr0=\E[m\017, + smacs=^N, + smcup=\E7\E[?47h, + smir=\E[4h, + smso=\E[7m, + smul=\E[4m, + tbc=\E[3g, + vpa=\E[%i%p1%dd, + +dvtm-256color|dynamic virtual terminal manager with 256 colors, + use=dvtm, + colors#256, + pairs#32767, + setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, + setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, diff -Nru dvtm-0.6/fibonacci.c dvtm-0.12/fibonacci.c --- dvtm-0.6/fibonacci.c 1970-01-01 00:00:00.000000000 +0000 +++ dvtm-0.12/fibonacci.c 2014-07-05 07:32:09.000000000 +0000 @@ -0,0 +1,100 @@ +static void fibonacci(int s) +{ + unsigned int nx, ny, nw, nnw, nh, nnh, i, n, m, nm, mod; + Client *c; + + for (n = 0, m = 0, c = clients; c; c = c->next, n++) + if (c->minimized) + m++; + /* number of non minimized windows */ + nm = n - m; + + /* initial position and dimensions */ + nx = wax; + ny = way; + nw = (n == 1) ? waw : screen.mfact * waw; + /* don't waste space dviding by 2 doesn't work for odd numbers + * plus we need space for the border too. therefore set up these + * variables for the next new width/height + */ + nnw = waw - nw - 1; + nnh = nh = (wah - m); /* leave space for the minimized clients */ + + /* set the mod factor, 2 for dwindle, 4 for spiral */ + mod = s ? 4 : 2; + + for (i = 0, c = clients; c; c = c->next, i++) { + if (!c->minimized) { + /* dwindle: even case, spiral: case 0 */ + if (i % mod == 0) { + if (i) { + if (s) { + nh = nnh; + ny -= nh; + } else { + ny += nh; + nh = nnh; + } + /* don't adjust the width for the last non-minimized client */ + if (i < nm - 1) { + nw /= 2; + nnw -= nw + 1; + } + mvaddch(ny, nx - 1, ACS_LTEE); + } + } else if (i % mod == 1) { /* dwindle: odd case, spiral: case 1 */ + nx += nw; + mvvline(ny, nx, ACS_VLINE, nh); + mvaddch(ny, nx, ACS_TTEE); + ++nx; + nw = nnw; + /* don't adjust the height for the last non-minimized client */ + if (i < nm - 1) { + nh /= 2; + nnh -= nh; + } + } else if (i % mod == 2 && s) { /* spiral: case 2 */ + ny += nh; + nh = nnh; + /* don't adjust the width for the last non-minimized client */ + if (i < nm - 1) { + nw /= 2; + nnw -= nw + 1; + nx += nnw; + mvvline(ny, nx, ACS_VLINE, nh); + mvaddch(ny, nx, ACS_TTEE); + ++nx; + } else { + mvaddch(ny, nx - 1, ACS_LTEE); + } + } else if (s) { /* spiral: case 3 */ + nw = nnw; + nx -= nw + 1; /* border */ + /* don't adjust the height for the last non-minimized client */ + if (i < nm - 1) { + nh /= 2; + nnh -= nh; + ny += nnh; + } + mvaddch(ny, nx - 1, ACS_LTEE); + } + } else { + nh = 1; + nw = waw; + nx = wax; + ny = way + wah - (n - i); + } + + resize(c, nx, ny, nw, nh); + } +} + +static void spiral(void) +{ + fibonacci(1); +} + +static void dwindle(void) +{ + fibonacci(0); +} diff -Nru dvtm-0.6/forkpty-aix.c dvtm-0.12/forkpty-aix.c --- dvtm-0.6/forkpty-aix.c 1970-01-01 00:00:00.000000000 +0000 +++ dvtm-0.12/forkpty-aix.c 2014-07-05 07:32:09.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2012 Ross Palmer Mohn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +/* Fatal errors. */ +#ifdef NDEBUG + #define debug(format, args...) +#else + #define debug eprint +#endif +#define fatal(msg) debug("%s: %s", __func__, msg); + +pid_t +forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) +{ + int slave, fd; + char *path; + pid_t pid; + struct termios tio2; + + if ((*master = open("/dev/ptc", O_RDWR|O_NOCTTY)) == -1) + return (-1); + + if ((path = ttyname(*master)) == NULL) + goto out; + if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) + goto out; + + switch (pid = fork()) { + case -1: + goto out; + case 0: + close(*master); + + fd = open(_PATH_TTY, O_RDWR|O_NOCTTY); + if (fd >= 0) { + ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } + + if (setsid() < 0) + fatal("setsid"); + + fd = open(_PATH_TTY, O_RDWR|O_NOCTTY); + if (fd >= 0) + fatal("open succeeded (failed to disconnect)"); + + fd = open(path, O_RDWR); + if (fd < 0) + fatal("open failed"); + close(fd); + + fd = open("/dev/tty", O_WRONLY); + if (fd < 0) + fatal("open failed"); + close(fd); + + if (tcgetattr(slave, &tio2) != 0) + fatal("tcgetattr failed"); + if (tio != NULL) + memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); + tio2.c_cc[VERASE] = '\177'; + if (tcsetattr(slave, TCSAFLUSH, &tio2) == -1) + fatal("tcsetattr failed"); + if (ioctl(slave, TIOCSWINSZ, ws) == -1) + fatal("ioctl failed"); + + dup2(slave, 0); + dup2(slave, 1); + dup2(slave, 2); + if (slave > 2) + close(slave); + return (0); + } + + close(slave); + return (pid); + +out: + if (*master != -1) + close(*master); + if (slave != -1) + close(slave); + return (-1); +} diff -Nru dvtm-0.6/fullscreen.c dvtm-0.12/fullscreen.c --- dvtm-0.6/fullscreen.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/fullscreen.c 2014-07-05 07:32:09.000000000 +0000 @@ -1,6 +1,5 @@ -static void -fullscreen(void) { - Client *c; - for(c = clients; c; c = c->next) +static void fullscreen(void) +{ + for (Client *c = clients; c; c = c->next) resize(c, wax, way, waw, wah); } diff -Nru dvtm-0.6/grid.c dvtm-0.12/grid.c --- dvtm-0.6/grid.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/grid.c 2014-07-05 07:32:09.000000000 +0000 @@ -1,50 +1,54 @@ -static void -grid(void) { +static void grid(void) +{ unsigned int i, m, nm, n, nx, ny, nw, nh, aw, ah, cols, rows; Client *c; - for(n = 0, m = 0, c = clients; c; c = c->next,n++) - if(c->minimized) + for (n = 0, m = 0, c = clients; c; c = c->next, n++) + if (c->minimized) m++; /* number of non minimized windows */ nm = n - m; /* grid dimensions */ - for(cols = 0; cols <= nm/2; cols++) - if(cols*cols >= nm) + for (cols = 0; cols <= nm / 2; cols++) + if (cols * cols >= nm) break; rows = (cols && (cols - 1) * cols >= nm) ? cols - 1 : cols; /* window geoms (cell height/width) */ nh = (wah - m) / (rows ? rows : 1); nw = waw / (cols ? cols : 1); - for(i = 0, c = clients; c; c = c->next,i++) { - if(!c->minimized){ + for (i = 0, c = clients; c; c = c->next, i++) { + if (!c->minimized) { /* if there are less clients in the last row than normal adjust the * split rate to fill the empty space */ - if(rows > 1 && i == (rows * cols) - cols && (nm - i) <= (nm % cols)) + if (rows > 1 && i == (rows * cols) - cols + && (nm - i) <= (nm % cols)) nw = waw / (nm - i); nx = (i % cols) * nw + wax; ny = (i / cols) * nh + way; /* adjust height/width of last row/column's windows */ - ah = (i >= cols * (rows -1)) ? wah - m - nh * rows : 0; + ah = (i >= cols * (rows - 1)) ? wah - m - nh * rows : 0; /* special case if there are less clients in the last row */ - if(rows > 1 && i == nm - 1 && (nm - i) < (nm % cols)) + if (rows > 1 && i == nm - 1 && (nm - i) < (nm % cols)) /* (n % cols) == number of clients in the last row */ aw = waw - nw * (nm % cols); else - aw = ((i + 1) % cols == 0) ? waw - nw * cols : 0; - if(i % cols){ + aw = ((i + 1) % cols == + 0) ? waw - nw * cols : 0; + if (i % cols) { mvvline(ny, nx, ACS_VLINE, nh + ah); /* if we are on the first row, or on the last one and there are fewer clients * than normal whose border does not match the line above, print a top tree char * otherwise a plus sign. */ - if(i <= cols || (i >= rows * cols - cols && nm % cols && (cols - (nm % cols)) % 2)) + if (i <= cols + || (i >= rows * cols - cols && nm % cols + && (cols - (nm % cols)) % 2)) mvaddch(ny, nx, ACS_TTEE); else mvaddch(ny, nx, ACS_PLUS); nx++, aw--; } } else { - if(i == nm){ /* first minimized client */ + if (i == nm) { /* first minimized client */ ny = way + wah - m; nx = wax; nw = waw; diff -Nru dvtm-0.6/LICENSE dvtm-0.12/LICENSE --- dvtm-0.6/LICENSE 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/LICENSE 2014-07-05 07:32:09.000000000 +0000 @@ -1,22 +1,22 @@ MIT/X Consortium License (c) 2006-2007 Anselm R. Garbe - (c) 2007-2009 Marc Andre Tanner + (c) 2007-2012 Marc Andre Tanner 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 +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. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff -Nru dvtm-0.6/madtty.c dvtm-0.12/madtty.c --- dvtm-0.6/madtty.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/madtty.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1394 +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 - Copyright © 2008 Marc Andre Tanner - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __linux__ -# include -#elif defined(__FreeBSD__) -# include -#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) -# include -#endif -#ifdef __CYGWIN__ -# include -#endif - -#include "madtty.h" - -#ifndef NCURSES_ATTR_SHIFT -# define NCURSES_ATTR_SHIFT 8 -#endif - -#define IS_CONTROL(ch) !((ch) & 0xffffff60UL) - -static int has_default, is_utf8, use_palette; - -static const unsigned palette_start = 1; -static const unsigned palette_end = 256; -static unsigned palette_cur; -static short *color2palette; -static unsigned color_hash(short f, short b) { return ((f+1) * (COLORS+1)) + b+1 ; } - -enum { - C0_NUL = 0x00, - C0_SOH, C0_STX, C0_ETX, C0_EOT, C0_ENQ, C0_ACK, C0_BEL, - C0_BS , C0_HT , C0_LF , C0_VT , C0_FF , C0_CR , C0_SO , C0_SI , - C0_DLE, C0_DC1, C0_DC2, D0_DC3, C0_DC4, C0_NAK, C0_SYN, C0_ETB, - C0_CAN, C0_EM , C0_SUB, C0_ESC, C0_IS4, C0_IS3, C0_IS2, C0_IS1, -}; - -enum { - C1_40 = 0x40, - C1_41 , C1_BPH, C1_NBH, C1_44 , C1_NEL, C1_SSA, C1_ESA, - C1_HTS, C1_HTJ, C1_VTS, C1_PLD, C1_PLU, C1_RI , C1_SS2, C1_SS3, - C1_DCS, C1_PU1, C1_PU2, C1_STS, C1_CCH, C1_MW , C1_SPA, C1_EPA, - C1_SOS, C1_59 , C1_SCI, C1_CSI, CS_ST , C1_OSC, C1_PM , C1_APC, -}; - -enum { - CSI_ICH = 0x40, - CSI_CUU, CSI_CUD, CSI_CUF, CSI_CUB, CSI_CNL, CSI_CPL, CSI_CHA, - CSI_CUP, CSI_CHT, CSI_ED , CSI_EL , CSI_IL , CSI_DL , CSI_EF , CSI_EA , - CSI_DCH, CSI_SEE, CSI_CPR, CSI_SU , CSI_SD , CSI_NP , CSI_PP , CSI_CTC, - CSI_ECH, CSI_CVT, CSI_CBT, CSI_SRS, CSI_PTX, CSI_SDS, CSI_SIMD, CSI_5F, - CSI_HPA, CSI_HPR, CSI_REP, CSI_DA , CSI_VPA, CSI_VPR, CSI_HVP, CSI_TBC, - CSI_SM , CSI_MC , CSI_HPB, CSI_VPB, CSI_RM , CSI_SGR, CSI_DSR, CSI_DAQ, - CSI_70 , CSI_71 , CSI_72 , CSI_73 , CSI_74 , CSI_75 , CSI_76 , CSI_77 , - CSI_78 , CSI_79 , CSI_7A , CSI_7B , CSI_7C , CSI_7D , CSI_7E , CSI_7F -}; - -struct madtty_t { - int pty; - pid_t childpid; - - /* flags */ - unsigned seen_input : 1; - unsigned insert : 1; - unsigned escaped : 1; - unsigned graphmode : 1; - unsigned curshid : 1; - unsigned curskeymode: 1; - unsigned bell : 1; - - /* geometry */ - int rows, cols, maxcols; - unsigned curattrs; - short curfg, curbg; - - /* scrollback buffer */ - struct t_row_t *scroll_buf; - int scroll_buf_sz; - int scroll_buf_ptr; - int scroll_buf_len; - int scroll_amount; - - struct t_row_t *lines; - struct t_row_t *scroll_top; - struct t_row_t *scroll_bot; - - /* cursor */ - struct t_row_t *curs_row; - int curs_col, curs_srow, curs_scol; - - /* buffers and parsing state */ - mbstate_t ps; - char rbuf[BUFSIZ]; - char ebuf[BUFSIZ]; - int rlen, elen; - - /* custom escape sequence handler */ - madtty_handler_t handler; - void *data; -}; - -typedef struct t_row_t { - wchar_t *text; - uint16_t *attr; - short *fg; - short *bg; - unsigned dirty : 1; -} t_row_t; - -static char const * const keytable[KEY_MAX+1] = { - ['\n'] = "\r", - /* for the arrow keys the CSI / SS3 sequences are not stored here - * because they depend on the current cursor terminal mode - */ - [KEY_UP] = "A", - [KEY_DOWN] = "B", - [KEY_RIGHT] = "C", - [KEY_LEFT] = "D", -#ifdef KEY_SUP - [KEY_SUP] = "\e[1;2A", -#endif -#ifdef KEY_SDOWN - [KEY_SDOWN] = "\e[1;2B", -#endif - [KEY_SRIGHT] = "\e[1;2C", - [KEY_SLEFT] = "\e[1;2D", - [KEY_BACKSPACE] = "\177", - [KEY_IC] = "\e[2~", - [KEY_DC] = "\e[3~", - [KEY_PPAGE] = "\e[5~", - [KEY_NPAGE] = "\e[6~", - [KEY_HOME] = "\e[7~", - [KEY_END] = "\e[8~", - [KEY_SUSPEND] = "\x1A", /* Ctrl+Z gets mapped to this */ - [KEY_F(1)] = "\e[11~", - [KEY_F(2)] = "\e[12~", - [KEY_F(3)] = "\e[13~", - [KEY_F(4)] = "\e[14~", - [KEY_F(5)] = "\e[15~", - [KEY_F(6)] = "\e[17~", - [KEY_F(7)] = "\e[18~", - [KEY_F(8)] = "\e[19~", - [KEY_F(9)] = "\e[20~", - [KEY_F(10)] = "\e[21~", - [KEY_F(11)] = "\e[23~", - [KEY_F(12)] = "\e[24~", - [KEY_F(13)] = "\e[25~", - [KEY_F(14)] = "\e[26~", - [KEY_F(15)] = "\e[28~", - [KEY_F(16)] = "\e[29~", - [KEY_F(17)] = "\e[31~", - [KEY_F(18)] = "\e[32~", - [KEY_F(19)] = "\e[33~", - [KEY_F(20)] = "\e[34~", -}; - -__attribute__((const)) -static uint16_t build_attrs(unsigned curattrs) -{ - return ((curattrs & ~A_COLOR) | COLOR_PAIR(curattrs & 0xff)) - >> NCURSES_ATTR_SHIFT; -} - -static void t_row_set(t_row_t *row, int start, int len, madtty_t *t) -{ - row->dirty = true; - wmemset(row->text + start, 0, len); - attr_t attr = t ? build_attrs(t->curattrs) : 0; - short fg = t ? t->curfg : -1; - short bg = t ? t->curbg : -1; - for (int i = start; i < len + start; i++) { - row->attr[i] = attr; - row->fg [i] = fg; - row->bg [i] = bg; - } -} - -static void t_row_roll(t_row_t *start, t_row_t *end, int count) -{ - int n = end - start; - - count %= n; - if (count < 0) - count += n; - - if (count) { - t_row_t *buf = alloca(count * sizeof(t_row_t)); - - memcpy(buf, start, count * sizeof(t_row_t)); - memmove(start, start + count, (n - count) * sizeof(t_row_t)); - memcpy(end - count, buf, count * sizeof(t_row_t)); - for (t_row_t *row = start; row < end; row++) { - row->dirty = true; - } - } -} - -static void clamp_cursor_to_bounds(madtty_t *t) -{ - if (t->curs_row < t->lines) { - t->curs_row = t->lines; - } - if (t->curs_row >= t->lines + t->rows) { - t->curs_row = t->lines + t->rows - 1; - } - - if (t->curs_col < 0) { - t->curs_col = 0; - } - if (t->curs_col >= t->cols) { - t->curs_col = t->cols - 1; - } -} - -static void fill_scroll_buf(madtty_t *t, int s) -{ - /* work in screenfuls */ - int ssz = t->scroll_bot - t->scroll_top; - if (s > ssz) { - fill_scroll_buf(t, ssz); - fill_scroll_buf(t, s-ssz); - return; - } - if (s < -ssz) { - fill_scroll_buf(t, -ssz); - fill_scroll_buf(t, s + ssz); - return; - } - - t->scroll_buf_len += s; - if (t->scroll_buf_len >= t->scroll_buf_sz) - t->scroll_buf_len = t->scroll_buf_sz; - - if (s > 0 && t->scroll_buf_sz) { - for (int i = 0; i < s; i++) { - struct t_row_t tmp = t->scroll_top[i]; - t->scroll_top[i] = t->scroll_buf[t->scroll_buf_ptr]; - t->scroll_buf[t->scroll_buf_ptr] = tmp; - - t->scroll_buf_ptr++; - if (t->scroll_buf_ptr == t->scroll_buf_sz) - t->scroll_buf_ptr = 0; - } - } - t_row_roll(t->scroll_top, t->scroll_bot, s); - if (s < 0 && t->scroll_buf_sz) { - for (int i = (-s)-1; i >= 0; i--) { - t->scroll_buf_ptr--; - if (t->scroll_buf_ptr == -1) - t->scroll_buf_ptr = t->scroll_buf_sz - 1; - - struct t_row_t tmp = t->scroll_top[i]; - t->scroll_top[i] = t->scroll_buf[t->scroll_buf_ptr]; - t->scroll_buf[t->scroll_buf_ptr] = tmp; - t->scroll_top[i].dirty = true; - } - } -} - -static void cursor_line_down(madtty_t *t) -{ - t_row_set(t->curs_row, t->cols, t->maxcols - t->cols, 0); - t->curs_row++; - if (t->curs_row < t->scroll_bot) - return; - - madtty_noscroll(t); - - t->curs_row = t->scroll_bot - 1; - fill_scroll_buf(t, 1); - t_row_set(t->curs_row, 0, t->cols, t); -} - -static void new_escape_sequence(madtty_t *t) -{ - t->escaped = true; - t->elen = 0; - t->ebuf[0] = '\0'; -} - -static void cancel_escape_sequence(madtty_t *t) -{ - t->escaped = false; - t->elen = 0; - t->ebuf[0] = '\0'; -} - -static bool is_valid_csi_ender(int c) -{ - return (c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c == '@' || c == '`'); -} - -/* interprets a 'set attribute' (SGR) CSI escape sequence */ -static void interpret_csi_SGR(madtty_t *t, int param[], int pcount) -{ - int i; - - if (pcount == 0) { - /* special case: reset attributes */ - t->curattrs = A_NORMAL; - t->curfg = t->curbg = -1; - return; - } - - for (i = 0; i < pcount; i++) { - switch (param[i]) { -#define CASE(x, op) case x: op; break - CASE(0, t->curattrs = A_NORMAL; t->curfg = t->curbg = -1); - CASE(1, t->curattrs |= A_BOLD); - CASE(4, t->curattrs |= A_UNDERLINE); - CASE(5, t->curattrs |= A_BLINK); - CASE(6, t->curattrs |= A_BLINK); - CASE(7, t->curattrs |= A_REVERSE); - CASE(8, t->curattrs |= A_INVIS); - CASE(22, t->curattrs &= ~A_BOLD); - CASE(24, t->curattrs &= ~A_UNDERLINE); - CASE(25, t->curattrs &= ~A_BLINK); - CASE(27, t->curattrs &= ~A_REVERSE); - CASE(28, t->curattrs &= ~A_INVIS); - - case 30 ... 37: /* fg */ - t->curfg = param[i]-30; - break; - - case 38: - assert(param[i+1] == 5); - t->curfg = param[i+2]; - i+=2; - break; - - case 39: - t->curfg = -1; - break; - - case 40 ... 47: /* bg */ - t->curbg = param[i] - 40; - break; - - case 48: - assert(param[i+1] == 5); - t->curbg = param[i+2]; - i+=2; - break; - - case 49: - t->curbg = -1; - break; - - default: - break; - } - } -} - -/* interprets an 'erase display' (ED) escape sequence */ -static void interpret_csi_ED(madtty_t *t, int param[], int pcount) -{ - t_row_t *row, *start, *end; - - /* decide range */ - if (pcount && param[0] == 2) { - start = t->lines; - end = t->lines + t->rows; - } else - if (pcount && param[0] == 1) { - start = t->lines; - end = t->curs_row; - t_row_set(t->curs_row, 0, t->curs_col + 1, t); - } else { - t_row_set(t->curs_row, t->curs_col, - t->cols - t->curs_col, t); - start = t->curs_row + 1; - end = t->lines + t->rows; - } - - for (row = start; row < end; row++) { - t_row_set(row, 0, t->cols, t); - } -} - -/* interprets a 'move cursor' (CUP) escape sequence */ -static void interpret_csi_CUP(madtty_t *t, int param[], int pcount) -{ - if (pcount == 0) { - /* special case */ - t->curs_row = t->lines; - t->curs_col = 0; - return; - } else - if (pcount < 2) { - return; /* malformed */ - } - - t->curs_row = t->lines + param[0] - 1; - t->curs_col = param[1] - 1; - - clamp_cursor_to_bounds(t); -} - -/* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL, - * CPL, CHA, HPR, VPA, VPR, HPA */ -static void -interpret_csi_C(madtty_t *t, char verb, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - - switch (verb) { - case 'A': t->curs_row -= n; break; - case 'B': case 'e': t->curs_row += n; break; - case 'C': case 'a': t->curs_col += n; break; - case 'D': t->curs_col -= n; break; - case 'E': t->curs_row += n; t->curs_col = 0; break; - case 'F': t->curs_row -= n; t->curs_col = 0; break; - case 'G': case '`': t->curs_col = param[0] - 1; break; - case 'd': t->curs_row = t->lines + param[0] - 1; break; - } - - clamp_cursor_to_bounds(t); -} - -/* Interpret the 'erase line' escape sequence */ -static void interpret_csi_EL(madtty_t *t, int param[], int pcount) -{ - switch (pcount ? param[0] : 0) { - case 1: - t_row_set(t->curs_row, 0, t->curs_col + 1, t); - break; - case 2: - t_row_set(t->curs_row, 0, t->cols, t); - break; - default: - t_row_set(t->curs_row, t->curs_col, t->cols - t->curs_col, t); - break; - } -} - -/* Interpret the 'insert blanks' sequence (ICH) */ -static void interpret_csi_ICH(madtty_t *t, int param[], int pcount) -{ - t_row_t *row = t->curs_row; - int n = (pcount && param[0] > 0) ? param[0] : 1; - int i; - - if (t->curs_col + n > t->cols) { - n = t->cols - t->curs_col; - } - - for (i = t->cols - 1; i >= t->curs_col + n; i--) { - row->text[i] = row->text[i - n]; - row->attr[i] = row->attr[i - n]; - row->bg [i] = row->bg [i - n]; - row->fg [i] = row->fg [i - n]; - } - - t_row_set(row, t->curs_col, n, t); -} - -/* Interpret the 'delete chars' sequence (DCH) */ -static void interpret_csi_DCH(madtty_t *t, int param[], int pcount) -{ - t_row_t *row = t->curs_row; - int n = (pcount && param[0] > 0) ? param[0] : 1; - int i; - - if (t->curs_col + n > t->cols) { - n = t->cols - t->curs_col; - } - - for (i = t->curs_col; i < t->cols - n; i++) { - row->text[i] = row->text[i + n]; - row->attr[i] = row->attr[i + n]; - row->bg [i] = row->bg [i + n]; - row->fg [i] = row->fg [i + n]; - } - - t_row_set(row, t->cols - n, n, t); -} - -/* Interpret a 'scroll reverse' (SR) */ -static void interpret_csi_SR(madtty_t *t) -{ - t_row_roll(t->scroll_top, t->scroll_bot, -1); - t_row_set(t->scroll_top, 0, t->cols, t); -} - -/* Interpret an 'insert line' sequence (IL) */ -static void interpret_csi_IL(madtty_t *t, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - - if (t->curs_row + n >= t->scroll_bot) { - for (t_row_t *row = t->curs_row; row < t->scroll_bot; row++) { - t_row_set(row, 0, t->cols, t); - } - } else { - t_row_roll(t->curs_row, t->scroll_bot, -n); - for (t_row_t *row = t->curs_row; row < t->curs_row + n; row++) { - t_row_set(row, 0, t->cols, t); - } - } -} - -/* Interpret a 'delete line' sequence (DL) */ -static void interpret_csi_DL(madtty_t *t, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - - if (t->curs_row + n >= t->scroll_bot) { - for (t_row_t *row = t->curs_row; row < t->scroll_bot; row++) { - t_row_set(row, 0, t->cols, t); - } - } else { - t_row_roll(t->curs_row, t->scroll_bot, n); - for (t_row_t *row = t->scroll_bot - n; row < t->scroll_bot; row++) { - t_row_set(row, 0, t->cols, t); - } - } -} - -/* Interpret an 'erase characters' (ECH) sequence */ -static void interpret_csi_ECH(madtty_t *t, int param[], int pcount) -{ - int n = (pcount && param[0] > 0) ? param[0] : 1; - - if (t->curs_col + n < t->cols) { - n = t->cols - t->curs_col; - } - t_row_set(t->curs_row, t->curs_col, n, t); -} - -/* Interpret a 'set scrolling region' (DECSTBM) sequence */ -static void interpret_csi_DECSTBM(madtty_t *t, int param[], int pcount) -{ - int new_top, new_bot; - - switch (pcount) { - case 0: - t->scroll_top = t->lines; - t->scroll_bot = t->lines + t->rows; - break; - default: - return; /* malformed */ - - case 2: - new_top = param[0] - 1; - new_bot = param[1]; - - /* clamp to bounds */ - if (new_top < 0) - new_top = 0; - if (new_top >= t->rows) - new_top = t->rows - 1; - if (new_bot < 0) - new_bot = 0; - if (new_bot >= t->rows) - new_bot = t->rows; - - /* check for range validity */ - if (new_top < new_bot) { - t->scroll_top = t->lines + new_top; - t->scroll_bot = t->lines + new_bot; - } - break; - } -} - -static void es_interpret_csi(madtty_t *t) -{ - static int csiparam[BUFSIZ]; - int param_count = 0; - const char *p = t->ebuf + 1; - char verb = t->ebuf[t->elen - 1]; - - p += t->ebuf[1] == '?'; /* CSI private mode */ - - /* parse numeric parameters */ - while (isdigit((unsigned char)*p) || *p == ';') { - if (*p == ';') { - if (param_count >= (int)sizeof(csiparam)) - 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++; - } - - if (t->ebuf[1] == '?') { - switch (verb) { - case 'l': - if (csiparam[0] == 25) - t->curshid = true; - if (csiparam[0] == 1) /* DECCKM: reset ANSI cursor (normal) key mode */ - t->curskeymode = 0; - if (csiparam[0] == 47) { - /* use normal screen buffer */ - t->curattrs = A_NORMAL; - t->curfg = t->curbg = -1; - } - break; - - case 'h': - if (csiparam[0] == 25) - t->curshid = false; - if (csiparam[0] == 1) /* DECCKM: set ANSI cursor (application) key mode */ - t->curskeymode = 1; - if (csiparam[0] == 47) { - /* use alternate screen buffer */ - t->curattrs = A_NORMAL; - t->curfg = t->curbg = -1; - } - break; - } - } - - /* delegate handling depending on command character (verb) */ - switch (verb) { - case 'h': - if (param_count == 1 && csiparam[0] == 4) /* insert mode */ - t->insert = true; - break; - case 'l': - if (param_count == 1 && csiparam[0] == 4) /* replace mode */ - t->insert = false; - break; - case 'm': /* it's a 'set attribute' sequence */ - interpret_csi_SGR(t, csiparam, param_count); break; - case 'J': /* it's an 'erase display' sequence */ - interpret_csi_ED(t, csiparam, param_count); break; - case 'H': case 'f': /* it's a 'move cursor' sequence */ - interpret_csi_CUP(t, 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(t, verb, csiparam, param_count); break; - case 'K': /* erase line */ - interpret_csi_EL(t, csiparam, param_count); break; - case '@': /* insert characters */ - interpret_csi_ICH(t, csiparam, param_count); break; - case 'P': /* delete characters */ - interpret_csi_DCH(t, csiparam, param_count); break; - case 'L': /* insert lines */ - interpret_csi_IL(t, csiparam, param_count); break; - case 'M': /* delete lines */ - interpret_csi_DL(t, csiparam, param_count); break; - case 'X': /* erase chars */ - interpret_csi_ECH(t, csiparam, param_count); break; - case 'r': /* set scrolling region */ - interpret_csi_DECSTBM(t, csiparam, param_count); break; - case 's': /* save cursor location */ - t->curs_srow = t->curs_row - t->lines; - t->curs_scol = t->curs_col; - break; - case 'u': /* restore cursor location */ - t->curs_row = t->lines + t->curs_srow; - t->curs_col = t->curs_scol; - clamp_cursor_to_bounds(t); - break; - default: - break; - } -} - -static void try_interpret_escape_seq(madtty_t *t) -{ - char lastchar = t->ebuf[t->elen-1]; - if(!*t->ebuf) - return; - if(t->handler){ - switch((*(t->handler))(t, t->ebuf)){ - case MADTTY_HANDLER_OK: - goto cancel; - case MADTTY_HANDLER_NOTYET: - if (t->elen + 1 >= (int)sizeof(t->ebuf)) - goto cancel; - return; - } - } - switch (*t->ebuf) { - case 'M': - interpret_csi_SR(t); - cancel_escape_sequence(t); - return; - - case '(': - case ')': - if (t->elen == 2) - goto cancel; - break; - - case ']': /* xterm thing */ - if (lastchar == '\a') - goto cancel; - break; - - default: - goto cancel; - - case '[': - if (is_valid_csi_ender(lastchar)) { - es_interpret_csi(t); - cancel_escape_sequence(t); - return; - } - break; - } - - if (t->elen + 1 >= (int)sizeof(t->ebuf)) { -cancel: -#ifndef NDEBUG - fprintf(stderr, "cancelled: \\033"); - for (int i = 0; i < (int)t->elen; i++) { - if (isprint(t->ebuf[i])) { - fputc(t->ebuf[i], stderr); - } else { - fprintf(stderr, "\\%03o", t->ebuf[i]); - } - } - fputc('\n', stderr); -#endif - cancel_escape_sequence(t); - } -} - -static void madtty_process_nonprinting(madtty_t *t, wchar_t wc) -{ - switch (wc) { - case C0_ESC: - new_escape_sequence(t); - break; - - case C0_BEL: - /* maybe a visual bell would be nice? */ - if(t->bell) - beep(); - break; - - case C0_BS: - if (t->curs_col > 0) - t->curs_col--; - break; - - case C0_HT: /* tab */ - t->curs_col = (t->curs_col + 8) & ~7; - if (t->curs_col >= t->cols) - t->curs_col = t->cols - 1; - break; - - case C0_CR: - t->curs_col = 0; - break; - - case C0_VT: - case C0_FF: - case C0_LF: - cursor_line_down(t); - break; - - case C0_SO: /* shift out - acs */ - t->graphmode = true; - break; - case C0_SI: /* shift in - acs */ - t->graphmode = false; - break; - } -} - -static void is_utf8_locale(void) -{ - const char *cset = nl_langinfo(CODESET) ?: "ANSI_X3.4-1968"; - is_utf8 = !strcmp(cset, "UTF-8"); -} - -// vt100 special graphics and line drawing -// 5f-7e standard vt100 -// 40-5e rxvt extension for extra curses acs chars -static uint16_t const vt100_utf8[62] = { // 41 .. 7e - 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47 hi mr. snowman! - 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f - 0, 0, 0, 0, 0, 0, 0, 0, // 50-57 - 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f - 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67 - 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f - 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77 - 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e -}; - -static uint32_t vt100[62]; - -void madtty_init_vt100_graphics(void) -{ - vt100['l' - 0x41] = ACS_ULCORNER; - vt100['m' - 0x41] = ACS_LLCORNER; - vt100['k' - 0x41] = ACS_URCORNER; - vt100['j' - 0x41] = ACS_LRCORNER; - vt100['u' - 0x41] = ACS_RTEE; - vt100['t' - 0x41] = ACS_LTEE; - vt100['v' - 0x41] = ACS_TTEE; - vt100['w' - 0x41] = ACS_BTEE; - vt100['q' - 0x41] = ACS_HLINE; - vt100['x' - 0x41] = ACS_VLINE; - vt100['n' - 0x41] = ACS_PLUS; - vt100['o' - 0x41] = ACS_S1; - vt100['s' - 0x41] = ACS_S9; - vt100['`' - 0x41] = ACS_DIAMOND; - vt100['a' - 0x41] = ACS_CKBOARD; - vt100['f' - 0x41] = ACS_DEGREE; - vt100['g' - 0x41] = ACS_PLMINUS; - vt100['~' - 0x41] = ACS_BULLET; -#if 0 /* out of bounds */ - vt100[',' - 0x41] = ACS_LARROW; - vt100['+' - 0x41] = ACS_RARROW; - vt100['.' - 0x41] = ACS_DARROW; - vt100['-' - 0x41] = ACS_UARROW; - vt100['0' - 0x41] = ACS_BLOCK; -#endif - vt100['h' - 0x41] = ACS_BOARD; - vt100['i' - 0x41] = ACS_LANTERN; - /* these defaults were invented for ncurses */ - vt100['p' - 0x41] = ACS_S3; - vt100['r' - 0x41] = ACS_S7; - vt100['y' - 0x41] = ACS_LEQUAL; - vt100['z' - 0x41] = ACS_GEQUAL; - vt100['{' - 0x41] = ACS_PI; - vt100['|' - 0x41] = ACS_NEQUAL; - vt100['}' - 0x41] = ACS_STERLING; - is_utf8_locale(); -} - -static void madtty_putc(madtty_t *t, wchar_t wc) -{ - int width = 0; - - if (!t->seen_input) { - t->seen_input = 1; - kill(-t->childpid, SIGWINCH); - } - - if (t->escaped) { - assert (t->elen + 1 < (int)sizeof(t->ebuf)); - t->ebuf[t->elen] = wc; - t->ebuf[++t->elen] = '\0'; - try_interpret_escape_seq(t); - } else if (IS_CONTROL(wc)) { - madtty_process_nonprinting(t, wc); - } else { - t_row_t *tmp; - - if (t->graphmode) { - if (wc >= 0x41 && wc <= 0x7e) { - wchar_t gc = is_utf8 ? vt100_utf8[wc - 0x41] : vt100[wc - 0x41]; - if (gc) - wc = gc; - } - width = 1; - } else { - width = wcwidth(wc) ?: 1; - } - - if (width == 2 && t->curs_col == t->cols - 1) { - tmp = t->curs_row; - tmp->dirty = true; - tmp->text[t->curs_col] = 0; - tmp->attr[t->curs_col] = build_attrs(t->curattrs); - tmp->bg[t->curs_col] = t->curbg; - tmp->fg[t->curs_col] = t->curfg; - t->curs_col++; - } - - if (t->curs_col >= t->cols) { - t->curs_col = 0; - cursor_line_down(t); - } - - tmp = t->curs_row; - tmp->dirty = true; - - if (t->insert) { - wmemmove(tmp->text + t->curs_col + width, tmp->text + t->curs_col, - (t->cols - t->curs_col - width)); - memmove(tmp->attr + t->curs_col + width, tmp->attr + t->curs_col, - (t->cols - t->curs_col - width) * sizeof(tmp->attr[0])); - memmove(tmp->fg + t->curs_col + width, tmp->fg + t->curs_col, - (t->cols - t->curs_col - width) * sizeof(tmp->fg[0])); - memmove(tmp->bg + t->curs_col + width, tmp->bg + t->curs_col, - (t->cols - t->curs_col - width) * sizeof(tmp->bg[0])); - } - - tmp->text[t->curs_col] = wc; - tmp->attr[t->curs_col] = build_attrs(t->curattrs); - tmp->bg[t->curs_col] = t->curbg; - tmp->fg[t->curs_col] = t->curfg; - t->curs_col++; - if (width == 2) { - tmp->text[t->curs_col] = 0; - tmp->attr[t->curs_col] = build_attrs(t->curattrs); - tmp->bg[t->curs_col] = t->curbg; - tmp->fg[t->curs_col] = t->curfg; - t->curs_col++; - } - } -} - -int madtty_process(madtty_t *t) -{ - int res, pos = 0; - - if (t->pty < 0) { - errno = EINVAL; - return -1; - } - - res = read(t->pty, t->rbuf + t->rlen, sizeof(t->rbuf) - t->rlen); - if (res < 0) - return -1; - - t->rlen += res; - while (pos < t->rlen) { - wchar_t wc; - ssize_t len; - - len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &t->ps); - if (len == -2) { - t->rlen -= pos; - memmove(t->rbuf, t->rbuf + pos, t->rlen); - return 0; - } - - if (len == -1) { - len = 1; - wc = t->rbuf[pos]; - } - - pos += len ? len : 1; - madtty_putc(t, wc); - } - - t->rlen -= pos; - memmove(t->rbuf, t->rbuf + pos, t->rlen); - return 0; -} - -madtty_t *madtty_create(int rows, int cols, int scroll_buf_sz) -{ - madtty_t *t; - int i; - - if (rows <= 0 || cols <= 0) - return NULL; - - t = (madtty_t*)calloc(sizeof(madtty_t), 1); - if (!t) - return NULL; - - /* record dimensions */ - t->rows = rows; - t->cols = cols; - t->maxcols = cols; - - /* default mode is replace */ - t->insert = false; - - /* create the cell matrix */ - t->lines = (t_row_t*)calloc(sizeof(t_row_t), t->rows); - for (i = 0; i < t->rows; i++) { - t->lines[i].text = (wchar_t *)calloc(sizeof(wchar_t), t->cols); - t->lines[i].attr = (uint16_t *)calloc(sizeof(uint16_t), t->cols); - t->lines[i].fg = calloc(sizeof(short), t->cols); - t->lines[i].bg = calloc(sizeof(short), t->cols); - } - - t->pty = -1; /* no pty for now */ - - /* initialization of other public fields */ - t->curs_row = t->lines; - t->curs_col = 0; - t->curattrs = A_NORMAL; /* white text over black background */ - t->curfg = t->curbg = -1; - - /* initial scrolling area is the whole window */ - t->scroll_top = t->lines; - t->scroll_bot = t->lines + t->rows; - - /* scrollback buffer */ - if (scroll_buf_sz < 0) - scroll_buf_sz = 0; - t->scroll_buf_sz = scroll_buf_sz; - t->scroll_buf = calloc(sizeof(t_row_t), t->scroll_buf_sz); - for (i = 0; i < t->scroll_buf_sz; i++) { - t->scroll_buf[i].text = calloc(sizeof(wchar_t), t->cols); - t->scroll_buf[i].attr = calloc(sizeof(uint16_t), t->cols); - t->scroll_buf[i].fg = calloc(sizeof(short), t->cols); - t->scroll_buf[i].bg = calloc(sizeof(short), t->cols); - } - t->scroll_buf_ptr = t->scroll_buf_len = 0; - t->scroll_amount = 0; - return t; -} - -void madtty_resize(madtty_t *t, int rows, int cols) -{ - struct winsize ws = { .ws_row = rows, .ws_col = cols }; - t_row_t *lines = t->lines; - - if (rows <= 0 || cols <= 0) - return; - - madtty_noscroll(t); - - if (t->rows != rows) { - if (t->curs_row > lines+rows) { - /* scroll up instead of simply chopping off bottom */ - fill_scroll_buf(t, t->rows - rows); - } - while (t->rows > rows) { - free(lines[t->rows - 1].text); - free(lines[t->rows - 1].attr); - t->rows--; - } - - lines = realloc(lines, sizeof(t_row_t) * rows); - } - - if (t->maxcols < cols) { - for (int row = 0; row < t->rows; row++) { - lines[row].text = realloc(lines[row].text, sizeof(wchar_t) * cols); - lines[row].attr = realloc(lines[row].attr, sizeof(uint16_t) * cols); - lines[row].fg = realloc(lines[row].fg, sizeof(short) * cols); - lines[row].bg = realloc(lines[row].bg, sizeof(short) * cols); - if (t->cols < cols) - t_row_set(lines + row, t->cols, cols - t->cols, 0); - lines[row].dirty = true; - } - t_row_t *sbuf = t->scroll_buf; - for (int row = 0; row < t->scroll_buf_sz; row++) { - sbuf[row].text = realloc(sbuf[row].text, sizeof(wchar_t) * cols); - sbuf[row].attr = realloc(sbuf[row].attr, sizeof(uint16_t) * cols); - sbuf[row].fg = realloc(sbuf[row].fg, sizeof(short) * cols); - sbuf[row].bg = realloc(sbuf[row].bg, sizeof(short) * cols); - if (t->cols < cols) - t_row_set(sbuf + row, t->cols, cols - t->cols, 0); - } - t->maxcols = cols; - t->cols = cols; - } else if (t->cols != cols) { - madtty_dirty(t); - t->cols = cols; - } - - while (t->rows < rows) { - lines[t->rows].text = (wchar_t *)calloc(sizeof(wchar_t), t->maxcols); - lines[t->rows].attr = (uint16_t *)calloc(sizeof(uint16_t), t->maxcols); - lines[t->rows].fg = calloc(sizeof(short), t->maxcols); - lines[t->rows].bg = calloc(sizeof(short), t->maxcols); - t_row_set(lines + t->rows, 0, t->maxcols, 0); - t->rows++; - } - - t->curs_row += lines - t->lines; - t->scroll_top = lines; - t->scroll_bot = lines + rows; - t->lines = lines; - clamp_cursor_to_bounds(t); - ioctl(t->pty, TIOCSWINSZ, &ws); - kill(-t->childpid, SIGWINCH); -} - -void madtty_destroy(madtty_t *t) -{ - int i; - if (!t) - return; - - for (i = 0; i < t->rows; i++) { - free(t->lines[i].text); - free(t->lines[i].attr); - free(t->lines[i].fg); - free(t->lines[i].bg); - } - free(t->lines); - for (i = 0; i < t->scroll_buf_sz; i++) { - free(t->scroll_buf[i].text); - free(t->scroll_buf[i].attr); - free(t->scroll_buf[i].fg); - free(t->scroll_buf[i].bg); - } - free(t->scroll_buf); - free(t); -} - -void madtty_dirty(madtty_t *t) -{ - for (int i = 0; i < t->rows; i++) - t->lines[i].dirty = true; -} - -void madtty_draw(madtty_t *t, WINDOW *win, int srow, int scol) -{ - curs_set(0); - for (int i = 0; i < t->rows; i++) { - t_row_t *row = t->lines + i; - - - if (!row->dirty) - continue; - - wmove(win, srow + i, scol); - for (int j = 0; j < t->cols; j++) { - if (!j - || row->attr[j] != row->attr[j - 1] - || row->fg[j] != row->fg[j-1] - || row->bg[j] != row->bg[j-1]) { - wattrset(win, (attr_t)row->attr[j] << NCURSES_ATTR_SHIFT); - madtty_color_set(win, row->fg[j], row->bg[j]); - } - if (is_utf8 && row->text[j] >= 128) { - char buf[MB_CUR_MAX + 1]; - int len; - - len = wcrtomb(buf, row->text[j], NULL); - waddnstr(win, buf, len); - if (wcwidth(row->text[j]) > 1) - j++; - } else { - waddch(win, row->text[j] > ' ' ? row->text[j] : ' '); - } - } - row->dirty = false; - } - - wmove(win, srow + t->curs_row - t->lines, scol + t->curs_col); - curs_set(madtty_cursor(t)); -} - -void madtty_scroll(madtty_t *t, int rows) -{ - if (rows < 0) { - /* scroll back */ - if (rows < -t->scroll_buf_len) - rows = -t->scroll_buf_len; - } else { - /* scroll forward */ - if (rows > t->scroll_amount) - rows = t->scroll_amount; - } - fill_scroll_buf(t, rows); - t->scroll_amount -= rows; -} - -void madtty_noscroll(madtty_t *t) -{ - if (t->scroll_amount) - madtty_scroll(t, t->scroll_amount); -} - -void madtty_bell(madtty_t *t, bool bell) -{ - t->bell = bell; -} - -void madtty_togglebell(madtty_t *t) -{ - t->bell = !t->bell; -} - -/******************************************************/ - -pid_t madtty_forkpty(madtty_t *t, const char *p, const char *argv[], const char *env[], int *pty) -{ - struct winsize ws; - pid_t pid; - const char **envp = env; - int fd, maxfd; - - ws.ws_row = t->rows; - ws.ws_col = t->cols; - ws.ws_xpixel = ws.ws_ypixel = 0; - - pid = forkpty(&t->pty, NULL, NULL, &ws); - if (pid < 0) - return -1; - - if (pid == 0) { - setsid(); - - maxfd = sysconf(_SC_OPEN_MAX); - for (fd = 3; fd < maxfd; fd++) - if (close(fd) == EBADF) - break; - - while (envp && envp[0]) { - setenv(envp[0], envp[1], 1); - envp += 2; - } - setenv("TERM", use_palette ? "rxvt-256color" : "rxvt", 1); - execv(p, (char *const*)argv); - fprintf(stderr, "\nexecv() failed.\nCommand: '%s'\n", argv[0]); - exit(1); - } - - if (pty) - *pty = t->pty; - return t->childpid = pid; -} - -int madtty_getpty(madtty_t *t) -{ - return t->pty; -} - -static void term_write(madtty_t *t, const char *buf, int len) -{ - while (len > 0) { - int res = write(t->pty, buf, len); - if (res < 0 && errno != EAGAIN && errno != EINTR) - return; - - buf += res; - len -= res; - } -} - -void madtty_keypress(madtty_t *t, int keycode) -{ - char c = (char)keycode; - - madtty_noscroll(t); - - if (keycode >= 0 && keycode < KEY_MAX && keytable[keycode]) { - switch(keycode) { - case KEY_UP: - case KEY_DOWN: - case KEY_RIGHT: - case KEY_LEFT: { - char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] }; - term_write(t, keyseq, 3); - break; - } - default: - term_write(t, keytable[keycode], strlen(keytable[keycode])); - } - } else - term_write(t, &c, 1); -} - -void madtty_keypress_sequence(madtty_t *t, const char *seq) -{ - int key, len = strlen(seq); - /* check for function keys from putty, this makes the - * keypad work but it's probably not the right way to - * do it. the sequence we look for is \eO + a character - * representing the number. - */ - if(len == 3 && seq[0] == '\e' && seq[1] == 'O') { - key = seq[2] - 64; - if(key >= '0' && key <= '9') - madtty_keypress(t, key); - } else - term_write(t, seq, len); -} - -void madtty_color_set(WINDOW *win, short fg, short bg) -{ - if (use_palette) { - if (fg == -1 && bg == -1) { - wcolor_set(win, 0, 0); - } else { - unsigned c = color_hash(fg, bg); - if (color2palette[c] == 0) { - short f, g; - color2palette[c] = palette_cur; - pair_content(palette_cur, &f, &g); - color2palette[color_hash(f,g)] = 0; - init_pair(palette_cur, fg, bg); - palette_cur++; - if (palette_cur >= palette_end) { - palette_cur = palette_start; - /* possibly use mvwinch/mvchgat to update palette */ - } - } - wcolor_set(win, color2palette[c], 0); - } - } else { - if (has_default) { - wcolor_set(win, (fg+1)*16 + bg+1, 0); - } else { - if (fg==-1) fg = COLOR_WHITE; - if (bg==-1) bg = COLOR_BLACK; - wcolor_set(win, (7-fg)*8 + bg, 0); - } - } -} - -void madtty_init_colors(void) -{ - int use_default = use_default_colors() == OK; - use_palette = 0; - - if (COLORS >= 256 && COLOR_PAIRS >= 256) { - use_palette = 1; - has_default = 1; - - color2palette = calloc((COLORS+1)*(COLORS+1), sizeof(short)); - int bg = 0, fg = 0; - for (int i = palette_start; i < palette_end; i++) { - init_pair(i, bg, fg); - color2palette[color_hash(bg,fg)] = i; - if (++fg == COLORS) { - fg = 0; - bg++; - } - } - palette_cur = palette_start; - } else if (COLOR_PAIRS > 64) { - has_default = 1; - - for (int bg = -1; bg < 8; bg++) { - for (int fg = -1; fg < 8; fg++) { - init_pair((fg + 1) * 16 + bg + 1, fg, bg); - } - } - } else { - for (int bg = 0; bg < 8; bg++) { - for (int fg = 0; fg < 8; fg++) { - if (use_default) { - init_pair((7 - fg) * 8 + bg, - fg == COLOR_WHITE ? -1 : fg, - bg == COLOR_BLACK ? -1 : bg); - } else { - init_pair((7 - fg) * 8 + bg, fg, bg); - } - } - } - } -} - -void madtty_set_handler(madtty_t *t, madtty_handler_t handler) -{ - t->handler = handler; -} - -void madtty_set_data(madtty_t *t, void *data) -{ - t->data = data; -} - -void *madtty_get_data(madtty_t *t) -{ - return t->data; -} - -unsigned madtty_cursor(madtty_t *t) -{ - return t->scroll_amount ? 0 : !t->curshid; -} diff -Nru dvtm-0.6/madtty.h dvtm-0.12/madtty.h --- dvtm-0.6/madtty.h 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/madtty.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +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_H -#define MADTTY_MADTTY_H - -#include -#include -#include -#include -#include -#include -#include - -enum { - /* means escape sequence was handled */ - MADTTY_HANDLER_OK, - /* means the escape sequence was not recognized yet, but - * there is hope that it still will once more characters - * arrive (i.e. it is not yet complete). - * - * The library will thus continue collecting characters - * and calling the handler as each character arrives until - * either OK or NOWAY is returned. - */ - MADTTY_HANDLER_NOTYET, - /* means the escape sequence was not recognized, and there - * is no chance that it will even if more characters are - * added to it. - */ - MADTTY_HANDLER_NOWAY -}; - -typedef struct madtty_t madtty_t; -typedef int (*madtty_handler_t)(madtty_t *, char *es); - -void madtty_init_colors(void); -void madtty_init_vt100_graphics(void); -void madtty_set_handler(madtty_t *, madtty_handler_t); -void madtty_set_data(madtty_t *, void *); -void *madtty_get_data(madtty_t *); - -madtty_t *madtty_create(int rows, int cols, int scroll_buf_sz); -void madtty_resize(madtty_t *, int rows, int cols); -void madtty_destroy(madtty_t *); -pid_t madtty_forkpty(madtty_t *, const char *, const char *argv[], const char *envp[], int *pty); -int madtty_getpty(madtty_t *); -unsigned madtty_cursor(madtty_t *t); - -int madtty_process(madtty_t *); -void madtty_keypress(madtty_t *, int keycode); -void madtty_keypress_sequence(madtty_t *, const char *seq); -void madtty_dirty(madtty_t *t); -void madtty_draw(madtty_t *, WINDOW *win, int startrow, int startcol); -void madtty_color_set(WINDOW *win, short fg, short bg); - -void madtty_scroll(madtty_t *, int rows); -void madtty_noscroll(madtty_t *); - -void madtty_bell(madtty_t *, bool bell); -void madtty_togglebell(madtty_t *); - -#endif /* MADTTY_MADTTY_H */ diff -Nru dvtm-0.6/Makefile dvtm-0.12/Makefile --- dvtm-0.6/Makefile 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/Makefile 2014-07-05 07:32:09.000000000 +0000 @@ -1,6 +1,6 @@ include config.mk -SRC += dvtm.c madtty.c +SRC += dvtm.c vt.c OBJ = ${SRC:.c=.o} all: clean options dvtm @@ -11,6 +11,9 @@ @echo "LDFLAGS = ${LDFLAGS}" @echo "CC = ${CC}" +config.h: + cp config.def.h config.h + .c.o: @echo CC $< @${CC} -c ${CFLAGS} $< @@ -31,10 +34,10 @@ dist: clean @echo creating dist tarball @mkdir -p dvtm-${VERSION} - @cp -R LICENSE Makefile README config.h config.mk \ - ${SRC} tile.c bstack.c tstack.c grid.c fullscreen.c \ - madtty.h statusbar.c mouse.c cmdfifo.c \ - dvtm-status dvtm.1 dvtm-${VERSION} + @cp -R LICENSE Makefile README testsuite.sh config.def.h config.mk \ + ${SRC} vt.h forkpty-aix.c tile.c bstack.c tstack.c vstack.c \ + grid.c fullscreen.c fibonacci.c dvtm-status dvtm.info dvtm.1 \ + dvtm-${VERSION} @tar -cf dvtm-${VERSION}.tar dvtm-${VERSION} @gzip dvtm-${VERSION}.tar @rm -rf dvtm-${VERSION} @@ -52,10 +55,13 @@ @mkdir -p ${DESTDIR}${MANPREFIX}/man1 @sed "s/VERSION/${VERSION}/g" < dvtm.1 > ${DESTDIR}${MANPREFIX}/man1/dvtm.1 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dvtm.1 + @echo installing terminfo description + @tic -o ${DESTDIR}${PREFIX}/share/terminfo -s dvtm.info uninstall: @echo removing executable file from ${DESTDIR}${PREFIX}/bin @rm -f ${DESTDIR}${PREFIX}/bin/dvtm + @rm -f ${DESTDIR}${PREFIX}/bin/dvtm-status @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 @rm -f ${DESTDIR}${MANPREFIX}/man1/dvtm.1 diff -Nru dvtm-0.6/mouse.c dvtm-0.12/mouse.c --- dvtm-0.6/mouse.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/mouse.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -static Client *msel = NULL; - -static void -mouse_focus(const char *args[]) { - focus(msel); - if (msel->minimized) - toggleminimize(NULL); -} - -static void -mouse_fullscreen(const char *args[]) { - mouse_focus(NULL); - if (isarrange(fullscreen)) - setlayout(NULL); - else - setlayout(args); -} - -static void -mouse_minimize(const char *args[]) { - focus(msel); - toggleminimize(NULL); -} - -static void -mouse_zoom(const char *args[]) { - focus(msel); - zoom(NULL); -} - -static Client* -get_client_by_coord(int x, int y) { - Client *c; - if (y < way || y >= wah) - return NULL; - if (isarrange(fullscreen)) - return sel; - for (c = clients; c; c = c->next) { - if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) { - debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order); - return c; - } - } - return NULL; -} - -static void -handle_mouse() { - MEVENT event; - unsigned int i; - if (getmouse(&event) != OK) - return; - msel = get_client_by_coord(event.x, event.y); - if (!msel) - return; - for (i = 0; i < countof(buttons); i++) - if (event.bstate & buttons[i].mask) - buttons[i].action.cmd(buttons[i].action.args); - msel = NULL; -} - -static void -mouse_setup() { - int i; - mmask_t mask; - for (i = 0, mask = 0; i < countof(buttons); i++) - mask |= buttons[i].mask; - if (mask) - mousemask(mask, NULL); -} - -static void -mouse_toggle() { - static int state = 0; - if(!state) - mousemask(0, NULL); - else - mouse_setup(); - state = !state; -} diff -Nru dvtm-0.6/README dvtm-0.12/README --- dvtm-0.6/README 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/README 2014-07-05 07:32:09.000000000 +0000 @@ -1,7 +1,7 @@ dvtm - dynamic virtual terminal manager ======================================= -dvtm brings dwm and it's concept of tiling window management to +dvtm brings dwm and it's concept of tiling window management to the console. See http://www.brain-dump.org/projects/dvtm for the latest version. diff -Nru dvtm-0.6/statusbar.c dvtm-0.12/statusbar.c --- dvtm-0.6/statusbar.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/statusbar.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -static int statusfd = -1; -static char stext[512]; -static int barpos = BARPOS; -static unsigned int bh = 1, by; - -static void -updatebarpos(void) { - by = 0; - wax = 0; - way = 0; - wah = height; - if (statusfd == -1) - return; - if (barpos == BarTop) { - wah -= bh; - way += bh; - } else if (barpos == BarBot) { - wah -= bh; - by = wah; - } -} - -static void -drawbar() { - wchar_t wbuf[sizeof stext]; - int w, maxwidth = width - 2; - if (barpos == BarOff || !*stext) - return; - curs_set(0); - attrset(BAR_ATTR); - madtty_color_set(stdscr, BAR_FG, BAR_BG); - mvaddch(by, 0, '['); - if (mbstowcs(wbuf, stext, sizeof stext) == -1) - return; - if ((w = wcswidth(wbuf, maxwidth)) == -1) - return; - if (BAR_ALIGN == ALIGN_RIGHT) { - for (int i = 0; i + w < maxwidth; i++) - addch(' '); - } - addstr(stext); - if (BAR_ALIGN == ALIGN_LEFT) { - for (; w < maxwidth; w++) - addch(' '); - } - mvaddch(by, width - 1, ']'); - attrset(NORMAL_ATTR); - if (sel) - curs_set(madtty_cursor(sel->term)); - refresh(); -} - -static void -togglebar(const char *args[]) { - if (barpos == BarOff) - barpos = (BARPOS == BarOff) ? BarTop : BARPOS; - else - barpos = BarOff; - updatebarpos(); - arrange(); - drawbar(); -} - -static void -handle_statusbar() { - char *p; - int r; - switch (r = read(statusfd, stext, sizeof stext - 1)) { - case -1: - strncpy(stext, strerror(errno), sizeof stext - 1); - stext[sizeof stext - 1] = '\0'; - statusfd = -1; - break; - case 0: - statusfd = -1; - break; - default: - stext[r] = '\0'; p = stext + strlen(stext) - 1; - for (; p >= stext && *p == '\n'; *p-- = '\0'); - for (; p >= stext && *p != '\n'; --p); - if (p > stext) - strncpy(stext, p + 1, sizeof stext); - drawbar(); - } -} diff -Nru dvtm-0.6/testsuite.sh dvtm-0.12/testsuite.sh --- dvtm-0.6/testsuite.sh 1970-01-01 00:00:00.000000000 +0000 +++ dvtm-0.12/testsuite.sh 2014-07-05 07:32:09.000000000 +0000 @@ -0,0 +1,55 @@ +#!/bin/sh + +MOD="" # CTRL+g +ESC="" # \e +DVTM="./dvtm" +LOG="dvtm.log" +TEST_LOG="$0.log" +UTF8_TEST_URL="http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt" + +[ ! -z "$1" ] && DVTM="$1" +[ ! -x "$DVTM" ] && echo "usage: $0 path-to-dvtm-binary" && exit 1 + +dvtm_input() { + printf "$1" +} + +dvtm_cmd() { + printf "${MOD}$1\n" + sleep 1 +} + +sh_cmd() { + printf "$1\n" + sleep 1 +} + +test_copymode() { # requires wget, diff, vi + local FILENAME="UTF-8-demo.txt" + [ ! -e "$FILENAME" ] && (wget "$UTF8_TEST_URL" -O "$FILENAME" > /dev/null 2>&1 || return 1) + sleep 1 + sh_cmd "cat $FILENAME" + dvtm_cmd 'v' + dvtm_input "?UTF-8 encoded\n" + dvtm_input '^kvGk$' + dvtm_input 'y' + rm -f "$FILENAME.copy" + sh_cmd "vi $FILENAME.copy" + dvtm_input 'i' + dvtm_cmd 'p' + dvtm_input "${ESC}dd:wq\n" + sleep 1 + dvtm_cmd 'q' + diff -u "$FILENAME" "$FILENAME.copy" 1>&2 + local RESULT=$? + rm "$FILENAME.copy" + return $RESULT +} + +{ + echo "Testing $DVTM" 1>&2 + $DVTM -v 1>&2 + test_copymode && echo "copymode: OK" 1>&2 || echo "copymode: FAIL" 1>&2; +} 2> "$TEST_LOG" | $DVTM -m ^g 2> $LOG + +cat "$TEST_LOG" && rm "$TEST_LOG" $LOG diff -Nru dvtm-0.6/tile.c dvtm-0.12/tile.c --- dvtm-0.6/tile.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/tile.c 2014-07-05 07:32:09.000000000 +0000 @@ -1,27 +1,27 @@ -static void -tile(void) { +static void tile(void) +{ unsigned int i, m, n, nx, ny, nw, nh, nm, mw, th; Client *c; - for(n = 0, m = 0, c = clients; c; c = c->next, n++) - if(c->minimized) + for (n = 0, m = 0, c = clients; c; c = c->next, n++) + if (c->minimized) m++; nm = n - m; /* window geoms */ - mw = (n == 1 || n - 1 == m) ? waw : mwfact * waw; + mw = (n == 1 || n - 1 == m) ? waw : screen.mfact * waw; /* check if there are at least 2 non minimized clients */ - if(n - 1 > m) + if (n - 1 > m) th = (wah - m) / (nm - 1); nx = wax; ny = way; - for(i = 0, c = clients; c; c = c->next, i++) { - if(i == 0) { /* master */ + for (i = 0, c = clients; c; c = c->next, i++) { + if (i == 0) { /* master */ nw = mw; nh = (n - 1 > m) ? wah : wah - m; - } else { /* tile window */ - if(!c->minimized){ - if(i == 1) { + } else { /* tile window */ + if (!c->minimized) { + if (i == 1) { ny = way; nx += mw; nw = waw - mw; @@ -30,9 +30,9 @@ nx++, nw--; } /* remainder */ - if(m == 0 && i + 1 == n) /* no minimized clients */ + if (m == 0 && i + 1 == n) /* no minimized clients */ nh = (way + wah) - ny; - else if(i == nm - 1) /* last not minimized client */ + else if (i == nm - 1) /* last not minimized client */ nh = (way + wah - (n - i - 1)) - ny; else nh = th; @@ -40,11 +40,11 @@ nh = 1; ny = way + wah - (n - i); } - if(i > 1 && nm > 1) + if (i > 1 && nm > 1) mvaddch(ny, nx - 1, ACS_LTEE); } - resize(c,nx,ny,nw,nh); - if(n > 1 && th != wah) + resize(c, nx, ny, nw, nh); + if (n > 1 && th != wah) ny += nh; } } diff -Nru dvtm-0.6/tstack.c dvtm-0.12/tstack.c --- dvtm-0.6/tstack.c 2010-10-08 16:06:05.000000000 +0000 +++ dvtm-0.12/tstack.c 2014-07-05 07:32:09.000000000 +0000 @@ -1,58 +1,54 @@ -void -tstack(void) { +void tstack(void) +{ unsigned int i, m, n, nx, ny, nw, nh, mh, tw; Client *c; - for(n = 0, m = 0, c = clients; c; c = c->next, n++) - if(c->minimized) + for (n = 0, m = 0, c = clients; c; c = c->next, n++) + if (c->minimized) m++; /* relative height */ - mh = (wah - m) * (n == 1 || n - 1 == m ? 1 : mwfact); + mh = (wah - m) * (n == 1 || n - 1 == m ? 1 : screen.mfact); /* true if there are at least 2 non minimized clients */ - if(n - 1 > m) + if (n - 1 > m) tw = waw / (n - m - 1); nx = wax, nw = waw; - for(i = 0, c = clients; c; c = c->next, i++){ - if(i == 0){ /* master */ + for (i = 0, c = clients; c; c = c->next, i++) { + if (i == 0) { /* master */ ny = way + wah - mh; nh = mh; - } - else { /* tile window */ - if(i == 1){ + } else { /* tile window */ + if (i == 1) { nx = wax; ny = way + m; nh = wah - mh - ny + way; } - if(i == n - m - 1){ /* last not minimized client */ + if (i == n - m - 1) { /* last not minimized client */ nw = (wax + waw) - nx; - } - else if(i == n - m) { /* first minimized client */ + } else if (i == n - m) { /* first minimized client */ nx = wax; --ny; nh = 1; nw = waw; - } - else if(c->minimized) { /* minimized window */ + } else if (c->minimized) { /* minimized window */ --ny; nh = 1; nw = waw; - } - else /* normal non minimized tile window */ + } else /* normal non minimized tile window */ nw = tw; - if(i > 1 && !c->minimized){ + if (i > 1 && !c->minimized) { mvvline(ny, nx, ACS_VLINE, nh); mvaddch(ny, nx, ACS_TTEE); ++nx, --nw; } } - resize(c,nx,ny,nw,nh); + resize(c, nx, ny, nw, nh); - if(n > 1 && i < n - m - 1) + if (n > 1 && i < n - m - 1) nx += nw; } } diff -Nru dvtm-0.6/vstack.c dvtm-0.12/vstack.c --- dvtm-0.6/vstack.c 1970-01-01 00:00:00.000000000 +0000 +++ dvtm-0.12/vstack.c 2014-07-05 07:32:09.000000000 +0000 @@ -0,0 +1,37 @@ +/* A vertical stack layout, all windows have the full screen width. */ +static void vstack(void) +{ + unsigned int i, m, n, ny, nh, mh, th; + Client *c; + + for (n = 0, m = 0, c = clients; c; c = c->next, n++) + if (c->minimized) + m++; + + if (n == 1) + mh = wah; + else if (n - 1 == m) + mh = wah - m; + else + mh = screen.mfact * (wah - m); + /* true if there are at least 2 non minimized clients */ + if (n - 1 > m) + th = (wah - mh) / (n - 1 - m); + + /* TODO: place all minimized windows on the last line */ + + ny = way; + for (i = 0, c = clients; c; c = c->next, i++) { + if (i == 0) { /* master */ + nh = mh; + } else if (i == n - m - 1) { /* last not minimized client */ + nh = (way + wah) - ny - m; + } else if (c->minimized) { + nh = 1; + } else { /* normal non minimized tile window */ + nh = th; + } + resize(c, wax, ny, waw, nh); + ny += nh; + } +} diff -Nru dvtm-0.6/vt.c dvtm-0.12/vt.c --- dvtm-0.6/vt.c 1970-01-01 00:00:00.000000000 +0000 +++ dvtm-0.12/vt.c 2014-07-05 07:32:09.000000000 +0000 @@ -0,0 +1,2454 @@ +/* + * Copyright © 2004 Bruno T. C. de Oliveira + * Copyright © 2006 Pierre Habouzit + * Copyright © 2008-2013 Marc André Tanner + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__linux__) || defined(__CYGWIN__) +# include +#elif defined(__FreeBSD__) +# include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) +# include +#endif +#if defined(__CYGWIN__) || defined(_AIX) +# include +#endif + +#include "vt.h" + +#ifdef _AIX +# include "forkpty-aix.c" +#endif + +#ifndef NCURSES_ATTR_SHIFT +# define NCURSES_ATTR_SHIFT 8 +#endif + +#ifndef NCURSES_ACS +# ifdef PDCURSES +# define NCURSES_ACS(c) (acs_map[(unsigned char)(c)]) +# else /* BSD curses */ +# define NCURSES_ACS(c) (_acs_map[(unsigned char)(c)]) +# endif +#endif + +#ifdef NCURSES_VERSION +# ifndef NCURSES_EXT_COLORS +# define NCURSES_EXT_COLORS 0 +# endif +# if !NCURSES_EXT_COLORS +# define MAX_COLOR_PAIRS 256 +# endif +#endif +#ifndef MAX_COLOR_PAIRS +# define MAX_COLOR_PAIRS COLOR_PAIRS +#endif + +#ifndef CTRL +# define CTRL(k) ((k) & 0x1F) +#endif + +#define IS_CONTROL(ch) !((ch) & 0xffffff60UL) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define sstrlen(str) (sizeof(str) - 1) + +#define COPYMODE_ATTR A_REVERSE +static bool is_utf8, has_default_colors; +static short color_pairs_reserved, color_pairs_max, color_pair_current; +static short *color2palette, default_fg, default_bg; +static char vt_term[32] = "dvtm"; + +typedef struct { + wchar_t *buf; + wchar_t *cursor; + wchar_t *end; + wchar_t *display; + int size; + int width; + int cursor_pos; + mbstate_t ps; + enum { + CMDLINE_INACTIVE = 0, + CMDLINE_INIT, + CMDLINE_ACTIVE, + } state; + void *data; + void (*callback)(void *data); + char prefix; +} Cmdline; + +typedef struct { + wchar_t text; + uint16_t attr; + short fg; + short bg; +} Cell; + +typedef struct { + Cell *cells; + unsigned dirty:1; +} Row; + +typedef struct { + Row *lines; + Row *curs_row; + Row *scroll_buf; + Row *scroll_top; + Row *scroll_bot; + bool *tabs; + int scroll_buf_size; + int scroll_buf_ptr; + int scroll_amount_above; + int scroll_amount_below; + int rows, cols, maxcols; + unsigned curattrs, savattrs; + int curs_col, curs_srow, curs_scol; + short curfg, curbg, savfg, savbg; +} Buffer; + +struct Vt { + Buffer buffer_normal; + Buffer buffer_alternate; + Buffer *buffer; + unsigned defattrs; + short deffg, defbg; + int pty; + pid_t childpid; + + /* flags */ + unsigned seen_input:1; + unsigned insert:1; + unsigned escaped:1; + unsigned curshid:1; + unsigned curskeymode:1; + unsigned copymode:1; + unsigned copymode_selecting:1; + unsigned bell:1; + unsigned relposmode:1; + unsigned mousetrack:1; + unsigned graphmode:1; + unsigned savgraphmode:1; + bool charsets[2]; + /* copymode */ + int copymode_curs_srow, copymode_curs_scol; + Row *copymode_sel_start_row; + int copymode_sel_start_col; + int copymode_cmd_multiplier; + Cmdline *cmdline; + /* buffers and parsing state */ + char rbuf[BUFSIZ]; + char ebuf[BUFSIZ]; + unsigned int rlen, elen; + /* last known start row, start column */ + int srow, scol; + + /* xterm style window title */ + char title[256]; + + vt_event_handler_t event_handler; + + /* custom escape sequence handler */ + vt_escseq_handler_t escseq_handler; + void *data; +}; + +static const char *keytable[KEY_MAX+1] = { + ['\n'] = "\r", + /* for the arrow keys the CSI / SS3 sequences are not stored here + * because they depend on the current cursor terminal mode + */ + [KEY_UP] = "A", + [KEY_DOWN] = "B", + [KEY_RIGHT] = "C", + [KEY_LEFT] = "D", +#ifdef KEY_SUP + [KEY_SUP] = "\e[1;2A", +#endif +#ifdef KEY_SDOWN + [KEY_SDOWN] = "\e[1;2B", +#endif + [KEY_SRIGHT] = "\e[1;2C", + [KEY_SLEFT] = "\e[1;2D", + [KEY_BACKSPACE] = "\177", + [KEY_IC] = "\e[2~", + [KEY_DC] = "\e[3~", + [KEY_PPAGE] = "\e[5~", + [KEY_NPAGE] = "\e[6~", + [KEY_HOME] = "\e[7~", + [KEY_END] = "\e[8~", + [KEY_BTAB] = "\e[Z", + [KEY_SUSPEND] = "\x1A", /* Ctrl+Z gets mapped to this */ + [KEY_F(1)] = "\e[11~", + [KEY_F(2)] = "\e[12~", + [KEY_F(3)] = "\e[13~", + [KEY_F(4)] = "\e[14~", + [KEY_F(5)] = "\e[15~", + [KEY_F(6)] = "\e[17~", + [KEY_F(7)] = "\e[18~", + [KEY_F(8)] = "\e[19~", + [KEY_F(9)] = "\e[20~", + [KEY_F(10)] = "\e[21~", + [KEY_F(11)] = "\e[23~", + [KEY_F(12)] = "\e[24~", + [KEY_F(13)] = "\e[23~", + [KEY_F(14)] = "\e[24~", + [KEY_F(15)] = "\e[25~", + [KEY_F(16)] = "\e[26~", + [KEY_F(17)] = "\e[28~", + [KEY_F(18)] = "\e[29~", + [KEY_F(19)] = "\e[31~", + [KEY_F(20)] = "\e[32~", + [KEY_F(21)] = "\e[33~", + [KEY_F(22)] = "\e[34~", + [KEY_RESIZE] = "", +#ifdef KEY_EVENT + [KEY_EVENT] = "", +#endif +}; + +static void puttab(Vt *t, int count); +static void process_nonprinting(Vt *t, wchar_t wc); +static void send_curs(Vt *t); +static void cmdline_hide_callback(void *t); +static void cmdline_free(Cmdline *c); + +static int xwcwidth(wchar_t c) { + int w = wcwidth(c); + if (w == -1) + w = 1; + return w; +} + +__attribute__ ((const)) +static uint16_t build_attrs(unsigned curattrs) +{ + return ((curattrs & ~A_COLOR) | COLOR_PAIR(curattrs & 0xff)) + >> NCURSES_ATTR_SHIFT; +} + +static void row_set(Row *row, int start, int len, Buffer *t) +{ + Cell cell = { + .text = L'\0', + .attr = t ? build_attrs(t->curattrs) : 0, + .fg = t ? t->curfg : -1, + .bg = t ? t->curbg : -1, + }; + + for (int i = start; i < len + start; i++) + row->cells[i] = cell; + row->dirty = true; +} + +static void row_roll(Row *start, Row *end, int count) +{ + int n = end - start; + + count %= n; + if (count < 0) + count += n; + + if (count) { + Row *buf = alloca(count * sizeof(Row)); + + memcpy(buf, start, count * sizeof(Row)); + memmove(start, start + count, (n - count) * sizeof(Row)); + memcpy(end - count, buf, count * sizeof(Row)); + for (Row *row = start; row < end; row++) + row->dirty = true; + } +} + +static void clamp_cursor_to_bounds(Vt *t) +{ + Buffer *b = t->buffer; + Row *lines = t->relposmode ? b->scroll_top : b->lines; + int rows = t->relposmode ? b->scroll_bot - b->scroll_top : b->rows; + + if (b->curs_row < lines) + b->curs_row = lines; + if (b->curs_row >= lines + rows) + b->curs_row = lines + rows - 1; + if (b->curs_col < 0) + b->curs_col = 0; + if (b->curs_col >= b->cols) + b->curs_col = b->cols - 1; +} + +static void save_curs(Vt *t) +{ + Buffer *b = t->buffer; + b->curs_srow = b->curs_row - b->lines; + b->curs_scol = b->curs_col; +} + +static void restore_curs(Vt *t) +{ + Buffer *b = t->buffer; + b->curs_row = b->lines + b->curs_srow; + b->curs_col = b->curs_scol; + clamp_cursor_to_bounds(t); +} + +static void save_attrs(Vt *t) +{ + Buffer *b = t->buffer; + b->savattrs = b->curattrs; + b->savfg = b->curfg; + b->savbg = b->curbg; + t->savgraphmode = t->graphmode; +} + +static void restore_attrs(Vt *t) +{ + Buffer *b = t->buffer; + b->curattrs = b->savattrs; + b->curfg = b->savfg; + b->curbg = b->savbg; + t->graphmode = t->savgraphmode; +} + +static void fill_scroll_buf(Buffer *t, int s) +{ + /* work in screenfuls */ + int ssz = t->scroll_bot - t->scroll_top; + if (s > ssz) { + fill_scroll_buf(t, ssz); + fill_scroll_buf(t, s - ssz); + return; + } + if (s < -ssz) { + fill_scroll_buf(t, -ssz); + fill_scroll_buf(t, s + ssz); + return; + } + + t->scroll_amount_above += s; + if (t->scroll_amount_above >= t->scroll_buf_size) + t->scroll_amount_above = t->scroll_buf_size; + + if (s > 0 && t->scroll_buf_size) { + for (int i = 0; i < s; i++) { + Row tmp = t->scroll_top[i]; + t->scroll_top[i] = t->scroll_buf[t->scroll_buf_ptr]; + t->scroll_buf[t->scroll_buf_ptr] = tmp; + + t->scroll_buf_ptr++; + if (t->scroll_buf_ptr == t->scroll_buf_size) + t->scroll_buf_ptr = 0; + } + } + row_roll(t->scroll_top, t->scroll_bot, s); + if (s < 0 && t->scroll_buf_size) { + for (int i = (-s) - 1; i >= 0; i--) { + t->scroll_buf_ptr--; + if (t->scroll_buf_ptr == -1) + t->scroll_buf_ptr = t->scroll_buf_size - 1; + + Row tmp = t->scroll_top[i]; + t->scroll_top[i] = t->scroll_buf[t->scroll_buf_ptr]; + t->scroll_buf[t->scroll_buf_ptr] = tmp; + t->scroll_top[i].dirty = true; + } + } +} + +static void cursor_line_down(Vt *t) +{ + Buffer *b = t->buffer; + row_set(b->curs_row, b->cols, b->maxcols - b->cols, NULL); + b->curs_row++; + if (b->curs_row < b->scroll_bot) + return; + + vt_noscroll(t); + + b->curs_row = b->scroll_bot - 1; + fill_scroll_buf(b, 1); + row_set(b->curs_row, 0, b->cols, b); +} + +static void new_escape_sequence(Vt *t) +{ + t->escaped = true; + t->elen = 0; + t->ebuf[0] = '\0'; +} + +static void cancel_escape_sequence(Vt *t) +{ + t->escaped = false; + t->elen = 0; + t->ebuf[0] = '\0'; +} + +static bool is_valid_csi_ender(int c) +{ + return (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c == '@' || c == '`'); +} + +/* interprets a 'set attribute' (SGR) CSI escape sequence */ +static void interpret_csi_sgr(Vt *t, int param[], int pcount) +{ + Buffer *b = t->buffer; + if (pcount == 0) { + /* special case: reset attributes */ + b->curattrs = A_NORMAL; + b->curfg = b->curbg = -1; + return; + } + + for (int i = 0; i < pcount; i++) { + switch (param[i]) { + case 0: + b->curattrs = A_NORMAL; + b->curfg = b->curbg = -1; + break; + case 1: + b->curattrs |= A_BOLD; + break; + case 4: + b->curattrs |= A_UNDERLINE; + break; + case 5: + b->curattrs |= A_BLINK; + break; + case 7: + b->curattrs |= A_REVERSE; + break; + case 8: + b->curattrs |= A_INVIS; + break; + case 22: + b->curattrs &= ~A_BOLD; + break; + case 24: + b->curattrs &= ~A_UNDERLINE; + break; + case 25: + b->curattrs &= ~A_BLINK; + break; + case 27: + b->curattrs &= ~A_REVERSE; + break; + case 28: + b->curattrs &= ~A_INVIS; + break; + case 30 ... 37: /* fg */ + b->curfg = param[i] - 30; + break; + case 38: + if ((i + 2) < pcount && param[i + 1] == 5) { + b->curfg = param[i + 2]; + i += 2; + } + break; + case 39: + b->curfg = -1; + break; + case 40 ... 47: /* bg */ + b->curbg = param[i] - 40; + break; + case 48: + if ((i + 2) < pcount && param[i + 1] == 5) { + b->curbg = param[i + 2]; + i += 2; + } + break; + case 49: + b->curbg = -1; + break; + case 90 ... 97: /* hi fg */ + b->curfg = param[i] - 82; + break; + case 100 ... 107: /* hi bg */ + b->curbg = param[i] - 92; + break; + default: + break; + } + } +} + +/* interprets an 'erase display' (ED) escape sequence */ +static void interpret_csi_ed(Vt *t, int param[], int pcount) +{ + Row *row, *start, *end; + Buffer *b = t->buffer; + + save_attrs(t); + b->curattrs = A_NORMAL; + b->curfg = b->curbg = -1; + + if (pcount && param[0] == 2) { + start = b->lines; + end = b->lines + b->rows; + } else if (pcount && param[0] == 1) { + start = b->lines; + end = b->curs_row; + row_set(b->curs_row, 0, b->curs_col + 1, b); + } else { + row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b); + start = b->curs_row + 1; + end = b->lines + b->rows; + } + + for (row = start; row < end; row++) + row_set(row, 0, b->cols, b); + + restore_attrs(t); +} + +/* interprets a 'move cursor' (CUP) escape sequence */ +static void interpret_csi_cup(Vt *t, int param[], int pcount) +{ + Buffer *b = t->buffer; + Row *lines = t->relposmode ? b->scroll_top : b->lines; + + if (pcount == 0) { + b->curs_row = lines; + b->curs_col = 0; + } else if (pcount == 1) { + b->curs_row = lines + param[0] - 1; + b->curs_col = 0; + } else { + b->curs_row = lines + param[0] - 1; + b->curs_col = param[1] - 1; + } + + clamp_cursor_to_bounds(t); +} + +/* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL, + * CPL, CHA, HPR, VPA, VPR, HPA */ +static void interpret_csi_c(Vt *t, char verb, int param[], int pcount) +{ + Buffer *b = t->buffer; + int n = (pcount && param[0] > 0) ? param[0] : 1; + + switch (verb) { + case 'A': + b->curs_row -= n; + break; + case 'B': + case 'e': + b->curs_row += n; + break; + case 'C': + case 'a': + b->curs_col += n; + break; + case 'D': + b->curs_col -= n; + break; + case 'E': + b->curs_row += n; + b->curs_col = 0; + break; + case 'F': + b->curs_row -= n; + b->curs_col = 0; + break; + case 'G': + case '`': + b->curs_col = param[0] - 1; + break; + case 'd': + b->curs_row = b->lines + param[0] - 1; + break; + } + + clamp_cursor_to_bounds(t); +} + +/* Interpret the 'erase line' escape sequence */ +static void interpret_csi_el(Vt *t, int param[], int pcount) +{ + Buffer *b = t->buffer; + switch (pcount ? param[0] : 0) { + case 1: + row_set(b->curs_row, 0, b->curs_col + 1, b); + break; + case 2: + row_set(b->curs_row, 0, b->cols, b); + break; + default: + row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b); + break; + } +} + +/* Interpret the 'insert blanks' sequence (ICH) */ +static void interpret_csi_ich(Vt *t, int param[], int pcount) +{ + Buffer *b = t->buffer; + Row *row = b->curs_row; + int n = (pcount && param[0] > 0) ? param[0] : 1; + + if (b->curs_col + n > b->cols) + n = b->cols - b->curs_col; + + for (int i = b->cols - 1; i >= b->curs_col + n; i--) + row->cells[i] = row->cells[i - n]; + + row_set(row, b->curs_col, n, b); +} + +/* Interpret the 'delete chars' sequence (DCH) */ +static void interpret_csi_dch(Vt *t, int param[], int pcount) +{ + Buffer *b = t->buffer; + Row *row = b->curs_row; + int n = (pcount && param[0] > 0) ? param[0] : 1; + + if (b->curs_col + n > b->cols) + n = b->cols - b->curs_col; + + for (int i = b->curs_col; i < b->cols - n; i++) + row->cells[i] = row->cells[i + n]; + + row_set(row, b->cols - n, n, b); +} + +/* Interpret an 'insert line' sequence (IL) */ +static void interpret_csi_il(Vt *t, int param[], int pcount) +{ + Buffer *b = t->buffer; + int n = (pcount && param[0] > 0) ? param[0] : 1; + + if (b->curs_row + n >= b->scroll_bot) { + for (Row *row = b->curs_row; row < b->scroll_bot; row++) + row_set(row, 0, b->cols, b); + } else { + row_roll(b->curs_row, b->scroll_bot, -n); + for (Row *row = b->curs_row; row < b->curs_row + n; row++) + row_set(row, 0, b->cols, b); + } +} + +/* Interpret a 'delete line' sequence (DL) */ +static void interpret_csi_dl(Vt *t, int param[], int pcount) +{ + Buffer *b = t->buffer; + int n = (pcount && param[0] > 0) ? param[0] : 1; + + if (b->curs_row + n >= b->scroll_bot) { + for (Row *row = b->curs_row; row < b->scroll_bot; row++) + row_set(row, 0, b->cols, b); + } else { + row_roll(b->curs_row, b->scroll_bot, n); + for (Row *row = b->scroll_bot - n; row < b->scroll_bot; row++) + row_set(row, 0, b->cols, b); + } +} + +/* Interpret an 'erase characters' (ECH) sequence */ +static void interpret_csi_ech(Vt *t, int param[], int pcount) +{ + Buffer *b = t->buffer; + int n = (pcount && param[0] > 0) ? param[0] : 1; + + if (b->curs_col + n > b->cols) + n = b->cols - b->curs_col; + + row_set(b->curs_row, b->curs_col, n, b); +} + +/* Interpret a 'set scrolling region' (DECSTBM) sequence */ +static void interpret_csi_decstbm(Vt *t, int param[], int pcount) +{ + Buffer *b = t->buffer; + int new_top, new_bot; + + switch (pcount) { + case 0: + b->scroll_top = b->lines; + b->scroll_bot = b->lines + b->rows; + break; + case 2: + new_top = param[0] - 1; + new_bot = param[1]; + + /* clamp to bounds */ + if (new_top < 0) + new_top = 0; + if (new_top >= b->rows) + new_top = b->rows - 1; + if (new_bot < 0) + new_bot = 0; + if (new_bot >= b->rows) + new_bot = b->rows; + + /* check for range validity */ + if (new_top < new_bot) { + b->scroll_top = b->lines + new_top; + b->scroll_bot = b->lines + new_bot; + } + break; + default: + return; /* malformed */ + } + b->curs_row = b->scroll_top; + b->curs_col = 0; +} + +static void interpret_csi_mode(Vt *t, int param[], int pcount, bool set) +{ + for (int i = 0; i < pcount; i++) { + switch (param[i]) { + case 4: /* insert/replace mode */ + t->insert = set; + break; + } + } +} + +static void interpret_csi_priv_mode(Vt *t, int param[], int pcount, bool set) +{ + for (int i = 0; i < pcount; i++) { + switch (param[i]) { + case 1: /* set application/normal cursor key mode (DECCKM) */ + t->curskeymode = set; + break; + case 6: /* set origin to relative/absolute (DECOM) */ + t->relposmode = set; + break; + case 25: /* make cursor visible/invisible (DECCM) */ + t->curshid = !set; + break; + case 47: /* use alternate/normal screen buffer */ + vt_copymode_leave(t); + t->buffer = set ? &t->buffer_alternate : &t->buffer_normal; + vt_dirty(t); + break; + case 1000: /* enable/disable normal mouse tracking */ + t->mousetrack = set; + break; + } + } +} + +static void interpret_csi(Vt *t) +{ + static int csiparam[BUFSIZ]; + Buffer *b = t->buffer; + int param_count = 0; + const char *p = t->ebuf + 1; + char verb = t->ebuf[t->elen - 1]; + + /* parse numeric parameters */ + for (p += (t->ebuf[1] == '?'); *p; p++) { + if (IS_CONTROL(*p)) { + process_nonprinting(t, *p); + } else if (*p == ';') { + if (param_count >= (int)sizeof(csiparam)) + return; /* too long! */ + csiparam[param_count++] = 0; + } else if (isdigit((unsigned char)*p)) { + if (param_count == 0) + csiparam[param_count++] = 0; + csiparam[param_count - 1] *= 10; + csiparam[param_count - 1] += *p - '0'; + } + } + + if (t->ebuf[1] == '?') { + switch (verb) { + case 'h': + case 'l': /* private set/reset mode */ + interpret_csi_priv_mode(t, csiparam, param_count, verb == 'h'); + break; + } + return; + } + + /* delegate handling depending on command character (verb) */ + switch (verb) { + case 'h': + case 'l': /* set/reset mode */ + interpret_csi_mode(t, csiparam, param_count, verb == 'h'); + break; + case 'm': /* set attribute */ + interpret_csi_sgr(t, csiparam, param_count); + break; + case 'J': /* erase display */ + interpret_csi_ed(t, csiparam, param_count); + break; + case 'H': + case 'f': /* move cursor */ + interpret_csi_cup(t, 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 '`': /* relative move */ + interpret_csi_c(t, verb, csiparam, param_count); + break; + case 'K': /* erase line */ + interpret_csi_el(t, csiparam, param_count); + break; + case '@': /* insert characters */ + interpret_csi_ich(t, csiparam, param_count); + break; + case 'P': /* delete characters */ + interpret_csi_dch(t, csiparam, param_count); + break; + case 'L': /* insert lines */ + interpret_csi_il(t, csiparam, param_count); + break; + case 'M': /* delete lines */ + interpret_csi_dl(t, csiparam, param_count); + break; + case 'X': /* erase chars */ + interpret_csi_ech(t, csiparam, param_count); + break; + case 'S': /* SU: scroll up */ + vt_scroll(t, param_count ? -csiparam[0] : -1); + break; + case 'T': /* SD: scroll down */ + vt_scroll(t, param_count ? csiparam[0] : 1); + break; + case 'Z': /* CBT: cursor backward tabulation */ + puttab(t, param_count ? -csiparam[0] : -1); + break; + case 'g': /* TBC: tabulation clear */ + switch (csiparam[0]) { + case 0: + b->tabs[b->curs_col] = false; + break; + case 3: + memset(b->tabs, 0, sizeof(*b->tabs) * b->maxcols); + break; + } + case 'r': /* set scrolling region */ + interpret_csi_decstbm(t, csiparam, param_count); + break; + case 's': /* save cursor location */ + save_curs(t); + break; + case 'u': /* restore cursor location */ + restore_curs(t); + break; + case 'n': /* query cursor location */ + if (param_count == 1 && csiparam[0] == 6) + send_curs(t); + break; + default: + break; + } +} + +/* Interpret an 'index' (IND) sequence */ +static void interpret_csi_ind(Vt *t) +{ + Buffer *b = t->buffer; + if (b->curs_row < b->lines + b->rows - 1) + b->curs_row++; +} + +/* Interpret a 'reverse index' (RI) sequence */ +static void interpret_csi_ri(Vt *t) +{ + Buffer *b = t->buffer; + if (b->curs_row > b->scroll_top) + b->curs_row--; + else { + row_roll(b->scroll_top, b->scroll_bot, -1); + row_set(b->scroll_top, 0, b->cols, b); + } +} + +/* Interpret a 'next line' (NEL) sequence */ +static void interpret_csi_nel(Vt *t) +{ + Buffer *b = t->buffer; + if (b->curs_row < b->lines + b->rows - 1) { + b->curs_row++; + b->curs_col = 0; + } +} + +/* Interpret a 'select character set' (SCS) sequence */ +static void interpret_csi_scs(Vt *t) +{ + /* ESC ( sets G0, ESC ) sets G1 */ + t->charsets[!!(t->ebuf[0] == ')')] = (t->ebuf[1] == '0'); + t->graphmode = t->charsets[0]; +} + +/* Interpret xterm specific escape sequences */ +static void interpret_esc_xterm(Vt *t) +{ + /* ESC]n;dataBEL -- the ESC is not part of t->ebuf */ + char *title = NULL; + + switch (t->ebuf[1]) { + case '0': + case '2': + t->ebuf[t->elen - 1] = '\0'; + if (t->elen > sstrlen("]n;\a")) + title = t->ebuf + sstrlen("]n;"); + + if (t->event_handler) + t->event_handler(t, VT_EVENT_TITLE, title); + } +} + +static void try_interpret_escape_seq(Vt *t) +{ + char lastchar = t->ebuf[t->elen - 1]; + + if (!*t->ebuf) + return; + + if (t->escseq_handler) { + switch ((*(t->escseq_handler)) (t, t->ebuf)) { + case VT_ESCSEQ_HANDLER_OK: + cancel_escape_sequence(t); + return; + case VT_ESCSEQ_HANDLER_NOTYET: + if (t->elen + 1 >= sizeof(t->ebuf)) + goto cancel; + return; + } + } + + switch (*t->ebuf) { + case '#': /* ignore DECDHL, DECSWL, DECDWL, DECHCP, DECFPP */ + if (t->elen == 2) { + if (lastchar == '8') { /* DECALN */ + interpret_csi_ed(t, (int []){ 2 }, 1); + goto handled; + } + goto cancel; + } + break; + case '(': + case ')': + if (t->elen == 2) { + interpret_csi_scs(t); + goto handled; + } + break; + case ']': /* xterm thing */ + if (lastchar == '\a' || + (lastchar == '\\' && t->elen >= 2 && t->ebuf[t->elen - 2] == '\e')) { + interpret_esc_xterm(t); + goto handled; + } + break; + case '[': + if (is_valid_csi_ender(lastchar)) { + interpret_csi(t); + goto handled; + } + break; + case '7': /* DECSC: save cursor and attributes */ + save_attrs(t); + save_curs(t); + goto handled; + case '8': /* DECRC: restore cursor and attributes */ + restore_attrs(t); + restore_curs(t); + goto handled; + case 'D': /* IND: index */ + interpret_csi_ind(t); + goto handled; + case 'M': /* RI: reverse index */ + interpret_csi_ri(t); + goto handled; + case 'E': /* NEL: next line */ + interpret_csi_nel(t); + goto handled; + case 'H': /* HTS: horizontal tab set */ + t->buffer->tabs[t->buffer->curs_col] = true; + goto handled; + default: + goto cancel; + } + + if (t->elen + 1 >= sizeof(t->ebuf)) { +cancel: +#ifndef NDEBUG + fprintf(stderr, "cancelled: \\033"); + for (unsigned int i = 0; i < t->elen; i++) { + if (isprint(t->ebuf[i])) { + fputc(t->ebuf[i], stderr); + } else { + fprintf(stderr, "\\%03o", t->ebuf[i]); + } + } + fputc('\n', stderr); +#endif +handled: + cancel_escape_sequence(t); + } +} + +static void puttab(Vt *t, int count) +{ + Buffer *b = t->buffer; + int direction = count >= 0 ? 1 : -1; + int col = b->curs_col + direction; + while (count) { + if (col < 0) { + b->curs_col = 0; + break; + } + if (col >= b->cols) { + b->curs_col = b->cols - 1; + break; + } + if (b->tabs[col]) { + b->curs_col = col; + count -= direction; + } + col += direction; + } +} + +static void process_nonprinting(Vt *t, wchar_t wc) +{ + Buffer *b = t->buffer; + switch (wc) { + case '\e': /* ESC */ + new_escape_sequence(t); + break; + case '\a': /* BEL */ + if (t->bell) + beep(); + break; + case '\b': /* BS */ + if (b->curs_col > 0) + b->curs_col--; + break; + case '\t': /* HT */ + puttab(t, 1); + break; + case '\r': /* CR */ + b->curs_col = 0; + break; + case '\v': /* VT */ + case '\f': /* FF */ + case '\n': /* LF */ + cursor_line_down(t); + break; + case '\016': /* SO: shift out, invoke the G1 character set */ + t->graphmode = t->charsets[1]; + break; + case '\017': /* SI: shift in, invoke the G0 character set */ + t->graphmode = t->charsets[0]; + break; + } +} + +static void is_utf8_locale(void) +{ + const char *cset = nl_langinfo(CODESET); + if (!cset) + cset = "ANSI_X3.4-1968"; + is_utf8 = !strcmp(cset, "UTF-8"); +} + +static wchar_t get_vt100_graphic(char c) +{ + static char vt100_acs[] = "`afgjklmnopqrstuvwxyz{|}~"; + + /* + * 5f-7e standard vt100 + * 40-5e rxvt extension for extra curses acs chars + */ + static uint16_t const vt100_utf8[62] = { + 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47 + 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f + 0, 0, 0, 0, 0, 0, 0, 0, // 50-57 + 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f + 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67 + 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f + 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77 + 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e + }; + + if (is_utf8) + return vt100_utf8[c - 0x41]; + else if (strchr(vt100_acs, c)) + return NCURSES_ACS(c); + return '\0'; +} + +static void put_wc(Vt *t, wchar_t wc) +{ + int width = 0; + + if (!t->seen_input) { + t->seen_input = 1; + kill(-t->childpid, SIGWINCH); + } + + if (t->escaped) { + if (t->elen + 1 < sizeof(t->ebuf)) { + t->ebuf[t->elen] = wc; + t->ebuf[++t->elen] = '\0'; + try_interpret_escape_seq(t); + } else { + cancel_escape_sequence(t); + } + } else if (IS_CONTROL(wc)) { + process_nonprinting(t, wc); + } else { + if (t->graphmode) { + if (wc >= 0x41 && wc <= 0x7e) { + wchar_t gc = get_vt100_graphic(wc); + if (gc) + wc = gc; + } + width = 1; + } else if ((width = wcwidth(wc)) < 1) { + width = 1; + } + Buffer *b = t->buffer; + Cell blank_cell = { L'\0', build_attrs(b->curattrs), b->curfg, b->curbg }; + if (width == 2 && b->curs_col == b->cols - 1) { + b->curs_row->cells[b->curs_col++] = blank_cell; + b->curs_row->dirty = true; + } + + if (b->curs_col >= b->cols) { + b->curs_col = 0; + cursor_line_down(t); + } + + if (t->insert) { + Cell *src = b->curs_row->cells + b->curs_col; + Cell *dest = src + width; + size_t len = b->cols - b->curs_col - width; + memmove(dest, src, len * sizeof *dest); + } + + b->curs_row->cells[b->curs_col] = blank_cell; + b->curs_row->cells[b->curs_col++].text = wc; + b->curs_row->dirty = true; + if (width == 2) + b->curs_row->cells[b->curs_col++] = blank_cell; + } +} + +int vt_process(Vt *t) +{ + int res; + unsigned int pos = 0; + mbstate_t ps; + memset(&ps, 0, sizeof(ps)); + + if (t->pty < 0) { + errno = EINVAL; + return -1; + } + + res = read(t->pty, t->rbuf + t->rlen, sizeof(t->rbuf) - t->rlen); + if (res < 0) + return -1; + + t->rlen += res; + while (pos < t->rlen) { + wchar_t wc; + ssize_t len; + + len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &ps); + if (len == -2) { + t->rlen -= pos; + memmove(t->rbuf, t->rbuf + pos, t->rlen); + return 0; + } + + if (len == -1) { + len = 1; + wc = t->rbuf[pos]; + } + + pos += len ? len : 1; + put_wc(t, wc); + } + + t->rlen -= pos; + memmove(t->rbuf, t->rbuf + pos, t->rlen); + return 0; +} + +void vt_set_default_colors(Vt *t, unsigned attrs, short fg, short bg) +{ + t->defattrs = attrs; + t->deffg = fg; + t->defbg = bg; +} + +static void buffer_free(Buffer *t) +{ + for (int i = 0; i < t->rows; i++) + free(t->lines[i].cells); + free(t->lines); + for (int i = 0; i < t->scroll_buf_size; i++) + free(t->scroll_buf[i].cells); + free(t->scroll_buf); + free(t->tabs); +} + +static bool buffer_init(Buffer *t, int rows, int cols, int scroll_buf_size) +{ + Row *lines, *scroll_buf; + t->lines = lines = calloc(rows, sizeof(Row)); + if (!lines) + return false; + t->curattrs = A_NORMAL; /* white text over black background */ + t->curfg = t->curbg = -1; + for (Row *row = lines, *end = lines + rows; row < end; row++) { + row->cells = malloc(cols * sizeof(Cell)); + if (!row->cells) { + t->rows = row - lines; + goto fail; + } + row_set(row, 0, cols, NULL); + } + t->rows = rows; + if (scroll_buf_size < 0) + scroll_buf_size = 0; + t->scroll_buf = scroll_buf = calloc(scroll_buf_size, sizeof(Row)); + if (!scroll_buf && scroll_buf_size) + goto fail; + for (Row *row = scroll_buf, *end = scroll_buf + scroll_buf_size; row < end; row++) { + row->cells = calloc(cols, sizeof(Cell)); + if (!row->cells) { + t->scroll_buf_size = row - scroll_buf; + goto fail; + } + } + t->tabs = calloc(cols, sizeof(*t->tabs)); + if (!t->tabs) + goto fail; + for (int col = 8; col < cols; col += 8) + t->tabs[col] = true; + t->curs_row = lines; + t->curs_col = 0; + /* initial scrolling area is the whole window */ + t->scroll_top = lines; + t->scroll_bot = lines + rows; + t->scroll_buf_size = scroll_buf_size; + t->maxcols = t->cols = cols; + return true; + +fail: + buffer_free(t); + return false; +} + +Vt *vt_create(int rows, int cols, int scroll_buf_size) +{ + Vt *t; + + if (rows <= 0 || cols <= 0) + return NULL; + + t = calloc(1, sizeof(Vt)); + if (!t) + return NULL; + + t->pty = -1; + t->deffg = t->defbg = -1; + if (!buffer_init(&t->buffer_normal, rows, cols, scroll_buf_size) || + !buffer_init(&t->buffer_alternate, rows, cols, 0)) { + free(t); + return NULL; + } + t->buffer = &t->buffer_normal; + t->copymode_cmd_multiplier = 0; + return t; +} + +static void buffer_resize(Buffer *t, int rows, int cols) +{ + Row *lines = t->lines; + + if (t->rows != rows) { + if (t->curs_row > lines + rows) { + /* scroll up instead of simply chopping off bottom */ + fill_scroll_buf(t, (t->curs_row - t->lines) - rows + 1); + } + while (t->rows > rows) { + free(lines[t->rows - 1].cells); + t->rows--; + } + + lines = realloc(lines, sizeof(Row) * rows); + } + + if (t->maxcols < cols) { + for (int row = 0; row < t->rows; row++) { + lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols); + if (t->cols < cols) + row_set(lines + row, t->cols, cols - t->cols, NULL); + lines[row].dirty = true; + } + Row *sbuf = t->scroll_buf; + for (int row = 0; row < t->scroll_buf_size; row++) { + sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols); + if (t->cols < cols) + row_set(sbuf + row, t->cols, cols - t->cols, NULL); + } + t->tabs = realloc(t->tabs, sizeof(*t->tabs) * cols); + for (int col = t->cols; col < cols; col++) + t->tabs[col] = !(col & 7); + t->maxcols = cols; + t->cols = cols; + } else if (t->cols != cols) { + for (int row = 0; row < t->rows; row++) + lines[row].dirty = true; + t->cols = cols; + } + + int deltarows = 0; + if (t->rows < rows) { + while (t->rows < rows) { + lines[t->rows].cells = calloc(t->maxcols, sizeof(Cell)); + row_set(lines + t->rows, 0, t->maxcols, t); + t->rows++; + } + + /* prepare for backfill */ + if (t->curs_row >= t->scroll_bot - 1) { + deltarows = t->lines + rows - t->curs_row - 1; + if (deltarows > t->scroll_amount_above) + deltarows = t->scroll_amount_above; + } + } + + t->curs_row += lines - t->lines; + t->scroll_top = lines; + t->scroll_bot = lines + rows; + t->lines = lines; + + /* perform backfill */ + if (deltarows > 0) { + fill_scroll_buf(t, -deltarows); + t->curs_row += deltarows; + } +} + +void vt_resize(Vt *t, int rows, int cols) +{ + struct winsize ws = { .ws_row = rows, .ws_col = cols }; + + if (rows <= 0 || cols <= 0) + return; + + vt_noscroll(t); + if (t->copymode) + vt_copymode_leave(t); + buffer_resize(&t->buffer_normal, rows, cols); + buffer_resize(&t->buffer_alternate, rows, cols); + clamp_cursor_to_bounds(t); + ioctl(t->pty, TIOCSWINSZ, &ws); + kill(-t->childpid, SIGWINCH); +} + +void vt_destroy(Vt *t) +{ + if (!t) + return; + buffer_free(&t->buffer_normal); + buffer_free(&t->buffer_alternate); + cmdline_free(t->cmdline); + close(t->pty); + free(t); +} + +void vt_dirty(Vt *t) +{ + Buffer *b = t->buffer; + for (Row *row = b->lines, *end = row + b->rows; row < end; row++) + row->dirty = true; +} + +static void copymode_get_selection_boundry(Vt *t, Row **start_row, int *start_col, Row **end_row, int *end_col, bool clip) { + Buffer *b = t->buffer; + if (t->copymode_sel_start_row >= b->lines && t->copymode_sel_start_row < b->lines + b->rows) { + /* within the current page */ + if (b->curs_row >= t->copymode_sel_start_row) { + *start_row = t->copymode_sel_start_row; + *end_row = b->curs_row; + *start_col = t->copymode_sel_start_col; + *end_col = b->curs_col; + } else { + *start_row = b->curs_row; + *end_row = t->copymode_sel_start_row; + *start_col = b->curs_col; + *end_col = t->copymode_sel_start_col; + } + if (b->curs_col < *start_col && *start_row == *end_row) { + *start_col = b->curs_col; + *end_col = t->copymode_sel_start_col; + } + } else { + /* part of the scrollback buffer is also selected */ + if (t->copymode_sel_start_row < b->lines) { + /* above the current page */ + if (clip) { + *start_row = b->lines; + *start_col = 0; + } else { + int copied_lines = b->lines - t->copymode_sel_start_row; + *start_row = &b->scroll_buf + [(b->scroll_buf_ptr - copied_lines + b->scroll_buf_size) % b->scroll_buf_size]; + *start_col = t->copymode_sel_start_col; + } + *end_row = b->curs_row; + *end_col = b->curs_col; + } else { + /* below the current page */ + *start_row = b->curs_row; + *start_col = b->curs_col; + if (clip) { + *end_row = b->lines + b->rows; + *end_col = b->cols - 1; + } else { + int copied_lines = t->copymode_sel_start_row -(b->lines + b->rows); + *end_row = &b->scroll_buf + [(b->scroll_buf_ptr + copied_lines) % b->scroll_buf_size]; + *end_col = t->copymode_sel_start_col; + } + } + } +} + +void vt_draw(Vt *t, WINDOW * win, int srow, int scol) +{ + Buffer *b = t->buffer; + bool sel = false; + Row *sel_row_start, *sel_row_end; + int sel_col_start, sel_col_end; + + if (srow != t->srow || scol != t->scol) { + vt_dirty(t); + t->srow = srow; + t->scol = scol; + } + + copymode_get_selection_boundry(t, &sel_row_start, &sel_col_start, &sel_row_end, &sel_col_end, true); + + for (int i = 0; i < b->rows; i++) { + Row *row = b->lines + i; + + if (!row->dirty) + continue; + + wmove(win, srow + i, scol); + Cell *cell = NULL; + for (int j = 0; j < b->cols; j++) { + Cell *prev_cell = cell; + cell = row->cells + j; + if (!prev_cell || cell->attr != prev_cell->attr + || cell->fg != prev_cell->fg + || cell->bg != prev_cell->bg) { + if (cell->attr == A_NORMAL) + cell->attr = t->defattrs; + if (cell->fg == -1) + cell->fg = t->deffg; + if (cell->bg == -1) + cell->bg = t->defbg; + wattrset(win, (attr_t) cell->attr << NCURSES_ATTR_SHIFT); + wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), NULL); + } + + if (t->copymode_selecting && ((row > sel_row_start && row < sel_row_end) || + (row == sel_row_start && j >= sel_col_start && (row != sel_row_end || j <= sel_col_end)) || + (row == sel_row_end && j <= sel_col_end && (row != sel_row_start || j >= sel_col_start)))) { + wattrset(win, (attr_t) ((cell->attr << NCURSES_ATTR_SHIFT)|COPYMODE_ATTR)); + sel = true; + } else if (sel) { + wattrset(win, (attr_t) cell->attr << NCURSES_ATTR_SHIFT); + wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), NULL); + sel = false; + } + + if (is_utf8 && cell->text >= 128) { + char buf[MB_CUR_MAX + 1]; + size_t len = wcrtomb(buf, cell->text, NULL); + if (len > 0) { + waddnstr(win, buf, len); + if (wcwidth(cell->text) > 1) + j++; + } + } else { + waddch(win, cell->text > ' ' ? cell->text : ' '); + } + } + + int x, y; + getyx(win, y, x); + (void)y; + if (x && x < b->cols - 1) + whline(win, ' ', b->cols - x); + + row->dirty = false; + } + + wmove(win, srow + b->curs_row - b->lines, scol + b->curs_col); + + if (t->cmdline && t->cmdline->state) { + wattrset(win, t->defattrs << NCURSES_ATTR_SHIFT); + mvwaddch(win, srow + b->rows - 1, 0, t->cmdline->prefix); + whline(win, ' ', b->cols - 1); + if (t->cmdline->state == CMDLINE_ACTIVE) { + waddnwstr(win, t->cmdline->display, b->cols - 1); + wmove(win, srow + b->rows - 1, 1 + t->cmdline->cursor_pos); + } else + wmove(win, srow + b->rows - 1, 1); + } +} + +void vt_scroll(Vt *t, int rows) +{ + Buffer *b = t->buffer; + if (!b->scroll_buf_size) + return; + if (rows < 0) { /* scroll back */ + if (rows < -b->scroll_amount_above) + rows = -b->scroll_amount_above; + } else { /* scroll forward */ + if (rows > b->scroll_amount_below) + rows = b->scroll_amount_below; + } + fill_scroll_buf(b, rows); + b->scroll_amount_below -= rows; + if (t->copymode_selecting) + t->copymode_sel_start_row -= rows; +} + +void vt_noscroll(Vt *t) +{ + int scroll_amount_below = t->buffer->scroll_amount_below; + if (scroll_amount_below) + vt_scroll(t, scroll_amount_below); +} + +void vt_bell(Vt *t, bool bell) +{ + t->bell = bell; +} + +void vt_togglebell(Vt *t) +{ + t->bell = !t->bell; +} + +pid_t vt_forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *pty) +{ + struct winsize ws; + pid_t pid; + const char **envp = env; + int fd, maxfd; + + ws.ws_row = t->buffer->rows; + ws.ws_col = t->buffer->cols; + ws.ws_xpixel = ws.ws_ypixel = 0; + + pid = forkpty(&t->pty, NULL, NULL, &ws); + if (pid < 0) + return -1; + + if (pid == 0) { + setsid(); + + maxfd = sysconf(_SC_OPEN_MAX); + for (fd = 3; fd < maxfd; fd++) + if (close(fd) == -1 && errno == EBADF) + break; + + while (envp && envp[0]) { + setenv(envp[0], envp[1], 1); + envp += 2; + } + setenv("TERM", vt_term, 1); + if (cwd) + chdir(cwd); + execv(p, (char *const *)argv); + fprintf(stderr, "\nexecv() failed.\nCommand: '%s'\n", argv[0]); + exit(1); + } + + if (pty) + *pty = t->pty; + return t->childpid = pid; +} + +int vt_getpty(Vt *t) +{ + return t->pty; +} + +int vt_write(Vt *t, const char *buf, int len) +{ + int ret = len; + + while (len > 0) { + int res = write(t->pty, buf, len); + if (res < 0) { + if (errno != EAGAIN && errno != EINTR) + return -1; + else + continue; + } + buf += res; + len -= res; + } + + return ret; +} + +static void send_curs(Vt *t) +{ + Buffer *b = t->buffer; + char keyseq[16]; + snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->curs_row - b->lines), b->curs_col); + vt_write(t, keyseq, strlen(keyseq)); +} + +void vt_keypress(Vt *t, int keycode) +{ + vt_noscroll(t); + + if (keycode >= 0 && keycode <= KEY_MAX && keytable[keycode]) { + switch (keycode) { + case KEY_UP: + case KEY_DOWN: + case KEY_RIGHT: + case KEY_LEFT: { + char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] }; + vt_write(t, keyseq, sizeof keyseq); + break; + } + default: + vt_write(t, keytable[keycode], strlen(keytable[keycode])); + } + } else if (keycode <= UCHAR_MAX) { + char c = keycode; + vt_write(t, &c, 1); + } else { +#ifndef NDEBUG + fprintf(stderr, "unhandled key %#o\n", keycode); +#endif + } +} + +static Row *buffer_next_row(Buffer *t, Row *row, int direction) +{ + bool has_scroll_buf = t->scroll_buf_size > 0; + Row *before_start_row, *before_end_row, *after_start_row, *after_end_row; + Row *first_row = t->lines; + Row *last_row = t->lines + t->rows - 1; + + if (has_scroll_buf) { + before_end_row = &t->scroll_buf + [(t->scroll_buf_ptr - 1 + t->scroll_buf_size) % t->scroll_buf_size]; + before_start_row = &t->scroll_buf + [(t->scroll_buf_ptr - t->scroll_amount_above + t->scroll_buf_size) % t->scroll_buf_size]; + after_start_row = &t->scroll_buf[t->scroll_buf_ptr]; + after_end_row = &t->scroll_buf + [(t->scroll_buf_ptr + t->scroll_amount_below - 1) % t->scroll_buf_size]; + } + + if (direction > 0) { + if (row >= first_row && row < last_row) + return ++row; + if (row == last_row) { + if (has_scroll_buf) { + if (t->scroll_amount_below) + return after_start_row; + else if (t->scroll_amount_above) + return before_start_row; + } + return first_row; + } + if (row == before_end_row) + return first_row; + if (row == after_end_row) + return t->scroll_amount_above ? before_start_row : first_row; + if (row == &t->scroll_buf[t->scroll_buf_size - 1]) + return t->scroll_buf; + return ++row; + } else { + if (row > first_row && row <= last_row) + return --row; + if (row == first_row) { + if (has_scroll_buf) { + if (t->scroll_amount_above) + return before_end_row; + else if (t->scroll_amount_below) + return after_end_row; + } + return last_row; + } + if (row == before_start_row) + return t->scroll_amount_below ? after_end_row : last_row; + if (row == after_start_row) + return last_row; + if (row == t->scroll_buf) + return &t->scroll_buf[t->scroll_buf_size - 1]; + return --row; + } +} + +static void row_show(Vt *t, Row *r) +{ + Buffer *b = t->buffer; + int below = b->scroll_amount_below; + int above = b->scroll_amount_above; + int ptr = b->scroll_buf_ptr; + int size = b->scroll_buf_size; + int row = r - b->scroll_buf; + int scroll = 0; + + if (b->lines <= r && r < b->lines + b->rows) { + b->curs_row = r; + return; + } + + if (!size) + return; + + if (row < ptr) { + if (row - ptr + size < below) + scroll = row - ptr + size + 1; + else if (ptr - row <= above) + scroll = row - ptr; + } else { + if (row - ptr < below) + scroll = row - ptr + 1; + else if (ptr - row + size <= above) + scroll = row - ptr - size; + } + + if (scroll) { + vt_scroll(t, scroll); + b->curs_row = b->lines + (scroll > 0 ? b->rows - 1 : 0); + } +} + +static void copymode_search(Vt *t, int direction) +{ + wchar_t *searchbuf = t->cmdline ? t->cmdline->buf : NULL; + if (!searchbuf || *searchbuf == '\0') + return; + + Buffer *b = t->buffer; + /* avoid match at current cursor position */ + Row *start_row = b->curs_row; + int start_col = b->curs_col + direction; + if (start_col >= b->cols) { + start_col = 0; + start_row = buffer_next_row(b, start_row, 1); + } else if (start_col < 0) { + start_col = b->cols - 1; + start_row = buffer_next_row(b, start_row, -1); + } + + Row *row = start_row, *matched_row = NULL; + int matched_col = 0; + int len = wcslen(searchbuf) - 1; + int s_start = direction > 0 ? 0 : len; + int s_end = direction > 0 ? len : 0; + int s = s_start; + + for (;;) { + int col = direction > 0 ? 0 : b->cols - 1; + if (row == start_row) + col = start_col; + for (; col >= 0 && col < b->cols; col += direction) { + if (searchbuf[s] == row->cells[col].text) { + if (s == s_start) { + matched_row = row; + matched_col = col; + } + if (s == s_end) { + b->curs_col = matched_col; + if (matched_row) + row_show(t, matched_row); + return; + } + s += direction; + int width = wcwidth(searchbuf[s]); + if (width < 0) + width = 0; + else if (width >= 1) + width--; + col += direction > 0 ? width : -width; + } else if (searchbuf[s_start] == row->cells[col].text) { + s = s_start + direction; + matched_row = row; + matched_col = col; + } else { + s = s_start; + } + } + + if ((row = buffer_next_row(b, row, direction)) == start_row) + break; + } +} + +static void cmdline_hide_callback(void *t) +{ + Buffer *b = ((Vt *)t)->buffer; + b->lines[b->rows - 1].dirty = true; +} + +static void cmdline_show(Cmdline *c, char prefix, int width, void (*callback)(void *t), void *data) +{ + if (!c) + return; + c->callback = callback; + memset(&c->ps, 0, sizeof(mbstate_t)); + if (!c->buf) { + c->size = width+1; + c->buf = calloc(c->size, sizeof(wchar_t)); + c->cursor = c->end = c->display = c->buf; + } + if (!c->buf) + return; + c->state = CMDLINE_INIT; + c->data = data; + c->prefix = prefix; + c->width = width - (prefix ? 1 : 0); +} + +static void cmdline_hide(Cmdline *c) +{ + if (!c) + return; + c->state = CMDLINE_INACTIVE; + c->callback(c->data); +} + +static void cmdline_adjust_cursor_pos(Cmdline *c) +{ + int pos = 0, w; + for (wchar_t *cur = c->display; cur < c->cursor; cur++) + pos += xwcwidth(*cur); + int extraspace = pos - c->width + 1; + if (extraspace > 0) { + for (w = 0; w < extraspace; w += xwcwidth(*c->display++)); + c->cursor_pos = pos - w; + } else + c->cursor_pos = pos; +} + +static void cmdline_keypress(Cmdline *c, int keycode) +{ + if (keycode != KEY_UP && c->state == CMDLINE_INIT) { + c->buf[0] = L'\0'; + c->display = c->cursor = c->end = c->buf; + c->cursor_pos = 0; + } + char keychar = (char)keycode; + wchar_t wc = L'\0'; + int width = -1; + ssize_t len; + size_t n = (c->end - c->cursor) * sizeof(wchar_t); + switch (keycode) { + case KEY_DC: + if (c->cursor == c->end) /* n == 0 */ + break; + memmove(c->cursor, c->cursor + 1, n - sizeof(wchar_t)); + c->end--; + *c->end = L'\0'; + cmdline_adjust_cursor_pos(c); + break; + case KEY_BACKSPACE: + if (c->cursor == c->buf) + break; + memmove(c->cursor - 1, c->cursor, n); + if (c->end > c->buf) + c->end--; + *c->end = L'\0'; + case KEY_LEFT: + if (c->cursor > c->buf) + c->cursor--; + if (c->cursor_pos == 0) { + c->display -= c->width / 2; + if (c->display < c->buf) + c->display = c->buf; + } + cmdline_adjust_cursor_pos(c); + break; + case KEY_UP: + break; + case KEY_DOWN: + c->buf[0] = L'\0'; + c->end = c->buf; + case KEY_HOME: + c->display = c->cursor = c->buf; + c->cursor_pos = 0; + break; + case KEY_END: + c->cursor = c->end; + width = 0; + wchar_t *disp = c->end; + while (disp >= c->buf) { + int tmp = xwcwidth(*disp); + if (width + tmp > c->width - 1) + break; + width += tmp; + disp--; + } + c->display = disp >= c->buf ? disp + 1: c->buf; + c->cursor_pos = (width < c->width - 1) ? width : c->width - 1; + break; + case CTRL('a') ... CTRL('z'): + if (keycode != '\n') + break; + copymode_search(c->data, c->prefix == '/' ? 1 : -1); + case '\e': + cmdline_hide(c); + return; + default: + len = (ssize_t)mbrtowc(&wc, &keychar, 1, &c->ps); + if (len == -2) + return; + if (len == -1) + wc = keycode; + + width = xwcwidth(wc); + + if (c->end - c->buf >= c->size - 2) { + c->size *= 2; + wchar_t *buf = realloc(c->buf, c->size * sizeof(wchar_t)); + if (!buf) + return; + ptrdiff_t diff = buf - c->buf; + c->cursor += diff; + c->end += diff; + c->display += diff; + c->buf = buf; + } + + if (c->cursor < c->end) + memmove(c->cursor + 1, c->cursor, n); + *c->cursor = wc; + *(++c->end) = L'\0'; + case KEY_RIGHT: + if (c->cursor_pos == c->width - 1) { + if (c->cursor < c->end) + c->cursor++; + if (c->cursor != c->end) { + c->display += c->width / 2; + if (c->display > c->end) + c->display = c->buf; + } + cmdline_adjust_cursor_pos(c); + } else { + if (width == -1) + width = xwcwidth(*c->cursor); + c->cursor_pos += width; + if (c->cursor_pos > c->width - 1) + c->cursor_pos = c->width - 1; + if (c->cursor < c->end) + c->cursor++; + } + break; + } + c->state = CMDLINE_ACTIVE; +} + +static void cmdline_free(Cmdline *c) +{ + if (!c) + return; + free(c->buf); + free(c); +} + +void vt_copymode_keypress(Vt *t, int keycode) +{ + Buffer *b = t->buffer; + Row *start_row, *end_row; + int direction, col, start_col, end_col, delta, scroll_page = b->rows / 2; + char *copybuf, keychar = (char)keycode; + bool found; + + if (!t->copymode) + return; + + if (t->cmdline && t->cmdline->state) { + cmdline_keypress(t->cmdline, keycode); + } else { + switch (keycode) { + case '0': + if (t->copymode_cmd_multiplier == 0) { + b->curs_col = 0; + break; + } + /* fall through */ + case '1' ... '9': + t->copymode_cmd_multiplier = (t->copymode_cmd_multiplier * 10) + (keychar - '0'); + return; + case KEY_PPAGE: + case CTRL('u'): + delta = b->curs_row - b->lines; + if (delta > scroll_page) + b->curs_row -= scroll_page; + else { + b->curs_row = b->lines; + vt_scroll(t, delta - scroll_page); + } + break; + case KEY_NPAGE: + case CTRL('d'): + delta = b->rows - (b->curs_row - b->lines); + if (delta > scroll_page) + b->curs_row += scroll_page; + else { + b->curs_row = b->lines + b->rows - 1; + vt_scroll(t, scroll_page - delta); + } + break; + case 'g': + if (b->scroll_amount_above) + vt_scroll(t, -b->scroll_amount_above); + /* fall through */ + case 'H': + b->curs_row = b->lines; + break; + case 'M': + b->curs_row = b->lines + (b->rows / 2); + break; + case 'G': + vt_noscroll(t); + /* fall through */ + case 'L': + b->curs_row = b->lines + b->rows - 1; + break; + case KEY_HOME: + case '^': + b->curs_col = 0; + break; + case KEY_END: + case '$': + start_col = b->cols - 1; + for (int i = 0; i < b->cols; i++) + if (b->curs_row->cells[i].text) + start_col = i; + b->curs_col = start_col; + break; + case '/': + case '?': + if (!t->cmdline) + t->cmdline = calloc(1, sizeof(Cmdline)); + cmdline_show(t->cmdline, keycode, b->cols, cmdline_hide_callback, t); + break; + case 'n': + case 'N': + copymode_search(t, keycode == 'n' ? 1 : -1); + break; + case 'v': + t->copymode_selecting = true; + t->copymode_sel_start_row = b->curs_row; + t->copymode_sel_start_col = b->curs_col; + break; + case 'y': + if (!t->copymode_selecting) { + b->curs_col = 0; + t->copymode_sel_start_row = b->curs_row + + (t->copymode_cmd_multiplier ? t->copymode_cmd_multiplier - 1 : 0); + if (t->copymode_sel_start_row >= b->lines + b->rows) + t->copymode_sel_start_row = b->lines + b->rows - 1; + t->copymode_sel_start_col = b->cols - 1; + } + + copymode_get_selection_boundry(t, &start_row, &start_col, &end_row, &end_col, false); + int line_count = t->copymode_sel_start_row > b->curs_row ? + t->copymode_sel_start_row - b->curs_row : + b->curs_row - t->copymode_sel_start_row; + copybuf = calloc(1, (line_count + 1) * b->cols * MB_CUR_MAX + 1); + + if (copybuf) { + char *s = copybuf; + mbstate_t ps; + memset(&ps, 0, sizeof(ps)); + Row *row = start_row; + for (;;) { + char *last_non_space = s; + int j = (row == start_row) ? start_col : 0; + int col = (row == end_row) ? end_col : b->cols - 1; + for (size_t len = 0; j <= col; j++) { + if (row->cells[j].text) { + len = wcrtomb(s, row->cells[j].text, &ps); + if (len > 0) + s += len; + last_non_space = s; + } else if (len) { + len = 0; + } else { + *s++ = ' '; + } + } + + s = last_non_space; + + if (row == end_row) + break; + else + *s++ = '\n'; + + row = buffer_next_row(b, row, 1); + } + *s = '\0'; + if (t->event_handler) + t->event_handler(t, VT_EVENT_COPY_TEXT, copybuf); + } + /* fall through */ + case '\e': + case 'q': + case CTRL('c'): + vt_copymode_leave(t); + return; + default: + for (int c = 0; c < (t->copymode_cmd_multiplier ? t->copymode_cmd_multiplier : 1); c++) { + int width; + switch (keycode) { + case 'w': + case 'W': + case 'b': + case 'B': + direction = (keycode == 'w' || keycode == 'W') ? 1 : -1; + start_col = (direction > 0) ? 0 : b->cols - 1; + end_col = (direction > 0) ? b->cols - 1 : 0; + col = b->curs_col; + found = false; + do { + for (;;) { + if (b->curs_row->cells[col].text == ' ') { + found = true; + break; + } + + if (col == end_col) + break; + col += direction; + } + + if (found) { + while (b->curs_row->cells[col].text == ' ') { + if (col == end_col) { + b->curs_row += direction; + break; + } + col += direction; + } + } else { + col = start_col; + b->curs_row += direction; + } + + if (b->curs_row < b->lines) { + b->curs_row = b->lines; + if (b->scroll_amount_above) + vt_scroll(t, -1); + else + break; + } + + if (b->curs_row >= b->lines + b->rows) { + b->curs_row = b->lines + b->rows - 1; + if (b->scroll_amount_below) + vt_scroll(t, 1); + else + break; + } + } while (!found); + + if (found) + b->curs_col = col; + break; + case KEY_UP: + case 'k': + if (b->curs_row == b->lines) + vt_scroll(t, -1); + else + b->curs_row--; + break; + case KEY_DOWN: + case 'j': + if (b->curs_row == b->lines + b->rows - 1) + vt_scroll(t, 1); + else + b->curs_row++; + break; + case KEY_RIGHT: + case 'l': + width = wcwidth(b->curs_row->cells[b->curs_col].text); + b->curs_col += width < 1 ? 1 : width; + if (b->curs_col >= b->cols) { + b->curs_col = b->cols - 1; + t->copymode_cmd_multiplier = 0; + } + break; + case KEY_LEFT: + case 'h': + width = 1; + if (b->curs_col >= 2 && !b->curs_row->cells[b->curs_col-1].text) + width = wcwidth(b->curs_row->cells[b->curs_col-2].text); + b->curs_col -= width < 1 ? 1 : width; + if (b->curs_col < 0) { + b->curs_col = 0; + t->copymode_cmd_multiplier = 0; + } + break; + } + } + break; + } + } + if (t->copymode_selecting) + vt_dirty(t); + t->copymode_cmd_multiplier = 0; +} + +void vt_mouse(Vt *t, int x, int y, mmask_t mask) +{ +#ifdef NCURSES_MOUSE_VERSION + char seq[6] = { '\e', '[', 'M' }, state = 0, button = 0; + + if (!t->mousetrack) + return; + + if (mask & (BUTTON1_PRESSED | BUTTON1_CLICKED)) + button = 0; + else if (mask & (BUTTON2_PRESSED | BUTTON2_CLICKED)) + button = 1; + else if (mask & (BUTTON3_PRESSED | BUTTON3_CLICKED)) + button = 2; + else if (mask & (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED)) + button = 3; + + if (mask & BUTTON_SHIFT) + state |= 4; + if (mask & BUTTON_ALT) + state |= 8; + if (mask & BUTTON_CTRL) + state |= 16; + + seq[3] = 32 + button + state; + seq[4] = 32 + x; + seq[5] = 32 + y; + + vt_write(t, seq, sizeof seq); + + if (mask & (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)) { + /* send a button release event */ + button = 3; + seq[3] = 32 + button + state; + vt_write(t, seq, sizeof seq); + } +#endif /* NCURSES_MOUSE_VERSION */ +} + +static unsigned int color_hash(short fg, short bg) +{ + if (fg == -1) + fg = COLORS; + if (bg == -1) + bg = COLORS + 1; + return fg * (COLORS + 2) + bg; +} + +short vt_color_get(Vt *t, short fg, short bg) +{ + if (fg >= COLORS) + fg = (t ? t->deffg : default_fg); + if (bg >= COLORS) + bg = (t ? t->defbg : default_bg); + + if (!has_default_colors) { + if (fg == -1) + fg = (t && t->deffg != -1 ? t->deffg : default_fg); + if (bg == -1) + bg = (t && t->defbg != -1 ? t->defbg : default_bg); + } + + if (!color2palette || (fg == -1 && bg == -1)) + return 0; + unsigned int index = color_hash(fg, bg); + if (color2palette[index] == 0) { + short oldfg, oldbg; + for (;;) { + if (++color_pair_current >= color_pairs_max) + color_pair_current = color_pairs_reserved + 1; + pair_content(color_pair_current, &oldfg, &oldbg); + unsigned int old_index = color_hash(oldfg, oldbg); + if (color2palette[old_index] >= 0) { + if (init_pair(color_pair_current, fg, bg) == OK) { + color2palette[old_index] = 0; + color2palette[index] = color_pair_current; + } + break; + } + } + } + + short color_pair = color2palette[index]; + return color_pair >= 0 ? color_pair : -color_pair; +} + +short vt_color_reserve(short fg, short bg) +{ + if (!color2palette || fg >= COLORS || bg >= COLORS) + return 0; + if (!has_default_colors && fg == -1) + fg = default_fg; + if (!has_default_colors && bg == -1) + bg = default_bg; + if (fg == -1 && bg == -1) + return 0; + unsigned int index = color_hash(fg, bg); + if (color2palette[index] >= 0) { + if (init_pair(++color_pairs_reserved, fg, bg) == OK) + color2palette[index] = -color_pairs_reserved; + } + short color_pair = color2palette[index]; + return color_pair >= 0 ? color_pair : -color_pair; +} + +static void init_colors(void) +{ + pair_content(0, &default_fg, &default_bg); + if (default_fg == -1) + default_fg = COLOR_WHITE; + if (default_bg == -1) + default_bg = COLOR_BLACK; + has_default_colors = (use_default_colors() == OK); + color_pairs_max = MIN(COLOR_PAIRS, MAX_COLOR_PAIRS); + if (COLORS) + color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(short)); + vt_color_reserve(COLOR_WHITE, COLOR_BLACK); +} + +void vt_init(void) +{ + init_colors(); + is_utf8_locale(); + char color_suffix[] = "-256color"; + char *term = getenv("DVTM_TERM"); + if (term) + strncpy(vt_term, term, sizeof(vt_term) - sizeof(color_suffix)); + if (COLORS >= 256) + strncat(vt_term, color_suffix, sizeof(color_suffix) - 1); +} + +void vt_set_keytable(const char * const keytable_overlay[], int count) +{ + for (int k = 0; k < count && k < KEY_MAX; k++) { + const char *keyseq = keytable_overlay[k]; + if (keyseq) + keytable[k] = keyseq; + } +} + +void vt_shutdown(void) +{ + free(color2palette); +} + +void vt_set_escseq_handler(Vt *t, vt_escseq_handler_t handler) +{ + t->escseq_handler = handler; +} + +void vt_set_event_handler(Vt *t, vt_event_handler_t handler) +{ + t->event_handler = handler; +} + +void vt_set_data(Vt *t, void *data) +{ + t->data = data; +} + +void *vt_get_data(Vt *t) +{ + return t->data; +} + +unsigned vt_cursor(Vt *t) +{ + if (t->copymode) + return 1; + return t->buffer->scroll_amount_below ? 0 : !t->curshid; +} + +unsigned vt_copymode(Vt *t) +{ + return t->copymode; +} + +void vt_copymode_enter(Vt *t) +{ + if (t->copymode) + return; + Buffer *b = t->buffer; + t->copymode_curs_srow = b->curs_row - b->lines; + t->copymode_curs_scol = b->curs_col; + t->copymode = true; +} + +void vt_copymode_leave(Vt *t) +{ + if (!t->copymode) + return; + Buffer *b = t->buffer; + t->copymode = false; + t->copymode_selecting = false; + t->copymode_sel_start_row = b->lines; + t->copymode_sel_start_col = 0; + t->copymode_cmd_multiplier = 0; + b->curs_row = b->lines + t->copymode_curs_srow; + b->curs_col = t->copymode_curs_scol; + cmdline_hide(t->cmdline); + vt_noscroll(t); + vt_dirty(t); +} diff -Nru dvtm-0.6/vt.h dvtm-0.12/vt.h --- dvtm-0.6/vt.h 1970-01-01 00:00:00.000000000 +0000 +++ dvtm-0.12/vt.h 2014-07-05 07:32:09.000000000 +0000 @@ -0,0 +1,99 @@ +/* + * Copyright © 2004 Bruno T. C. de Oliveira + * Copyright © 2006 Pierre Habouzit + * Copyright © 2008-2013 Marc André Tanner + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef VT_VT_H +#define VT_VT_H + +#include +#include +#include +#include +#include +#include +#include + +#ifndef NCURSES_MOUSE_VERSION +#define mmask_t unsigned long +#endif + +enum { + /* means escape sequence was handled */ + VT_ESCSEQ_HANDLER_OK, + /* means the escape sequence was not recognized yet, but + * there is hope that it still will once more characters + * arrive (i.e. it is not yet complete). + * + * The library will thus continue collecting characters + * and calling the handler as each character arrives until + * either OK or NOWAY is returned. + */ + VT_ESCSEQ_HANDLER_NOTYET, + /* means the escape sequence was not recognized, and there + * is no chance that it will even if more characters are + * added to it. + */ + VT_ESCSEQ_HANDLER_NOWAY +}; + +typedef struct Vt Vt; +typedef int (*vt_escseq_handler_t)(Vt *, char *es); + +enum { + VT_EVENT_TITLE, + VT_EVENT_COPY_TEXT, +}; + +typedef void (*vt_event_handler_t)(Vt *, int event, void *data); + +void vt_init(void); +void vt_set_keytable(char const * const keytable_overlay[], int count); +void vt_shutdown(void); +void vt_set_escseq_handler(Vt *, vt_escseq_handler_t); +void vt_set_event_handler(Vt *, vt_event_handler_t); +void vt_set_data(Vt *, void *); +void *vt_get_data(Vt *); +void vt_set_default_colors(Vt *, unsigned attrs, short fg, short bg); + +Vt *vt_create(int rows, int cols, int scroll_buf_sz); +void vt_resize(Vt *, int rows, int cols); +void vt_destroy(Vt *); +pid_t vt_forkpty(Vt *, const char *, const char *argv[], const char *cwd, const char *envp[], int *pty); +int vt_getpty(Vt *); +unsigned vt_cursor(Vt *t); + +int vt_process(Vt *); +void vt_keypress(Vt *, int keycode); +int vt_write(Vt *t, const char *buf, int len); +void vt_mouse(Vt *t, int x, int y, mmask_t mask); +void vt_dirty(Vt *t); +void vt_draw(Vt *, WINDOW *win, int startrow, int startcol); +short vt_color_get(Vt *t, short fg, short bg); +short vt_color_reserve(short fg, short bg); + +void vt_scroll(Vt *, int rows); +void vt_noscroll(Vt *); + +void vt_bell(Vt *, bool bell); +void vt_togglebell(Vt *); + +void vt_copymode_enter(Vt *t); +void vt_copymode_leave(Vt *t); +unsigned vt_copymode(Vt *t); +void vt_copymode_keypress(Vt *t, int keycode); + +#endif /* VT_VT_H */