From 38d2f7ba589e8ee0ee5ded7915952ed094e3a51a Mon Sep 17 00:00:00 2001 From: ZachIR Date: Tue, 8 Jul 2025 20:39:57 -0500 Subject: Start from scratch on version 0.9.2 --- st.c | 1221 ++++++++++++++++-------------------------------------------------- 1 file changed, 293 insertions(+), 928 deletions(-) (limited to 'st.c') diff --git a/st.c b/st.c index 1ec550a..b9f66e7 100644 --- a/st.c +++ b/st.c @@ -33,11 +33,8 @@ #define UTF_SIZ 4 #define ESC_BUF_SIZ (128*UTF_SIZ) #define ESC_ARG_SIZ 16 -#define CAR_PER_ARG 4 #define STR_BUF_SIZ ESC_BUF_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ -#define HISTSIZE 2000 -#define RESIZEBUFFER 2000 /* macros */ #define IS_SET(flag) ((term.mode & (flag)) != 0) @@ -45,24 +42,6 @@ #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define ISDELIM(u) (u && wcschr(worddelimiters, u)) -#define STRESCARGREST(n) ((n) == 0 ? strescseq.buf : strescseq.argp[(n)-1] + 1) -#define STRESCARGJUST(n) (*(strescseq.argp[n]) = '\0', STRESCARGREST(n)) - -#define TLINE(y) ( \ - (y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \ - : term.line[(y) - term.scr] \ -) - -#define TLINEABS(y) ( \ - (y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \ -) - -#define UPDATEWRAPNEXT(alt, col) do { \ - if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] < col) { \ - term.c.x += term.wrapcwidth[alt]; \ - term.c.state &= ~CURSOR_WRAPNEXT; \ - } \ -} while (0); enum term_mode { MODE_WRAP = 1 << 0, @@ -74,12 +53,6 @@ enum term_mode { MODE_UTF8 = 1 << 6, }; -enum scroll_mode { - SCROLL_RESIZE = -1, - SCROLL_NOSAVEHIST = 0, - SCROLL_SAVEHIST = 1 -}; - enum cursor_movement { CURSOR_SAVE, CURSOR_LOAD @@ -141,11 +114,7 @@ typedef struct { int row; /* nb row */ int col; /* nb col */ Line *line; /* screen */ - Line hist[HISTSIZE]; /* history buffer */ - int histi; /* history index */ - int histf; /* nb history available */ - int scr; /* scroll back */ - int wrapcwidth[2]; /* used in updating WRAPNEXT when resizing */ + Line *alt; /* alternate screen */ int *dirty; /* dirtyness of lines */ TCursor c; /* cursor */ int ocx; /* old cursor col */ @@ -170,7 +139,6 @@ typedef struct { int arg[ESC_ARG_SIZ]; int narg; /* nb of args */ char mode[2]; - int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */ } CSIEscape; /* STR Escape sequence structs */ @@ -180,24 +148,17 @@ typedef struct { char *buf; /* allocated raw string */ size_t siz; /* allocation size */ size_t len; /* raw string length */ - char *argp[STR_ARG_SIZ]; /* pointers to the end of nth argument */ + char *args[STR_ARG_SIZ]; int narg; /* nb of args */ } STREscape; -typedef struct { - int state; - size_t length; -} URLdfa; - static void execsh(char *, char **); static void stty(char **); static void sigchld(int); static void ttywriteraw(const char *, size_t); -static void histclean(void); static void csidump(void); static void csihandle(void); -static void readcolonargs(char **, int, int[][CAR_PER_ARG]); static void csiparse(void); static void csireset(void); static void osc_color_response(int, int, int); @@ -211,54 +172,40 @@ 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, int); +static void tclearregion(int, int, int, int); static void tcursor(int); -static void tclearglyph(Glyph *, int); -static void tresetcursor(void); static void tdeletechar(int); static void tdeleteline(int); static void tinsertblank(int); static void tinsertblankline(int); -static int tlinelen(Line len); -static int tiswrapped(Line line); -static char *tgetglyphs(char *, const Glyph *, const Glyph *); -static size_t tgetline(char *, const Glyph *); +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, int, int); +static void tscrollup(int, int); static void tscrolldown(int, int); -static void treflow(int, int); -static void rscrolldown(int); -static void tresizedef(int, int); -static void tresizealt(int, int); static void tsetattr(const int *, int); static void tsetchar(Rune, const Glyph *, int, int); static void tsetdirt(int, int); static void tsetscroll(int, int); static void tswapscreen(void); -static void tloaddefscreen(int, int); -static void tloadaltscreen(int, int); static void tsetmode(int, int, const 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(const int *, int *, int); static void tdeftran(char); static void tstrsequence(uchar); -static int daddch(URLdfa *, char); static void drawregion(int, int, int, int); static void selnormalize(void); -static void selscroll(int, int, int); -static void selmove(int); -static void selremove(void); -static int regionselected(int, int, int, int); +static void selscroll(int, int); static void selsnap(int *, int *, int); static size_t utf8decode(const char *, Rune *, size_t); @@ -285,33 +232,6 @@ static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; -#include -static int su = 0; -struct timespec sutv; - -static void -tsync_begin() -{ - clock_gettime(CLOCK_MONOTONIC, &sutv); - su = 1; -} - -static void -tsync_end() -{ - su = 0; -} - -int -tinsync(uint timeout) -{ - struct timespec now; - if (su && !clock_gettime(CLOCK_MONOTONIC, &now) - && TIMEDIFF(now, sutv) >= timeout) - su = 0; - return su; -} - ssize_t xwrite(int fd, const char *s, size_t len) { @@ -485,46 +405,17 @@ selinit(void) } int -tlinelen(Line line) +tlinelen(int y) { - int i = term.col - 1; + int i = term.col; - for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--); - return i + 1; -} + if (term.line[y][i - 1].mode & ATTR_WRAP) + return i; -int -tiswrapped(Line line) -{ - int len = tlinelen(line); + while (i > 0 && term.line[y][i - 1].u == ' ') + --i; - return len > 0 && (line[len - 1].mode & ATTR_WRAP); -} - -char * -tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp) -{ - while (gp <= lgp) - if (gp->mode & ATTR_WDUMMY) { - gp++; - } else { - buf += utf8encode((gp++)->u, buf); - } - return buf; -} - -size_t -tgetline(char *buf, const Glyph *fgp) -{ - char *ptr; - const Glyph *lgp = &fgp[term.col - 1]; - - while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP))) - lgp--; - ptr = tgetglyphs(buf, fgp, lgp); - if (!(lgp->mode & ATTR_WRAP)) - *(ptr++) = '\n'; - return ptr - buf; + return i; } void @@ -564,11 +455,10 @@ selextend(int col, int row, int type, int done) sel.oe.x = col; sel.oe.y = row; - sel.type = type; selnormalize(); + sel.type = type; - if (oldey != sel.oe.y || oldex != sel.oe.x || - oldtype != sel.type || sel.mode == SEL_EMPTY) + 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; @@ -595,50 +485,43 @@ selnormalize(void) /* expand selection over line breaks */ if (sel.type == SEL_RECTANGULAR) return; - - i = tlinelen(TLINE(sel.nb.y)); - if (sel.nb.x > i) + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) sel.nb.x = i; - if (sel.ne.x >= tlinelen(TLINE(sel.ne.y))) - sel.ne.x = term.col - 1; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; } int -regionselected(int x1, int y1, int x2, int y2) +selected(int x, int y) { - if (sel.ob.x == -1 || sel.mode == SEL_EMPTY || - sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.ne.y < y1) + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) return 0; - return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne.x >= x1 - : (sel.nb.y != y2 || sel.nb.x <= x2) && - (sel.ne.y != y1 || sel.ne.x >= x1); -} + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); -int -selected(int x, int y) -{ - return regionselected(x, y, x, y); + 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 rtop = 0, rbot = term.row - 1; int delim, prevdelim; const Glyph *gp, *prevgp; - if (!IS_SET(MODE_ALTSCREEN)) - rtop += -term.histf + term.scr, rbot += term.scr; - switch (sel.snap) { case SNAP_WORD: /* * Snap around if the word wraps around at the end or * beginning of a line. */ - prevgp = &TLINE(*y)[*x]; + prevgp = &term.line[*y][*x]; prevdelim = ISDELIM(prevgp->u); for (;;) { newx = *x + direction; @@ -646,24 +529,24 @@ selsnap(int *x, int *y, int direction) if (!BETWEEN(newx, 0, term.col - 1)) { newy += direction; newx = (newx + term.col) % term.col; - if (!BETWEEN(newy, rtop, rbot)) + if (!BETWEEN(newy, 0, term.row - 1)) break; if (direction > 0) yt = *y, xt = *x; else yt = newy, xt = newx; - if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + if (!(term.line[yt][xt].mode & ATTR_WRAP)) break; } - if (newx >= tlinelen(TLINE(newy))) + if (newx >= tlinelen(newy)) break; - gp = &TLINE(newy)[newx]; + gp = &term.line[newy][newx]; delim = ISDELIM(gp->u); - if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim || - (delim && !(gp->u == ' ' && prevgp->u == ' ')))) + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) break; *x = newx; @@ -680,14 +563,18 @@ selsnap(int *x, int *y, int direction) */ *x = (direction < 0) ? 0 : term.col - 1; if (direction < 0) { - for (; *y > rtop; *y -= 1) { - if (!tiswrapped(TLINE(*y-1))) + for (; *y > 0; *y += direction) { + if (!(term.line[*y-1][term.col-1].mode + & ATTR_WRAP)) { break; + } } } else if (direction > 0) { - for (; *y < rbot; *y += 1) { - if (!tiswrapped(TLINE(*y))) + for (; *y < term.row-1; *y += direction) { + if (!(term.line[*y][term.col-1].mode + & ATTR_WRAP)) { break; + } } } break; @@ -698,34 +585,40 @@ char * getsel(void) { char *str, *ptr; - int y, lastx, linelen; - const Glyph *gp, *lgp; + int y, bufsize, lastx, linelen; + const Glyph *gp, *last; - if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) + if (sel.ob.x == -1) return NULL; - str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ); - ptr = str; + 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++) { - Line line = TLINE(y); - - if ((linelen = tlinelen(line)) == 0) { + if ((linelen = tlinelen(y)) == 0) { *ptr++ = '\n'; continue; } if (sel.type == SEL_RECTANGULAR) { - gp = &line[sel.nb.x]; + gp = &term.line[y][sel.nb.x]; lastx = sel.ne.x; } else { - gp = &line[sel.nb.y == y ? sel.nb.x : 0]; + gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; } - lgp = &line[MIN(lastx, linelen-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); + } - ptr = tgetglyphs(ptr, gp, lgp); /* * Copy and pasting of line endings is inconsistent * in the inconsistent terminal and GUI world. @@ -736,10 +629,10 @@ getsel(void) * FIXME: Fix the computer world. */ if ((y < sel.ne.y || lastx >= linelen) && - (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) *ptr++ = '\n'; } - *ptr = '\0'; + *ptr = 0; return str; } @@ -748,15 +641,9 @@ selclear(void) { if (sel.ob.x == -1) return; - selremove(); - tsetdirt(sel.nb.y, sel.ne.y); -} - -void -selremove(void) -{ sel.mode = SEL_IDLE; sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); } void @@ -927,9 +814,6 @@ ttynew(const char *line, char *cmd, const char *out, char **args) return cmdfd; } -static int twrite_aborted = 0; -int ttyread_pending() { return twrite_aborted; } - size_t ttyread(void) { @@ -938,7 +822,7 @@ ttyread(void) int ret, written; /* append read bytes to unprocessed bytes */ - ret = twrite_aborted ? 1 : read(cmdfd, buf+buflen, LEN(buf)-buflen); + ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); switch (ret) { case 0: @@ -946,7 +830,7 @@ ttyread(void) case -1: die("couldn't read from shell: %s\n", strerror(errno)); default: - buflen += twrite_aborted ? 0 : ret; + buflen += ret; written = twrite(buf, buflen, 0); buflen -= written; /* keep any incomplete UTF-8 byte sequence for the next call */ @@ -961,7 +845,6 @@ ttywrite(const char *s, size_t n, int may_echo) { const char *next; - kscrolldown(&((Arg){ .i = term.scr })); if (may_echo && IS_SET(MODE_ECHO)) twrite(s, n, 1); @@ -985,14 +868,6 @@ ttywrite(const char *s, size_t n, int may_echo) } } -void -histclean() -{ - for (int i = 0; i < HISTSIZE; i++) - term.hist[i] = xmalloc(term.col * sizeof(Glyph)); - term.histf = 0; -} - void ttywriteraw(const char *s, size_t n) { @@ -1097,11 +972,6 @@ tsetdirt(int top, int bot) term.dirty[i] = 1; } -int tisaltscr(void) -{ - return IS_SET(MODE_ALTSCREEN); -} - void tsetdirtattr(int attr) { @@ -1110,7 +980,7 @@ tsetdirtattr(int attr) for (i = 0; i < term.row-1; i++) { for (j = 0; j < term.col-1; j++) { if (term.line[i][j].mode & attr) { - term.dirty[i] = 1; + tsetdirt(i, i); break; } } @@ -1120,9 +990,7 @@ tsetdirtattr(int attr) void tfulldirt(void) { - tsync_end(); - for (int i = 0; i < term.row; i++) - term.dirty[i] = 1; + tsetdirt(0, term.row-1); } void @@ -1139,258 +1007,110 @@ tcursor(int mode) } } -void -tresetcursor(void) -{ - term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg = defaultbg }, - .x = 0, .y = 0, .state = CURSOR_DEFAULT }; -} - void treset(void) { uint i; - int x, y; - tresetcursor(); + 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.histf = 0; - term.scr = 0; term.bot = term.row - 1; term.mode = MODE_WRAP|MODE_UTF8; memset(term.trantbl, CS_USA, sizeof(term.trantbl)); term.charset = 0; - selremove(); for (i = 0; i < 2; i++) { - tcursor(CURSOR_SAVE); /* reset saved cursor */ - for (y = 0; y < term.row; y++) - for (x = 0; x < term.col; x++) - tclearglyph(&term.line[y][x], 0); + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); tswapscreen(); } - tfulldirt(); } void tnew(int col, int row) { - int i, j; - - for (i = 0; i < 2; i++) { - term.line = xmalloc(row * sizeof(Line)); - for (j = 0; j < row; j++) - term.line[j] = xmalloc(col * sizeof(Glyph)); - term.col = col, term.row = row; - tswapscreen(); - } - term.dirty = xmalloc(row * sizeof(*term.dirty)); - term.tabs = xmalloc(col * sizeof(*term.tabs)); - for (i = 0; i < HISTSIZE; i++) - term.hist[i] = xmalloc(col * sizeof(Glyph)); - treset(); + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); } -/* handle it with care */ void tswapscreen(void) { - static Line *altline; - static int altcol, altrow; - Line *tmpline = term.line; - int tmpcol = term.col, tmprow = term.row; + Line *tmp = term.line; - term.line = altline; - term.col = altcol, term.row = altrow; - altline = tmpline; - altcol = tmpcol, altrow = tmprow; + term.line = term.alt; + term.alt = tmp; term.mode ^= MODE_ALTSCREEN; -} - -void -tloaddefscreen(int clear, int loadcursor) -{ - int col, row, alt = IS_SET(MODE_ALTSCREEN); - - if (alt) { - if (clear) - tclearregion(0, 0, term.col-1, term.row-1, 1); - col = term.col, row = term.row; - tswapscreen(); - } - if (loadcursor) - tcursor(CURSOR_LOAD); - if (alt) - tresizedef(col, row); -} - -void -tloadaltscreen(int clear, int savecursor) -{ - int col, row, def = !IS_SET(MODE_ALTSCREEN); - - if (savecursor) - tcursor(CURSOR_SAVE); - if (def) { - col = term.col, row = term.row; - tswapscreen(); - term.scr = 0; - tresizealt(col, row); - } - if (clear) - tclearregion(0, 0, term.col-1, term.row-1, 1); -} - -int -tisaltscreen(void) -{ - return IS_SET(MODE_ALTSCREEN); -} - -void -kscrolldown(const Arg* a) -{ - int n = a->i; - - if (!term.scr || IS_SET(MODE_ALTSCREEN)) - return; - - if (n < 0) - n = MAX(term.row / -n, 1); - - if (n <= term.scr) { - term.scr -= n; - } else { - n = term.scr; - term.scr = 0; - } - - if (sel.ob.x != -1 && !sel.alt) - selmove(-n); /* negate change in term.scr */ tfulldirt(); } void -kscrollup(const Arg* a) +tscrolldown(int orig, int n) { - int n = a->i; - - if (!term.histf || IS_SET(MODE_ALTSCREEN)) - return; - - if (n < 0) - n = MAX(term.row / -n, 1); - - if (term.scr + n <= term.histf) { - term.scr += n; - } else { - n = term.histf - term.scr; - term.scr = term.histf; - } - - if (sel.ob.x != -1 && !sel.alt) - selmove(n); /* negate change in term.scr */ - tfulldirt(); -} - -void -tscrolldown(int top, int n) -{ - int i, bot = term.bot; + int i; Line temp; - if (n <= 0) - return; - n = MIN(n, bot-top+1); + LIMIT(n, 0, term.bot-orig+1); - tsetdirt(top, bot-n); - tclearregion(0, bot-n+1, term.col-1, bot, 1); + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); - for (i = bot; i >= top+n; i--) { + for (i = term.bot; i >= orig+n; i--) { temp = term.line[i]; term.line[i] = term.line[i-n]; term.line[i-n] = temp; } - if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN)) - selscroll(top, bot, n); + selscroll(orig, n); } void -tscrollup(int top, int bot, int n, int mode) +tscrollup(int orig, int n) { - int i, j, s; - int alt = IS_SET(MODE_ALTSCREEN); - int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST; + int i; Line temp; - if (n <= 0) - return; - n = MIN(n, bot-top+1); - - if (savehist) { - for (i = 0; i < n; i++) { - term.histi = (term.histi + 1) % HISTSIZE; - temp = term.hist[term.histi]; - for (j = 0; j < term.col; j++) - tclearglyph(&temp[j], 1); - term.hist[term.histi] = term.line[i]; - term.line[i] = temp; - } - term.histf = MIN(term.histf + n, HISTSIZE); - s = n; - if (term.scr) { - j = term.scr; - term.scr = MIN(j + n, HISTSIZE); - s = j + n - term.scr; - } - if (mode != SCROLL_RESIZE) - tfulldirt(); - } else { - tclearregion(0, top, term.col-1, top+n-1, 1); - tsetdirt(top+n, bot); - } + LIMIT(n, 0, term.bot-orig+1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); - for (i = top; i <= bot-n; i++) { + for (i = orig; i <= term.bot-n; i++) { temp = term.line[i]; term.line[i] = term.line[i+n]; term.line[i+n] = temp; } - if (sel.ob.x != -1 && sel.alt == alt) { - if (!savehist) { - selscroll(top, bot, -n); - } else if (s > 0) { - selmove(-s); - if (-term.scr + sel.nb.y < -term.histf) - selremove(); - } - } + selscroll(orig, -n); } void -selmove(int n) +selscroll(int orig, int n) { - sel.ob.y += n, sel.nb.y += n; - sel.oe.y += n, sel.ne.y += n; -} - -void -selscroll(int top, int bot, int n) -{ - /* turn absolute coordinates into relative */ - top += term.scr, bot += term.scr; + if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) + return; - if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, bot)) { + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { selclear(); - } else if (BETWEEN(sel.nb.y, top, bot)) { - selmove(n); - if (sel.nb.y < top || sel.ne.y > bot) + } 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(); + } } } @@ -1400,35 +1120,13 @@ tnewline(int first_col) int y = term.c.y; if (y == term.bot) { - tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST); + tscrollup(term.top, 1); } else { y++; } tmoveto(first_col ? 0 : term.c.x, y); } -void -readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG]) -{ - int i = 0; - for (; i < CAR_PER_ARG; i++) - params[cursor][i] = -1; - - if (**p != ':') - return; - - char *np = NULL; - i = 0; - - while (**p == ':' && i < CAR_PER_ARG) { - while (**p == ':') - (*p)++; - params[cursor][i] = strtol(*p, &np, 10); - *p = np; - i++; - } -} - void csiparse(void) { @@ -1451,7 +1149,6 @@ csiparse(void) v = -1; csiescseq.arg[csiescseq.narg++] = v; p = np; - readcolonargs(&p, csiescseq.narg-1, csiescseq.carg); if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) break; p++; @@ -1518,80 +1215,70 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) term.dirty[y] = 1; term.line[y][x] = *attr; term.line[y][x].u = u; - term.line[y][x].mode |= ATTR_SET; - - if (isboxdraw(u)) - term.line[y][x].mode |= ATTR_BOXDRAW; } void -tclearglyph(Glyph *gp, int usecurattr) -{ - if (usecurattr) { - gp->fg = term.c.attr.fg; - gp->bg = term.c.attr.bg; - } else { - gp->fg = defaultfg; - gp->bg = defaultbg; - } - gp->mode =ATTR_NULL; - gp->u = ' '; -} - -void -tclearregion(int x1, int y1, int x2, int y2, int usecurattr) +tclearregion(int x1, int y1, int x2, int y2) { int x, y, temp; Glyph *gp; - /* regionselected() takes relative coordinates */ - if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr)) - selremove(); + 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++) - tclearglyph(&term.line[y][x], usecurattr); + 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 src, dst, size; - Line line; + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); - if (n <= 0) - return; dst = term.c.x; - src = MIN(term.c.x + n,term.col); + src = term.c.x + n; size = term.col - src; + line = term.line[term.c.y]; - if (size > 0) { /* otherwise src would point beyond the array - https://stackoverflow.com/questions/29844298 */ - line = term.line[term.c.y]; - memmove(&line[dst], &line[src], size * sizeof(Glyph)); - } - tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1); + 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 src, dst, size; - Line line; + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); - if (n <= 0) - return; - dst= MIN(term.c.x + n, term.col); + dst = term.c.x + n; src = term.c.x; size = term.col - dst; + line = term.line[term.c.y]; - if (size > 0) { /* otherwise dst would point beyond the array */ - line = term.line[term.c.y]; - memmove(&line[dst], &line[src], size * sizeof(Glyph)); - } - tclearregion(src, term.c.y, dst - 1, term.c.y, 1); + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); } void @@ -1605,7 +1292,7 @@ void tdeleteline(int n) { if (BETWEEN(term.c.y, term.top, term.bot)) - tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST); + tscrollup(term.c.y, n); } int32_t @@ -1678,10 +1365,6 @@ tsetattr(const int *attr, int l) ATTR_STRUCK ); term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; - term.c.attr.ustyle = -1; - term.c.attr.ucolor[0] = -1; - term.c.attr.ucolor[1] = -1; - term.c.attr.ucolor[2] = -1; break; case 1: term.c.attr.mode |= ATTR_BOLD; @@ -1693,14 +1376,7 @@ tsetattr(const int *attr, int l) term.c.attr.mode |= ATTR_ITALIC; break; case 4: - term.c.attr.ustyle = csiescseq.carg[i][0]; - - if (term.c.attr.ustyle != 0) - term.c.attr.mode |= ATTR_UNDERLINE; - else - term.c.attr.mode &= ~ATTR_UNDERLINE; - - term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + term.c.attr.mode |= ATTR_UNDERLINE; break; case 5: /* slow blink */ /* FALLTHROUGH */ @@ -1751,18 +1427,6 @@ tsetattr(const int *attr, int l) case 49: term.c.attr.bg = defaultbg; break; - case 58: - term.c.attr.ucolor[0] = csiescseq.carg[i][1]; - term.c.attr.ucolor[1] = csiescseq.carg[i][2]; - term.c.attr.ucolor[2] = csiescseq.carg[i][3]; - term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; - break; - case 59: - term.c.attr.ucolor[0] = -1; - term.c.attr.ucolor[1] = -1; - term.c.attr.ucolor[2] = -1; - term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; - break; default: if (BETWEEN(attr[i], 30, 37)) { term.c.attr.fg = attr[i] - 30; @@ -1802,7 +1466,7 @@ tsetscroll(int t, int b) void tsetmode(int priv, int set, const int *args, int narg) { - const int *lim; + int alt; const int *lim; for (lim = args + narg; args < lim; ++args) { if (priv) { @@ -1863,18 +1527,25 @@ tsetmode(int priv, int set, const int *args, int narg) 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: /* swap screen, clearing alternate screen */ + case 1047: if (!allowaltscreen) break; - if (set) - tloadaltscreen(*args == 1049, *args == 1049); - else - tloaddefscreen(*args == 1047, *args == 1049); - 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: - if (!allowaltscreen) - break; tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); break; case 2004: /* 2004: bracketed paste mode */ @@ -1926,7 +1597,7 @@ void csihandle(void) { char buf[40]; - int n, x; + int len; switch (csiescseq.mode[0]) { default: @@ -1972,7 +1643,7 @@ csihandle(void) ttywrite(vtiden, strlen(vtiden), 0); break; case 'b': /* REP -- if last char is printable print it more times */ - DEFAULT(csiescseq.arg[0], 1); + LIMIT(csiescseq.arg[0], 1, 65535); if (term.lastc) while (csiescseq.arg[0]-- > 0) tputc(term.lastc); @@ -2024,31 +1695,20 @@ csihandle(void) 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, 1); + 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, 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, 1); - tclearregion(0, term.c.y, term.c.x, term.c.y, 1); + 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 */ - if (IS_SET(MODE_ALTSCREEN)) { - tclearregion(0, 0, term.col-1, term.row-1, 1); - break; - } - /* vte does this: - tscrollup(0, term.row-1, term.row, SCROLL_SAVEHIST); */ - - /* alacritty does this: */ - for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--); - if (n >= 0) - tscrollup(0, term.row-1, n+1, SCROLL_SAVEHIST); - tscrollup(0, term.row-1, term.row-n-1, SCROLL_NOSAVEHIST); - histclean(); - break; + tclearregion(0, 0, term.col-1, term.row-1); + break; default: goto unknown; } @@ -2056,20 +1716,21 @@ csihandle(void) 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, 1); + 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, 1); + 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, 1); + tclearregion(0, term.c.y, term.col-1, term.c.y); break; } break; case 'S': /* SU -- Scroll line up */ + if (csiescseq.priv) break; DEFAULT(csiescseq.arg[0], 1); - /* xterm, urxvt, alacritty save this in history */ - tscrollup(term.top, term.bot, csiescseq.arg[0], SCROLL_SAVEHIST); + tscrollup(term.top, csiescseq.arg[0]); break; case 'T': /* SD -- Scroll line down */ DEFAULT(csiescseq.arg[0], 1); @@ -2087,11 +1748,9 @@ csihandle(void) tdeleteline(csiescseq.arg[0]); break; case 'X': /* ECH -- Erase char */ - if (csiescseq.arg[0] < 0) - return; DEFAULT(csiescseq.arg[0], 1); - x = MIN(term.c.x + csiescseq.arg[0], term.col) - 1; - tclearregion(term.c.x, term.c.y, x, term.c.y, 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); @@ -2111,11 +1770,18 @@ csihandle(void) 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) { - n = snprintf(buf, sizeof(buf), "\033[%i;%iR", - term.c.y+1, term.c.x+1); - ttywrite(buf, n, 0); + case 'n': /* DSR -- Device Status Report */ + switch (csiescseq.arg[0]) { + case 5: /* Status Report "OK" `0n` */ + ttywrite("\033[0n", sizeof("\033[0n") - 1, 0); + break; + case 6: /* Report Cursor Position (CPR) ";R" */ + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + break; + default: + goto unknown; } break; case 'r': /* DECSTBM -- Set Scrolling Region */ @@ -2144,33 +1810,6 @@ csihandle(void) goto unknown; } break; - case 't': /* title stack operations */ - switch (csiescseq.arg[0]) { - case 22: /* pust current title on stack */ - switch (csiescseq.arg[1]) { - case 0: - case 1: - case 2: - xpushtitle(); - break; - default: - goto unknown; - } - break; - case 23: /* pop last title from stack */ - switch (csiescseq.arg[1]) { - case 0: - case 1: - case 2: - xsettitle(NULL, 1); - break; - default: - goto unknown; - } - break; - default: - goto unknown; - } } } @@ -2241,30 +1880,29 @@ strhandle(void) }; term.esc &= ~(ESC_STR_END|ESC_STR); - strescseq.buf[strescseq.len] = '\0'; + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; switch (strescseq.type) { case ']': /* OSC -- Operating System Command */ - strparse(); - par = (narg = strescseq.narg) ? atoi(STRESCARGJUST(0)) : 0; switch (par) { case 0: if (narg > 1) { - xsettitle(STRESCARGREST(1), 0); - xseticontitle(STRESCARGREST(1)); + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); } return; case 1: if (narg > 1) - xseticontitle(STRESCARGREST(1)); + xseticontitle(strescseq.args[1]); return; case 2: if (narg > 1) - xsettitle(STRESCARGREST(1), 0); + xsettitle(strescseq.args[1]); return; case 52: if (narg > 2 && allowwindowops) { - dec = base64dec(STRESCARGREST(2)); + dec = base64dec(strescseq.args[2]); if (dec) { xsetsel(dec); xclipcopy(); @@ -2278,7 +1916,7 @@ strhandle(void) case 12: if (narg < 2) break; - p = STRESCARGREST(1); + p = strescseq.args[1]; if ((j = par - 10) < 0 || j >= LEN(osc_table)) break; /* shouldn't be possible */ @@ -2294,16 +1932,18 @@ strhandle(void) case 4: /* color set */ if (narg < 3) break; - p = STRESCARGJUST(2); + p = strescseq.args[2]; /* FALLTHROUGH */ case 104: /* color reset */ - j = (narg > 1) ? atoi(STRESCARGJUST(1)) : -1; + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; if (p && !strcmp(p, "?")) { osc_color_response(j, 0, 1); } else if (xsetcolorname(j, p)) { - if (par == 104 && narg <= 1) + if (par == 104 && narg <= 1) { + xloadcols(); return; /* color reset without parameter */ + } fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", j, p ? p : "(null)"); } else { @@ -2317,15 +1957,9 @@ strhandle(void) } break; case 'k': /* old title set compatibility */ - xsettitle(strescseq.buf, 0); + xsettitle(strescseq.args[0]); return; case 'P': /* DCS -- Device Control String */ - /* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */ - if (strstr(strescseq.buf, "=1s") == strescseq.buf) - tsync_begin(); /* BSU */ - else if (strstr(strescseq.buf, "=2s") == strescseq.buf) - tsync_end(); /* ESU */ - return; case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2342,17 +1976,18 @@ strparse(void) 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++; - strescseq.argp[strescseq.narg++] = p; + ++p; if (c == '\0') return; - p++; + *p++ = '\0'; } } @@ -2441,8 +2076,16 @@ tdumpsel(void) void tdumpline(int n) { - char str[(term.col + 1) * UTF_SIZ]; - tprinter(str, tgetline(str, &term.line[n][0])); + char buf[UTF_SIZ]; + const 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 @@ -2663,7 +2306,7 @@ eschandle(uchar ascii) return 0; case 'D': /* IND -- Linefeed */ if (term.c.y == term.bot) { - tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST); + tscrollup(term.top, 1); } else { tmoveto(term.c.x, term.c.y+1); } @@ -2686,9 +2329,9 @@ eschandle(uchar ascii) break; case 'c': /* RIS -- Reset to initial state */ treset(); - xfreetitlestack(); resettitle(); xloadcols(); + xsetmode(0, MODE_HIDE); break; case '=': /* DECPAM -- Application keypad */ xsetmode(1, MODE_APPKEYPAD); @@ -2781,6 +2424,9 @@ check_control_code: * they must not cause conflicts with sequences. */ if (control) { + /* in UTF-8 mode ignore handling C1 control characters */ + if (IS_SET(MODE_UTF8) && ISCONTROLC1(u)) + return; tcontrolcode(u); /* * control codes are not shown ever @@ -2817,8 +2463,7 @@ check_control_code: */ return; } - /* selected() takes relative coordinates */ - if (selected(term.c.x + term.scr, term.c.y + term.scr)) + if (selected(term.c.x, term.c.y)) selclear(); gp = &term.line[term.c.y][term.c.x]; @@ -2828,11 +2473,16 @@ check_control_code: gp = &term.line[term.c.y][term.c.x]; } - if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + gp->mode &= ~ATTR_WIDE; + } if (term.c.x+width > term.col) { - tnewline(1); + if (IS_SET(MODE_WRAP)) + tnewline(1); + else + tmoveto(term.col - width, term.c.y); gp = &term.line[term.c.y][term.c.x]; } @@ -2853,7 +2503,6 @@ check_control_code: if (term.c.x+width < term.col) { tmoveto(term.c.x+width, term.c.y); } else { - term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width; term.c.state |= CURSOR_WRAPNEXT; } } @@ -2865,9 +2514,6 @@ twrite(const char *buf, int buflen, int show_ctrl) Rune u; int n; - int su0 = su; - twrite_aborted = 0; - for (n = 0; n < buflen; n += charsize) { if (IS_SET(MODE_UTF8)) { /* process a complete utf8 char */ @@ -2878,10 +2524,6 @@ twrite(const char *buf, int buflen, int show_ctrl) u = buf[n] & 0xFF; charsize = 1; } - if (su0 && !su) { - twrite_aborted = 1; - break; // ESU - allow rendering before a new BSU - } if (show_ctrl && ISCONTROL(u)) { if (u & 0x80) { u &= 0x7f; @@ -2897,282 +2539,92 @@ twrite(const char *buf, int buflen, int show_ctrl) return n; } -void -treflow(int col, int row) -{ - int i, j; - int oce, nce, bot, scr; - int ox = 0, oy = -term.histf, nx = 0, ny = -1, len; - int cy = -1; /* proxy for new y coordinate of cursor */ - int nlines; - Line *buf, line; - - /* y coordinate of cursor line end */ - for (oce = term.c.y; oce < term.row - 1 && - tiswrapped(term.line[oce]); oce++); - - nlines = term.histf + oce + 1; - if (col < term.col) { - /* each line can take this many lines after reflow */ - j = (term.col + col - 1) / col; - nlines = j * nlines; - if (nlines > HISTSIZE + RESIZEBUFFER + row) { - nlines = HISTSIZE + RESIZEBUFFER + row; - oy = -(nlines / j - oce - 1); - } - } - buf = xmalloc(nlines * sizeof(Line)); - do { - if (!nx) - buf[++ny] = xmalloc(col * sizeof(Glyph)); - if (!ox) { - line = TLINEABS(oy); - len = tlinelen(line); - } - if (oy == term.c.y) { - if (!ox) - len = MAX(len, term.c.x + 1); - /* update cursor */ - if (cy < 0 && term.c.x - ox < col - nx) { - term.c.x = nx + term.c.x - ox, cy = ny; - UPDATEWRAPNEXT(0, col); - } - } - /* get reflowed lines in buf */ - if (col - nx > len - ox) { - memcpy(&buf[ny][nx], &line[ox], (len-ox) * sizeof(Glyph)); - nx += len - ox; - if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) { - for (j = nx; j < col; j++) - tclearglyph(&buf[ny][j], 0); - nx = 0; - } else if (nx > 0) { - buf[ny][nx - 1].mode &= ~ATTR_WRAP; - } - ox = 0, oy++; - } else if (col - nx == len - ox) { - memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph)); - ox = 0, oy++, nx = 0; - } else/* if (col - nx < len - ox) */ { - memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph)); - ox += col - nx; - buf[ny][col - 1].mode |= ATTR_WRAP; - nx = 0; - } - } while (oy <= oce); - if (nx) - for (j = nx; j < col; j++) - tclearglyph(&buf[ny][j], 0); - - /* free extra lines */ - for (i = row; i < term.row; i++) - free(term.line[i]); - /* resize to new height */ - term.line = xrealloc(term.line, row * sizeof(Line)); - - bot = MIN(ny, row - 1); - scr = MAX(row - term.row, 0); - /* update y coordinate of cursor line end */ - nce = MIN(oce + scr, bot); - /* update cursor y coordinate */ - term.c.y = nce - (ny - cy); - if (term.c.y < 0) { - j = nce, nce = MIN(nce + -term.c.y, bot); - term.c.y += nce - j; - while (term.c.y < 0) { - free(buf[ny--]); - term.c.y++; - } - } - /* allocate new rows */ - for (i = row - 1; i > nce; i--) { - term.line[i] = xmalloc(col * sizeof(Glyph)); - for (j = 0; j < col; j++) - tclearglyph(&term.line[i][j], 0); - } - /* fill visible area */ - for (/*i = nce */; i >= term.row; i--, ny--) - term.line[i] = buf[ny]; - for (/*i = term.row - 1 */; i >= 0; i--, ny--) { - free(term.line[i]); - term.line[i] = buf[ny]; - } - /* fill lines in history buffer and update term.histf */ - for (/*i = -1 */; ny >= 0 && i >= -HISTSIZE; i--, ny--) { - j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; - free(term.hist[j]); - term.hist[j] = buf[ny]; - } - term.histf = -i - 1; - term.scr = MIN(term.scr, term.histf); - /* resize rest of the history lines */ - for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) { - j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; - term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyph)); - } - free(buf); -} - -void -rscrolldown(int n) -{ - int i; - Line temp; - - /* can never be true as of now - if (IS_SET(MODE_ALTSCREEN)) - return; */ - - if ((n = MIN(n, term.histf)) <= 0) - return; - - for (i = term.c.y + n; i >= n; i--) { - temp = term.line[i]; - term.line[i] = term.line[i-n]; - term.line[i-n] = temp; - } - for (/*i = n - 1 */; i >= 0; i--) { - temp = term.line[i]; - term.line[i] = term.hist[term.histi]; - term.hist[term.histi] = temp; - term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; - } - term.c.y += n; - term.histf -= n; - if ((i = term.scr - n) >= 0) { - term.scr = i; - } else { - term.scr = 0; - if (sel.ob.x != -1 && !sel.alt) - selmove(-i); - } -} - void tresize(int col, int row) { + int i; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); int *bp; + TCursor c; - /* col and row are always MAX(_, 1) if (col < 1 || row < 1) { - fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row); - return; - } */ - - term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); - term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); - 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; - } - - if (IS_SET(MODE_ALTSCREEN)) - tresizealt(col, row); - else - tresizedef(col, row); -} - -void -tresizedef(int col, int row) -{ - int i, j; - - /* return if dimensions haven't changed */ - if (term.col == col && term.row == row) { - tfulldirt(); + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); return; } - if (col != term.col) { - if (!sel.alt) - selremove(); - treflow(col, row); - } else { - /* slide screen up if otherwise cursor would get out of the screen */ - if (term.c.y >= row) { - tscrollup(0, term.row - 1, term.c.y - row + 1, SCROLL_RESIZE); - term.c.y = row - 1; - } - for (i = row; i < term.row; i++) - free(term.line[i]); - - /* resize to new height */ - term.line = xrealloc(term.line, row * sizeof(Line)); - /* allocate any new rows */ - for (i = term.row; i < row; i++) { - term.line[i] = xmalloc(col * sizeof(Glyph)); - for (j = 0; j < col; j++) - tclearglyph(&term.line[i][j], 0); - } - /* scroll down as much as height has increased */ - rscrolldown(row - term.row); - } - /* update terminal size */ - term.col = col, term.row = row; - /* reset scrolling region */ - term.top = 0, term.bot = row - 1; - /* dirty all lines */ - tfulldirt(); -} -void -tresizealt(int col, int row) -{ - int i, j; - - /* return if dimensions haven't changed */ - if (term.col == col && term.row == row) { - tfulldirt(); - return; - } - if (sel.alt) - selremove(); - /* slide screen up if otherwise cursor would get out of the screen */ - for (i = 0; i <= term.c.y - row; i++) + /* + * 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) { - /* ensure that both src and dst are not NULL */ memmove(term.line, term.line + i, row * sizeof(Line)); - term.c.y = row - 1; + memmove(term.alt, term.alt + i, row * sizeof(Line)); } - for (i += row; i < term.row; i++) + 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)); - /* resize to new width */ - for (i = 0; i < MIN(row, term.row); i++) { + 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)); - for (j = term.col; j < col; j++) - tclearglyph(&term.line[i][j], 0); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); } + /* allocate any new rows */ - for (/*i = MIN(row, term.row) */; i < row; i++) { + for (/* i = minrow */; i < row; i++) { term.line[i] = xmalloc(col * sizeof(Glyph)); - for (j = 0; j < col; j++) - tclearglyph(&term.line[i][j], 0); + term.alt[i] = xmalloc(col * sizeof(Glyph)); } - /* update cursor */ - if (term.c.x >= col) { - term.c.state &= ~CURSOR_WRAPNEXT; - term.c.x = col - 1; - } else { - UPDATEWRAPNEXT(1, col); + 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; + term.col = col; + term.row = row; /* reset scrolling region */ - term.top = 0, term.bot = row - 1; - /* dirty all lines */ - tfulldirt(); + 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, 0); + xsettitle(NULL); } void @@ -3185,7 +2637,7 @@ drawregion(int x1, int y1, int x2, int y2) continue; term.dirty[y] = 0; - xdrawline(TLINE(y), x1, y, x2); + xdrawline(term.line[y], x1, y, x2); } } @@ -3221,90 +2673,3 @@ redraw(void) tfulldirt(); draw(); } - -int -daddch(URLdfa *dfa, char c) -{ - /* () and [] can appear in urls, but excluding them here will reduce false - * positives when figuring out where a given url ends. - */ - static const char URLCHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789-._~:/?#@!$&'*+,;=%"; - static const char RPFX[] = "//:sptth"; - - if (!strchr(URLCHARS, c)) { - dfa->length = 0; - dfa->state = 0; - - return 0; - } - - dfa->length++; - - if (dfa->state == 2 && c == '/') { - dfa->state = 0; - } else if (dfa->state == 3 && c == 'p') { - dfa->state++; - } else if (c != RPFX[dfa->state]) { - dfa->state = 0; - return 0; - } - - if (dfa->state++ == 7) { - dfa->state = 0; - return 1; - } - - return 0; -} - -/* -** Select and copy the previous url on screen (do nothing if there's no url). -*/ -void -copyurl(const Arg *arg) { - int row = 0, /* row of current URL */ - col = 0, /* column of current URL start */ - colend = 0, /* column of last occurrence */ - passes = 0; /* how many rows have been scanned */ - - const char *c = NULL, - *match = NULL; - URLdfa dfa = { 0 }; - - row = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.y : term.bot; - LIMIT(row, term.top, term.bot); - - colend = (sel.ob.x >= 0 && sel.nb.y > 0) ? sel.nb.x : term.col; - LIMIT(colend, 0, term.col); - - /* - ** Scan from (term.row - 1,term.col - 1) to (0,0) and find - ** next occurrance of a URL - */ - for (passes = 0; passes < term.row; passes++) { - /* Read in each column of every row until - ** we hit previous occurrence of URL - */ - for (col = colend; col--;) - if (daddch(&dfa, term.line[row][col].u < 128 ? term.line[row][col].u : ' ')) - break; - - if (col >= 0) - break; - - if (--row < 0) - row = term.row - 1; - - colend = term.col; - } - - if (passes < term.row) { - selstart(col, row, 0); - selextend((col + dfa.length - 1) % term.col, row + (col + dfa.length - 1) / term.col, SEL_REGULAR, 0); - selextend((col + dfa.length - 1) % term.col, row + (col + dfa.length - 1) / term.col, SEL_REGULAR, 1); - xsetsel(getsel()); - xclipcopy(); - } -} -- cgit v1.2.3