git.y1.nz

mtm

Ncurses-based terminal multiplexer
download: https://git.y1.nz/archives/mtm.tar.gz
Files | Log | Refs

mtm.c

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

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