X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=st.c;h=4c4a0c35bf57e8a0198600a3f26a497128b34adc;hb=6681af165b06c32cf65bf970a295600d46cb7025;hp=653cd49a5af05cf214cd394f4bce159b0e10d4f4;hpb=4a8574b439e2118bd4cb40374185fba861903620;p=st.git diff --git a/st.c b/st.c index 653cd49..4c4a0c3 100644 --- a/st.c +++ b/st.c @@ -70,6 +70,9 @@ char *argv0; #define LEN(a) (sizeof(a) / sizeof(a)[0]) #define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f)) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg) #define IS_SET(flag) ((term.mode & (flag)) != 0) @@ -149,7 +152,7 @@ enum charset { enum escape_state { ESC_START = 1, ESC_CSI = 2, - ESC_STR = 4, /* DSC, 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 */ @@ -390,6 +393,8 @@ static void tsetdirtattr(int); static void tsetmode(bool, bool, int *, int); static void tfulldirt(void); static void techo(char *, int); +static bool tcontrolcode(uchar ); +static void tdectest(char ); static int32_t tdefcolor(int *, int *, int); static void tselcs(void); static void tdeftran(char); @@ -444,6 +449,8 @@ static char *getsel(void); static void selcopy(void); static void selscroll(int, int); static void selsnap(int, int *, int *, int); +static void getbuttoninfo(XEvent *); +static void mousereport(XEvent *); static size_t utf8decode(char *, long *, size_t); static long utf8decodebyte(char, size_t *); @@ -452,11 +459,13 @@ static char utf8encodebyte(long, size_t); static size_t utf8len(char *); static size_t utf8validate(long *, size_t); -static ssize_t xwrite(int, char *, size_t); +static ssize_t xwrite(int, const char *, size_t); static void *xmalloc(size_t); static void *xrealloc(void *, size_t); static char *xstrdup(char *); +static void usage(void); + static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, [ClientMessage] = cmessage, @@ -518,7 +527,7 @@ static Fontcache frc[16]; static int frclen = 0; ssize_t -xwrite(int fd, char *s, size_t len) { +xwrite(int fd, const char *s, size_t len) { size_t aux = len; while(len > 0) { @@ -1228,19 +1237,11 @@ ttynew(void) { opt_io, strerror(errno)); } } + break; } } void -dump(char c) { - static int col; - - fprintf(stderr, " %02x '%c' ", c, isprint(c)?c:'.'); - if(++col % 10 == 0) - fprintf(stderr, "\n"); -} - -void ttyread(void) { static char buf[BUFSIZ]; static int buflen = 0; @@ -1270,7 +1271,7 @@ ttyread(void) { void ttywrite(const char *s, size_t n) { - if(write(cmdfd, s, n) == -1) + if(xwrite(cmdfd, s, n) == -1) die("write error on tty: %s\n", strerror(errno)); } @@ -1673,6 +1674,7 @@ tdefcolor(int *attr, int *npar, int l) { default: fprintf(stderr, "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; } return idx; @@ -1781,7 +1783,6 @@ tsetmode(bool priv, bool set, int *args, int narg) { for(lim = args + narg; args < lim; ++args) { if(priv) { switch(*args) { - break; case 1: /* DECCKM -- Cursor key */ MODBIT(term.mode, set, MODE_APPCURSOR); break; @@ -2081,8 +2082,8 @@ csihandle(void) { len = snprintf(buf, sizeof(buf),"\033[%i;%iR", term.c.y+1, term.c.x+1); ttywrite(buf, len); - break; } + break; case 'r': /* DECSTBM -- Set Scrolling Region */ if(csiescseq.priv) { goto unknown; @@ -2135,6 +2136,7 @@ strhandle(void) { char *p = NULL; int j, narg, par; + term.esc &= ~(ESC_STR_END|ESC_STR); strparse(); narg = strescseq.narg; par = atoi(strescseq.args[0]); @@ -2170,7 +2172,7 @@ strhandle(void) { case 'k': /* old title set compatibility */ xsettitle(strescseq.args[0]); return; - case 'P': /* DSC -- Device Control String */ + case 'P': /* DCS -- Device Control String */ case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ return; @@ -2299,8 +2301,12 @@ techo(char *buf, int len) { for(; len > 0; buf++, len--) { char c = *buf; - if(c < 0x20 || c == 0177) { /* control code */ - if(c != '\n' && c != '\r' && c != '\t') { + if(ISCONTROL(c)) { /* control code */ + if(c & 0x80) { + c &= 0x7f; + tputc("^", 1); + tputc("[", 1); + } else if(c != '\n' && c != '\r' && c != '\t') { c ^= '\x40'; tputc("^", 1); } @@ -2339,57 +2345,150 @@ tselcs(void) { ATTR_GFX); } +bool +tcontrolcode(uchar ascii) { + static char question[UTF_SIZ] = "?"; + + switch(ascii) { + case '\t': /* HT */ + tputtab(1); + break; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + break; + case '\r': /* CR */ + tmoveto(0, term.c.y); + break; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + break; + case '\a': /* BEL */ + if(term.esc & ESC_STR_END) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + if(!(xw.state & WIN_FOCUSED)) + xseturgency(1); + if (bellvolume) + XBell(xw.dpy, bellvolume); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return 1; + case '\016': /* SO */ + term.charset = 0; + tselcs(); + break; + case '\017': /* SI */ + term.charset = 1; + tselcs(); + break; + case '\032': /* SUB */ + tsetchar(question, &term.c.attr, term.c.x, term.c.y); + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + case 0x84: /* TODO: IND */ + case 0x85: /* TODO: NEL */ + case 0x88: /* TODO: HTS */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x90: /* TODO: DCS */ + case 0x98: /* TODO: SOS */ + case 0x9a: /* TODO: DECID */ + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + case 0x9d: /* TODO: OSC */ + case 0x9e: /* TODO: PM */ + case 0x9f: /* TODO: APC */ + break; + default: + return 0; + } + term.esc &= ~(ESC_STR_END|ESC_STR); + return 1; +} + +void +tdectest(char c) { + static char E[UTF_SIZ] = "E"; + int x, y; + + if(c == '8') { /* DEC screen alignment test. */ + for(x = 0; x < term.col; ++x) { + for(y = 0; y < term.row; ++y) + tsetchar(E, &term.c.attr, x, y); + } + } +} + void tputc(char *c, int len) { - uchar ascii = *c; - bool control = ascii < '\x20' || ascii == 0177; + uchar ascii; + bool control; long unicodep; int width; + Glyph *gp; if(len == 1) { width = 1; + unicodep = ascii = *c; } else { utf8decode(c, &unicodep, UTF_SIZ); width = wcwidth(unicodep); + control = ISCONTROLC1(unicodep); + ascii = unicodep; } if(IS_SET(MODE_PRINT)) tprinter(c, len); + control = ISCONTROL(unicodep); /* - * STR sequences must be checked before anything else - * because it can use some control codes as part of the sequence. + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. */ if(term.esc & ESC_STR) { - switch(ascii) { - case '\033': - term.esc = ESC_START | ESC_STR_END; - break; - case '\a': /* backwards compatibility to xterm */ - term.esc = 0; - strhandle(); - break; - default: - if(strescseq.len + len < sizeof(strescseq.buf) - 1) { - memmove(&strescseq.buf[strescseq.len], c, len); - strescseq.len += len; - } else { - /* - * Here is a bug in terminals. If the user never sends - * some code to stop the str or esc command, then st - * will stop responding. But this is better than - * silently failing with unknown characters. At least - * then users will report back. - * - * In the case users ever get fixed, here is the code: - */ - /* - * term.esc = 0; - * strhandle(); - */ - } + if(width == 1 && + (ascii == '\a' || ascii == 030 || + ascii == 032 || ascii == 033 || + ISCONTROLC1(unicodep))) { + term.esc &= ~ESC_STR; + term.esc |= ESC_STR_END; + } else if(strescseq.len + len < sizeof(strescseq.buf) - 1) { + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } else { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + return; } - return; } /* @@ -2398,51 +2497,8 @@ tputc(char *c, int len) { * they must not cause conflicts with sequences. */ if(control) { - switch(ascii) { - case '\t': /* HT */ - tputtab(1); - return; - case '\b': /* BS */ - tmoveto(term.c.x-1, term.c.y); - return; - case '\r': /* CR */ - tmoveto(0, term.c.y); - return; - case '\f': /* LF */ - case '\v': /* VT */ - case '\n': /* LF */ - /* go to first col if the mode is set */ - tnewline(IS_SET(MODE_CRLF)); - return; - case '\a': /* BEL */ - if(!(xw.state & WIN_FOCUSED)) - xseturgency(1); - if (bellvolume) - XBell(xw.dpy, bellvolume); - return; - case '\033': /* ESC */ - csireset(); - term.esc = ESC_START; + if (tcontrolcode(ascii)) return; - case '\016': /* SO */ - term.charset = 0; - tselcs(); - return; - case '\017': /* SI */ - term.charset = 1; - tselcs(); - return; - case '\032': /* SUB */ - case '\030': /* CAN */ - csireset(); - return; - case '\005': /* ENQ (IGNORED) */ - case '\000': /* NUL (IGNORED) */ - case '\021': /* XON (IGNORED) */ - case '\023': /* XOFF (IGNORED) */ - case 0177: /* DEL (IGNORED) */ - return; - } } else if(term.esc & ESC_START) { if(term.esc & ESC_CSI) { csiescseq.buf[csiescseq.len++] = ascii; @@ -2453,33 +2509,20 @@ tputc(char *c, int len) { csiparse(); csihandle(); } - } else if(term.esc & ESC_STR_END) { - term.esc = 0; - if(ascii == '\\') - strhandle(); + return; } else if(term.esc & ESC_ALTCHARSET) { tdeftran(ascii); tselcs(); - term.esc = 0; } else if(term.esc & ESC_TEST) { - if(ascii == '8') { /* DEC screen alignment test. */ - char E[UTF_SIZ] = "E"; - int x, y; - - for(x = 0; x < term.col; ++x) { - for(y = 0; y < term.row; ++y) - tsetchar(E, &term.c.attr, x, y); - } - } - term.esc = 0; + tdectest(ascii); } else { switch(ascii) { case '[': term.esc |= ESC_CSI; - break; + return; case '#': term.esc |= ESC_TEST; - break; + return; case 'P': /* DCS -- Device Control String */ case '_': /* APC -- Application Program Command */ case '^': /* PM -- Privacy Message */ @@ -2488,29 +2531,26 @@ tputc(char *c, int len) { strreset(); strescseq.type = ascii; term.esc |= ESC_STR; - break; + return; case '(': /* set primary charset G0 */ case ')': /* set secondary charset G1 */ case '*': /* set tertiary charset G2 */ case '+': /* set quaternary charset G3 */ term.icharset = ascii - '('; term.esc |= ESC_ALTCHARSET; - break; + return; case 'D': /* IND -- Linefeed */ if(term.c.y == term.bot) { tscrollup(term.top, 1); } else { tmoveto(term.c.x, term.c.y+1); } - term.esc = 0; break; case 'E': /* NEL -- Next line */ tnewline(1); /* always go to first col */ - term.esc = 0; break; case 'H': /* HTS -- Horizontal tab stop */ term.tabs[term.c.x] = 1; - term.esc = 0; break; case 'M': /* RI -- Reverse index */ if(term.c.y == term.top) { @@ -2518,43 +2558,38 @@ tputc(char *c, int len) { } else { tmoveto(term.c.x, term.c.y-1); } - term.esc = 0; break; case 'Z': /* DECID -- Identify Terminal */ ttywrite(VT102ID, sizeof(VT102ID) - 1); - term.esc = 0; break; case 'c': /* RIS -- Reset to inital state */ treset(); - term.esc = 0; xresettitle(); xloadcols(); break; case '=': /* DECPAM -- Application keypad */ term.mode |= MODE_APPKEYPAD; - term.esc = 0; break; case '>': /* DECPNM -- Normal keypad */ term.mode &= ~MODE_APPKEYPAD; - term.esc = 0; break; case '7': /* DECSC -- Save Cursor */ tcursor(CURSOR_SAVE); - term.esc = 0; break; case '8': /* DECRC -- Restore Cursor */ tcursor(CURSOR_LOAD); - term.esc = 0; break; - case '\\': /* ST -- Stop */ - term.esc = 0; + case '\\': /* ST -- String Terminator */ + if(term.esc & ESC_STR_END) + strhandle(); break; default: fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", (uchar) ascii, isprint(ascii)? ascii:'.'); - term.esc = 0; + break; } } + term.esc = 0; /* * All characters which form part of a sequence are not * printed @@ -2568,16 +2603,15 @@ tputc(char *c, int len) { return; if(sel.ob.x != -1 && BETWEEN(term.c.y, sel.ob.y, sel.oe.y)) selclear(NULL); + + gp = &term.line[term.c.y][term.c.x]; if(IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { - term.line[term.c.y][term.c.x].mode |= ATTR_WRAP; + gp->mode |= ATTR_WRAP; tnewline(1); } - if(IS_SET(MODE_INSERT) && term.c.x+1 < term.col) { - memmove(&term.line[term.c.y][term.c.x+1], - &term.line[term.c.y][term.c.x], - (term.col - term.c.x - 1) * sizeof(Glyph)); - } + if(IS_SET(MODE_INSERT) && term.c.x+1 < term.col) + memmove(gp+1, gp, (term.col - term.c.x - 1) * sizeof(Glyph)); if(term.c.x+width > term.col) tnewline(1); @@ -2585,10 +2619,10 @@ tputc(char *c, int len) { tsetchar(c, &term.c.attr, term.c.x, term.c.y); if(width == 2) { - term.line[term.c.y][term.c.x].mode |= ATTR_WIDE; + gp->mode |= ATTR_WIDE; if(term.c.x+1 < term.col) { - term.line[term.c.y][term.c.x+1].c[0] = '\0'; - term.line[term.c.y][term.c.x+1].mode = ATTR_WDUMMY; + gp[1].c[0] = '\0'; + gp[1].mode = ATTR_WDUMMY; } } if(term.c.x+width < term.col) { @@ -3452,12 +3486,9 @@ drawregion(int x1, int y1, int x2, int y2) { int ic, ib, x, y, ox, sl; Glyph base, new; char buf[DRAW_BUF_SIZ]; - bool ena_sel = sel.ob.x != -1; + bool ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); long unicodep; - if(sel.alt != IS_SET(MODE_ALTSCREEN)) - ena_sel = 0; - if(!(xw.state & WIN_VISIBLE)) return; @@ -3534,7 +3565,7 @@ void xseturgency(int add) { XWMHints *h = XGetWMHints(xw.dpy, xw.win); - h->flags = add ? (h->flags | XUrgencyHint) : (h->flags & ~XUrgencyHint); + MODBIT(h->flags, add, XUrgencyHint); XSetWMHints(xw.dpy, xw.win, h); XFree(h); }