X-Git-Url: https://git.armaanb.net/?p=st.git;a=blobdiff_plain;f=st.c;h=50226d1677b230d290d9e802742e85f27dc1d4b4;hp=58f79411839d948b46ccc6e55dfc60e7f99a72f0;hb=7ceb3d1f72eabfa678e5cfae176c57630ad98c43;hpb=69e32a61df15787c410a48eaa10a89240c36257d diff --git a/st.c b/st.c index 58f7941..50226d1 100644 --- a/st.c +++ b/st.c @@ -3,31 +3,20 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include -#include -#include #include #include #include -#include #include -#include -#include #include -/* X11 */ -#include -#include - #include "st.h" #include "win.h" @@ -41,20 +30,29 @@ /* Arbitrary sizes */ #define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 #define ESC_BUF_SIZ (128*UTF_SIZ) #define ESC_ARG_SIZ 16 #define STR_BUF_SIZ ESC_BUF_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ /* macros */ -#define NUMMAXLEN(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) +#define IS_SET(flag) ((term.mode & (flag)) != 0) #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177') #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) -#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL) - -/* constants */ -#define ISO14755CMD "dmenu -w \"$WINDOWID\" -p codepoint: ] [;]] []] */ typedef struct { @@ -109,23 +152,10 @@ typedef struct { int narg; /* nb of args */ } STREscape; -/* function definitions used in config.h */ -static void clipcopy(const Arg *); -static void clippaste(const Arg *); -static void numlock(const Arg *); -static void selpaste(const Arg *); -static void printsel(const Arg *); -static void printscreen(const Arg *) ; -static void iso14755(const Arg *); -static void toggleprinter(const Arg *); -static void sendbreak(const Arg *); - -/* config.h for applying patches and the configuration. */ -#include "config.h" - -static void execsh(char **); +static void execsh(char *, char **); static void stty(char **); static void sigchld(int); +static void ttywriteraw(const char *, size_t); static void csidump(void); static void csihandle(void); @@ -158,11 +188,12 @@ static void tscrollup(int, int); static void tscrolldown(int, int); static void tsetattr(int *, int); static void tsetchar(Rune, 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 int twrite(const char *, int, int); static void tfulldirt(void); -static void techo(Rune); static void tcontrolcode(uchar ); static void tdectest(char ); static void tdefutf8(char); @@ -170,43 +201,36 @@ static int32_t tdefcolor(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 char *utf8strchr(char *s, Rune u); static size_t utf8validate(Rune *, size_t); static char *base64dec(const char *); +static char base64dec_getc(const char **); static ssize_t xwrite(int, const char *, size_t); /* Globals */ -TermWindow win; -Term term; -Selection sel; -int cmdfd; -pid_t pid; -int oldbutton = 3; /* button event on startup: 3 = release */ - +static Term term; +static Selection sel; static CSIEscape csiescseq; static STREscape strescseq; 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}; -/* config.h array lengths */ -size_t colornamelen = LEN(colorname); -size_t mshortcutslen = LEN(mshortcuts); -size_t shortcutslen = LEN(shortcuts); -size_t selmaskslen = LEN(selmasks); -size_t keyslen = LEN(key); -size_t mappedkeyslen = LEN(mappedkeys); - ssize_t xwrite(int fd, const char *s, size_t len) { @@ -227,10 +251,10 @@ xwrite(int fd, const char *s, size_t len) void * xmalloc(size_t len) { - void *p = malloc(len); + void *p; - if (!p) - die("Out of memory\n"); + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); return p; } @@ -239,7 +263,7 @@ void * xrealloc(void *p, size_t len) { if ((p = realloc(p, len)) == NULL) - die("Out of memory\n"); + die("realloc: %s\n", strerror(errno)); return p; } @@ -248,13 +272,13 @@ char * xstrdup(char *s) { if ((s = strdup(s)) == NULL) - die("Out of memory\n"); + die("strdup: %s\n", strerror(errno)); return s; } size_t -utf8decode(char *c, Rune *u, size_t clen) +utf8decode(const char *c, Rune *u, size_t clen) { size_t i, j, len, type; Rune udecoded; @@ -312,23 +336,6 @@ utf8encodebyte(Rune u, size_t i) return utfbyte[i] | (u & ~utfmask[i]); } -char * -utf8strchr(char *s, Rune u) -{ - Rune r; - size_t i, j, len; - - len = strlen(s); - for (i = 0, j = 0; i < len; i += j) { - if (!(j = utf8decode(&s[i], &r, len - i))) - break; - if (r == u) - return &(s[i]); - } - - return NULL; -} - size_t utf8validate(Rune *u, size_t i) { @@ -359,7 +366,7 @@ char base64dec_getc(const char **src) { while (**src && !isprint(**src)) (*src)++; - return *((*src)++); + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ } char * @@ -377,6 +384,10 @@ base64dec(const char *src) int c = base64_digits[(unsigned char) base64dec_getc(&src)]; int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + *dst++ = (a << 2) | ((b & 0x30) >> 4); if (c == -1) break; @@ -392,13 +403,9 @@ base64dec(const char *src) void selinit(void) { - clock_gettime(CLOCK_MONOTONIC, &sel.tclick1); - clock_gettime(CLOCK_MONOTONIC, &sel.tclick2); sel.mode = SEL_IDLE; sel.snap = 0; sel.ob.x = -1; - sel.primary = NULL; - sel.clipboard = NULL; } int @@ -415,6 +422,52 @@ tlinelen(int y) return i; } +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + 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; +} + void selnormalize(void) { @@ -446,7 +499,8 @@ selnormalize(void) int selected(int x, int y) { - if (sel.mode == SEL_EMPTY) + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) return 0; if (sel.type == SEL_RECTANGULAR) @@ -585,24 +639,6 @@ getsel(void) return str; } -void -selpaste(const Arg *dummy) -{ - xselpaste(); -} - -void -clipcopy(const Arg *dummy) -{ - xclipcopy(); -} - -void -clippaste(const Arg *dummy) -{ - xclippaste(); -} - void selclear(void) { @@ -625,7 +661,7 @@ die(const char *errstr, ...) } void -execsh(char **args) +execsh(char *cmd, char **args) { char *sh, *prog; const struct passwd *pw; @@ -633,13 +669,13 @@ execsh(char **args) errno = 0; if ((pw = getpwuid(getuid())) == NULL) { if (errno) - die("getpwuid:%s\n", strerror(errno)); + die("getpwuid: %s\n", strerror(errno)); else die("who are you?\n"); } if ((sh = getenv("SHELL")) == NULL) - sh = (pw->pw_shell[0]) ? pw->pw_shell : shell; + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; if (args) prog = args[0]; @@ -676,17 +712,18 @@ sigchld(int a) pid_t p; if ((p = waitpid(pid, &stat, WNOHANG)) < 0) - die("Waiting for pid %hd failed: %s\n", pid, strerror(errno)); + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); if (pid != p) return; - if (!WIFEXITED(stat) || WEXITSTATUS(stat)) - die("child finished with error '%d'\n", stat); + 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 stty(char **args) { @@ -708,14 +745,13 @@ stty(char **args) } *q = '\0'; if (system(cmd) != 0) - perror("Couldn't call stty"); + perror("Couldn't call stty"); } -void -ttynew(char *line, char *out, char **args) +int +ttynew(char *line, char *cmd, char *out, char **args) { int m, s; - struct winsize w = {term.row, term.col, 0, 0}; if (out) { term.mode |= MODE_PRINT; @@ -729,19 +765,20 @@ ttynew(char *line, char *out, char **args) if (line) { if ((cmdfd = open(line, O_RDWR)) < 0) - die("open line failed: %s\n", strerror(errno)); + die("open line '%s' failed: %s\n", + line, strerror(errno)); dup2(cmdfd, 0); stty(args); - return; + return cmdfd; } /* seems to work fine on linux, openbsd and freebsd */ - if (openpty(&m, &s, NULL, NULL, &w) < 0) + if (openpty(&m, &s, NULL, NULL, NULL) < 0) die("openpty failed: %s\n", strerror(errno)); switch (pid = fork()) { case -1: - die("fork failed\n"); + die("fork failed: %s\n", strerror(errno)); break; case 0: close(iofd); @@ -753,14 +790,23 @@ ttynew(char *line, char *out, char **args) die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); close(s); close(m); - execsh(args); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif + execsh(cmd, args); break; default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif close(s); cmdfd = m; signal(SIGCHLD, sigchld); break; } + return cmdfd; } size_t @@ -768,44 +814,53 @@ ttyread(void) { static char buf[BUFSIZ]; static int buflen = 0; - char *ptr; - int charsize; /* size of utf8 char in bytes */ - Rune unicodep; + int written; int ret; /* append read bytes to unprocessed bytes */ if ((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) - die("Couldn't read from shell: %s\n", strerror(errno)); - + die("couldn't read from shell: %s\n", strerror(errno)); buflen += ret; - ptr = buf; - - for (;;) { - if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { - /* process a complete utf8 char */ - charsize = utf8decode(ptr, &unicodep, buflen); - if (charsize == 0) - break; - tputc(unicodep); - ptr += charsize; - buflen -= charsize; - } else { - if (buflen <= 0) - break; - tputc(*ptr++ & 0xFF); - buflen--; - } - } + written = twrite(buf, buflen, 0); + buflen -= written; /* keep any uncomplete utf8 char for the next call */ if (buflen > 0) - memmove(buf, ptr, buflen); + memmove(buf, buf + written, buflen); return ret; } void -ttywrite(const char *s, size_t n) +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) { fd_set wfd, rfd; ssize_t r; @@ -861,32 +916,6 @@ write_error: die("write error on tty: %s\n", strerror(errno)); } -void -ttysend(char *s, size_t n) -{ - int len; - char *t, *lim; - Rune u; - - ttywrite(s, n); - if (!IS_SET(MODE_ECHO)) - return; - - lim = &s[n]; - for (t = s; t < lim; t += len) { - if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { - len = utf8decode(t, &u, n); - } else { - u = *t & 0xFF; - len = 1; - } - if (len <= 0) - break; - techo(u); - n -= len; - } -} - void ttyresize(int tw, int th) { @@ -900,6 +929,13 @@ ttyresize(int tw, int th) fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); } +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + int tattrset(int attr) { @@ -995,8 +1031,6 @@ tnew(int col, int row) { term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; tresize(col, row); - term.numlock = 1; - treset(); } @@ -1406,7 +1440,8 @@ tsetattr(int *attr, int l) } else { fprintf(stderr, "erresc(default): gfx attr %d unknown\n", - attr[i]), csidump(); + attr[i]); + csidump(); } break; } @@ -1432,20 +1467,16 @@ tsetscroll(int t, int b) void tsetmode(int priv, int set, int *args, int narg) { - int *lim, mode; - int alt; + int alt, *lim; for (lim = args + narg; args < lim; ++args) { if (priv) { switch (*args) { case 1: /* DECCKM -- Cursor key */ - MODBIT(term.mode, set, MODE_APPCURSOR); + xsetmode(set, MODE_APPCURSOR); break; case 5: /* DECSCNM -- Reverse video */ - mode = term.mode; - MODBIT(term.mode, set, MODE_REVERSE); - if (mode != term.mode) - redraw(); + xsetmode(set, MODE_REVERSE); break; case 6: /* DECOM -- Origin */ MODBIT(term.c.state, set, CURSOR_ORIGIN); @@ -1465,36 +1496,36 @@ tsetmode(int priv, int set, int *args, int narg) case 12: /* att610 -- Start blinking cursor (IGNORED) */ break; case 25: /* DECTCEM -- Text Cursor Enable Mode */ - MODBIT(term.mode, !set, MODE_HIDE); + xsetmode(!set, MODE_HIDE); break; case 9: /* X10 mouse compatibility mode */ xsetpointermotion(0); - MODBIT(term.mode, 0, MODE_MOUSE); - MODBIT(term.mode, set, MODE_MOUSEX10); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEX10); break; case 1000: /* 1000: report button press */ xsetpointermotion(0); - MODBIT(term.mode, 0, MODE_MOUSE); - MODBIT(term.mode, set, MODE_MOUSEBTN); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEBTN); break; case 1002: /* 1002: report motion on button press */ xsetpointermotion(0); - MODBIT(term.mode, 0, MODE_MOUSE); - MODBIT(term.mode, set, MODE_MOUSEMOTION); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMOTION); break; case 1003: /* 1003: enable all mouse motions */ xsetpointermotion(set); - MODBIT(term.mode, 0, MODE_MOUSE); - MODBIT(term.mode, set, MODE_MOUSEMANY); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMANY); break; case 1004: /* 1004: send focus events to tty */ - MODBIT(term.mode, set, MODE_FOCUS); + xsetmode(set, MODE_FOCUS); break; case 1006: /* 1006: extended reporting mode */ - MODBIT(term.mode, set, MODE_MOUSESGR); + xsetmode(set, MODE_MOUSESGR); break; case 1034: - MODBIT(term.mode, set, MODE_8BIT); + xsetmode(set, MODE_8BIT); break; case 1049: /* swap screen & set/restore cursor as xterm */ if (!allowaltscreen) @@ -1519,7 +1550,7 @@ tsetmode(int priv, int set, int *args, int narg) tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); break; case 2004: /* 2004: bracketed paste mode */ - MODBIT(term.mode, set, MODE_BRCKTPASTE); + xsetmode(set, MODE_BRCKTPASTE); break; /* Not implemented mouse modes. See comments there. */ case 1001: /* mouse highlight mode; can hang the @@ -1530,6 +1561,7 @@ tsetmode(int priv, int set, int *args, int narg) case 1015: /* urxvt mangled mouse mode; incompatible and can be mistaken for other control codes. */ + break; default: fprintf(stderr, "erresc: unknown private set/reset mode %d\n", @@ -1540,8 +1572,8 @@ tsetmode(int priv, int set, int *args, int narg) switch (*args) { case 0: /* Error (IGNORED) */ break; - case 2: /* KAM -- keyboard action */ - MODBIT(term.mode, set, MODE_KBDLOCK); + case 2: + xsetmode(set, MODE_KBDLOCK); break; case 4: /* IRM -- Insertion-replacement */ MODBIT(term.mode, set, MODE_INSERT); @@ -1609,7 +1641,7 @@ csihandle(void) break; case 'c': /* DA -- Device Attributes */ if (csiescseq.arg[0] == 0) - ttywrite(vtiden, sizeof(vtiden) - 1); + ttywrite(vtiden, strlen(vtiden), 0); break; case 'C': /* CUF -- Cursor Forward */ case 'a': /* HPR -- Cursor Forward */ @@ -1656,7 +1688,6 @@ csihandle(void) tputtab(csiescseq.arg[0]); break; case 'J': /* ED -- Clear screen */ - selclear(); switch (csiescseq.arg[0]) { case 0: /* below */ tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); @@ -1737,7 +1768,7 @@ csihandle(void) if (csiescseq.arg[0] == 6) { len = snprintf(buf, sizeof(buf),"\033[%i;%iR", term.c.y+1, term.c.x+1); - ttywrite(buf, len); + ttywrite(buf, len, 0); } break; case 'r': /* DECSTBM -- Set Scrolling Region */ @@ -1759,11 +1790,8 @@ csihandle(void) case ' ': switch (csiescseq.mode[1]) { case 'q': /* DECSCUSR -- Set Cursor Style */ - DEFAULT(csiescseq.arg[0], 1); - if (!BETWEEN(csiescseq.arg[0], 0, 6)) { + if (xsetcursor(csiescseq.arg[0])) goto unknown; - } - win.cursor = csiescseq.arg[0]; break; default: goto unknown; @@ -1805,7 +1833,7 @@ csireset(void) void strhandle(void) { - char *p = NULL; + char *p = NULL, *dec; int j, narg, par; term.esc &= ~(ESC_STR_END|ESC_STR); @@ -1823,12 +1851,10 @@ strhandle(void) return; case 52: if (narg > 2) { - char *dec; - dec = base64dec(strescseq.args[2]); if (dec) { - xsetsel(dec, CurrentTime); - clipcopy(NULL); + xsetsel(dec); + xclipcopy(); } else { fprintf(stderr, "erresc: invalid base64\n"); } @@ -1842,7 +1868,10 @@ strhandle(void) case 104: /* color reset, here p = NULL */ j = (narg > 1) ? atoi(strescseq.args[1]) : -1; if (xsetcolorname(j, p)) { - fprintf(stderr, "erresc: invalid color %s\n", p); + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); } else { /* * TODO if defaultbg color is changed, borders @@ -1939,28 +1968,6 @@ tprinter(char *s, size_t len) } } -void -iso14755(const Arg *arg) -{ - FILE *p; - char *us, *e, codepoint[9], uc[UTF_SIZ]; - unsigned long utf32; - - if (!(p = popen(ISO14755CMD, "r"))) - return; - - us = fgets(codepoint, sizeof(codepoint), p); - pclose(p); - - if (!us || *us == '\0' || *us == '-' || strlen(us) > 7) - return; - if ((utf32 = strtoul(us, &e, 16)) == ULONG_MAX || - (*e != '\n' && *e != '\0')) - return; - - ttysend(uc, utf8encode(utf32, uc)); -} - void toggleprinter(const Arg *arg) { @@ -2031,22 +2038,6 @@ tputtab(int n) term.c.x = LIMIT(x, 0, term.col-1); } -void -techo(Rune u) -{ - if (ISCONTROL(u)) { /* control code */ - if (u & 0x80) { - u &= 0x7f; - tputc('^'); - tputc('['); - } else if (u != '\n' && u != '\r' && u != '\t') { - u ^= 0x40; - tputc('^'); - } - } - tputc(u); -} - void tdefutf8(char ascii) { @@ -2187,7 +2178,7 @@ tcontrolcode(uchar ascii) case 0x99: /* TODO: SGCI */ break; case 0x9a: /* DECID -- Identify Terminal */ - ttywrite(vtiden, sizeof(vtiden) - 1); + ttywrite(vtiden, strlen(vtiden), 0); break; case 0x9b: /* TODO: CSI */ case 0x9c: /* TODO: ST */ @@ -2259,18 +2250,18 @@ eschandle(uchar ascii) } break; case 'Z': /* DECID -- Identify Terminal */ - ttywrite(vtiden, sizeof(vtiden) - 1); + ttywrite(vtiden, strlen(vtiden), 0); break; - case 'c': /* RIS -- Reset to inital state */ + case 'c': /* RIS -- Reset to initial state */ treset(); resettitle(); xloadcols(); break; case '=': /* DECPAM -- Application keypad */ - term.mode |= MODE_APPKEYPAD; + xsetmode(1, MODE_APPKEYPAD); break; case '>': /* DECPNM -- Normal keypad */ - term.mode &= ~MODE_APPKEYPAD; + xsetmode(0, MODE_APPKEYPAD); break; case '7': /* DECSC -- Save Cursor */ tcursor(CURSOR_SAVE); @@ -2332,7 +2323,6 @@ tputc(Rune u) goto check_control_code; } - if (IS_SET(MODE_SIXEL)) { /* TODO: implement sixel mode */ return; @@ -2340,7 +2330,7 @@ tputc(Rune u) if (term.esc&ESC_DCS && strescseq.len == 0 && u == 'q') term.mode |= MODE_SIXEL; - if (strescseq.len+len >= sizeof(strescseq.buf)-1) { + if (strescseq.len+len >= sizeof(strescseq.buf)) { /* * Here is a bug in terminals. If the user never sends * some code to stop the str or esc command, then st @@ -2437,6 +2427,38 @@ check_control_code: } } +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + void tresize(int col, int row) { @@ -2526,14 +2548,45 @@ resettitle(void) } void -redraw(void) +drawregion(int x1, int y1, int x2, int y2) { - tfulldirt(); - draw(); + int y; + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(term.line[y], x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + 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, term.ocy = term.c.y; + xfinishdraw(); + xximspot(term.ocx, term.ocy); } void -numlock(const Arg *dummy) +redraw(void) { - term.numlock ^= 1; + tfulldirt(); + draw(); }