git home / emma home
logo

mtm

Terminal Multiplexer. Emma's branch.
git clone https://git.y1.nz/archives/mtm.tar.gz
Files | Log | Refs

mtm.c


      1 #include <errno.h>
      2 #include <fcntl.h>
      3 #include <limits.h>
      4 #include <locale.h>
      5 #include <pwd.h>
      6 #include <signal.h>
      7 #include <stdbool.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 #include <sys/ioctl.h>
     11 #include <sys/select.h>
     12 #include <sys/types.h>
     13 #include <unistd.h>
     14 #include <wchar.h>
     15 #include <wctype.h>
     16 
     17 #include "vtparser.h"
     18 
     19 /*** CONFIGURATION */
     20 #include "config.h"
     21 
     22 #define MIN(x, y) ((x) < (y)? (x) : (y))
     23 #define MAX(x, y) ((x) > (y)? (x) : (y))
     24 #define CTL(x) ((x) & 0x1f)
     25 #define USAGE "usage: mtm [-T NAME] [-t NAME] [-c KEY]\n"
     26 
     27 /*** DATA TYPES */
     28 typedef enum{
     29 	HORIZONTAL,
     30 	VERTICAL,
     31 	VIEW
     32 } Node;
     33 
     34 typedef struct SCRN SCRN;
     35 struct SCRN{
     36 	int sy, sx, vis, tos;
     37 	short fg, bg, sfg, sbg, sp;
     38 	bool insert, oxenl, xenl, saved;
     39 	attr_t sattr;
     40 	WINDOW *win;
     41 };
     42 
     43 typedef struct NODE NODE;
     44 struct NODE{
     45 	Node t;
     46 	int y, x, h, w, pt, ntabs;
     47 	bool *tabs, pnm, decom, am, lnm;
     48 	wchar_t repc;
     49 	NODE *p, *c1, *c2;
     50 	SCRN pri, alt, *s;
     51 	wchar_t *g0, *g1, *g2, *g3, *gc, *gs, *sgc, *sgs;
     52 	VTPARSER vp;
     53 };
     54 
     55 /*** GLOBALS AND PROTOTYPES */
     56 static NODE *root, *focused, *lastfocused = NULL;
     57 static int commandkey = CTL(COMMAND_KEY), nfds = 1; /* stdin */
     58 static fd_set fds;
     59 static char iobuf[BUFSIZ];
     60 
     61 static void setupevents(NODE *n);
     62 static void reshape(NODE *n, int y, int x, int h, int w);
     63 static void draw(NODE *n);
     64 static void reshapechildren(NODE *n);
     65 static const char *term = NULL;
     66 static void freenode(NODE *n, bool recursive);
     67 void start_pairs(void);
     68 short mtm_alloc_pair(int fg, int bg);
     69 
     70 /*** UTILITY FUNCTIONS */
     71 static void
     72 quit(int rc, const char *m) /* Shut down MTM. */
     73 {
     74 	if (m)
     75 		fprintf(stderr, "%s\n", m);
     76 	if (root)
     77 		freenode(root, true);
     78 	endwin();
     79 	exit(rc);
     80 }
     81 
     82 static void
     83 safewrite(int fd, const char *b, size_t n) /* Write, checking for errors. */
     84 {
     85 	size_t w = 0;
     86 	while (w < n) {
     87 		ssize_t s = write(fd, b + w, n - w);
     88 		if (s < 0 && errno != EINTR)
     89 			return;
     90 		if (s < 0)
     91 			s = 0;
     92 		w += (size_t)s;
     93 	}
     94 }
     95 
     96 static const char *
     97 getshell(void) /* Get the user's preferred shell. */
     98 {
     99 	if (getenv("SHELL"))
    100 		return getenv("SHELL");
    101 	struct passwd *pwd = getpwuid(getuid());
    102 	if (pwd)
    103 		return pwd->pw_shell;
    104 	return "/bin/sh";
    105 }
    106 
    107 /*** TERMINAL EMULATION HANDLERS
    108  * These functions implement the various terminal commands activated by
    109  * escape sequences and printing to the terminal. Large amounts of boilerplate
    110  * code is shared among all these functions, and is factored out into the
    111  * macros below:
    112  *   PD(n, d): Parameter n, with default d.
    113  *   P0(n): Parameter n, default 0.
    114  *   P1(n): Parameter n, default 1.
    115  *   CALL(h): Call handler h with no arguments.
    116  *   SENDN(n, s, c): Write string c bytes of s to n.
    117  *   SEND(n, s): Write string s to node n's host.
    118  *   (END)HANDLER: Declare/end a handler function
    119  *   COMMONVARS: All of the common variables for a handler.
    120  *   x, y: cursor position
    121  *   mx, my: max possible values for x and y
    122  *   px, py: physical cursor position in scrollback
    123  *   n: the current node
    124  *   win: the current window
    125  *   top, bot: the scrolling region
    126  *   tos: top of the screen in the pad
    127  *   s: the current SCRN buffer
    128  * The funny names for handlers are from their ANSI/ECMA/DEC mnemonics.
    129  */
    130 #define PD(x, d) (argc < (x) || !argv? (d) : argv[(x)])
    131 #define P0(x) PD(x, 0)
    132 #define P1(x) (!P0(x)? 1 : P0(x))
    133 #define CALL(x) (x)(v, n, 0, 0, 0, NULL, NULL)
    134 #define SENDN(n, s, c) safewrite(n->pt, s, c)
    135 #define SEND(n, s) SENDN(n, s, strlen(s))
    136 #define COMMONVARS \
    137 	NODE *n = (NODE *)p; \
    138 	SCRN *s = n->s; \
    139 	WINDOW *win = s->win; \
    140 	int py, px, y, x, my, mx, top = 0, bot = 0, tos = s->tos; \
    141 	(void)v; (void)p; (void)w; (void)iw; (void)argc; (void)argv; \
    142 	(void)win; (void)y; (void)x; (void)my; (void)mx; (void)osc; \
    143 	(void)tos; \
    144 	getyx(win, py, px); y = py - s->tos; x = px; \
    145 	getmaxyx(win, my, mx); my -= s->tos; \
    146 	wgetscrreg(win, &top, &bot); \
    147 	bot++; bot -= s->tos; \
    148 	top = top <= tos? 0 : top - tos; \
    149 
    150 #define HANDLER(name) \
    151 	static void \
    152 	name (VTPARSER *v, void *p, wchar_t w, wchar_t iw, \
    153 		  int argc, int *argv, const wchar_t *osc) \
    154 	{ COMMONVARS
    155 #define ENDHANDLER n->repc = 0; } /* control sequences aren't repeated */
    156 
    157 HANDLER(bell) /* Terminal bell. */
    158 	beep();
    159 ENDHANDLER
    160 
    161 HANDLER(numkp) /* Application/Numeric Keypad Mode */
    162 	n->pnm = (w == L'=');
    163 ENDHANDLER
    164 
    165 HANDLER(vis) /* Cursor visibility */
    166 	s->vis = iw == L'6'? 0 : 1;
    167 ENDHANDLER
    168 
    169 HANDLER(cup) /* CUP - Cursor Position */
    170 	s->xenl = false;
    171 	wmove(win, tos + (n->decom? top : 0) + P1(0) - 1, P1(1) - 1);
    172 ENDHANDLER
    173 
    174 HANDLER(dch) /* DCH - Delete Character */
    175 	for (int i = 0; i < P1(0); i++)
    176 		wdelch(win);
    177 ENDHANDLER
    178 
    179 HANDLER(ich) /* ICH - Insert Character */
    180 	for (int i = 0; i < P1(0); i++)
    181 		wins_nwstr(win, L" ", 1);
    182 ENDHANDLER
    183 
    184 HANDLER(cuu) /* CUU - Cursor Up */
    185 	wmove(win, MAX(py - P1(0), tos + top), x);
    186 ENDHANDLER
    187 
    188 HANDLER(cud) /* CUD - Cursor Down */
    189 	wmove(win, MIN(py + P1(0), tos + bot - 1), x);
    190 ENDHANDLER
    191 
    192 HANDLER(cuf) /* CUF - Cursor Forward */
    193 	wmove(win, py, MIN(x + P1(0), mx - 1));
    194 ENDHANDLER
    195 
    196 HANDLER(ack) /* ACK - Acknowledge Enquiry */
    197 	SEND(n, "\006");
    198 ENDHANDLER
    199 
    200 HANDLER(hts) /* HTS - Horizontal Tab Set */
    201 	if (x < n->ntabs && x > 0)
    202 		n->tabs[x] = true;
    203 ENDHANDLER
    204 
    205 HANDLER(ri) /* RI - Reverse Index */
    206 	int otop = 0, obot = 0;
    207 	wgetscrreg(win, &otop, &obot);
    208 	wsetscrreg(win, otop >= tos? otop : tos, obot);
    209 	y == top? wscrl(win, -1) : wmove(win, MAX(tos, py - 1), x);
    210 	wsetscrreg(win, otop, obot);
    211 ENDHANDLER
    212 
    213 HANDLER(decid) /* DECID - Send Terminal Identification */
    214 	if (w == L'c')
    215 		SEND(n, iw == L'>'? "\033[>1;10;0c" : "\033[?1;2c");
    216 	else if (w == L'Z')
    217 		SEND(n, "\033[?6c");
    218 ENDHANDLER
    219 
    220 HANDLER(hpa) /* HPA - Cursor Horizontal Absolute */
    221 	wmove(win, py, MIN(P1(0) - 1, mx - 1));
    222 ENDHANDLER
    223 
    224 HANDLER(hpr) /* HPR - Cursor Horizontal Relative */
    225 	wmove(win, py, MIN(px + P1(0), mx - 1));
    226 ENDHANDLER
    227 
    228 HANDLER(vpa) /* VPA - Cursor Vertical Absolute */
    229 	wmove(win, MIN(tos + bot - 1, MAX(tos + top, tos + P1(0) - 1)), x);
    230 ENDHANDLER
    231 
    232 HANDLER(vpr) /* VPR - Cursor Vertical Relative */
    233 	wmove(win, MIN(tos + bot - 1, MAX(tos + top, py + P1(0))), x);
    234 ENDHANDLER
    235 
    236 HANDLER(cbt) /* CBT - Cursor Backwards Tab */
    237 	for (int i = x - 1; i < n->ntabs && i >= 0; i--) if (n->tabs[i]){
    238 		wmove(win, py, i);
    239 		return;
    240 	}
    241 	wmove(win, py, 0);
    242 ENDHANDLER
    243 
    244 HANDLER(ht) /* HT - Horizontal Tab */
    245 	for (int i = x + 1; i < n->w && i < n->ntabs; i++) if (n->tabs[i]){
    246 		wmove(win, py, i);
    247 		return;
    248 	}
    249 	wmove(win, py, mx - 1);
    250 ENDHANDLER
    251 
    252 HANDLER(tab) /* Tab forwards or backwards */
    253 	for (int i = 0; i < P1(0); i++)
    254 		switch (w) {
    255 		case L'I': CALL(ht); break;
    256 		case L'\t': CALL(ht); break;
    257 		case L'Z': CALL(cbt); break;
    258 		}
    259 ENDHANDLER
    260 
    261 HANDLER(decaln) /* DECALN - Screen Alignment Test */
    262 	chtype e[] = {COLOR_PAIR(0) | 'E', 0};
    263 	for (int r = 0; r < my; r++)
    264 		for (int c = 0; c <= mx; c++)
    265 			mvwaddchnstr(win, tos + r, c, e, 1);
    266 	wmove(win, py, px);
    267 ENDHANDLER
    268 
    269 HANDLER(su) /* SU - Scroll Up/Down */
    270 	wscrl(win, (w == L'T' || w == L'^')? -P1(0) : P1(0));
    271 ENDHANDLER
    272 
    273 HANDLER(sc) /* SC - Save Cursor */
    274 	s->sx = px; /* save X position */
    275 	s->sy = py; /* save Y position */
    276 	wattr_get(win, &s->sattr, &s->sp, NULL); /* save attrs and color pair */
    277 	s->sfg = s->fg; /* save foreground color */
    278 	s->sbg = s->bg; /* save background color */
    279 	s->oxenl = s->xenl; /* save xenl state */
    280 	s->saved = true; /* save data is valid */
    281 	n->sgc = n->gc; n->sgs = n->gs; /* save character sets */
    282 ENDHANDLER
    283 
    284 HANDLER(rc) /* RC - Restore Cursor */
    285 	if (iw == L'#'){
    286 		CALL(decaln);
    287 		return;
    288 	}
    289 
    290 	if (!s->saved)
    291 		return;
    292 
    293 	wmove(win, s->sy, s->sx); /* get old position */
    294 	wattr_set(win, s->sattr, s->sp, NULL); /* get attrs and color pair */
    295 	s->fg = s->sfg; /* get foreground color */
    296 	s->bg = s->sbg; /* get background color */
    297 	s->xenl = s->oxenl; /* get xenl state */
    298 	n->gc = n->sgc; n->gs = n->sgs; /* save character sets */
    299 
    300 	/* restore colors */
    301 	int cp = mtm_alloc_pair(s->fg, s->bg);
    302 	wcolor_set(win, cp, NULL);
    303 	cchar_t c;
    304 	setcchar(&c, L" ", A_NORMAL, cp, NULL);
    305 	wbkgrndset(win, &c);
    306 ENDHANDLER
    307 
    308 HANDLER(tbc) /* TBC - Tabulation Clear */
    309 	switch (P0(0)) {
    310 	case 0: n->tabs[x < n->ntabs? x : 0] = false; break;
    311 	case 3: memset(n->tabs, 0, sizeof(bool) * (n->ntabs)); break;
    312 	}
    313 ENDHANDLER
    314 
    315 HANDLER(cub) /* CUB - Cursor Backward */
    316 	s->xenl = false;
    317 	wmove(win, py, MAX(x - P1(0), 0));
    318 ENDHANDLER
    319 
    320 HANDLER(el) /* EL - Erase in Line */
    321 	cchar_t b;
    322 	setcchar(&b, L" ", A_NORMAL, mtm_alloc_pair(s->fg, s->bg), NULL);
    323 
    324 	switch (P0(0)) {
    325 		case 0: wclrtoeol(win); break;
    326 		case 1:
    327 			for (int i = 0; i <= x; i++)
    328 				mvwadd_wchnstr(win, py, i, &b, 1); break;
    329 		case 2: wmove(win, py, 0); wclrtoeol(win); break;
    330 	}
    331 
    332 	wmove(win, py, x);
    333 ENDHANDLER
    334 
    335 HANDLER(ed) /* ED - Erase in Display */
    336 	int o = 1;
    337 
    338 	switch (P0(0)){
    339 	case 0: wclrtobot(win); break;
    340 	case 3: werase(win); break;
    341 	case 2: wmove(win, tos, 0); wclrtobot(win); break;
    342 	case 1:
    343 		for (int i = tos; i < py; i++){
    344 			wmove(win, i, 0);
    345 			wclrtoeol(win);
    346 		}
    347 		wmove(win, py, x);
    348 		el(v, p, w, iw, 1, &o, NULL);
    349 		break;
    350 	}
    351 
    352 	wmove(win, py, px);
    353 ENDHANDLER
    354 
    355 HANDLER(ech) /* ECH - Erase Character */
    356 	cchar_t c;
    357 	setcchar(&c, L" ", A_NORMAL, mtm_alloc_pair(s->fg, s->bg), NULL);
    358 	for (int i = 0; i < P1(0); i++)
    359 		mvwadd_wchnstr(win, py, x + i, &c, 1);
    360 	wmove(win, py, px);
    361 ENDHANDLER
    362 
    363 HANDLER(dsr) /* DSR - Device Status Report */
    364 	char buf[100] = {0};
    365 	if (P0(0) == 6)
    366 		snprintf(
    367 			buf,
    368 			sizeof(buf) - 1,
    369 			"\033[%d;%dR",
    370 			(n->decom? y - top : y) + 1,
    371 			x + 1
    372 		);
    373 	else
    374 		snprintf(buf, sizeof(buf) - 1, "\033[0n");
    375 	SEND(n, buf);
    376 ENDHANDLER
    377 
    378 HANDLER(idl) /* IL or DL - Insert/Delete Line */
    379 	/* we don't use insdelln here because it inserts above and not below,
    380 	 * and has a few other edge cases... */
    381 	int otop = 0, obot = 0, p1 = MIN(P1(0), (my - 1) - y);
    382 	wgetscrreg(win, &otop, &obot);
    383 	wsetscrreg(win, py, obot);
    384 	wscrl(win, w == L'L'? -p1 : p1);
    385 	wsetscrreg(win, otop, obot);
    386 	wmove(win, py, 0);
    387 ENDHANDLER
    388 
    389 HANDLER(csr) /* CSR - Change Scrolling Region */
    390 	if (wsetscrreg(win, tos + P1(0) - 1, tos + PD(1, my) - 1) == OK)
    391 		CALL(cup);
    392 ENDHANDLER
    393 
    394 HANDLER(decreqtparm) /* DECREQTPARM - Request Device Parameters */
    395 	SEND(n, P0(0)? "\033[3;1;2;120;1;0x" : "\033[2;1;2;120;128;1;0x");
    396 ENDHANDLER
    397 
    398 HANDLER(sgr0) /* Reset SGR to default */
    399 	wattrset(win, A_NORMAL);
    400 	wcolor_set(win, 0, NULL);
    401 	s->fg = s->bg = -1;
    402 	wbkgdset(win, COLOR_PAIR(0) | ' ');
    403 ENDHANDLER
    404 
    405 HANDLER(cls) /* Clear screen */
    406 	CALL(cup);
    407 	wclrtobot(win);
    408 	CALL(cup);
    409 ENDHANDLER
    410 
    411 HANDLER(ris) /* RIS - Reset to Initial State */
    412 	n->gs = n->gc = n->g0 = CSET_US; n->g1 = CSET_GRAPH;
    413 	n->g2 = CSET_US; n->g3 = CSET_GRAPH;
    414 	n->decom = s->insert = s->oxenl = s->xenl = n->lnm = false;
    415 	CALL(cls);
    416 	CALL(sgr0);
    417 	n->am = true;
    418 	n->pnm = false;
    419 	n->pri.vis = n->alt.vis = 1;
    420 	n->s = &n->pri;
    421 	wsetscrreg(n->pri.win, 0, MAX(SCROLLBACK, n->h) - 1);
    422 	wsetscrreg(n->alt.win, 0, n->h - 1);
    423 	for (int i = 0; i < n->ntabs; i++)
    424 		n->tabs[i] = (i % 8 == 0);
    425 ENDHANDLER
    426 
    427 HANDLER(mode) /* Set or Reset Mode */
    428 	bool set = (w == L'h');
    429 	for (int i = 0; i < argc; i++)
    430 		switch (P0(i)) {
    431 		case 1: n->pnm = set; break;
    432 		case 3: CALL(cls); break;
    433 		case 4: s->insert = set; break;
    434 		case 6: n->decom = set; CALL(cup); break;
    435 		case 7: n->am = set; break;
    436 		case 20: n->lnm = set; break;
    437 		case 25: s->vis = set? 1 : 0; break;
    438 		case 34: s->vis = set? 1 : 2; break;
    439 		case 1048: CALL((set? sc : rc)); break;
    440 		case 1049:
    441 			CALL((set? sc : rc)); /* fall-through */
    442 		case 47:
    443 		case 1047:
    444 			if (set && n->s != &n->alt) {
    445 				n->s = &n->alt;
    446 				CALL(cls);
    447 			} else if (!set && n->s != &n->pri) {
    448 				n->s = &n->pri;
    449 			}
    450 			break;
    451 		}
    452 ENDHANDLER
    453 
    454 HANDLER(sgr) /* SGR - Select Graphic Rendition */
    455 	bool doc = false;
    456 	bool do8 = COLORS >= 8;
    457 	bool do16 = COLORS >= 16;
    458 	bool do256 = COLORS >= 256;
    459 
    460 	if (!argc)
    461 		CALL(sgr0);
    462 
    463 	short bg = s->bg, fg = s->fg;
    464 	for (int i = 0; i < argc; i++) {
    465 		switch (P0(i)) {
    466 		case 0: CALL(sgr0); break;
    467 		case 1: wattron(win, A_BOLD); break;
    468 		case 2: wattron(win, A_DIM); break;
    469 		case 4: wattron(win, A_UNDERLINE); break;
    470 		case 5: wattron(win, A_BLINK); break;
    471 		case 7: wattron(win, A_REVERSE); break;
    472 		case 8: wattron(win, A_INVIS); break;
    473 		case 22: wattroff(win, A_DIM); wattroff(win, A_BOLD); break;
    474 		case 24: wattroff(win, A_UNDERLINE); break;
    475 		case 25: wattroff(win, A_BLINK); break;
    476 		case 27: wattroff(win, A_REVERSE); break;
    477 		case 30: fg = COLOR_BLACK; doc = do8; break;
    478 		case 31: fg = COLOR_RED; doc = do8; break;
    479 		case 32: fg = COLOR_GREEN; doc = do8; break;
    480 		case 33: fg = COLOR_YELLOW; doc = do8; break;
    481 		case 34: fg = COLOR_BLUE; doc = do8; break;
    482 		case 35: fg = COLOR_MAGENTA; doc = do8; break;
    483 		case 36: fg = COLOR_CYAN; doc = do8; break;
    484 		case 37: fg = COLOR_WHITE; doc = do8; break;
    485 		case 38: fg = P0(i+1) == 5? P0(i+2) : s->fg; i += 2; doc = do256; break;
    486 		case 39: fg = -1; doc = true; break;
    487 		case 40: bg = COLOR_BLACK; doc = do8; break;
    488 		case 41: bg = COLOR_RED; doc = do8; break;
    489 		case 42: bg = COLOR_GREEN; doc = do8; break;
    490 		case 43: bg = COLOR_YELLOW; doc = do8; break;
    491 		case 44: bg = COLOR_BLUE; doc = do8; break;
    492 		case 45: bg = COLOR_MAGENTA; doc = do8; break;
    493 		case 46: bg = COLOR_CYAN; doc = do8; break;
    494 		case 47: bg = COLOR_WHITE; doc = do8; break;
    495 		case 48: bg = P0(i+1) == 5? P0(i+2) : s->bg; i += 2; doc = do256; break;
    496 		case 49: bg = -1; doc = true; break;
    497 		case 90: fg = COLOR_BLACK; doc = do16; break;
    498 		case 91: fg = COLOR_RED; doc = do16; break;
    499 		case 92: fg = COLOR_GREEN; doc = do16; break;
    500 		case 93: fg = COLOR_YELLOW; doc = do16; break;
    501 		case 94: fg = COLOR_BLUE; doc = do16; break;
    502 		case 95: fg = COLOR_MAGENTA; doc = do16; break;
    503 		case 96: fg = COLOR_CYAN; doc = do16; break;
    504 		case 97: fg = COLOR_WHITE; doc = do16; break;
    505 		case 100: bg = COLOR_BLACK; doc = do16; break;
    506 		case 101: bg = COLOR_RED; doc = do16; break;
    507 		case 102: bg = COLOR_GREEN; doc = do16; break;
    508 		case 103: bg = COLOR_YELLOW; doc = do16; break;
    509 		case 104: bg = COLOR_BLUE; doc = do16; break;
    510 		case 105: bg = COLOR_MAGENTA; doc = do16; break;
    511 		case 106: bg = COLOR_CYAN; doc = do16; break;
    512 		case 107: bg = COLOR_WHITE; doc = do16; break;
    513 
    514 		#if defined(A_ITALIC) && !defined(NO_ITALICS) && !defined(REVERSE_ITALICS)
    515 		case 3: wattron(win, A_ITALIC); break;
    516 		case 23: wattroff(win, A_ITALIC); break;
    517 		#endif
    518 
    519 		#if defined(REVERSE_ITALICS)
    520 		case 3: wattron(win, A_REVERSE); break;
    521 		case 23: wattroff(win, A_REVERSE); break;
    522 		#endif
    523 		}
    524 	}
    525 
    526 	if (doc) {
    527 		int p = mtm_alloc_pair(s->fg = fg, s->bg = bg);
    528 		wcolor_set(win, p, NULL);
    529 		cchar_t c;
    530 		setcchar(&c, L" ", A_NORMAL, p, NULL);
    531 		wbkgrndset(win, &c);
    532 	}
    533 }
    534 
    535 HANDLER(cr) /* CR - Carriage Return */
    536 	s->xenl = false;
    537 	wmove(win, py, 0);
    538 ENDHANDLER
    539 
    540 HANDLER(ind) /* IND - Index */
    541 	y == (bot - 1)? scroll(win) : wmove(win, py + 1, x);
    542 ENDHANDLER
    543 
    544 HANDLER(nel) /* NEL - Next Line */
    545 	CALL(cr); CALL(ind);
    546 ENDHANDLER
    547 
    548 HANDLER(pnl) /* NL - Newline */
    549 	CALL((n->lnm? nel : ind));
    550 ENDHANDLER
    551 
    552 HANDLER(cpl) /* CPL - Cursor Previous Line */
    553 	wmove(win, MAX(tos + top, py - P1(0)), 0);
    554 ENDHANDLER
    555 
    556 HANDLER(cnl) /* CNL - Cursor Next Line */
    557 	wmove(win, MIN(tos + bot - 1, py + P1(0)), 0);
    558 ENDHANDLER
    559 
    560 HANDLER(print) /* Print a character to the terminal */
    561 	if (wcwidth(w) < 0)
    562 		return;
    563 
    564 	if (s->insert)
    565 		CALL(ich);
    566 
    567 	if (s->xenl) {
    568 		s->xenl = false;
    569 		if (n->am)
    570 			CALL(nel);
    571 		getyx(win, y, x);
    572 		y -= tos;
    573 	}
    574 
    575 	if (w < MAXMAP && n->gc[w])
    576 		w = n->gc[w];
    577 	n->repc = w;
    578 
    579 	if (x == mx - wcwidth(w)) {
    580 		s->xenl = true;
    581 		wins_nwstr(win, &w, 1);
    582 	} else {
    583 		waddnwstr(win, &w, 1);
    584 	}
    585 	n->gc = n->gs;
    586 } /* no ENDHANDLER because we don't want to reset repc */
    587 
    588 HANDLER(rep) /* REP - Repeat Character */
    589 	for (int i = 0; i < P1(0) && n->repc; i++)
    590 		print(v, p, n->repc, 0, 0, NULL, NULL);
    591 ENDHANDLER
    592 
    593 HANDLER(scs) /* Select Character Set */
    594 	wchar_t **t = NULL;
    595 
    596 	switch (iw){
    597 	case L'(': t = &n->g0; break;
    598 	case L')': t = &n->g1; break;
    599 	case L'*': t = &n->g2; break;
    600 	case L'+': t = &n->g3; break;
    601 	default: return;
    602 	}
    603 
    604 	switch (w){
    605 	case L'A': *t = CSET_UK; break;
    606 	case L'B': *t = CSET_US; break;
    607 	case L'0': *t = CSET_GRAPH; break;
    608 	case L'1': *t = CSET_US; break;
    609 	case L'2': *t = CSET_GRAPH; break;
    610 	}
    611 ENDHANDLER
    612 
    613 HANDLER(so) /* Switch Out/In Character Set */
    614 	if (w == 0x0e) {
    615 		n->gs = n->gc = n->g1; /* locking shift */
    616 	} else if (w == 0xf) {
    617 		n->gs = n->gc = n->g0; /* locking shift */
    618 	} else if (w == L'n') {
    619 		n->gs = n->gc = n->g2; /* locking shift */
    620 	} else if (w == L'o') {
    621 		n->gs = n->gc = n->g3; /* locking shift */
    622 	} else if (w == L'N') {
    623 		n->gs = n->gc; /* non-locking shift */
    624 		n->gc = n->g2;
    625 	} else if (w == L'O'){
    626 		n->gs = n->gc; /* non-locking shift */
    627 		n->gc = n->g3;
    628 	}
    629 ENDHANDLER
    630 
    631 static void
    632 setupevents(NODE *n)
    633 {
    634 	n->vp.p = n;
    635 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x05, ack);
    636 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x07, bell);
    637 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x08, cub);
    638 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x09, tab);
    639 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x0a, pnl);
    640 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x0b, pnl);
    641 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x0c, pnl);
    642 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x0d, cr);
    643 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x0e, so);
    644 	vtonevent(&n->vp, VTPARSER_CONTROL, 0x0f, so);
    645 	vtonevent(&n->vp, VTPARSER_CSI, L'A', cuu);
    646 	vtonevent(&n->vp, VTPARSER_CSI, L'B', cud);
    647 	vtonevent(&n->vp, VTPARSER_CSI, L'C', cuf);
    648 	vtonevent(&n->vp, VTPARSER_CSI, L'D', cub);
    649 	vtonevent(&n->vp, VTPARSER_CSI, L'E', cnl);
    650 	vtonevent(&n->vp, VTPARSER_CSI, L'F', cpl);
    651 	vtonevent(&n->vp, VTPARSER_CSI, L'G', hpa);
    652 	vtonevent(&n->vp, VTPARSER_CSI, L'H', cup);
    653 	vtonevent(&n->vp, VTPARSER_CSI, L'I', tab);
    654 	vtonevent(&n->vp, VTPARSER_CSI, L'J', ed);
    655 	vtonevent(&n->vp, VTPARSER_CSI, L'K', el);
    656 	vtonevent(&n->vp, VTPARSER_CSI, L'L', idl);
    657 	vtonevent(&n->vp, VTPARSER_CSI, L'M', idl);
    658 	vtonevent(&n->vp, VTPARSER_CSI, L'P', dch);
    659 	vtonevent(&n->vp, VTPARSER_CSI, L'S', su);
    660 	vtonevent(&n->vp, VTPARSER_CSI, L'T', su);
    661 	vtonevent(&n->vp, VTPARSER_CSI, L'X', ech);
    662 	vtonevent(&n->vp, VTPARSER_CSI, L'Z', tab);
    663 	vtonevent(&n->vp, VTPARSER_CSI, L'`', hpa);
    664 	vtonevent(&n->vp, VTPARSER_CSI, L'^', su);
    665 	vtonevent(&n->vp, VTPARSER_CSI, L'@', ich);
    666 	vtonevent(&n->vp, VTPARSER_CSI, L'a', hpr);
    667 	vtonevent(&n->vp, VTPARSER_CSI, L'b', rep);
    668 	vtonevent(&n->vp, VTPARSER_CSI, L'c', decid);
    669 	vtonevent(&n->vp, VTPARSER_CSI, L'd', vpa);
    670 	vtonevent(&n->vp, VTPARSER_CSI, L'e', vpr);
    671 	vtonevent(&n->vp, VTPARSER_CSI, L'f', cup);
    672 	vtonevent(&n->vp, VTPARSER_CSI, L'g', tbc);
    673 	vtonevent(&n->vp, VTPARSER_CSI, L'h', mode);
    674 	vtonevent(&n->vp, VTPARSER_CSI, L'l', mode);
    675 	vtonevent(&n->vp, VTPARSER_CSI, L'm', sgr);
    676 	vtonevent(&n->vp, VTPARSER_CSI, L'n', dsr);
    677 	vtonevent(&n->vp, VTPARSER_CSI, L'r', csr);
    678 	vtonevent(&n->vp, VTPARSER_CSI, L's', sc);
    679 	vtonevent(&n->vp, VTPARSER_CSI, L'u', rc);
    680 	vtonevent(&n->vp, VTPARSER_CSI, L'x', decreqtparm);
    681 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'0', scs);
    682 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'1', scs);
    683 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'2', scs);
    684 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'7', sc);
    685 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'8', rc);
    686 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'A', scs);
    687 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'B', scs);
    688 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'D', ind);
    689 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'E', nel);
    690 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'H', hts);
    691 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'M', ri);
    692 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'Z', decid);
    693 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'c', ris);
    694 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'p', vis);
    695 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'=', numkp);
    696 	vtonevent(&n->vp, VTPARSER_ESCAPE, L'>', numkp);
    697 	vtonevent(&n->vp, VTPARSER_PRINT, 0, print);
    698 }
    699 
    700 /*** MTM FUNCTIONS
    701  * These functions do the user-visible work of MTM: creating nodes in the
    702  * tree, updating the display, and so on.
    703  */
    704 static bool *
    705 newtabs(int w, int ow, bool *oldtabs) /* Initialize default tabstops. */
    706 {
    707 	bool *tabs = calloc(w, sizeof(bool));
    708 	if (!tabs)
    709 		return NULL;
    710 	for (int i = 0; i < w; i++) /* keep old overlapping tabs */
    711 		tabs[i] = i < ow? oldtabs[i] : (i % 8 == 0);
    712 	return tabs;
    713 }
    714 
    715 static NODE *
    716 newnode(Node t, NODE *p, int y, int x, int h, int w) /* Create a new node. */
    717 {
    718 	NODE *n = calloc(1, sizeof(NODE));
    719 	bool *tabs = newtabs(w, 0, NULL);
    720 	if (!n || h < 2 || w < 2 || !tabs)
    721 		return free(n), free(tabs), NULL;
    722 
    723 	n->t = t;
    724 	n->pt = -1;
    725 	n->p = p;
    726 	n->y = y;
    727 	n->x = x;
    728 	n->h = h;
    729 	n->w = w;
    730 	n->tabs = tabs;
    731 	n->ntabs = w;
    732 
    733 	return n;
    734 }
    735 
    736 static void
    737 freenode(NODE *n, bool recurse) /* Free a node. */
    738 {
    739 	if (!n) return;
    740 
    741 	if (lastfocused == n)
    742 		lastfocused = NULL;
    743 	if (n->pri.win)
    744 		delwin(n->pri.win);
    745 	if (n->alt.win)
    746 		delwin(n->alt.win);
    747 	if (recurse)
    748 		freenode(n->c1, true);
    749 	if (recurse)
    750 		freenode(n->c2, true);
    751 	if (n->pt >= 0) {
    752 		close(n->pt);
    753 		FD_CLR(n->pt, &fds);
    754 	}
    755 	free(n->tabs);
    756 	free(n);
    757 }
    758 
    759 static void
    760 fixcursor(void) /* Move the terminal cursor to the active view. */
    761 {
    762 	if (!focused) return;
    763 
    764 	int y, x;
    765 	curs_set(focused->s->off != focused->s->tos ? 0 : focused->s->vis);
    766 	getyx(focused->s->win, y, x);
    767 	y = MIN(MAX(y, focused->s->tos), focused->s->tos + focused->h - 1);
    768 	wmove(focused->s->win, y, x);
    769 }
    770 
    771 static const char *
    772 getterm(void)
    773 {
    774 	const char *envterm = getenv("TERM");
    775 	if (term)
    776 		return term;
    777 	if (envterm && COLORS >= 256 && !strstr(DEFAULT_TERMINAL, "-256color"))
    778 		return DEFAULT_256_COLOR_TERMINAL;
    779 	return DEFAULT_TERMINAL;
    780 }
    781 
    782 static NODE *
    783 newview(NODE *p, int y, int x, int h, int w) /* Open a new view. */
    784 {
    785 	struct winsize ws = {.ws_row = h, .ws_col = w};
    786 	NODE *n = newnode(VIEW, p, y, x, h, w);
    787 	if (!n)
    788 		return NULL;
    789 
    790 	SCRN *pri = &n->pri, *alt = &n->alt;
    791 	pri->win = newpad(MAX(h, SCROLLBACK), w);
    792 	alt->win = newpad(h, w);
    793 	if (!pri->win || !alt->win)
    794 		return freenode(n, false), NULL;
    795 	pri->tos = MAX(0, SCROLLBACK - h);
    796 	n->s = pri;
    797 
    798 	nodelay(pri->win, TRUE); nodelay(alt->win, TRUE);
    799 	scrollok(pri->win, TRUE); scrollok(alt->win, TRUE);
    800 	keypad(pri->win, TRUE); keypad(alt->win, TRUE);
    801 
    802 	setupevents(n);
    803 	ris(&n->vp, n, L'c', 0, 0, NULL, NULL);
    804 
    805 	pid_t pid = forkpty(&n->pt, NULL, NULL, &ws);
    806 	if (pid < 0) {
    807 		if (!p)
    808 			perror("forkpty");
    809 		return freenode(n, false), NULL;
    810 	} else if (pid == 0) {
    811 		char buf[100] = {0};
    812 		snprintf(buf, sizeof(buf) - 1, "%lu", (unsigned long)getppid());
    813 		setsid();
    814 		setenv("MTM", buf, 1);
    815 		setenv("TERM", getterm(), 1);
    816 		signal(SIGCHLD, SIG_DFL);
    817 		execl(getshell(), getshell(), NULL);
    818 		return NULL;
    819 	}
    820 
    821 	FD_SET(n->pt, &fds);
    822 	fcntl(n->pt, F_SETFL, O_NONBLOCK);
    823 	nfds = n->pt > nfds? n->pt : nfds;
    824 	return n;
    825 }
    826 
    827 static NODE *
    828 newcontainer(
    829 	Node t,
    830 	NODE *p,
    831 	int y, int x,
    832 	int h, int w,
    833 	NODE *c1,
    834 	NODE *c2
    835 ) {
    836 	/* Create a new container */
    837 	NODE *n = newnode(t, p, y, x, h, w);
    838 	if (!n) return NULL;
    839 
    840 	n->c1 = c1;
    841 	n->c2 = c2;
    842 	c1->p = c2->p = n;
    843 
    844 	reshapechildren(n);
    845 	return n;
    846 }
    847 
    848 static void
    849 focus(NODE *n) /* Focus a node. */
    850 {
    851 	if (!n) return;
    852 
    853 	if (n->t == VIEW) {
    854 		lastfocused = focused;
    855 		focused = n;
    856 		return;
    857 	}
    858 
    859 	focus(n->c1? n->c1 : n->c2);
    860 }
    861 
    862 #define ABOVE(n) n->y - 2, n->x + n->w / 2
    863 #define BELOW(n) n->y + n->h + 2, n->x + n->w / 2
    864 #define LEFT(n) n->y + n->h / 2, n->x - 2
    865 #define RIGHT(n) n->y + n->h / 2, n->x + n->w + 2
    866 
    867 static NODE *
    868 findnode(NODE *n, int y, int x) /* Find the node enclosing y,x. */
    869 {
    870 	#define IN(n, y, x) ( \
    871 		y >= n->y && y <= n->y + n->h && \
    872 		x >= n->x && x <= n->x + n->w \
    873 	)
    874 
    875 	if (!IN(n, y, x)) return NULL;
    876 
    877 	if (n->c1 && IN(n->c1, y, x))
    878 		return findnode(n->c1, y, x);
    879 
    880 	if (n->c2 && IN(n->c2, y, x))
    881 		return findnode(n->c2, y, x);
    882 
    883 	return n;
    884 }
    885 
    886 static void
    887 replacechild(NODE *n, NODE *c1, NODE *c2) /* Replace c1 of n with c2. */
    888 {
    889 	c2->p = n;
    890 	if (!n) {
    891 		root = c2;
    892 		reshape(c2, 0, 0, LINES, COLS);
    893 	} else if (n->c1 == c1) {
    894 		n->c1 = c2;
    895 	} else if (n->c2 == c1) {
    896 		n->c2 = c2;
    897 	}
    898 
    899 	n = n? n : root;
    900 	reshape(n, n->y, n->x, n->h, n->w);
    901 	draw(n);
    902 }
    903 
    904 static void
    905 removechild(NODE *p, const NODE *c) /* Replace p with other child. */
    906 {
    907 	replacechild(p->p, p, c == p->c1? p->c2 : p->c1);
    908 	freenode(p, false);
    909 }
    910 
    911 static void
    912 deletenode(NODE *n) /* Delete a node. */
    913 {
    914 	if (!n || !n->p)
    915 		quit(EXIT_SUCCESS, NULL);
    916 	if (n == focused)
    917 		focus(n->p->c1 == n? n->p->c2 : n->p->c1);
    918 	removechild(n->p, n);
    919 	freenode(n, true);
    920 }
    921 
    922 static void
    923 reshapeview(NODE *node, int d, int ow) /* Reshape a view. */
    924 {
    925 	int oy, ox;
    926 	bool *tabs = newtabs(node->w, ow, node->tabs);
    927 	struct winsize ws = {.ws_row = node->h, .ws_col = node->w};
    928 
    929 	if (tabs) {
    930 		free(node->tabs);
    931 		node->tabs = tabs;
    932 		node->ntabs = node->w;
    933 	}
    934 
    935 	getyx(node->s->win, oy, ox);
    936 	wresize(node->pri.win, MAX(node->h, SCROLLBACK), MAX(node->w, 2));
    937 	wresize(node->alt.win, MAX(node->h, 2), MAX(node->w, 2));
    938 	node->pri.tos = node->pri.off = MAX(0, SCROLLBACK - node->h);
    939 	node->alt.tos = node->alt.off = 0;
    940 	wsetscrreg(node->pri.win, 0, MAX(SCROLLBACK, node->h) - 1);
    941 	wsetscrreg(node->alt.win, 0, node->h - 1);
    942 	if (d > 0){ /* make sure the new top line syncs up after reshape */
    943 		wmove(node->s->win, oy + d, ox);
    944 		wscrl(node->s->win, -d);
    945 	}
    946 	doupdate();
    947 	refresh();
    948 	ioctl(node->pt, TIOCSWINSZ, &ws);
    949 }
    950 
    951 static void
    952 reshapechildren(NODE *n) /* Reshape all children of a view. */
    953 {
    954 	if (n->t == HORIZONTAL){
    955 		int i = n->w % 2? 0 : 1;
    956 		reshape(n->c1, n->y, n->x, n->h, n->w / 2);
    957 		reshape(n->c2, n->y, n->x + n->w / 2 + 1, n->h, n->w / 2 - i);
    958 	} else if (n->t == VERTICAL){
    959 		int i = n->h % 2? 0 : 1;
    960 		reshape(n->c1, n->y, n->x, n->h / 2, n->w);
    961 		reshape(n->c2, n->y + n->h / 2 + 1, n->x, n->h / 2 - i, n->w);
    962 	}
    963 }
    964 
    965 static void
    966 reshape(NODE *n, int y, int x, int h, int w) /* Reshape a node. */
    967 {
    968 	if (n->y == y && n->x == x && n->h == h && n->w == w && n->t == VIEW)
    969 		return;
    970 
    971 	int d = n->h - h;
    972 	int ow = n->w;
    973 	n->y = y;
    974 	n->x = x;
    975 	n->h = MAX(h, 1);
    976 	n->w = MAX(w, 1);
    977 
    978 	if (n->t == VIEW)
    979 		reshapeview(n, d, ow);
    980 	else
    981 		reshapechildren(n);
    982 	draw(n);
    983 }
    984 
    985 static void
    986 drawchildren(const NODE *n) /* Draw all children of n. */
    987 {
    988 	draw(n->c1);
    989 	if (n->t == HORIZONTAL)
    990 		mvvline(n->y, n->x + n->w / 2, ACS_VLINE, n->h);
    991 	else
    992 		mvhline(n->y + n->h / 2, n->x, ACS_HLINE, n->w);
    993 	wnoutrefresh(stdscr);
    994 	draw(n->c2);
    995 }
    996 
    997 static void
    998 draw(NODE *n) /* Draw a node. */
    999 {
   1000 	if (n->t == VIEW) {
   1001 		pnoutrefresh(
   1002 			n->s->win, n->s->off, 0, n->y, n->x,
   1003 			n->y + n->h - 1, n->x + n->w - 1
   1004 		);
   1005 		return;
   1006 	}
   1007 
   1008 	drawchildren(n);
   1009 }
   1010 
   1011 static void
   1012 split(NODE *n, Node t) /* Split a node. */
   1013 {
   1014 	int nh = t == VERTICAL? (n->h - 1) / 2 : n->h;
   1015 	int nw = t == HORIZONTAL? (n->w) / 2 : n->w;
   1016 
   1017 	NODE *p = n->p;
   1018 
   1019 	NODE *v = newview(NULL, 0, 0, MAX(0, nh), MAX(0, nw));
   1020 	if (!v) return;
   1021 
   1022 	NODE *c = newcontainer(t, n->p, n->y, n->x, n->h, n->w, n, v);
   1023 	if (!c){
   1024 		freenode(v, false);
   1025 		return;
   1026 	}
   1027 
   1028 	replacechild(p, n, c);
   1029 	focus(v);
   1030 	draw(p? p : root);
   1031 }
   1032 
   1033 static bool
   1034 getinput(NODE *n, fd_set *f) /* Recursively check all ptty's for input. */
   1035 {
   1036 	if (n && n->c1 && !getinput(n->c1, f))
   1037 		return false;
   1038 
   1039 	if (n && n->c2 && !getinput(n->c2, f))
   1040 		return false;
   1041 
   1042 	if (n && n->t == VIEW && n->pt > 0 && FD_ISSET(n->pt, f)){
   1043 		ssize_t r = read(n->pt, iobuf, sizeof(iobuf));
   1044 		if (r > 0)
   1045 			vtwrite(&n->vp, iobuf, r);
   1046 		if (r <= 0 && errno != EINTR && errno != EWOULDBLOCK)
   1047 			return deletenode(n), false;
   1048 	}
   1049 
   1050 	return true;
   1051 }
   1052 
   1053 static void
   1054 sendarrow(const NODE *n, const char *k)
   1055 {
   1056 	char buf[100] = {0};
   1057 	snprintf(buf, sizeof(buf) - 1, "\033%s%s", n->pnm? "O" : "[", k);
   1058 	SEND(n, buf);
   1059 }
   1060 
   1061 static bool
   1062 handlechar(int r, int k) /* Handle a single input character. */
   1063 {
   1064 	const char cmdstr[] = {commandkey, 0};
   1065 	static bool cmd = false;
   1066 	NODE *n = focused;
   1067 
   1068 	#define KERR(i)  (r == ERR && (i) == k)
   1069 	#define KEY(i)  (r == OK  && (i) == k)
   1070 	#define CODE(i)  (r == KEY_CODE_YES && (i) == k)
   1071 	#define INSCR  (n->s->tos != n->s->off)
   1072 	#define DO(s, t, a) \
   1073 		if (s == cmd && (t)) { \
   1074 			a ; cmd = false; return true; \
   1075 		}
   1076 
   1077 	DO(cmd, KERR(k), return false)
   1078 	DO(cmd, CODE(KEY_RESIZE), reshape(root, 0, 0, LINES, COLS))
   1079 	DO(false, KEY(commandkey), return cmd = true)
   1080 	DO(false, KEY(0), SENDN(n, "\000", 1))
   1081 	DO(false, KEY(L'\n'), SEND(n, "\n"))
   1082 	DO(false, KEY(L'\r'), SEND(n, n->lnm? "\r\n" : "\r"))
   1083 	DO(false, CODE(KEY_ENTER), SEND(n, n->lnm? "\r\n" : "\r"))
   1084 	DO(false, CODE(KEY_UP), sendarrow(n, "A"));
   1085 	DO(false, CODE(KEY_DOWN), sendarrow(n, "B"));
   1086 	DO(false, CODE(KEY_RIGHT), sendarrow(n, "C"));
   1087 	DO(false, CODE(KEY_LEFT), sendarrow(n, "D"));
   1088 	DO(false, CODE(KEY_HOME), SEND(n, "\033[1~"))
   1089 	DO(false, CODE(KEY_END), SEND(n, "\033[4~"))
   1090 	DO(false, CODE(KEY_PPAGE), SEND(n, "\033[5~"))
   1091 	DO(false, CODE(KEY_NPAGE), SEND(n, "\033[6~"))
   1092 	DO(false, CODE(KEY_BACKSPACE), SEND(n, "\177"))
   1093 	DO(false, CODE(KEY_DC), SEND(n, "\033[3~"))
   1094 	DO(false, CODE(KEY_IC), SEND(n, "\033[2~"))
   1095 	DO(false, CODE(KEY_BTAB), SEND(n, "\033[Z"))
   1096 	DO(false, CODE(KEY_F(1)), SEND(n, "\033OP"))
   1097 	DO(false, CODE(KEY_F(2)), SEND(n, "\033OQ"))
   1098 	DO(false, CODE(KEY_F(3)), SEND(n, "\033OR"))
   1099 	DO(false, CODE(KEY_F(4)), SEND(n, "\033OS"))
   1100 	DO(false, CODE(KEY_F(5)), SEND(n, "\033[15~"))
   1101 	DO(false, CODE(KEY_F(6)), SEND(n, "\033[17~"))
   1102 	DO(false, CODE(KEY_F(7)), SEND(n, "\033[18~"))
   1103 	DO(false, CODE(KEY_F(8)), SEND(n, "\033[19~"))
   1104 	DO(false, CODE(KEY_F(9)), SEND(n, "\033[20~"))
   1105 	DO(false, CODE(KEY_F(10)), SEND(n, "\033[21~"))
   1106 	DO(false, CODE(KEY_F(11)), SEND(n, "\033[23~"))
   1107 	DO(false, CODE(KEY_F(12)), SEND(n, "\033[24~"))
   1108 	DO(true, MOVE_UP, focus(findnode(root, ABOVE(n))))
   1109 	DO(true, MOVE_DOWN, focus(findnode(root, BELOW(n))))
   1110 	DO(true, MOVE_LEFT, focus(findnode(root, LEFT(n))))
   1111 	DO(true, MOVE_RIGHT, focus(findnode(root, RIGHT(n))))
   1112 	DO(true, MOVE_OTHER, focus(lastfocused))
   1113 	DO(true, HSPLIT, split(n, HORIZONTAL))
   1114 	DO(true, VSPLIT, split(n, VERTICAL))
   1115 	DO(true, DELETE_NODE, deletenode(n))
   1116 	DO(true, BAILOUT, (void)1)
   1117 	DO(true, REDRAW, touchwin(stdscr); draw(root); redrawwin(stdscr))
   1118 	DO(true, KEY(commandkey), SENDN(n, cmdstr, 1));
   1119 
   1120 	char c[MB_LEN_MAX + 1] = {0};
   1121 
   1122 	if (wctomb(c, k) > 0) {
   1123 		scrollbottom(n);
   1124 		SEND(n, c);
   1125 	}
   1126 
   1127 	return cmd = false, true;
   1128 }
   1129 
   1130 static void
   1131 run(void) /* Run MTM. */
   1132 {
   1133 	while (root) {
   1134 		wint_t w = 0;
   1135 		fd_set sfds = fds;
   1136 		if (select(nfds + 1, &sfds, NULL, NULL, NULL) < 0)
   1137 			FD_ZERO(&sfds);
   1138 
   1139 		int r = wget_wch(focused->s->win, &w);
   1140 		while (handlechar(r, w))
   1141 			r = wget_wch(focused->s->win, &w);
   1142 		getinput(root, &sfds);
   1143 
   1144 		draw(root);
   1145 		doupdate();
   1146 		fixcursor();
   1147 		draw(focused);
   1148 		doupdate();
   1149 	}
   1150 }
   1151 
   1152 int
   1153 main(int argc, char **argv)
   1154 {
   1155 	FD_SET(STDIN_FILENO, &fds);
   1156 	setlocale(LC_ALL, "");
   1157 	signal(SIGCHLD, SIG_IGN); /* automatically reap children */
   1158 
   1159 	int c = 0;
   1160 	for (;;) {
   1161 		c = getopt(argc, argv, "c:T:t:");
   1162 		if (c == -1) break;
   1163 
   1164 		switch (c) {
   1165 		case 'c': commandkey = CTL(optarg[0]); break;
   1166 		case 'T': setenv("TERM", optarg, 1); break;
   1167 		case 't': term = optarg; break;
   1168 		default: quit(EXIT_FAILURE, USAGE); break;
   1169 		}
   1170 	}
   1171 
   1172 	if (!initscr())
   1173 		quit(EXIT_FAILURE, "could not initialize terminal");
   1174 
   1175 	ESCDELAY = ESCAPE_TIME;
   1176 	raw();
   1177 	noecho();
   1178 	nonl();
   1179 	intrflush(stdscr, FALSE);
   1180 	start_color();
   1181 	use_default_colors();
   1182 	start_pairs();
   1183 
   1184 	root = newview(NULL, 0, 0, LINES, COLS);
   1185 	if (!root)
   1186 		quit(EXIT_FAILURE, "could not open root window");
   1187 
   1188 	focus(root);
   1189 	draw(root);
   1190 	run();
   1191 
   1192 	quit(EXIT_SUCCESS, NULL);
   1193 
   1194 	return EXIT_SUCCESS; /* not reached */
   1195 }
   1196 

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.