From 42041e10534b6129db624866b8baec9d9069d77f Mon Sep 17 00:00:00 2001 From: Ohio2 Date: Thu, 29 Jul 2021 23:45:33 +0200 Subject: [PATCH] scroll add --- config.def.h | 2 + config.def.h.orig | 31 +- config.def.h.rej | 71 +- config.h | 2 + st | Bin 0 -> 102456 bytes st-scrollback-0.8.4.diff | 351 +++++ st-scrollback-mouse-0.8.2.diff | 85 + st-themed_cursor-0.8.1.diff | 68 + st.c | 125 +- st.c.orig | 2718 ++++++++++++++++++++++++++++++++ st.h | 2 + st.h.orig | 128 ++ st.o | Bin 0 -> 79960 bytes x.c.orig | 19 +- x.c.rej | 10 + x.o | Bin 0 -> 71840 bytes 16 files changed, 3503 insertions(+), 109 deletions(-) create mode 100755 st create mode 100644 st-scrollback-0.8.4.diff create mode 100644 st-scrollback-mouse-0.8.2.diff create mode 100644 st-themed_cursor-0.8.1.diff create mode 100644 st.c.orig create mode 100644 st.h.orig create mode 100644 st.o create mode 100644 x.c.rej create mode 100644 x.o diff --git a/config.def.h b/config.def.h index 8e26726..0fc1570 100644 --- a/config.def.h +++ b/config.def.h @@ -192,6 +192,8 @@ static Shortcut shortcuts[] = { { TERMMOD, XK_Y, selpaste, {.i = 0} }, { ShiftMask, XK_Insert, selpaste, {.i = 0} }, { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, { MODKEY, XK_l, copyurl, {.i = 0} }, }; diff --git a/config.def.h.orig b/config.def.h.orig index e4d9f99..8e26726 100644 --- a/config.def.h.orig +++ b/config.def.h.orig @@ -95,26 +95,25 @@ unsigned int tabspaces = 8; /* Terminal colors (16 first used in escape sequence) */ static const char *colorname[] = { - /* 8 normal colors */ [0] = "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */ - [1] = "#cc241d", /* red */ - [2] = "#98971a", /* green */ - [3] = "#d79921", /* yellow */ - [4] = "#458588", /* blue */ - [5] = "#b16286", /* magenta */ - [6] = "#689d6a", /* cyan */ - [7] = "#a89984", /* white */ + [1] = "#ea6962", /* red */ + [2] = "#a9b665", /* green */ + [3] = "#d8a657", /* yellow */ + [4] = "#7daea3", /* blue */ + [5] = "#d3869b", /* magenta */ + [6] = "#89b482", /* cyan */ + [7] = "#d4be98", /* white */ /* 8 bright colors */ [8] = "#928374", /* black */ - [9] = "#fb4934", /* red */ - [10] = "#b8bb26", /* green */ - [11] = "#fabd2f", /* yellow */ - [12] = "#83a598", /* blue */ - [13] = "#d3869b", /* magenta */ - [14] = "#8ec07c", /* cyan */ - [15] = "#ebdbb2", /* white */ + [9] = "#ef938e", /* red */ + [10] = "#bbc585", /* green */ + [11] = "#e1bb7e", /* yellow */ + [12] = "#9dc2ba", /* blue */ + [13] = "#e1acbb", /* magenta */ + [14] = "#a7c7a2", /* cyan */ + [15] = "#e2d3ba", /* white */ }; /* @@ -133,7 +132,7 @@ static unsigned int defaultrcs = 257; * 6: Bar ("|") * 7: Snowman ("☃") */ -static unsigned int cursorshape = 2; +static unsigned int cursorshape = 6; /* * Default columns and rows numbers diff --git a/config.def.h.rej b/config.def.h.rej index a5f5712..842c96f 100644 --- a/config.def.h.rej +++ b/config.def.h.rej @@ -1,68 +1,11 @@ --- config.def.h +++ config.def.h -@@ -84,41 +84,35 @@ static unsigned int tabspaces = 8; - - /* Terminal colors (16 first used in escape sequence) */ - static const char *colorname[] = { -- /* 8 normal colors */ -- "black", -- "red3", -- "green3", -- "yellow3", -- "blue2", -- "magenta3", -- "cyan3", -- "gray90", -- -- /* 8 bright colors */ -- "gray50", -- "red", -- "green", -- "yellow", -- "#5c5cff", -- "magenta", -- "cyan", -- "white", -- -- [255] = 0, -- -- /* more colors can be added after 255 to use with DefaultXX */ -- "#cccccc", -- "#555555", --}; - -+ /* 8 normal colors */ -+ [0] = "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */ -+ [1] = "#ea6962", /* red */ -+ [2] = "#a9b665", /* green */ -+ [3] = "#d8a657", /* yellow */ -+ [4] = "#7daea3", /* blue */ -+ [5] = "#d3869b", /* magenta */ -+ [6] = "#89b482", /* cyan */ -+ [7] = "#d4be98", /* white */ -+ -+ /* 8 bright colors */ -+ [8] = "#928374", /* black */ -+ [9] = "#ef938e", /* red */ -+ [10] = "#bbc585", /* green */ -+ [11] = "#e1bb7e", /* yellow */ -+ [12] = "#9dc2ba", /* blue */ -+ [13] = "#e1acbb", /* magenta */ -+ [14] = "#a7c7a2", /* cyan */ -+ [15] = "#e2d3ba", /* white */ -+}; - - /* - * Default colors (colorname index) -- * foreground, background, cursor, reverse cursor -+ * foreground, background, cursor - */ --unsigned int defaultfg = 7; --unsigned int defaultbg = 0; --static unsigned int defaultcs = 256; -+unsigned int defaultfg = 15; -+unsigned int defaultbg = 0; -+static unsigned int defaultcs = 15; - static unsigned int defaultrcs = 257; +@@ -199,6 +199,8 @@ static Shortcut shortcuts[] = { + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, ++ { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, ++ { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, + }; /* diff --git a/config.h b/config.h index 8874a14..1b4bbe9 100644 --- a/config.h +++ b/config.h @@ -193,6 +193,8 @@ static Shortcut shortcuts[] = { { TERMMOD, XK_Y, selpaste, {.i = 0} }, { ShiftMask, XK_Insert, selpaste, {.i = 0} }, { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, { MODKEY, XK_l, copyurl, {.i = 0} }, }; diff --git a/st b/st new file mode 100755 index 0000000000000000000000000000000000000000..5d3f2755572012860149b15321533cd68c438d4e GIT binary patch literal 102456 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 0 && term.line[y][i - 1].u == ' ') ++ while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +@@ -526,7 +533,7 @@ selsnap(int *x, int *y, int direction) + * Snap around if the word wraps around at the end or + * beginning of a line. + */ +- prevgp = &term.line[*y][*x]; ++ prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; +@@ -541,14 +548,14 @@ selsnap(int *x, int *y, int direction) + yt = *y, xt = *x; + else + yt = newy, xt = newx; +- if (!(term.line[yt][xt].mode & ATTR_WRAP)) ++ if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + +- gp = &term.line[newy][newx]; ++ gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) +@@ -569,14 +576,14 @@ selsnap(int *x, int *y, int direction) + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { +- if (!(term.line[*y-1][term.col-1].mode ++ if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { +- if (!(term.line[*y][term.col-1].mode ++ if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } +@@ -607,13 +614,13 @@ getsel(void) + } + + if (sel.type == SEL_RECTANGULAR) { +- gp = &term.line[y][sel.nb.x]; ++ gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { +- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; ++ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } +- last = &term.line[y][MIN(lastx, linelen-1)]; ++ last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + +@@ -848,6 +855,9 @@ void + ttywrite(const char *s, size_t n, int may_echo) + { + const char *next; ++ Arg arg = (Arg) { .i = term.scr }; ++ ++ kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); +@@ -1059,13 +1069,53 @@ tswapscreen(void) + } + + void +-tscrolldown(int orig, int n) ++kscrolldown(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (n > term.scr) ++ n = term.scr; ++ ++ if (term.scr > 0) { ++ term.scr -= n; ++ selscroll(0, -n); ++ tfulldirt(); ++ } ++} ++ ++void ++kscrollup(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (term.scr <= HISTSIZE-n) { ++ term.scr += n; ++ selscroll(0, n); ++ tfulldirt(); ++ } ++} ++ ++void ++tscrolldown(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + ++ if (copyhist) { ++ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[term.bot]; ++ term.line[term.bot] = temp; ++ } ++ + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + +@@ -1075,17 +1125,28 @@ tscrolldown(int orig, int n) + term.line[i-n] = temp; + } + +- selscroll(orig, n); ++ if (term.scr == 0) ++ selscroll(orig, n); + } + + void +-tscrollup(int orig, int n) ++tscrollup(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + ++ if (copyhist) { ++ term.histi = (term.histi + 1) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[orig]; ++ term.line[orig] = temp; ++ } ++ ++ if (term.scr > 0 && term.scr < HISTSIZE) ++ term.scr = MIN(term.scr + n, HISTSIZE-1); ++ + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + +@@ -1095,7 +1156,8 @@ tscrollup(int orig, int n) + term.line[i+n] = temp; + } + +- selscroll(orig, -n); ++ if (term.scr == 0) ++ selscroll(orig, -n); + } + + void +@@ -1124,7 +1186,7 @@ tnewline(int first_col) + int y = term.c.y; + + if (y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + y++; + } +@@ -1289,14 +1351,14 @@ void + tinsertblankline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrolldown(term.c.y, n); ++ tscrolldown(term.c.y, n, 0); + } + + void + tdeleteline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrollup(term.c.y, n); ++ tscrollup(term.c.y, n, 0); + } + + int32_t +@@ -1733,11 +1795,11 @@ csihandle(void) + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); +- tscrollup(term.top, csiescseq.arg[0]); ++ tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); +- tscrolldown(term.top, csiescseq.arg[0]); ++ tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); +@@ -2241,7 +2303,7 @@ eschandle(uchar ascii) + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } +@@ -2254,7 +2316,7 @@ eschandle(uchar ascii) + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { +- tscrolldown(term.top, 1); ++ tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } +@@ -2464,7 +2526,7 @@ twrite(const char *buf, int buflen, int show_ctrl) + void + tresize(int col, int row) + { +- int i; ++ int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; +@@ -2501,6 +2563,14 @@ tresize(int col, int row) + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + ++ for (i = 0; i < HISTSIZE; i++) { ++ term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); ++ for (j = mincol; j < col; j++) { ++ term.hist[i][j] = term.c.attr; ++ term.hist[i][j].u = ' '; ++ } ++ } ++ + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); +@@ -2559,7 +2629,7 @@ drawregion(int x1, int y1, int x2, int y2) + continue; + + term.dirty[y] = 0; +- xdrawline(term.line[y], x1, y, x2); ++ xdrawline(TLINE(y), x1, y, x2); + } + } + +@@ -2580,8 +2650,9 @@ draw(void) + cx--; + + drawregion(0, 0, term.col, term.row); +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ if (term.scr == 0) ++ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], ++ term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); +diff --git a/st.h b/st.h +index 3d351b6..f44e1d3 100644 +--- a/st.h ++++ b/st.h +@@ -81,6 +81,8 @@ void die(const char *, ...); + void redraw(void); + void draw(void); + ++void kscrolldown(const Arg *); ++void kscrollup(const Arg *); + void printscreen(const Arg *); + void printsel(const Arg *); + void sendbreak(const Arg *); diff --git a/st-scrollback-mouse-0.8.2.diff b/st-scrollback-mouse-0.8.2.diff new file mode 100644 index 0000000..c49bf89 --- /dev/null +++ b/st-scrollback-mouse-0.8.2.diff @@ -0,0 +1,85 @@ +From 315f69069017122ca69d0319d0ad9481113fe5c8 Mon Sep 17 00:00:00 2001 +From: Jacob Prosser +Date: Fri, 26 Apr 2019 17:23:27 +1000 +Subject: [PATCH] Scrollback-Mouse for 0.8.2 + +--- + config.def.h | 10 ++++++++-- + st.h | 8 ++++++++ + x.c | 9 +++++++++ + 3 files changed, 25 insertions(+), 2 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 7326a74..ad20c4c 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -156,8 +156,14 @@ static unsigned int defaultattr = 11; + */ + static MouseShortcut mshortcuts[] = { + /* button mask string */ +- { Button4, XK_ANY_MOD, "\031" }, +- { Button5, XK_ANY_MOD, "\005" }, ++ { Button4, XK_NO_MOD, "\031" }, ++ { Button5, XK_NO_MOD, "\005" }, ++}; ++ ++MouseKey mkeys[] = { ++ /* button mask function argument */ ++ { Button4, ShiftMask, kscrollup, {.i = 1} }, ++ { Button5, ShiftMask, kscrolldown, {.i = 1} }, + }; + + /* Internal keyboard shortcuts. */ +diff --git a/st.h b/st.h +index 17a79e0..3ac7f57 100644 +--- a/st.h ++++ b/st.h +@@ -76,6 +76,13 @@ typedef union { + const void *v; + } Arg; + ++typedef struct { ++ uint b; ++ uint mask; ++ void (*func)(const Arg *); ++ const Arg arg; ++} MouseKey; ++ + void die(const char *, ...); + void redraw(void); + void draw(void); +@@ -122,3 +129,4 @@ extern char *termname; + extern unsigned int tabspaces; + extern unsigned int defaultfg; + extern unsigned int defaultbg; ++extern MouseKey mkeys[]; +diff --git a/x.c b/x.c +index 0422421..e9fd6e9 100644 +--- a/x.c ++++ b/x.c +@@ -409,6 +409,7 @@ bpress(XEvent *e) + { + struct timespec now; + MouseShortcut *ms; ++ MouseKey *mk; + int snap; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { +@@ -424,6 +425,14 @@ bpress(XEvent *e) + } + } + ++ for (mk = mkeys; mk < mkeys + LEN(mkeys); mk++) { ++ if (e->xbutton.button == mk->b ++ && match(mk->mask, e->xbutton.state)) { ++ mk->func(&mk->arg); ++ return; ++ } ++ } ++ + if (e->xbutton.button == Button1) { + /* + * If the user clicks below predefined timeouts specific +-- +2.21.0 + diff --git a/st-themed_cursor-0.8.1.diff b/st-themed_cursor-0.8.1.diff new file mode 100644 index 0000000..ee89ecc --- /dev/null +++ b/st-themed_cursor-0.8.1.diff @@ -0,0 +1,68 @@ +diff --git a/config.def.h b/config.def.h +index 82b1b09..ffd6cde 100644 ++++ b/config.def.h +@@ -138,11 +138,10 @@ static unsigned int cols = 80; + static unsigned int rows = 24; + + /* +- * Default colour and shape of the mouse cursor ++ * Default shape of the mouse cursor + */ +-static unsigned int mouseshape = XC_xterm; +-static unsigned int mousefg = 7; +-static unsigned int mousebg = 0; ++ ++static char* mouseshape = "xterm"; + + /* + * Color used to display font attributes when fontconfig selected a font which +diff --git a/config.mk b/config.mk +index 039c42c..a0cb4fd 100644 +--- a/config.mk ++++ b/config.mk +@@ -14,7 +14,7 @@ X11LIB = /usr/X11R6/lib + INCS = -I$(X11INC) \ + `pkg-config --cflags fontconfig` \ + `pkg-config --cflags freetype2` +-LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ ++LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXcursor \ + `pkg-config --libs fontconfig` \ + `pkg-config --libs freetype2` + +diff --git a/x.c b/x.c +index c343ba2..5a7461e 100644 +--- a/x.c ++++ b/x.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + + static char *argv0; + #include "arg.h" +@@ -1076,23 +1079,9 @@ xinit(int cols, int rows) + die("XCreateIC failed. Could not obtain input method.\n"); + + /* white cursor, black outline */ +- cursor = XCreateFontCursor(xw.dpy, mouseshape); ++ cursor = XcursorLibraryLoadCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + +- if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { +- xmousefg.red = 0xffff; +- xmousefg.green = 0xffff; +- xmousefg.blue = 0xffff; +- } +- +- if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { +- xmousebg.red = 0x0000; +- xmousebg.green = 0x0000; +- xmousebg.blue = 0x0000; +- } +- +- XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); +- + xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); + xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); + xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); diff --git a/st.c b/st.c index 00dd3e4..270630a 100644 --- a/st.c +++ b/st.c @@ -35,6 +35,7 @@ #define ESC_ARG_SIZ 16 #define STR_BUF_SIZ ESC_BUF_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ +#define HISTSIZE 2000 /* macros */ #define IS_SET(flag) ((term.mode & (flag)) != 0) @@ -42,6 +43,9 @@ #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISDELIM(u) (u && wcschr(worddelimiters, u)) +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ + term.scr + HISTSIZE + 1) % HISTSIZE] : \ + term.line[(y) - term.scr]) enum term_mode { MODE_WRAP = 1 << 0, @@ -115,6 +119,9 @@ typedef struct { int col; /* nb col */ Line *line; /* screen */ Line *alt; /* alternate screen */ + Line hist[HISTSIZE]; /* history buffer */ + int histi; /* history index */ + int scr; /* scroll back */ int *dirty; /* dirtyness of lines */ TCursor c; /* cursor */ int ocx; /* old cursor col */ @@ -184,8 +191,8 @@ static void tnewline(int); static void tputtab(int); static void tputc(Rune); static void treset(void); -static void tscrollup(int, int); -static void tscrolldown(int, int); +static void tscrollup(int, int, int); +static void tscrolldown(int, int, int); static void tsetattr(int *, int); static void tsetchar(Rune, Glyph *, int, int); static void tsetdirt(int, int); @@ -416,10 +423,10 @@ tlinelen(int y) { int i = term.col; - if (term.line[y][i - 1].mode & ATTR_WRAP) + if (TLINE(y)[i - 1].mode & ATTR_WRAP) return i; - while (i > 0 && term.line[y][i - 1].u == ' ') + while (i > 0 && TLINE(y)[i - 1].u == ' ') --i; return i; @@ -528,7 +535,7 @@ selsnap(int *x, int *y, int direction) * Snap around if the word wraps around at the end or * beginning of a line. */ - prevgp = &term.line[*y][*x]; + prevgp = &TLINE(*y)[*x]; prevdelim = ISDELIM(prevgp->u); for (;;) { newx = *x + direction; @@ -543,14 +550,14 @@ selsnap(int *x, int *y, int direction) yt = *y, xt = *x; else yt = newy, xt = newx; - if (!(term.line[yt][xt].mode & ATTR_WRAP)) + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) break; } if (newx >= tlinelen(newy)) break; - gp = &term.line[newy][newx]; + gp = &TLINE(newy)[newx]; delim = ISDELIM(gp->u); if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim || (delim && gp->u != prevgp->u))) @@ -571,14 +578,14 @@ selsnap(int *x, int *y, int direction) *x = (direction < 0) ? 0 : term.col - 1; if (direction < 0) { for (; *y > 0; *y += direction) { - if (!(term.line[*y-1][term.col-1].mode + if (!(TLINE(*y-1)[term.col-1].mode & ATTR_WRAP)) { break; } } } else if (direction > 0) { for (; *y < term.row-1; *y += direction) { - if (!(term.line[*y][term.col-1].mode + if (!(TLINE(*y)[term.col-1].mode & ATTR_WRAP)) { break; } @@ -609,13 +616,13 @@ getsel(void) } if (sel.type == SEL_RECTANGULAR) { - gp = &term.line[y][sel.nb.x]; + gp = &TLINE(y)[sel.nb.x]; lastx = sel.ne.x; } else { - gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; } - last = &term.line[y][MIN(lastx, linelen-1)]; + last = &TLINE(y)[MIN(lastx, linelen-1)]; while (last >= gp && last->u == ' ') --last; @@ -850,6 +857,9 @@ void ttywrite(const char *s, size_t n, int may_echo) { const char *next; + Arg arg = (Arg) { .i = term.scr }; + + kscrolldown(&arg); if (may_echo && IS_SET(MODE_ECHO)) twrite(s, n, 1); @@ -1061,13 +1071,53 @@ tswapscreen(void) } void -tscrolldown(int orig, int n) +kscrolldown(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (n > term.scr) + n = term.scr; + + if (term.scr > 0) { + term.scr -= n; + selscroll(0, -n); + tfulldirt(); + } +} + +void +kscrollup(const Arg* a) +{ + int n = a->i; + + if (n < 0) + n = term.row + n; + + if (term.scr <= HISTSIZE-n) { + term.scr += n; + selscroll(0, n); + tfulldirt(); + } +} + +void +tscrolldown(int orig, int n, int copyhist) { int i; Line temp; LIMIT(n, 0, term.bot-orig+1); + if (copyhist) { + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[term.bot]; + term.line[term.bot] = temp; + } + tsetdirt(orig, term.bot-n); tclearregion(0, term.bot-n+1, term.col-1, term.bot); @@ -1077,17 +1127,28 @@ tscrolldown(int orig, int n) term.line[i-n] = temp; } - selscroll(orig, n); + if (term.scr == 0) + selscroll(orig, n); } void -tscrollup(int orig, int n) +tscrollup(int orig, int n, int copyhist) { int i; Line temp; LIMIT(n, 0, term.bot-orig+1); + if (copyhist) { + term.histi = (term.histi + 1) % HISTSIZE; + temp = term.hist[term.histi]; + term.hist[term.histi] = term.line[orig]; + term.line[orig] = temp; + } + + if (term.scr > 0 && term.scr < HISTSIZE) + term.scr = MIN(term.scr + n, HISTSIZE-1); + tclearregion(0, orig, term.col-1, orig+n-1); tsetdirt(orig+n, term.bot); @@ -1097,7 +1158,8 @@ tscrollup(int orig, int n) term.line[i+n] = temp; } - selscroll(orig, -n); + if (term.scr == 0) + selscroll(orig, -n); } void @@ -1126,7 +1188,7 @@ tnewline(int first_col) int y = term.c.y; if (y == term.bot) { - tscrollup(term.top, 1); + tscrollup(term.top, 1, 1); } else { y++; } @@ -1291,14 +1353,14 @@ void tinsertblankline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrolldown(term.c.y, n); + tscrolldown(term.c.y, n, 0); } void tdeleteline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrollup(term.c.y, n); + tscrollup(term.c.y, n, 0); } int32_t @@ -1735,11 +1797,11 @@ csihandle(void) break; case 'S': /* SU -- Scroll line up */ DEFAULT(csiescseq.arg[0], 1); - tscrollup(term.top, csiescseq.arg[0]); + tscrollup(term.top, csiescseq.arg[0], 0); break; case 'T': /* SD -- Scroll line down */ DEFAULT(csiescseq.arg[0], 1); - tscrolldown(term.top, csiescseq.arg[0]); + tscrolldown(term.top, csiescseq.arg[0], 0); break; case 'L': /* IL -- Insert blank lines */ DEFAULT(csiescseq.arg[0], 1); @@ -2243,7 +2305,7 @@ eschandle(uchar ascii) return 0; case 'D': /* IND -- Linefeed */ if (term.c.y == term.bot) { - tscrollup(term.top, 1); + tscrollup(term.top, 1, 1); } else { tmoveto(term.c.x, term.c.y+1); } @@ -2256,7 +2318,7 @@ eschandle(uchar ascii) break; case 'M': /* RI -- Reverse index */ if (term.c.y == term.top) { - tscrolldown(term.top, 1); + tscrolldown(term.top, 1, 1); } else { tmoveto(term.c.x, term.c.y-1); } @@ -2466,7 +2528,7 @@ twrite(const char *buf, int buflen, int show_ctrl) void tresize(int col, int row) { - int i; + int i, j; int minrow = MIN(row, term.row); int mincol = MIN(col, term.col); int *bp; @@ -2503,6 +2565,14 @@ tresize(int col, int row) term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + for (i = 0; i < HISTSIZE; i++) { + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); + for (j = mincol; j < col; j++) { + term.hist[i][j] = term.c.attr; + term.hist[i][j].u = ' '; + } + } + /* resize each row to new width, zero-pad if needed */ for (i = 0; i < minrow; i++) { term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); @@ -2561,7 +2631,7 @@ drawregion(int x1, int y1, int x2, int y2) continue; term.dirty[y] = 0; - xdrawline(term.line[y], x1, y, x2); + xdrawline(TLINE(y), x1, y, x2); } } @@ -2582,8 +2652,9 @@ draw(void) cx--; drawregion(0, 0, term.col, term.row); - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], - term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + if (term.scr == 0) + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); term.ocx = cx; term.ocy = term.c.y; xfinishdraw(); diff --git a/st.c.orig b/st.c.orig new file mode 100644 index 0000000..00dd3e4 --- /dev/null +++ b/st.c.orig @@ -0,0 +1,2718 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, +}; + +enum cursor_movement { + CURSOR_SAVE, + CURSOR_LOAD +}; + +enum cursor_state { + CURSOR_DEFAULT = 0, + CURSOR_WRAPNEXT = 1, + CURSOR_ORIGIN = 2 +}; + +enum charset { + CS_GRAPHIC0, + CS_GRAPHIC1, + CS_UK, + CS_USA, + CS_MULTI, + CS_GER, + CS_FIN +}; + +enum escape_state { + ESC_START = 1, + ESC_CSI = 2, + ESC_STR = 4, /* DCS, OSC, PM, APC */ + ESC_ALTCHARSET = 8, + ESC_STR_END = 16, /* a final string was encountered */ + ESC_TEST = 32, /* Enter in test mode */ + ESC_UTF8 = 64, +}; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + Rune lastc; /* last printed char outside of sequence, 0 if control */ +} Term; + +/* CSI Escape sequence structs */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + size_t len; /* raw string length */ + char priv; + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; +} CSIEscape; + +/* STR Escape sequence structs */ +/* ESC type [[ [] [;]] ] ESC '\' */ +typedef struct { + char type; /* ESC type ... */ + char *buf; /* allocated raw string */ + size_t siz; /* allocation size */ + size_t len; /* raw string length */ + char *args[STR_ARG_SIZ]; + int narg; /* nb of args */ +} STREscape; + +static void execsh(char *, char **); +static void stty(char **); +static void sigchld(int); +static void ttywriteraw(const char *, size_t); + +static void csidump(void); +static void csihandle(void); +static void csiparse(void); +static void csireset(void); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int); +static void tscrolldown(int, int); +static void tsetattr(int *, int); +static void tsetchar(Rune, Glyph *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, int *, int); +static int twrite(const char *, int, int); +static void tfulldirt(void); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32_t tdefcolor(int *, int *, int); +static void tdeftran(char); +static void tstrsequence(uchar); +static void tsetcolor(int, int, int, uint32_t, uint32_t); +static char * findlastany(char *, const char**, size_t); + +static void drawregion(int, int, int, int); + +static void selnormalize(void); +static void selscroll(int, int); +static void selsnap(int *, int *, int); + +static size_t utf8decode(const char *, Rune *, size_t); +static Rune utf8decodebyte(char, size_t *); +static char utf8encodebyte(Rune, size_t); +static size_t utf8validate(Rune *, size_t); + +static char *base64dec(const char *); +static char base64dec_getc(const char **); + +static ssize_t xwrite(int, const char *, size_t); + +/* Globals */ +static Term term; +static Selection sel; +static CSIEscape csiescseq; +static STREscape strescseq; +static int iofd = 1; +static int cmdfd; +static pid_t pid; + +static uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +ssize_t +xwrite(int fd, const char *s, size_t len) +{ + size_t aux = len; + ssize_t r; + + while (len > 0) { + r = write(fd, s, len); + if (r < 0) + return r; + len -= r; + s += r; + } + + return aux; +} + +void * +xmalloc(size_t len) +{ + void *p; + + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); + + return p; +} + +void * +xrealloc(void *p, size_t len) +{ + if ((p = realloc(p, len)) == NULL) + die("realloc: %s\n", strerror(errno)); + + return p; +} + +char * +xstrdup(char *s) +{ + if ((s = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return s; +} + +size_t +utf8decode(const char *c, Rune *u, size_t clen) +{ + size_t i, j, len, type; + Rune udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Rune +utf8decodebyte(char c, size_t *i) +{ + for (*i = 0; *i < LEN(utfmask); ++(*i)) + if (((uchar)c & utfmask[*i]) == utfbyte[*i]) + return (uchar)c & ~utfmask[*i]; + + return 0; +} + +size_t +utf8encode(Rune u, char *c) +{ + size_t len, i; + + len = utf8validate(&u, 0); + if (len > UTF_SIZ) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = utf8encodebyte(u, 0); + u >>= 6; + } + c[0] = utf8encodebyte(u, len); + + return len; +} + +char +utf8encodebyte(Rune u, size_t i) +{ + return utfbyte[i] | (u & ~utfmask[i]); +} + +size_t +utf8validate(Rune *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + + return i; +} + +static const char base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint(**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SEL_IDLE; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + + if (term.line[y][i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && term.line[y][i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + Glyph *gp, *prevgp; + + switch (sel.snap) { + case SNAP_WORD: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &term.line[*y][*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(term.line[yt][xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &term.line[newy][newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(term.line[*y-1][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(term.line[*y][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + Glyph *gp, *last; + + if (sel.ob.x == -1) + return NULL; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for (y = sel.nb.y; y <= sel.ne.y; y++) { + if ((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if (sel.type == SEL_RECTANGULAR) { + gp = &term.line[y][sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &term.line[y][MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + *ptr++ = '\n'; + } + *ptr = 0; + return str; +} + +void +selclear(void) +{ + if (sel.ob.x == -1) + return; + sel.mode = SEL_IDLE; + sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +execsh(char *cmd, char **args) +{ + char *sh, *prog, *arg; + const struct passwd *pw; + + errno = 0; + if ((pw = getpwuid(getuid())) == NULL) { + if (errno) + die("getpwuid: %s\n", strerror(errno)); + else + die("who are you?\n"); + } + + if ((sh = getenv("SHELL")) == NULL) + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; + + if (args) { + prog = args[0]; + arg = NULL; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = NULL; + } else { + prog = sh; + arg = NULL; + } + DEFAULT(args, ((char *[]) {prog, arg, NULL})); + + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + setenv("LOGNAME", pw->pw_name, 1); + setenv("USER", pw->pw_name, 1); + setenv("SHELL", sh, 1); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + execvp(prog, args); + _exit(1); +} + +void +sigchld(int a) +{ + int stat; + pid_t p; + + if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + + if (pid != p) + return; + + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + die("child exited with status %d\n", WEXITSTATUS(stat)); + else if (WIFSIGNALED(stat)) + die("child terminated due to signal %d\n", WTERMSIG(stat)); + _exit(0); +} + +void +stty(char **args) +{ + char cmd[_POSIX_ARG_MAX], **p, *q, *s; + size_t n, siz; + + if ((n = strlen(stty_args)) > sizeof(cmd)-1) + die("incorrect stty parameters\n"); + memcpy(cmd, stty_args, n); + q = cmd + n; + siz = sizeof(cmd) - n; + for (p = args; p && (s = *p); ++p) { + if ((n = strlen(s)) > siz-1) + die("stty parameter length too long\n"); + *q++ = ' '; + memcpy(q, s, n); + q += n; + siz -= n + 1; + } + *q = '\0'; + if (system(cmd) != 0) + perror("Couldn't call stty"); +} + +int +ttynew(char *line, char *cmd, char *out, char **args) +{ + int m, s; + + if (out) { + term.mode |= MODE_PRINT; + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); + if (iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + out, strerror(errno)); + } + } + + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) + die("open line '%s' failed: %s\n", + line, strerror(errno)); + dup2(cmdfd, 0); + stty(args); + return cmdfd; + } + + /* seems to work fine on linux, openbsd and freebsd */ + if (openpty(&m, &s, NULL, NULL, NULL) < 0) + die("openpty failed: %s\n", strerror(errno)); + + switch (pid = fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + close(iofd); + setsid(); /* create a new process group */ + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + if (ioctl(s, TIOCSCTTY, NULL) < 0) + die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); + close(s); + close(m); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif + execsh(cmd, args); + break; + default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif + close(s); + cmdfd = m; + signal(SIGCHLD, sigchld); + break; + } + return cmdfd; +} + +size_t +ttyread(void) +{ + static char buf[BUFSIZ]; + static int buflen = 0; + int ret, written; + + /* append read bytes to unprocessed bytes */ + ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + + switch (ret) { + case 0: + exit(0); + case -1: + die("couldn't read from shell: %s\n", strerror(errno)); + default: + buflen += ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + return ret; + } +} + +void +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + die("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + static TCursor c[2]; + int alt = IS_SET(MODE_ALTSCREEN); + + if (mode == CURSOR_SAVE) { + c[alt] = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); + } +} + +void +treset(void) +{ + uint i; + + term.c = (TCursor){{ + .mode = ATTR_NULL, + .fg = defaultfg, + .bg = defaultbg + }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = MODE_WRAP|MODE_UTF8; + memset(term.trantbl, CS_USA, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } +} + +void +tnew(int col, int row) +{ + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +tscrolldown(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + selscroll(orig, n); +} + +void +tscrollup(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { + selclear(); + } else if (BETWEEN(sel.nb.y, orig, term.bot)) { + sel.ob.y += n; + sel.oe.y += n; + if (sel.ob.y < term.top || sel.ob.y > term.bot || + sel.oe.y < term.top || sel.oe.y > term.bot) { + selclear(); + } else { + selnormalize(); + } + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + + csiescseq.narg = 0; + if (*p == '?') { + csiescseq.priv = 1; + p++; + } + + csiescseq.buf[csiescseq.len] = '\0'; + while (p < csiescseq.buf+csiescseq.len) { + np = NULL; + v = strtol(p, &np, 10); + if (np == p) + v = 0; + if (v == LONG_MAX || v == LONG_MIN) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; + } + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; +} + +/* for absolute user moves, when decom is set */ +void +tmoveato(int x, int y) +{ + tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); +} + +void +tmoveto(int x, int y) +{ + int miny, maxy; + + if (term.c.state & CURSOR_ORIGIN) { + miny = term.top; + maxy = term.bot; + } else { + miny = 0; + maxy = term.row - 1; + } + term.c.state &= ~CURSOR_WRAPNEXT; + term.c.x = LIMIT(x, 0, term.col-1); + term.c.y = LIMIT(y, miny, maxy); +} + +void +tsetchar(Rune u, Glyph *attr, int x, int y) +{ + static char *vt100_0[62] = { /* 0x41 - 0x7e */ + "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + + /* + * The table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CS_GRAPHIC0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + + if (term.line[y][x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; + } + } else if (term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, temp; + Glyph *gp; + + if (x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n); +} + +int32_t +tdefcolor(int *attr, int *npar, int l) +{ + int32_t idx = -1; + uint r, g, b; + + switch (attr[*npar + 1]) { + case 2: /* direct color in RGB space */ + if (*npar + 4 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + r = attr[*npar + 2]; + g = attr[*npar + 3]; + b = attr[*npar + 4]; + *npar += 4; + if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) + fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", + r, g, b); + else + idx = TRUECOLOR(r, g, b); + break; + case 5: /* indexed color */ + if (*npar + 2 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + *npar += 2; + if (!BETWEEN(attr[*npar], 0, 255)) + fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); + else + idx = attr[*npar]; + break; + case 0: /* implemented defined (only foreground) */ + case 1: /* transparent */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ + default: + fprintf(stderr, + "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; + } + + return idx; +} + +void +tsetattr(int *attr, int l) +{ + int i; + int32_t idx; + + for (i = 0; i < l; i++) { + switch (attr[i]) { + case 0: + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; + break; + case 1: + term.c.attr.mode |= ATTR_BOLD; + break; + case 2: + term.c.attr.mode |= ATTR_FAINT; + break; + case 3: + term.c.attr.mode |= ATTR_ITALIC; + break; + case 4: + term.c.attr.mode |= ATTR_UNDERLINE; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ + case 6: /* rapid blink */ + term.c.attr.mode |= ATTR_BLINK; + break; + case 7: + term.c.attr.mode |= ATTR_REVERSE; + break; + case 8: + term.c.attr.mode |= ATTR_INVISIBLE; + break; + case 9: + term.c.attr.mode |= ATTR_STRUCK; + break; + case 22: + term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); + break; + case 23: + term.c.attr.mode &= ~ATTR_ITALIC; + break; + case 24: + term.c.attr.mode &= ~ATTR_UNDERLINE; + break; + case 25: + term.c.attr.mode &= ~ATTR_BLINK; + break; + case 27: + term.c.attr.mode &= ~ATTR_REVERSE; + break; + case 28: + term.c.attr.mode &= ~ATTR_INVISIBLE; + break; + case 29: + term.c.attr.mode &= ~ATTR_STRUCK; + break; + case 38: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.fg = idx; + break; + case 39: + term.c.attr.fg = defaultfg; + break; + case 48: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.bg = idx; + break; + case 49: + term.c.attr.bg = defaultbg; + break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; + } else if (BETWEEN(attr[i], 40, 47)) { + term.c.attr.bg = attr[i] - 40; + } else if (BETWEEN(attr[i], 90, 97)) { + term.c.attr.fg = attr[i] - 90 + 8; + } else if (BETWEEN(attr[i], 100, 107)) { + term.c.attr.bg = attr[i] - 100 + 8; + } else { + fprintf(stderr, + "erresc(default): gfx attr %d unknown\n", + attr[i]); + csidump(); + } + break; + } + } +} + +void +tsetscroll(int t, int b) +{ + int temp; + + LIMIT(t, 0, term.row-1); + LIMIT(b, 0, term.row-1); + if (t > b) { + temp = t; + t = b; + b = temp; + } + term.top = t; + term.bot = b; +} + +void +tsetmode(int priv, int set, int *args, int narg) +{ + int alt, *lim; + + for (lim = args + narg; args < lim; ++args) { + if (priv) { + switch (*args) { + case 1: /* DECCKM -- Cursor key */ + xsetmode(set, MODE_APPCURSOR); + break; + case 5: /* DECSCNM -- Reverse video */ + xsetmode(set, MODE_REVERSE); + break; + case 6: /* DECOM -- Origin */ + MODBIT(term.c.state, set, CURSOR_ORIGIN); + tmoveato(0, 0); + break; + case 7: /* DECAWM -- Auto wrap */ + MODBIT(term.mode, set, MODE_WRAP); + break; + case 0: /* Error (IGNORED) */ + case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ + case 3: /* DECCOLM -- Column (IGNORED) */ + case 4: /* DECSCLM -- Scroll (IGNORED) */ + case 8: /* DECARM -- Auto repeat (IGNORED) */ + case 18: /* DECPFF -- Printer feed (IGNORED) */ + case 19: /* DECPEX -- Printer extent (IGNORED) */ + case 42: /* DECNRCM -- National characters (IGNORED) */ + case 12: /* att610 -- Start blinking cursor (IGNORED) */ + break; + case 25: /* DECTCEM -- Text Cursor Enable Mode */ + xsetmode(!set, MODE_HIDE); + break; + case 9: /* X10 mouse compatibility mode */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEX10); + break; + case 1000: /* 1000: report button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEBTN); + break; + case 1002: /* 1002: report motion on button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMOTION); + break; + case 1003: /* 1003: enable all mouse motions */ + xsetpointermotion(set); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMANY); + break; + case 1004: /* 1004: send focus events to tty */ + xsetmode(set, MODE_FOCUS); + break; + case 1006: /* 1006: extended reporting mode */ + xsetmode(set, MODE_MOUSESGR); + break; + case 1034: + xsetmode(set, MODE_8BIT); + break; + case 1049: /* swap screen & set/restore cursor as xterm */ + if (!allowaltscreen) + break; + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + /* FALLTHROUGH */ + case 47: /* swap screen */ + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(MODE_ALTSCREEN); + if (alt) { + tclearregion(0, 0, term.col-1, + term.row-1); + } + if (set ^ alt) /* set is always 1 or 0 */ + tswapscreen(); + if (*args != 1049) + break; + /* FALLTHROUGH */ + case 1048: + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + break; + case 2004: /* 2004: bracketed paste mode */ + xsetmode(set, MODE_BRCKTPASTE); + break; + /* Not implemented mouse modes. See comments there. */ + case 1001: /* mouse highlight mode; can hang the + terminal by design when implemented. */ + case 1005: /* UTF-8 mouse mode; will confuse + applications not supporting UTF-8 + and luit. */ + case 1015: /* urxvt mangled mouse mode; incompatible + and can be mistaken for other control + codes. */ + break; + default: + fprintf(stderr, + "erresc: unknown private set/reset mode %d\n", + *args); + break; + } + } else { + switch (*args) { + case 0: /* Error (IGNORED) */ + break; + case 2: + xsetmode(set, MODE_KBDLOCK); + break; + case 4: /* IRM -- Insertion-replacement */ + MODBIT(term.mode, set, MODE_INSERT); + break; + case 12: /* SRM -- Send/Receive */ + MODBIT(term.mode, !set, MODE_ECHO); + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, MODE_CRLF); + break; + default: + fprintf(stderr, + "erresc: unknown set/reset mode %d\n", + *args); + break; + } + } + } +} + +void +csihandle(void) +{ + char buf[40]; + int len; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case '@': /* ICH -- Insert blank char */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblank(csiescseq.arg[0]); + break; + case 'A': /* CUU -- Cursor Up */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); + break; + case 'B': /* CUD -- Cursor Down */ + case 'e': /* VPR --Cursor Down */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); + break; + case 'i': /* MC -- Media Copy */ + switch (csiescseq.arg[0]) { + case 0: + tdump(); + break; + case 1: + tdumpline(term.c.y); + break; + case 2: + tdumpsel(); + break; + case 4: + term.mode &= ~MODE_PRINT; + break; + case 5: + term.mode |= MODE_PRINT; + break; + } + break; + case 'c': /* DA -- Device Attributes */ + if (csiescseq.arg[0] == 0) + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; + case 'C': /* CUF -- Cursor Forward */ + case 'a': /* HPR -- Cursor Forward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x+csiescseq.arg[0], term.c.y); + break; + case 'D': /* CUB -- Cursor Backward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x-csiescseq.arg[0], term.c.y); + break; + case 'E': /* CNL -- Cursor Down and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y+csiescseq.arg[0]); + break; + case 'F': /* CPL -- Cursor Up and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y-csiescseq.arg[0]); + break; + case 'g': /* TBC -- Tabulation clear */ + switch (csiescseq.arg[0]) { + case 0: /* clear current tab stop */ + term.tabs[term.c.x] = 0; + break; + case 3: /* clear all the tabs */ + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + break; + default: + goto unknown; + } + break; + case 'G': /* CHA -- Move to */ + case '`': /* HPA */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(csiescseq.arg[0]-1, term.c.y); + break; + case 'H': /* CUP -- Move to */ + case 'f': /* HVP */ + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], 1); + tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); + break; + case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(csiescseq.arg[0]); + break; + case 'J': /* ED -- Clear screen */ + switch (csiescseq.arg[0]) { + case 0: /* below */ + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + if (term.c.y < term.row-1) { + tclearregion(0, term.c.y+1, term.col-1, + term.row-1); + } + break; + case 1: /* above */ + if (term.c.y > 1) + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, 0, term.col-1, term.row-1); + break; + default: + goto unknown; + } + break; + case 'K': /* EL -- Clear line */ + switch (csiescseq.arg[0]) { + case 0: /* right */ + tclearregion(term.c.x, term.c.y, term.col-1, + term.c.y); + break; + case 1: /* left */ + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, term.c.y, term.col-1, term.c.y); + break; + } + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0]); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0]); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC["); + for (i = 0; i < csiescseq.len; i++) { + c = csiescseq.buf[i] & 0xff; + if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + putc('\n', stderr); +} + +void +csireset(void) +{ + memset(&csiescseq, 0, sizeof(csiescseq)); +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + + term.esc &= ~(ESC_STR_END|ESC_STR); + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; + + switch (strescseq.type) { + case ']': /* OSC -- Operating System Command */ + switch (par) { + case 0: + case 1: + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset, here p = NULL */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); + } else { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + redraw(); + } + return; + } + break; + case 'k': /* old title set compatibility */ + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; + } + + fprintf(stderr, "erresc: unknown str "); + strdump(); +} + +void +strparse(void) +{ + int c; + char *p = strescseq.buf; + + strescseq.narg = 0; + strescseq.buf[strescseq.len] = '\0'; + + if (*p == '\0') + return; + + while (strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while ((c = *p) != ';' && c != '\0') + ++p; + if (c == '\0') + return; + *p++ = '\0'; + } +} + +void +strdump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC%c", strescseq.type); + for (i = 0; i < strescseq.len; i++) { + c = strescseq.buf[i] & 0xff; + if (c == '\0') { + putc('\n', stderr); + return; + } else if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + fprintf(stderr, "ESC\\\n"); +} + +void +strreset(void) +{ + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; +} + +void +sendbreak(const Arg *arg) +{ + if (tcsendbreak(cmdfd, 0)) + perror("Error sending break"); +} + +void +tprinter(char *s, size_t len) +{ + if (iofd != -1 && xwrite(iofd, s, len) < 0) { + perror("Error writing to output file"); + close(iofd); + iofd = -1; + } +} + +void +toggleprinter(const Arg *arg) +{ + term.mode ^= MODE_PRINT; +} + +void +printscreen(const Arg *arg) +{ + tdump(); +} + +void +printsel(const Arg *arg) +{ + tdumpsel(); +} + +void +tdumpsel(void) +{ + char *ptr; + + if ((ptr = getsel())) { + tprinter(ptr, strlen(ptr)); + free(ptr); + } +} + +void +tdumpline(int n) +{ + char buf[UTF_SIZ]; + Glyph *bp, *end; + + bp = &term.line[n][0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) + tprinter(buf, utf8encode(bp->u, buf)); + } + tprinter("\n", 1); +} + +void +tdump(void) +{ + int i; + + for (i = 0; i < term.row; ++i) + tdumpline(i); +} + +void +tputtab(int n) +{ + uint x = term.c.x; + + if (n > 0) { + while (x < term.col && n--) + for (++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if (n < 0) { + while (x > 0 && n++) + for (--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; + } + term.c.x = LIMIT(x, 0, term.col-1); +} + +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= MODE_UTF8; + else if (ascii == '@') + term.mode &= ~MODE_UTF8; +} + +void +tdeftran(char ascii) +{ + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; + + if ((p = strchr(cs, ascii)) == NULL) { + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) +{ + int x, y; + + if (c == '8') { /* DEC screen alignment test. */ + for (x = 0; x < term.col; ++x) { + for (y = 0; y < term.row; ++y) + tsetchar('E', &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) +{ + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; +} + +void +tcontrolcode(uchar ascii) +{ + switch (ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + return; + case '\a': /* BEL */ + if (term.esc & ESC_STR_END) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + xbell(); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return; + case '\016': /* SO (LS1 -- Locking shift 1) */ + case '\017': /* SI (LS0 -- Locking shift 0) */ + term.charset = 1 - (ascii - '\016'); + return; + case '\032': /* SUB */ + tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= ESC_ALTCHARSET; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (u < 127 || !IS_SET(MODE_UTF8)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) + width = 1; + } + + if (IS_SET(MODE_PRINT)) + tprinter(c, len); + + /* + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. + */ + if (term.esc & ESC_STR) { + if (u == '\a' || u == 030 || u == 032 || u == 033 || + ISCONTROLC1(u)) { + term.esc &= ~(ESC_START|ESC_STR); + term.esc |= ESC_STR_END; + goto check_control_code; + } + + if (strescseq.len+len >= strescseq.siz) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); + } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } + +check_control_code: + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if (control) { + tcontrolcode(u); + /* + * control codes are not shown ever + */ + if (!term.esc) + term.lastc = 0; + return; + } else if (term.esc & ESC_START) { + if (term.esc & ESC_CSI) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + term.esc = 0; + csiparse(); + csihandle(); + } + return; + } else if (term.esc & ESC_UTF8) { + tdefutf8(u); + } else if (term.esc & ESC_ALTCHARSET) { + tdeftran(u); + } else if (term.esc & ESC_TEST) { + tdectest(u); + } else { + if (!eschandle(u)) + return; + /* sequence already finished */ + } + term.esc = 0; + /* + * All characters which form part of a sequence are not + * printed + */ + return; + } + if (selected(term.c.x, term.c.y)) + selclear(); + + gp = &term.line[term.c.y][term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + + if (term.c.x+width > term.col) { + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; + + if (width == 2) { + gp->mode |= ATTR_WIDE; + if (term.c.x+1 < term.col) { + gp[1].u = '\0'; + gp[1].mode = ATTR_WDUMMY; + } + } + if (term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); + } else { + term.c.state |= CURSOR_WRAPNEXT; + } +} + +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +tresize(int col, int row) +{ + int i; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + TCursor c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(term.line[y], x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} + +void +tsetcolor( int row, int start, int end, uint32_t fg, uint32_t bg ) +{ + int i = start; + for( ; i < end; ++i ) + { + term.line[row][i].fg = fg; + term.line[row][i].bg = bg; + } +} + +char * +findlastany(char *str, const char** find, size_t len) +{ + char* found = NULL; + int i = 0; + for(found = str + strlen(str) - 1; found >= str; --found) { + for(i = 0; i < len; i++) { + if(strncmp(found, find[i], strlen(find[i])) == 0) { + return found; + } + } + } + + return NULL; +} + +/* +** Select and copy the previous url on screen (do nothing if there's no url). +** +** FIXME: doesn't handle urls that span multiple lines; will need to add support +** for multiline "getsel()" first +*/ +void +copyurl(const Arg *arg) { + /* () and [] can appear in urls, but excluding them here will reduce false + * positives when figuring out where a given url ends. + */ + static char URLCHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-._~:/?#@!$&'*+,;=%"; + + static const char* URLSTRINGS[] = {"http://", "https://"}; + + /* remove highlighting from previous selection if any */ + if(sel.ob.x >= 0 && sel.oe.x >= 0) + tsetcolor(sel.nb.y, sel.ob.x, sel.oe.x + 1, defaultfg, defaultbg); + + int i = 0, + row = 0, /* row of current URL */ + col = 0, /* column of current URL start */ + startrow = 0, /* row of last occurrence */ + colend = 0, /* column of last occurrence */ + passes = 0; /* how many rows have been scanned */ + + char *linestr = calloc(term.col+1, sizeof(Rune)); + char *c = NULL, + *match = NULL; + + row = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.y : term.bot; + LIMIT(row, term.top, term.bot); + startrow = row; + + colend = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.x : term.col; + LIMIT(colend, 0, term.col); + + /* + ** Scan from (term.bot,term.col) to (0,0) and find + ** next occurrance of a URL + */ + while(passes !=term.bot + 2) { + /* Read in each column of every row until + ** we hit previous occurrence of URL + */ + for (col = 0, i = 0; col < colend; ++col,++i) { + linestr[i] = term.line[row][col].u; + } + linestr[term.col] = '\0'; + + if ((match = findlastany(linestr, URLSTRINGS, + sizeof(URLSTRINGS)/sizeof(URLSTRINGS[0])))) + break; + + if (--row < term.top) + row = term.bot; + + colend = term.col; + passes++; + }; + + if (match) { + /* must happen before trim */ + selclear(); + sel.ob.x = strlen(linestr) - strlen(match); + + /* trim the rest of the line from the url match */ + for (c = match; *c != '\0'; ++c) + if (!strchr(URLCHARS, *c)) { + *c = '\0'; + break; + } + + /* highlight selection by inverting terminal colors */ + tsetcolor(row, sel.ob.x, sel.ob.x + strlen( match ), defaultbg, defaultfg); + + /* select and copy */ + sel.mode = 1; + sel.type = SEL_REGULAR; + sel.oe.x = sel.ob.x + strlen(match)-1; + sel.ob.y = sel.oe.y = row; + selnormalize(); + tsetdirt(sel.nb.y, sel.ne.y); + xsetsel(getsel()); + xclipcopy(); + } + + free(linestr); +} diff --git a/st.h b/st.h index d84d3fc..adcda8a 100644 --- a/st.h +++ b/st.h @@ -81,6 +81,8 @@ void die(const char *, ...); void redraw(void); void draw(void); +void kscrolldown(const Arg *); +void kscrollup(const Arg *); void printscreen(const Arg *); void printsel(const Arg *); void sendbreak(const Arg *); diff --git a/st.h.orig b/st.h.orig new file mode 100644 index 0000000..adcda8a --- /dev/null +++ b/st.h.orig @@ -0,0 +1,128 @@ +/* See LICENSE for license details. */ + +#include +#include + +/* macros */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) + +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) + +enum glyph_attribute { + ATTR_NULL = 0, + ATTR_BOLD = 1 << 0, + ATTR_FAINT = 1 << 1, + ATTR_ITALIC = 1 << 2, + ATTR_UNDERLINE = 1 << 3, + ATTR_BLINK = 1 << 4, + ATTR_REVERSE = 1 << 5, + ATTR_INVISIBLE = 1 << 6, + ATTR_STRUCK = 1 << 7, + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, +}; + +enum selection_mode { + SEL_IDLE = 0, + SEL_EMPTY = 1, + SEL_READY = 2 +}; + +enum selection_type { + SEL_REGULAR = 1, + SEL_RECTANGULAR = 2 +}; + +enum selection_snap { + SNAP_WORD = 1, + SNAP_LINE = 2 +}; + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned short ushort; + +typedef uint_least32_t Rune; + +#define Glyph Glyph_ +typedef struct { + Rune u; /* character code */ + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ +} Glyph; + +typedef Glyph *Line; + +typedef union { + int i; + uint ui; + float f; + const void *v; + const char *s; +} Arg; + +void die(const char *, ...); +void redraw(void); +void draw(void); + +void kscrolldown(const Arg *); +void kscrollup(const Arg *); +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); +void copyurl(const Arg *); + +int tattrset(int); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void ttyhangup(void); +int ttynew(char *, char *, char *, char **); +size_t ttyread(void); +void ttyresize(int, int); +void ttywrite(const char *, size_t, int); + +void resettitle(void); + +void selclear(void); +void selinit(void); +void selstart(int, int, int); +void selextend(int, int, int, int); +int selected(int, int); +char *getsel(void); + +size_t utf8encode(Rune, char *); + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(char *); + +/* config.h globals */ +extern char *utmp; +extern char *scroll; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern int allowaltscreen; +extern int allowwindowops; +extern char *termname; +extern unsigned int tabspaces; +extern unsigned int defaultfg; +extern unsigned int defaultbg; diff --git a/st.o b/st.o new file mode 100644 index 0000000000000000000000000000000000000000..cc4aa91c69e7918fb4bafc0422a9a726e2b7d2f4 GIT binary patch literal 79960 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 literal 0 HcmV?d00001 diff --git a/x.c.orig b/x.c.orig index 210f184..3d81165 100644 --- a/x.c.orig +++ b/x.c.orig @@ -1488,6 +1488,7 @@ void xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) { Color drawcol; + XRenderColor colbg; /* remove the old cursor */ if (selected(ox, oy)) @@ -1517,10 +1518,24 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) g.fg = defaultfg; g.bg = defaultrcs; } else { + /** this is the main part of the dynamic cursor color patch */ + g.bg = g.fg; g.fg = defaultbg; - g.bg = defaultcs; } - drawcol = dc.col[g.bg]; + + /** + * and this is the second part of the dynamic cursor color patch. + * it handles the `drawcol` variable + */ + if (IS_TRUECOL(g.bg)) { + colbg.alpha = 0xffff; + colbg.red = TRUERED(g.bg); + colbg.green = TRUEGREEN(g.bg); + colbg.blue = TRUEBLUE(g.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &drawcol); + } else { + drawcol = dc.col[g.bg]; + } } /* draw the new one */ diff --git a/x.c.rej b/x.c.rej new file mode 100644 index 0000000..035510e --- /dev/null +++ b/x.c.rej @@ -0,0 +1,10 @@ +--- x.c ++++ x.c +@@ -409,6 +409,7 @@ bpress(XEvent *e) + { + struct timespec now; + MouseShortcut *ms; ++ MouseKey *mk; + int snap; + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { diff --git a/x.o b/x.o new file mode 100644 index 0000000000000000000000000000000000000000..9dcf7f4e98808549c0cacd53e790398596d60544 GIT binary patch literal 71840 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 I{nF|G7l3zhL;wH) literal 0 HcmV?d00001