aboutsummaryrefslogtreecommitdiff
path: root/st.c
diff options
context:
space:
mode:
authortwells46 <173561638+twells46@users.noreply.github.com>2025-11-29 08:18:48 -0600
committertwells46 <173561638+twells46@users.noreply.github.com>2025-11-29 08:18:48 -0600
commit9d70dfc76159cffc6d85530ce7d9897118d9677c (patch)
treee2171e954cdeed661b8e8af3500992d9b656f96e /st.c
parentb1c93ae35772f4fb3a923605fefa02bf1fee585e (diff)
Add sixel patch
Diffstat (limited to 'st.c')
-rw-r--r--st.c581
1 files changed, 443 insertions, 138 deletions
diff --git a/st.c b/st.c
index 8e57991..269d553 100644
--- a/st.c
+++ b/st.c
@@ -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;