X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=st.c;h=5198749af5ecb16ca5a4dbcf9c743f14ac58c827;hb=5f91983541014d34274087dbf723b48551f90fc0;hp=297eb3f812c3a54efd80ab59ece742c722aadcf7;hpb=7f1e02e4dbb6e6f033797a09a7deea4fe29f2ca6;p=st.git diff --git a/st.c b/st.c index 297eb3f..5198749 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) @@ -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 *); @@ -457,6 +464,8 @@ 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, @@ -665,18 +674,13 @@ selsort(void) { static inline bool selected(int x, int y) { - if(sel.ne.y == y && sel.nb.y == y) - return BETWEEN(x, sel.nb.x, sel.ne.x); - - if(sel.type == SEL_RECTANGULAR) { - return ((sel.nb.y <= y && y <= sel.ne.y) - && (sel.nb.x <= x && x <= sel.ne.x)); - } + if(sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); - return ((sel.nb.y < y && y < sel.ne.y) - || (y == sel.ne.y && x <= sel.ne.x)) - || (y == sel.nb.y && x >= sel.nb.x - && (x <= sel.ne.x || sel.nb.y != sel.ne.y)); + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); } void @@ -913,60 +917,59 @@ getsel(void) { int x, y, bufsize, size, i, ex; Glyph *gp, *last; - if(sel.ob.x == -1) { - str = NULL; - } else { - bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; - ptr = str = xmalloc(bufsize); + if(sel.ob.x == -1) + return NULL; - /* append every set & selected glyph to the selection */ - for(y = sel.nb.y; y < sel.ne.y + 1; y++) { - gp = &term.line[y][0]; - last = &gp[term.col-1]; + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); - while(last >= gp && !(selected(last - gp, y) && - strcmp(last->c, " ") != 0)) { - --last; - } + /* append every set & selected glyph to the selection */ + for(y = sel.nb.y; y < sel.ne.y + 1; y++) { + gp = &term.line[y][0]; + last = &gp[term.col-1]; - for(x = 0; gp <= last; x++, ++gp) { - if(!selected(x, y) || (gp->mode & ATTR_WDUMMY)) - continue; + while(last >= gp && !(selected(last - gp, y) && + strcmp(last->c, " ") != 0)) { + --last; + } - size = utf8len(gp->c); - memcpy(ptr, gp->c, size); - ptr += size; - } + for(x = 0; gp <= last; x++, ++gp) { + if(!selected(x, y) || (gp->mode & ATTR_WDUMMY)) + continue; - /* - * Copy and pasting of line endings is inconsistent - * in the inconsistent terminal and GUI world. - * The best solution seems like to produce '\n' when - * something is copied from st and convert '\n' to - * '\r', when something to be pasted is received by - * st. - * FIXME: Fix the computer world. - */ - if(y < sel.ne.y && x > 0 && !((gp-1)->mode & ATTR_WRAP)) - *ptr++ = '\n'; + size = utf8len(gp->c); + memcpy(ptr, gp->c, size); + ptr += size; + } - /* - * If the last selected line expands in the selection - * after the visible text '\n' is appended. - */ - if(y == sel.ne.y) { - i = term.col; - while(--i > 0 && term.line[y][i].c[0] == ' ') - /* nothing */; - ex = sel.ne.x; - if(sel.nb.y == sel.ne.y && sel.ne.x < sel.nb.x) - ex = sel.nb.x; - if(i < ex) - *ptr++ = '\n'; - } + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if(y < sel.ne.y && x > 0 && !((gp-1)->mode & ATTR_WRAP)) + *ptr++ = '\n'; + + /* + * If the last selected line expands in the selection + * after the visible text '\n' is appended. + */ + if(y == sel.ne.y) { + i = term.col; + while(--i > 0 && term.line[y][i].c[0] == ' ') + /* nothing */; + ex = sel.ne.x; + if(sel.nb.y == sel.ne.y && sel.ne.x < sel.nb.x) + ex = sel.nb.x; + if(i < ex) + *ptr++ = '\n'; } - *ptr = 0; } + *ptr = 0; return str; } @@ -1228,19 +1231,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; @@ -1673,6 +1668,7 @@ tdefcolor(int *attr, int *npar, int l) { default: fprintf(stderr, "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; } return idx; @@ -2080,8 +2076,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; @@ -2134,6 +2130,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]); @@ -2298,8 +2295,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); } @@ -2338,57 +2339,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; } /* @@ -2397,51 +2491,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; - 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) */ + if (tcontrolcode(ascii)) return; - } } else if(term.esc & ESC_START) { if(term.esc & ESC_CSI) { csiescseq.buf[csiescseq.len++] = ascii; @@ -2452,33 +2503,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 */ @@ -2487,29 +2525,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) { @@ -2517,43 +2552,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 @@ -2567,16 +2597,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); @@ -2584,10 +2613,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) { @@ -2959,13 +2988,9 @@ xunloadfont(Font *f) { void xunloadfonts(void) { - int i; - /* Free the loaded fonts in the font cache. */ - for(i = 0; i < frclen; i++) { - XftFontClose(xw.dpy, frc[i].font); - } - frclen = 0; + while(frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); xunloadfont(&dc.font); xunloadfont(&dc.bfont); @@ -3451,12 +3476,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; @@ -3533,7 +3555,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); }