diff options
| author | twells46 <173561638+twells46@users.noreply.github.com> | 2025-11-29 08:18:48 -0600 |
|---|---|---|
| committer | twells46 <173561638+twells46@users.noreply.github.com> | 2025-11-29 08:18:48 -0600 |
| commit | 9d70dfc76159cffc6d85530ce7d9897118d9677c (patch) | |
| tree | e2171e954cdeed661b8e8af3500992d9b656f96e /st.c | |
| parent | b1c93ae35772f4fb3a923605fefa02bf1fee585e (diff) | |
Add sixel patch
Diffstat (limited to 'st.c')
| -rw-r--r-- | st.c | 581 |
1 files changed, 443 insertions, 138 deletions
@@ -14,12 +14,15 @@ #include <sys/types.h> #include <sys/wait.h> #include <termios.h> +#include <time.h> #include <unistd.h> #include <wchar.h> #include "st.h" #include "win.h" +#include "sixel.h" + #if defined(__linux) #include <pty.h> #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) @@ -35,22 +38,27 @@ #define ESC_ARG_SIZ 16 #define STR_BUF_SIZ ESC_BUF_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ +#define STR_TERM_ST "\033\\" +#define STR_TERM_BEL "\007" /* 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)) +#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, + 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, + MODE_SIXEL = 1 << 7, + MODE_SIXEL_CUR_RT = 1 << 8, + MODE_SIXEL_SDM = 1 << 9 }; enum cursor_movement { @@ -82,16 +90,10 @@ enum escape_state { ESC_STR_END = 16, /* a final string was encountered */ ESC_TEST = 32, /* Enter in test mode */ ESC_UTF8 = 64, + ESC_DCS =128, }; typedef struct { - Glyph attr; /* current char attributes */ - int x; - int y; - char state; -} TCursor; - -typedef struct { int mode; int type; int snap; @@ -109,27 +111,6 @@ typedef struct { 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 '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */ typedef struct { @@ -150,6 +131,7 @@ typedef struct { size_t len; /* raw string length */ char *args[STR_ARG_SIZ]; int narg; /* nb of args */ + char *term; /* terminator: ST or BEL */ } STREscape; static void execsh(char *, char **); @@ -159,6 +141,7 @@ static void ttywriteraw(const char *, size_t); static void csidump(void); static void csihandle(void); +static void dcshandle(void); static void csiparse(void); static void csireset(void); static void osc_color_response(int, int, int); @@ -174,7 +157,9 @@ static void tdumpline(int); static void tdump(void); static void tclearregion(int, int, int, int); static void tcursor(int); +static void tresetcursor(void); static void tdeletechar(int); +static void tdeleteimages(void); static void tdeleteline(int); static void tinsertblank(int); static void tinsertblankline(int); @@ -191,27 +176,24 @@ 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 inline void tsetsixelattr(Line line, int x1, int x2); static void tswapscreen(void); 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 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 inline Rune utf8decodebyte(char, size_t *); +static inline char utf8encodebyte(Rune, size_t); +static inline size_t utf8validate(Rune *, size_t); static char *base64dec(const char *); static char base64dec_getc(const char **); @@ -219,19 +201,20 @@ 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; +sixel_state_t sixel_st; 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) { @@ -273,7 +256,6 @@ char * xstrdup(const char *s) { char *p; - if ((p = strdup(s)) == NULL) die("strdup: %s\n", strerror(errno)); @@ -283,24 +265,27 @@ xstrdup(const char *s) size_t utf8decode(const char *c, Rune *u, size_t clen) { - size_t i, j, len, type; + size_t i, len; Rune udecoded; *u = UTF_INVALID; if (!clen) return 0; udecoded = utf8decodebyte(c[0], &len); - if (!BETWEEN(len, 1, UTF_SIZ)) + if (!BETWEEN(len, 2, UTF_SIZ)) { + *u = (len == 1) ? udecoded : UTF_INVALID; 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) + clen = MIN(clen, len); + for (i = 1; i < clen; ++i) { + if ((c[i] & 0xC0) != 0x80) + return i; + udecoded = (udecoded << 6) | (c[i] & 0x3F); + } + if (i < len) return 0; - *u = udecoded; - utf8validate(u, len); + *u = (!BETWEEN(udecoded, utfmin[len], utfmax[len]) || BETWEEN(udecoded, 0xD800, 0xDFFF)) + ? UTF_INVALID : udecoded; return len; } @@ -455,8 +440,8 @@ selextend(int col, int row, int type, int done) sel.oe.x = col; sel.oe.y = row; - selnormalize(); sel.type = type; + selnormalize(); 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)); @@ -485,6 +470,7 @@ selnormalize(void) /* 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; @@ -564,15 +550,15 @@ 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 - & ATTR_WRAP)) { + 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)) { + if (!(term.line[*y][term.col-1].mode & ATTR_WRAP)) + { break; } } @@ -595,7 +581,8 @@ getsel(void) ptr = str = xmalloc(bufsize); /* append every set & selected glyph to the selection */ - for (y = sel.nb.y; y <= sel.ne.y; y++) { + for (y = sel.nb.y; y <= sel.ne.y; y++) + { if ((linelen = tlinelen(y)) == 0) { *ptr++ = '\n'; continue; @@ -608,6 +595,7 @@ getsel(void) 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; @@ -628,8 +616,8 @@ getsel(void) * st. * FIXME: Fix the computer world. */ - if ((y < sel.ne.y || lastx >= linelen) && - (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + if ((y < sel.ne.y || lastx >= linelen) + && (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) *ptr++ = '\n'; } *ptr = 0; @@ -641,9 +629,15 @@ 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 @@ -697,6 +691,7 @@ execsh(char *cmd, char **args) setenv("SHELL", sh, 1); setenv("HOME", pw->pw_dir, 1); setenv("TERM", termname, 1); + setenv("COLORTERM", "truecolor", 1); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); @@ -715,17 +710,16 @@ 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; + while ((p = waitpid(-1, &stat, WNOHANG)) > 0) { + if (p == pid) { - 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); + 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 @@ -756,6 +750,7 @@ int ttynew(const char *line, char *cmd, const char *out, char **args) { int m, s; + struct sigaction sa; if (out) { term.mode |= MODE_PRINT; @@ -794,7 +789,7 @@ ttynew(const char *line, char *cmd, const char *out, char **args) if (ioctl(s, TIOCSCTTY, NULL) < 0) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); if (s > 2) - close(s); + close(s); #ifdef __OpenBSD__ if (pledge("stdio getpw proc exec", NULL) == -1) die("pledge\n"); @@ -808,7 +803,10 @@ ttynew(const char *line, char *cmd, const char *out, char **args) #endif close(s); cmdfd = m; - signal(SIGCHLD, sigchld); + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigchld; + sigaction(SIGCHLD, &sa, NULL); break; } return cmdfd; @@ -960,6 +958,12 @@ tattrset(int attr) return 0; } +int +tisaltscr(void) +{ + return IS_SET(MODE_ALTSCREEN); +} + void tsetdirt(int top, int bot) { @@ -988,6 +992,13 @@ tsetdirtattr(int attr) } void +tsetsixelattr(Line line, int x1, int x2) +{ + for (; x1 <= x2; x1++) + line[x1].mode |= ATTR_SIXEL; +} + +void tfulldirt(void) { tsetdirt(0, term.row-1); @@ -1008,15 +1019,18 @@ 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; - term.c = (TCursor){{ - .mode = ATTR_NULL, - .fg = defaultfg, - .bg = defaultbg - }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + tresetcursor(); memset(term.tabs, 0, term.col * sizeof(*term.tabs)); for (i = tabspaces; i < term.col; i += tabspaces) @@ -1031,6 +1045,7 @@ treset(void) tmoveto(0, 0); tcursor(CURSOR_SAVE); tclearregion(0, 0, term.col-1, term.row-1); + tdeleteimages(); tswapscreen(); } } @@ -1047,9 +1062,12 @@ void tswapscreen(void) { Line *tmp = term.line; + ImageList *im = term.images; term.line = term.alt; term.alt = tmp; + term.images = term.images_alt; + term.images_alt = im; term.mode ^= MODE_ALTSCREEN; tfulldirt(); } @@ -1057,8 +1075,13 @@ tswapscreen(void) void tscrolldown(int orig, int n) { + int i; Line temp; + int bot = term.bot; + int scr = 0; + int itop = orig + scr, ibot = bot + scr; + ImageList *im, *next; LIMIT(n, 0, term.bot-orig+1); @@ -1071,14 +1094,29 @@ tscrolldown(int orig, int n) term.line[i-n] = temp; } + /* move images, if they are inside the scrolling region */ + for (im = term.images; im; im = next) { + next = im->next; + if (im->y >= itop && im->y <= ibot) { + im->y += n; + if (im->y > ibot) + delete_image(im); + } + } + selscroll(orig, n); } void tscrollup(int orig, int n) { + int i; Line temp; + int bot = term.bot; + int scr = 0; + int itop = orig + scr, ibot = bot + scr; + ImageList *im, *next; LIMIT(n, 0, term.bot-orig+1); @@ -1091,6 +1129,16 @@ tscrollup(int orig, int n) term.line[i+n] = temp; } + /* move images, if they are inside the scrolling region */ + for (im = term.images; im; im = next) { + next = im->next; + if (im->y >= itop && im->y <= ibot) { + im->y -= n; + if (im->y < itop) + delete_image(im); + } + } + selscroll(orig, -n); } @@ -1218,6 +1266,7 @@ 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; + } void @@ -1292,10 +1341,22 @@ tinsertblankline(int n) } void +tdeleteimages(void) +{ + ImageList *im, *next; + + for (im = term.images; im; im = next) { + next = im->next; + delete_image(im); + } +} + +void tdeleteline(int n) { - if (BETWEEN(term.c.y, term.top, term.bot)) + if (BETWEEN(term.c.y, term.top, term.bot)) { tscrollup(term.c.y, n); + } } int32_t @@ -1475,7 +1536,8 @@ tsetscroll(int t, int b) void tsetmode(int priv, int set, const int *args, int narg) { - int alt; const int *lim; + int alt; + const int *lim; for (lim = args + narg; args < lim; ++args) { if (priv) { @@ -1546,8 +1608,7 @@ tsetmode(int priv, int set, const int *args, int narg) break; alt = IS_SET(MODE_ALTSCREEN); if (alt) { - tclearregion(0, 0, term.col-1, - term.row-1); + tclearregion(0, 0, term.col-1, term.row-1); } if (set ^ alt) /* set is always 1 or 0 */ tswapscreen(); @@ -1570,6 +1631,12 @@ tsetmode(int priv, int set, const int *args, int narg) and can be mistaken for other control codes. */ break; + case 80: /* DECSDM -- Sixel Display Mode */ + MODBIT(term.mode, set, MODE_SIXEL_SDM); + break; + case 8452: /* sixel scrolling leaves cursor to right of graphic */ + MODBIT(term.mode, set, MODE_SIXEL_CUR_RT); + break; default: fprintf(stderr, "erresc: unknown private set/reset mode %d\n", @@ -1605,8 +1672,11 @@ tsetmode(int priv, int set, const int *args, int narg) void csihandle(void) { - char buf[40]; - int len; + char buffer[40]; + int n = 0, len; + ImageList *im, *next; + int pi, pa; + int maxcol = term.col; switch (csiescseq.mode[0]) { default: @@ -1704,19 +1774,30 @@ 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); - if (term.c.y < term.row-1) { - tclearregion(0, term.c.y+1, term.col-1, - term.row-1); - } + tclearregion(term.c.x, term.c.y, maxcol-1, term.c.y); + if (term.c.y < term.row-1) + tclearregion(0, term.c.y+1, maxcol-1, term.row-1); break; case 1: /* above */ if (term.c.y > 0) - tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, 0, maxcol-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); + case 2: /* screen */ + + tclearregion(0, 0, maxcol-1, term.row-1); + tdeleteimages(); + break; + case 3: /* scrollback */ + for (im = term.images; im; im = next) { + next = im->next; + if (im->y < 0) + delete_image(im); + } + break; + case 6: /* sixels */ + tdeleteimages(); + tfulldirt(); break; default: goto unknown; @@ -1725,19 +1806,43 @@ 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); + tclearregion(term.c.x, term.c.y, maxcol-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); + tclearregion(0, term.c.y, maxcol-1, term.c.y); break; } break; - case 'S': /* SU -- Scroll <n> line up */ - if (csiescseq.priv) break; + case 'S': /* SU -- Scroll <n> line up ; XTSMGRAPHICS */ + if (csiescseq.priv) { + if (csiescseq.narg > 1) { + /* XTSMGRAPHICS */ + pi = csiescseq.arg[0]; + pa = csiescseq.arg[1]; + if (pi == 1 && (pa == 1 || pa == 2 || pa == 4)) { + /* number of sixel color registers */ + /* (read, reset and read the maximum value give the same response) */ + n = snprintf(buffer, sizeof buffer, "\033[?1;0;%dS", DECSIXEL_PALETTE_MAX); + ttywrite(buffer, n, 1); + break; + } else if (pi == 2 && (pa == 1 || pa == 2 || pa == 4)) { + /* sixel graphics geometry (in pixels) */ + /* (read, reset and read the maximum value give the same response) */ + n = snprintf(buffer, sizeof buffer, "\033[?2;0;%d;%dS", + MIN(term.col * win.cw, DECSIXEL_WIDTH_MAX), + MIN(term.row * win.ch, DECSIXEL_HEIGHT_MAX)); + ttywrite(buffer, n, 1); + break; + } + /* the number of color registers and sixel geometry can't be changed */ + n = snprintf(buffer, sizeof buffer, "\033[?%d;3;0S", pi); /* failure */ + ttywrite(buffer, n, 1); + } + goto unknown; + } DEFAULT(csiescseq.arg[0], 1); tscrollup(term.top, csiescseq.arg[0]); break; @@ -1785,9 +1890,9 @@ csihandle(void) ttywrite("\033[0n", sizeof("\033[0n") - 1, 0); break; case 6: /* Report Cursor Position (CPR) "<row>;<column>R" */ - len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + len = snprintf(buffer, sizeof(buffer), "\033[%i;%iR", term.c.y+1, term.c.x+1); - ttywrite(buf, len, 0); + ttywrite(buffer, len, 0); break; default: goto unknown; @@ -1806,6 +1911,27 @@ csihandle(void) case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ tcursor(CURSOR_SAVE); break; + case 't': /* title stack operations ; XTWINOPS */ + switch (csiescseq.arg[0]) { + case 14: /* text area size in pixels */ + if (csiescseq.narg > 1) + goto unknown; + n = snprintf(buffer, sizeof buffer, "\033[4;%d;%dt", + term.row * win.ch, term.col * win.cw); + ttywrite(buffer, n, 1); + break; + case 16: /* character cell size in pixels */ + n = snprintf(buffer, sizeof buffer, "\033[6;%d;%dt", win.ch, win.cw); + ttywrite(buffer, n, 1); + break; + case 18: /* size of the text area in characters */ + n = snprintf(buffer, sizeof buffer, "\033[8;%d;%dt", term.row, term.col); + ttywrite(buffer, n, 1); + break; + default: + goto unknown; + } + break; case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ if (csiescseq.priv) { goto unknown; @@ -1870,8 +1996,8 @@ osc_color_response(int num, int index, int is_osc4) return; } - n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", - is_osc4 ? "4;" : "", num, r, r, g, g, b, b); + n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x%s", + is_osc4 ? "4;" : "", num, r, r, g, g, b, b, strescseq.term); if (n < 0 || n >= sizeof(buf)) { fprintf(stderr, "error: %s while printing %s response\n", n < 0 ? "snprintf failed" : "truncation occurred", @@ -1891,6 +2017,11 @@ strhandle(void) { defaultbg, "background" }, { defaultcs, "cursor" } }; + ImageList *im, *newimages, *next, *tail = NULL; + int i, x1, y1, x2, y2, y, numimages; + int cx, cy; + Line line; + int scr = 0; term.esc &= ~(ESC_STR_END|ESC_STR); strparse(); @@ -1924,6 +2055,8 @@ strhandle(void) } } return; + case 8: /* Clear Hyperlinks */ + return; case 10: /* set dynamic VT100 text foreground color */ case 11: /* set dynamic VT100 text background color */ case 12: /* set dynamic text cursor color */ @@ -1986,6 +2119,95 @@ strhandle(void) xsettitle(strescseq.args[0]); return; case 'P': /* DCS -- Device Control String */ + if (IS_SET(MODE_SIXEL)) { + term.mode &= ~MODE_SIXEL; + if (!sixel_st.image.data) { + sixel_parser_deinit(&sixel_st); + return; + } + cx = IS_SET(MODE_SIXEL_SDM) ? 0 : term.c.x; + cy = IS_SET(MODE_SIXEL_SDM) ? 0 : term.c.y; + if ((numimages = sixel_parser_finalize(&sixel_st, &newimages, + cx, cy + scr, win.cw, win.ch)) <= 0) { + sixel_parser_deinit(&sixel_st); + perror("sixel_parser_finalize() failed"); + return; + } + sixel_parser_deinit(&sixel_st); + x1 = newimages->x; + y1 = newimages->y; + x2 = x1 + newimages->cols; + y2 = y1 + numimages; + /* Delete the old images that are covered by the new image(s). We also need + * to check if they have already been deleted before adding the new ones. */ + if (term.images) { + char transparent[numimages]; + for (i = 0, im = newimages; im; im = im->next, i++) { + transparent[i] = im->transparent; + } + for (im = term.images; im; im = next) { + next = im->next; + if (im->y >= y1 && im->y < y2) { + y = im->y - scr; + if (y >= 0 && y < term.row && term.dirty[y]) { + line = term.line[y]; + j = MIN(im->x + im->cols, term.col); + for (i = im->x; i < j; i++) { + if (line[i].mode & ATTR_SIXEL) + break; + } + if (i == j) { + delete_image(im); + continue; + } + } + if (im->x >= x1 && im->x + im->cols <= x2 && !transparent[im->y - y1]) { + delete_image(im); + continue; + } + } + tail = im; + } + } + if (tail) { + tail->next = newimages; + newimages->prev = tail; + } else { + term.images = newimages; + } + x2 = MIN(x2, term.col) - 1; + if (IS_SET(MODE_SIXEL_SDM)) { + /* Sixel display mode: put the sixel in the upper left corner of + * the screen, disable scrolling (the sixel will be truncated if + * it is too long) and do not change the cursor position. */ + for (i = 0, im = newimages; im; im = next, i++) { + next = im->next; + if (i >= term.row) { + delete_image(im); + continue; + } + im->y = i + scr; + tsetsixelattr(term.line[i], x1, x2); + term.dirty[MIN(im->y, term.row-1)] = 1; + } + } else { + for (i = 0, im = newimages; im; im = next, i++) { + next = im->next; + im->y = term.c.y + scr; + tsetsixelattr(term.line[term.c.y], x1, x2); + term.dirty[MIN(im->y, term.row-1)] = 1; + if (i < numimages-1) { + im->next = NULL; + tnewline(0); + im->next = next; + } + } + /* if mode 8452 is set, sixel scrolling leaves cursor to right of graphic */ + if (IS_SET(MODE_SIXEL_CUR_RT)) + term.c.x = MIN(term.c.x + newimages->cols, term.col-1); + } + } + return; case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2007,6 +2229,16 @@ strparse(void) if (*p == '\0') return; + /* preserve semicolons in window titles, icon names and OSC 7 sequences */ + if (strescseq.type == ']' && ( + p[0] <= '2' + ) && p[1] == ';') { + strescseq.args[strescseq.narg++] = p; + strescseq.args[strescseq.narg++] = p + 2; + p[1] = '\0'; + return; + } + while (strescseq.narg < STR_ARG_SIZ) { strescseq.args[strescseq.narg++] = p; while ((c = *p) != ';' && c != '\0') @@ -2041,7 +2273,7 @@ strdump(void) fprintf(stderr, "(%02x)", c); } } - fprintf(stderr, "ESC\\\n"); + fprintf(stderr, (strescseq.term[0] == 0x1b) ? "ESC\\\n" : "BEL\n"); } void @@ -2179,9 +2411,12 @@ 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 = '_'; @@ -2193,7 +2428,6 @@ tstrsequence(uchar c) c = ']'; break; } - strreset(); strescseq.type = c; term.esc |= ESC_STR; } @@ -2220,6 +2454,7 @@ tcontrolcode(uchar ascii) case '\a': /* BEL */ if (term.esc & ESC_STR_END) { /* backwards compatibility to xterm */ + strescseq.term = STR_TERM_BEL; strhandle(); } else { xbell(); @@ -2295,6 +2530,38 @@ tcontrolcode(uchar ascii) term.esc &= ~(ESC_STR_END|ESC_STR); } +void +dcshandle(void) +{ + int bgcolor, transparent; + unsigned char r, g, b, a = 255; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case 'q': /* DECSIXEL */ + transparent = (csiescseq.narg >= 2 && csiescseq.arg[1] == 1); + if (IS_TRUECOL(term.c.attr.bg)) { + r = term.c.attr.bg >> 16 & 255; + g = term.c.attr.bg >> 8 & 255; + b = term.c.attr.bg >> 0 & 255; + } else { + xgetcolor(term.c.attr.bg, &r, &g, &b); + if (term.c.attr.bg == defaultbg) + a = dc.col[defaultbg].pixel >> 24 & 255; + } + bgcolor = a << 24 | r << 16 | g << 8 | b; + if (sixel_parser_init(&sixel_st, transparent, (255 << 24), bgcolor, 1, win.cw, win.ch) != 0) + perror("sixel_parser_init() failed"); + term.mode |= MODE_SIXEL; + break; + } +} + /* * returns 1 when the sequence is finished and it hasn't to read * more characters for this sequence, otherwise 0 @@ -2313,6 +2580,7 @@ eschandle(uchar ascii) term.esc |= ESC_UTF8; return 0; case 'P': /* DCS -- Device Control String */ + term.esc |= ESC_DCS; case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ case ']': /* OSC -- Operating System Command */ @@ -2372,8 +2640,10 @@ eschandle(uchar ascii) tcursor(CURSOR_LOAD); break; case '\\': /* ST -- String Terminator */ - if (term.esc & ESC_STR_END) + if (term.esc & ESC_STR_END) { + strescseq.term = STR_TERM_ST; strhandle(); + } break; default: fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", @@ -2392,7 +2662,8 @@ tputc(Rune u) Glyph *gp; control = ISCONTROL(u); - if (u < 127 || !IS_SET(MODE_UTF8)) { + if (u < 127 || !IS_SET(MODE_UTF8)) + { c[0] = u; width = len = 1; } else { @@ -2413,11 +2684,14 @@ 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); + term.esc &= ~(ESC_START|ESC_STR|ESC_DCS); term.esc |= ESC_STR_END; goto check_control_code; } + if (term.esc & ESC_DCS) + goto check_control_code; + if (strescseq.len+len >= strescseq.siz) { /* * Here is a bug in terminals. If the user never sends @@ -2471,6 +2745,15 @@ check_control_code: csihandle(); } return; + } else if (term.esc & ESC_DCS) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + csiparse(); + dcshandle(); + } + return; } else if (term.esc & ESC_UTF8) { tdefutf8(u); } else if (term.esc & ESC_ALTCHARSET) { @@ -2489,6 +2772,7 @@ check_control_code: */ return; } + if (selected(term.c.x, term.c.y)) selclear(); @@ -2541,7 +2825,11 @@ twrite(const char *buf, int buflen, int show_ctrl) int n; for (n = 0; n < buflen; n += charsize) { - if (IS_SET(MODE_UTF8)) { + if (IS_SET(MODE_SIXEL) && sixel_st.state != PS_ESC) { + charsize = sixel_parser_parse(&sixel_st, (const unsigned char*)buf + n, buflen - n); + continue; + } else if (IS_SET(MODE_UTF8)) + { /* process a complete utf8 char */ charsize = utf8decode(buf + n, &u, buflen - n); if (charsize == 0) @@ -2568,11 +2856,13 @@ 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; - TCursor c; + int x2; + Line line; + ImageList *im, *next; if (col < 1 || row < 1) { fprintf(stderr, @@ -2580,23 +2870,19 @@ tresize(int col, int 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]); + /* scroll both screens independently */ + if (row < term.row) { + tcursor(CURSOR_SAVE); + tsetscroll(0, term.row - 1); + for (i = 0; i < 2; i++) { + if (term.c.y >= row) { + tscrollup(0, term.c.y - row + 1); + } + for (j = row; j < term.row; j++) + free(term.line[j]); + tswapscreen(); + tcursor(CURSOR_LOAD); + } } /* resize to new height */ @@ -2616,25 +2902,27 @@ tresize(int col, int row) term.line[i] = xmalloc(col * sizeof(Glyph)); term.alt[i] = xmalloc(col * sizeof(Glyph)); } - if (col > term.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; + /* 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++) { + tmoveto(term.c.x, term.c.y); /* make use of the LIMIT in tmoveto */ + tcursor(CURSOR_SAVE); if (mincol < col && 0 < minrow) { tclearregion(mincol, 0, col - 1, minrow - 1); } @@ -2644,7 +2932,22 @@ tresize(int col, int row) tswapscreen(); tcursor(CURSOR_LOAD); } - term.c = c; + + /* expand images into new text cells */ + for (i = 0; i < 2; i++) { + for (im = term.images; im; im = next) { + next = im->next; + if (im->y < 0 || im->y >= term.row) { + delete_image(im); + continue; + } + line = term.line[im->y]; + x2 = MIN(im->x + im->cols, col) - 1; + if (mincol < col && x2 >= mincol && im->x < col) + tsetsixelattr(line, MAX(im->x, mincol), x2); + } + tswapscreen(); + } } void @@ -2667,6 +2970,7 @@ drawregion(int x1, int y1, int x2, int y2) } } + void draw(void) { @@ -2684,6 +2988,7 @@ 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]); term.ocx = cx; |