X-Git-Url: https://git.xinqibao.xyz/st.git/blobdiff_plain/e997303502ddd5c26cfc41af0ff5356bffc04359..a0467c802d4f86ed162486e3453dd61181423902:/st.c?ds=sidebyside diff --git a/st.c b/st.c index a545724..de2dd0e 100644 --- a/st.c +++ b/st.c @@ -38,7 +38,7 @@ /* macros */ #define IS_SET(flag) ((term.mode & (flag)) != 0) -#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') +#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)) @@ -51,7 +51,6 @@ enum term_mode { MODE_ECHO = 1 << 4, MODE_PRINT = 1 << 5, MODE_UTF8 = 1 << 6, - MODE_SIXEL = 1 << 7, }; enum cursor_movement { @@ -78,12 +77,11 @@ enum charset { enum escape_state { ESC_START = 1, ESC_CSI = 2, - ESC_STR = 4, /* OSC, PM, APC */ + 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, - ESC_DCS =128, }; typedef struct { @@ -129,6 +127,7 @@ typedef struct { 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 */ @@ -187,18 +186,18 @@ 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 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 tsetmode(int, int, 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(int *, int *, int); +static int32_t tdefcolor(const int *, int *, int); static void tdeftran(char); static void tstrsequence(uchar); @@ -227,10 +226,10 @@ 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}; +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +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}; ssize_t xwrite(int fd, const char *s, size_t len) @@ -270,12 +269,14 @@ xrealloc(void *p, size_t len) } char * -xstrdup(char *s) +xstrdup(const char *s) { - if ((s = strdup(s)) == NULL) + char *p; + + if ((p = strdup(s)) == NULL) die("strdup: %s\n", strerror(errno)); - return s; + return p; } size_t @@ -519,7 +520,7 @@ selsnap(int *x, int *y, int direction) { int newx, newy, xt, yt; int delim, prevdelim; - Glyph *gp, *prevgp; + const Glyph *gp, *prevgp; switch (sel.snap) { case SNAP_WORD: @@ -592,7 +593,7 @@ getsel(void) { char *str, *ptr; int y, bufsize, lastx, linelen; - Glyph *gp, *last; + const Glyph *gp, *last; if (sel.ob.x == -1) return NULL; @@ -634,7 +635,8 @@ getsel(void) * st. * FIXME: Fix the computer world. */ - if ((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP)) + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) *ptr++ = '\n'; } *ptr = 0; @@ -730,7 +732,7 @@ sigchld(int a) 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); + _exit(0); } void @@ -758,7 +760,7 @@ stty(char **args) } int -ttynew(char *line, char *cmd, char *out, char **args) +ttynew(const char *line, char *cmd, const char *out, char **args) { int m, s; @@ -791,14 +793,15 @@ ttynew(char *line, char *cmd, char *out, char **args) break; case 0: close(iofd); + close(m); 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); + if (s > 2) + close(s); #ifdef __OpenBSD__ if (pledge("stdio getpw proc exec", NULL) == -1) die("pledge\n"); @@ -830,7 +833,6 @@ ttyread(void) switch (ret) { case 0: - fputs("found EOF in input\n", stderr); exit(0); case -1: die("couldn't read from shell: %s\n", strerror(errno)); @@ -842,7 +844,6 @@ ttyread(void) if (buflen > 0) memmove(buf, buf + written, buflen); return ret; - } } @@ -1106,27 +1107,17 @@ selscroll(int orig, int n) if (sel.ob.x == -1) return; - if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) { - if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) { + 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(); - return; - } - if (sel.type == SEL_RECTANGULAR) { - if (sel.ob.y < term.top) - sel.ob.y = term.top; - if (sel.oe.y > term.bot) - sel.oe.y = term.bot; } else { - if (sel.ob.y < term.top) { - sel.ob.y = term.top; - sel.ob.x = 0; - } - if (sel.oe.y > term.bot) { - sel.oe.y = term.bot; - sel.oe.x = term.col; - } + selnormalize(); } - selnormalize(); } } @@ -1198,9 +1189,9 @@ tmoveto(int x, int y) } void -tsetchar(Rune u, Glyph *attr, int x, int y) +tsetchar(Rune u, const Glyph *attr, int x, int y) { - static char *vt100_0[62] = { /* 0x41 - 0x7e */ + static const 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 */ @@ -1312,7 +1303,7 @@ tdeleteline(int n) } int32_t -tdefcolor(int *attr, int *npar, int l) +tdefcolor(const int *attr, int *npar, int l) { int32_t idx = -1; uint r, g, b; @@ -1362,7 +1353,7 @@ tdefcolor(int *attr, int *npar, int l) } void -tsetattr(int *attr, int l) +tsetattr(const int *attr, int l) { int i; int32_t idx; @@ -1480,9 +1471,9 @@ tsetscroll(int t, int b) } void -tsetmode(int priv, int set, int *args, int narg) +tsetmode(int priv, int set, const int *args, int narg) { - int alt, *lim; + int alt; const int *lim; for (lim = args + narg; args < lim; ++args) { if (priv) { @@ -1658,6 +1649,12 @@ csihandle(void) 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); @@ -1781,7 +1778,7 @@ csihandle(void) break; case 'n': /* DSR – Device Status Report (cursor position) */ if (csiescseq.arg[0] == 6) { - len = snprintf(buf, sizeof(buf),"\033[%i;%iR", + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", term.c.y+1, term.c.x+1); ttywrite(buf, len, 0); } @@ -1845,6 +1842,42 @@ csireset(void) memset(&csiescseq, 0, sizeof(csiescseq)); } +void +osc4_color_response(int num) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(num, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc4 color %d\n", num); + return; + } + + n = snprintf(buf, sizeof buf, "\033]4;%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + +void +osc_color_response(int index, int num) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch osc color %d\n", index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + num, r, r, g, g, b, b); + + ttywrite(buf, n, 1); +} + void strhandle(void) { @@ -1859,13 +1892,21 @@ strhandle(void) case ']': /* OSC -- Operating System Command */ switch (par) { case 0: + if (narg > 1) { + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); + } + return; case 1: + if (narg > 1) + xseticontitle(strescseq.args[1]); + return; case 2: if (narg > 1) xsettitle(strescseq.args[1]); return; case 52: - if (narg > 2) { + if (narg > 2 && allowwindowops) { dec = base64dec(strescseq.args[2]); if (dec) { xsetsel(dec); @@ -1875,14 +1916,56 @@ strhandle(void) } } return; + case 10: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultfg, 10); + else if (xsetcolorname(defaultfg, p)) + fprintf(stderr, "erresc: invalid foreground color: %s\n", p); + else + redraw(); + return; + case 11: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultbg, 11); + else if (xsetcolorname(defaultbg, p)) + fprintf(stderr, "erresc: invalid background color: %s\n", p); + else + redraw(); + return; + case 12: + if (narg < 2) + break; + + p = strescseq.args[1]; + + if (!strcmp(p, "?")) + osc_color_response(defaultcs, 12); + else if (xsetcolorname(defaultcs, p)) + fprintf(stderr, "erresc: invalid cursor color: %s\n", p); + else + redraw(); + return; case 4: /* color set */ if (narg < 3) break; p = strescseq.args[2]; /* FALLTHROUGH */ - case 104: /* color reset, here p = NULL */ + case 104: /* color reset */ j = (narg > 1) ? atoi(strescseq.args[1]) : -1; - if (xsetcolorname(j, p)) { + + if (p && !strcmp(p, "?")) + osc4_color_response(j); + else if (xsetcolorname(j, p)) { if (par == 104 && narg <= 1) return; /* color reset without parameter */ fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", @@ -1901,7 +1984,6 @@ strhandle(void) xsettitle(strescseq.args[0]); return; case 'P': /* DCS -- Device Control String */ - term.mode |= ESC_DCS; case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2019,12 +2101,12 @@ void tdumpline(int n) { char buf[UTF_SIZ]; - Glyph *bp, *end; + 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) + for ( ; bp <= end; ++bp) tprinter(buf, utf8encode(bp->u, buf)); } tprinter("\n", 1); @@ -2095,12 +2177,9 @@ tdectest(char c) void tstrsequence(uchar c) { - strreset(); - switch (c) { case 0x90: /* DCS -- Device Control String */ c = 'P'; - term.esc |= ESC_DCS; break; case 0x9f: /* APC -- Application Program Command */ c = '_'; @@ -2112,6 +2191,7 @@ tstrsequence(uchar c) c = ']'; break; } + strreset(); strescseq.type = c; term.esc |= ESC_STR; } @@ -2154,6 +2234,7 @@ tcontrolcode(uchar ascii) return; case '\032': /* SUB */ tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ case '\030': /* CAN */ csireset(); break; @@ -2308,15 +2389,13 @@ tputc(Rune u) Glyph *gp; control = ISCONTROL(u); - if (!IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { + if (u < 127 || !IS_SET(MODE_UTF8)) { c[0] = u; width = len = 1; } else { len = utf8encode(u, c); - if (!control && (width = wcwidth(u)) == -1) { - memcpy(c, "\357\277\275", 4); /* UTF_INVALID */ + if (!control && (width = wcwidth(u)) == -1) width = 1; - } } if (IS_SET(MODE_PRINT)) @@ -2331,23 +2410,11 @@ tputc(Rune u) if (term.esc & ESC_STR) { if (u == '\a' || u == 030 || u == 032 || u == 033 || ISCONTROLC1(u)) { - term.esc &= ~(ESC_START|ESC_STR|ESC_DCS); - if (IS_SET(MODE_SIXEL)) { - /* TODO: render sixel */; - term.mode &= ~MODE_SIXEL; - return; - } + term.esc &= ~(ESC_START|ESC_STR); term.esc |= ESC_STR_END; goto check_control_code; } - if (IS_SET(MODE_SIXEL)) { - /* TODO: implement sixel mode */ - return; - } - if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q') - term.mode |= MODE_SIXEL; - if (strescseq.len+len >= strescseq.siz) { /* * Here is a bug in terminals. If the user never sends @@ -2384,6 +2451,8 @@ check_control_code: /* * control codes are not shown ever */ + if (!term.esc) + term.lastc = 0; return; } else if (term.esc & ESC_START) { if (term.esc & ESC_CSI) { @@ -2414,7 +2483,7 @@ check_control_code: */ return; } - if (sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y)) + if (selected(term.c.x, term.c.y)) selclear(); gp = &term.line[term.c.y][term.c.x]; @@ -2433,6 +2502,7 @@ check_control_code: } tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; if (width == 2) { gp->mode |= ATTR_WIDE; @@ -2456,7 +2526,7 @@ twrite(const char *buf, int buflen, int show_ctrl) int n; for (n = 0; n < buflen; n += charsize) { - if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { + if (IS_SET(MODE_UTF8)) { /* process a complete utf8 char */ charsize = utf8decode(buf + n, &u, buflen - n); if (charsize == 0) @@ -2572,6 +2642,7 @@ void drawregion(int x1, int y1, int x2, int y2) { int y; + for (y = y1; y < y2; y++) { if (!term.dirty[y]) continue; @@ -2584,7 +2655,7 @@ drawregion(int x1, int y1, int x2, int y2) void draw(void) { - int cx = term.c.x; + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; if (!xstartdraw()) return; @@ -2600,9 +2671,11 @@ draw(void) 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; + term.ocx = cx; + term.ocy = term.c.y; xfinishdraw(); - xximspot(term.ocx, term.ocy); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); } void