/* 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))
MODE_ECHO = 1 << 4,
MODE_PRINT = 1 << 5,
MODE_UTF8 = 1 << 6,
- MODE_SIXEL = 1 << 7,
};
enum cursor_movement {
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 {
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 */
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);
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)
}
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
{
int newx, newy, xt, yt;
int delim, prevdelim;
- Glyph *gp, *prevgp;
+ const Glyph *gp, *prevgp;
switch (sel.snap) {
case SNAP_WORD:
{
char *str, *ptr;
int y, bufsize, lastx, linelen;
- Glyph *gp, *last;
+ const Glyph *gp, *last;
if (sel.ob.x == -1)
return NULL;
* 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;
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
}
int
-ttynew(char *line, char *cmd, char *out, char **args)
+ttynew(const char *line, char *cmd, const char *out, char **args)
{
int m, s;
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");
if (buflen > 0)
memmove(buf, buf + written, buflen);
return ret;
-
}
}
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();
}
}
}
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 */
}
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;
}
void
-tsetattr(int *attr, int l)
+tsetattr(const int *attr, int l)
{
int i;
int32_t idx;
}
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) {
if (csiescseq.arg[0] == 0)
ttywrite(vtiden, strlen(vtiden), 0);
break;
+ case 'b': /* REP -- if last char is printable print it <n> more times */
+ DEFAULT(csiescseq.arg[0], 1);
+ if (term.lastc)
+ while (csiescseq.arg[0]-- > 0)
+ tputc(term.lastc);
+ break;
case 'C': /* CUF -- Cursor <n> Forward */
case 'a': /* HPR -- Cursor <n> Forward */
DEFAULT(csiescseq.arg[0], 1);
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);
}
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)
{
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);
}
}
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",
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;
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);
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 = '_';
c = ']';
break;
}
+ strreset();
strescseq.type = c;
term.esc |= ESC_STR;
}
return;
case '\032': /* SUB */
tsetchar('?', &term.c.attr, term.c.x, term.c.y);
+ /* FALLTHROUGH */
case '\030': /* CAN */
csireset();
break;
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))
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
/*
* control codes are not shown ever
*/
+ if (!term.esc)
+ term.lastc = 0;
return;
} else if (term.esc & ESC_START) {
if (term.esc & ESC_CSI) {
*/
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];
}
tsetchar(u, &term.c.attr, term.c.x, term.c.y);
+ term.lastc = u;
if (width == 2) {
gp->mode |= ATTR_WIDE;
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)
drawregion(int x1, int y1, int x2, int y2)
{
int y;
+
for (y = y1; y < y2; y++) {
if (!term.dirty[y])
continue;
void
draw(void)
{
- int cx = term.c.x;
+ int cx = term.c.x, ocx = term.ocx, ocy = term.ocy;
if (!xstartdraw())
return;
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