X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=st.c;h=77ea0c8e9b744d3bff79d63597af6382b6318691;hb=7a4eefe87cb7661c8a77286d05b6c3afb467f806;hp=362de23bfaf6f0ed7ea097bb57747780bb331ef0;hpb=aaee0e8b28a353c215b6d1c8fc06d20038d7b426;p=st.git diff --git a/st.c b/st.c index 362de23..77ea0c8 100644 --- a/st.c +++ b/st.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "arg.h" @@ -76,6 +77,7 @@ char *argv0; #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg) #define IS_SET(flag) ((term.mode & (flag)) != 0) #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_usec-t2.tv_usec)/1000) +#define CEIL(x) (((x) != (int) (x)) ? (x) + 1 : (x)) #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) #define IS_TRUECOL(x) (1 << 24 & (x)) @@ -95,6 +97,8 @@ enum glyph_attribute { ATTR_ITALIC = 16, ATTR_BLINK = 32, ATTR_WRAP = 64, + ATTR_WIDE = 128, + ATTR_WDUMMY = 256, }; enum cursor_movement { @@ -128,10 +132,21 @@ enum term_mode { MODE_FOCUS = 65536, MODE_MOUSEX10 = 131072, MODE_MOUSEMANY = 262144, + MODE_BRCKTPASTE = 524288, MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ |MODE_MOUSEMANY, }; +enum charset { + CS_GRAPHIC0, + CS_GRAPHIC1, + CS_UK, + CS_USA, + CS_MULTI, + CS_GER, + CS_FIN +}; + enum escape_state { ESC_START = 1, ESC_CSI = 2, @@ -164,7 +179,7 @@ typedef unsigned short ushort; typedef struct { char c[UTF_SIZ]; /* character code */ - uchar mode; /* attribute flags */ + ushort mode; /* attribute flags */ ulong fg; /* foreground */ ulong bg; /* background */ } Glyph; @@ -211,6 +226,9 @@ typedef struct { 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 */ bool numlock; /* lock numbers in keyboard */ bool *tabs; } Term; @@ -361,7 +379,9 @@ static void tsetdirtattr(int); static void tsetmode(bool, bool, int *, int); static void tfulldirt(void); static void techo(char *, int); -static ulong tdefcolor(int *, int *, int); +static long tdefcolor(int *, int *, int); +static void tselcs(void); +static void tdeftran(char); static inline bool match(uint, uint); static void ttynew(void); static void ttyread(void); @@ -419,7 +439,6 @@ static int isfullutf8(char *, int); static ssize_t xwrite(int, char *, size_t); static void *xmalloc(size_t); static void *xrealloc(void *, size_t); -static void *xcalloc(size_t, size_t); static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, @@ -508,16 +527,6 @@ xrealloc(void *p, size_t len) { return p; } -void * -xcalloc(size_t nmemb, size_t size) { - void *p = calloc(nmemb, size); - - if(!p) - die("Out of memory\n"); - - return p; -} - int utf8decode(char *s, long *u) { uchar c; @@ -729,8 +738,13 @@ selsnap(int mode, int *x, int *y, int direction) { } } + if(term.line[*y][*x+direction].mode & ATTR_WDUMMY) { + *x += direction; + continue; + } + if(strchr(worddelimiters, - term.line[*y][*x + direction].c[0])) { + term.line[*y][*x+direction].c[0])) { break; } @@ -825,18 +839,23 @@ mousereport(XEvent *e) { button = oldbutton + 32; ox = x; oy = y; - } else if(!IS_SET(MODE_MOUSESGR) - && (e->xbutton.type == ButtonRelease - || button == AnyButton)) { - button = 3; } else { - button -= Button1; - if(button >= 3) - button += 64 - 3; + if(!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) { + button = 3; + } else { + button -= Button1; + if(button >= 3) + button += 64 - 3; + } if(e->xbutton.type == ButtonPress) { oldbutton = button; ox = x; oy = y; + } else if(e->xbutton.type == ButtonRelease) { + oldbutton = 3; + /* MODE_MOUSEX10: no button release reporting */ + if(IS_SET(MODE_MOUSEX10)) + return; } } @@ -853,8 +872,7 @@ mousereport(XEvent *e) { e->xbutton.type == ButtonRelease ? 'm' : 'M'); } else if(x < 223 && y < 223) { len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", - IS_SET(MODE_MOUSEX10)? button-1 : 32+button, - 32+x+1, 32+y+1); + 32+button, 32+x+1, 32+y+1); } else { return; } @@ -942,7 +960,7 @@ selcopy(void) { /* nothing */; for(x = 0; gp <= last; x++, ++gp) { - if(!selected(x, y)) + if(!selected(x, y) || (gp->mode & ATTR_WDUMMY)) continue; size = utf8size(gp->c); @@ -959,7 +977,7 @@ selcopy(void) { * st. * FIXME: Fix the computer world. */ - if(y < sel.ne.y && !((gp-1)->mode & ATTR_WRAP)) + if(y < sel.ne.y && x > 0 && !((gp-1)->mode & ATTR_WRAP)) *ptr++ = '\n'; /* @@ -1011,7 +1029,11 @@ selnotify(XEvent *e) { *repl++ = '\r'; } + if(IS_SET(MODE_BRCKTPASTE)) + ttywrite("\033[200~", 6); ttywrite((const char *)data, nitems * format / 8); + if(IS_SET(MODE_BRCKTPASTE)) + ttywrite("\033[201~", 6); XFree(data); /* number of 32-bit chunks returned */ ofs += nitems * format / 32; @@ -1335,13 +1357,14 @@ tfulldirt(void) { void tcursor(int mode) { - static TCursor c; + static TCursor c[2]; + bool alt = IS_SET(MODE_ALTSCREEN); if(mode == CURSOR_SAVE) { - c = term.c; + c[alt] = term.c; } else if(mode == CURSOR_LOAD) { - term.c = c; - tmoveto(c.x, c.y); + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); } } @@ -1361,6 +1384,8 @@ treset(void) { term.top = 0; term.bot = term.row - 1; term.mode = MODE_WRAP; + memset(term.trantbl, sizeof(term.trantbl), CS_USA); + term.charset = 0; tclearregion(0, 0, term.col-1, term.row-1); tmoveto(0, 0); @@ -1369,7 +1394,7 @@ treset(void) { void tnew(int col, int row) { - memset(&term, 0, sizeof(Term)); + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; tresize(col, row); term.numlock = 1; @@ -1543,6 +1568,16 @@ tsetchar(char *c, Glyph *attr, int x, int y) { } } + if(term.line[y][x].mode & ATTR_WIDE) { + if(x+1 < term.col) { + term.line[y][x+1].c[0] = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; + } + } else if(term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].c[0] = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + term.dirty[y] = 1; term.line[y][x] = *attr; memcpy(term.line[y][x].c, c, UTF_SIZ); @@ -1625,7 +1660,7 @@ tdeleteline(int n) { tscrollup(term.c.y, n); } -ulong +long tdefcolor(int *attr, int *npar, int l) { long idx = -1; uint r, g, b; @@ -1676,7 +1711,7 @@ tdefcolor(int *attr, int *npar, int l) { void tsetattr(int *attr, int l) { int i; - ulong idx; + long idx; for(i = 0; i < l; i++) { switch(attr[i]) { @@ -1837,12 +1872,12 @@ tsetmode(bool priv, bool set, int *args, int narg) { case 1034: MODBIT(term.mode, set, MODE_8BIT); break; - case 1049: /* = 1047 and 1048 */ - case 47: + case 1049: /* swap screen & set/restore cursor as xterm */ + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + case 47: /* swap screen */ case 1047: if (!allowaltscreen) break; - alt = IS_SET(MODE_ALTSCREEN); if(alt) { tclearregion(0, 0, term.col-1, @@ -1856,6 +1891,9 @@ tsetmode(bool priv, bool set, int *args, int narg) { case 1048: tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); break; + case 2004: /* 2004: bracketed paste mode */ + MODBIT(term.mode, set, MODE_BRCKTPASTE); + break; /* Not implemented mouse modes. See comments there. */ case 1001: /* mouse highlight mode; can hang the terminal by design when implemented. */ @@ -1899,6 +1937,9 @@ tsetmode(bool priv, bool set, int *args, int narg) { void csihandle(void) { + char buf[40]; + int len; + switch(csiescseq.mode) { default: unknown: @@ -2047,6 +2088,13 @@ csihandle(void) { case 'm': /* SGR -- Terminal attribute (color) */ tsetattr(csiescseq.arg, csiescseq.narg); break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf),"\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len); + break; + } case 'r': /* DECSTBM -- Set Scrolling Region */ if(csiescseq.priv) { goto unknown; @@ -2229,9 +2277,45 @@ techo(char *buf, int len) { } void +tdeftran(char ascii) { + char c, (*bp)[2]; + static char tbl[][2] = { + {'0', CS_GRAPHIC0}, {'1', CS_GRAPHIC1}, {'A', CS_UK}, + {'B', CS_USA}, {'<', CS_MULTI}, {'K', CS_GER}, + {'5', CS_FIN}, {'C', CS_FIN}, + {0, 0} + }; + + for (bp = &tbl[0]; (c = (*bp)[0]) && c != ascii; ++bp) + /* nothing */; + + if (c == 0) + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + else + term.trantbl[term.icharset] = (*bp)[1]; +} + +void +tselcs(void) { + if (term.trantbl[term.charset] == CS_GRAPHIC0) + term.c.attr.mode |= ATTR_GFX; + else + term.c.attr.mode &= ~ATTR_GFX; +} + +void tputc(char *c, int len) { uchar ascii = *c; bool control = ascii < '\x20' || ascii == 0177; + long u8char; + int width; + + if(len == 1) { + width = 1; + } else { + utf8decode(c, &u8char); + width = wcwidth(u8char); + } if(iofd != -1) { if(xwrite(iofd, c, len) < 0) { @@ -2303,19 +2387,20 @@ tputc(char *c, int len) { 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 */ - /* - * Different charsets are hard to handle. Applications - * should use the right alt charset escapes for the - * only reason they still exist: line drawing. The - * rest is incompatible history st should not support. - */ + term.charset = 1; + tselcs(); return; case '\032': /* SUB */ case '\030': /* CAN */ @@ -2343,22 +2428,8 @@ tputc(char *c, int len) { if(ascii == '\\') strhandle(); } else if(term.esc & ESC_ALTCHARSET) { - switch(ascii) { - case '0': /* Line drawing set */ - term.c.attr.mode |= ATTR_GFX; - break; - case 'B': /* USASCII */ - term.c.attr.mode &= ~ATTR_GFX; - break; - case 'A': /* UK (IGNORED) */ - case '<': /* multinational charset (IGNORED) */ - case '5': /* Finnish (IGNORED) */ - case 'C': /* Finnish (IGNORED) */ - case 'K': /* German (IGNORED) */ - break; - default: - fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); - } + tdeftran(ascii); + tselcs(); term.esc = 0; } else if(term.esc & ESC_TEST) { if(ascii == '8') { /* DEC screen alignment test. */ @@ -2389,13 +2460,12 @@ tputc(char *c, int len) { term.esc |= ESC_STR; break; 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; - case ')': /* set secondary charset G1 (IGNORED) */ - case '*': /* set tertiary charset G2 (IGNORED) */ - case '+': /* set quaternary charset G3 (IGNORED) */ - term.esc = 0; - break; case 'D': /* IND -- Linefeed */ if(term.c.y == term.bot) { tscrollup(term.top, 1); @@ -2479,9 +2549,20 @@ tputc(char *c, int len) { (term.col - term.c.x - 1) * sizeof(Glyph)); } + if(term.c.x+width > term.col) + tnewline(1); + tsetchar(c, &term.c.attr, term.c.x, term.c.y); - if(term.c.x+1 < term.col) { - tmoveto(term.c.x+1, term.c.y); + + if(width == 2) { + term.line[term.c.y][term.c.x].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; + } + } + if(term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); } else { term.c.state |= CURSOR_WRAPNEXT; } @@ -2535,8 +2616,8 @@ tresize(int col, int row) { /* allocate any new rows */ for(/* i == minrow */; i < row; i++) { term.dirty[i] = 1; - term.line[i] = xcalloc(col, sizeof(Glyph)); - term.alt [i] = xcalloc(col, sizeof(Glyph)); + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); } if(col > term.col) { bp = term.tabs + term.col; @@ -2777,8 +2858,8 @@ xloadfonts(char *fontstr, int fontsize) { die("st: can't open font %s\n", fontstr); /* Setting character width and height. */ - xw.cw = dc.font.width; - xw.ch = dc.font.height; + xw.cw = CEIL(dc.font.width * cwscale); + xw.ch = CEIL(dc.font.height * chscale); FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); @@ -2960,6 +3041,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { Colour *fg, *bg, *temp, revfg, revbg, truefg, truebg; XRenderColor colfg, colbg; Rectangle r; + int oneatatime; frcflags = FRC_NORMAL; @@ -3087,6 +3169,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { u8fs = s; u8fblen = 0; u8fl = 0; + oneatatime = font->width != xw.cw; for(;;) { u8c = s; u8cblen = utf8decode(s, &u8char); @@ -3094,8 +3177,8 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { bytelen -= u8cblen; doesexist = XftCharExists(xw.dpy, font->match, u8char); - if(!doesexist || bytelen <= 0) { - if(bytelen <= 0) { + if(oneatatime || !doesexist || bytelen <= 0) { + if(oneatatime || bytelen <= 0) { if(doesexist) { u8fl++; u8fblen += u8cblen; @@ -3108,7 +3191,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { winy + font->ascent, (FcChar8 *)u8fs, u8fblen); - xp += font->width * u8fl; + xp += xw.cw * u8fl; } break; @@ -3117,8 +3200,11 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { u8fl++; u8fblen += u8cblen; } - if(doesexist) + if(doesexist) { + if (oneatatime) + continue; break; + } /* Search the font cache. */ for(i = 0; i < frclen; i++) { @@ -3178,7 +3264,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { xp, winy + frc[i].font->ascent, (FcChar8 *)u8c, u8cblen); - xp += font->width; + xp += xw.cw * wcwidth(u8char); } /* @@ -3198,18 +3284,27 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { void xdrawcursor(void) { static int oldx = 0, oldy = 0; - int sl; + int sl, width, curx; Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs}; LIMIT(oldx, 0, term.col-1); LIMIT(oldy, 0, term.row-1); + curx = term.c.x; + + /* adjust position if in dummy */ + if(term.line[oldy][oldx].mode & ATTR_WDUMMY) + oldx--; + if(term.line[term.c.y][curx].mode & ATTR_WDUMMY) + curx--; + memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ); /* remove the old cursor */ sl = utf8size(term.line[oldy][oldx].c); + width = (term.line[oldy][oldx].mode & ATTR_WIDE)? 2 : 1; xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx, - oldy, 1, sl); + oldy, width, sl); /* draw the new one */ if(!(IS_SET(MODE_HIDE))) { @@ -3221,26 +3316,28 @@ xdrawcursor(void) { } sl = utf8size(g.c); - xdraws(g.c, g, term.c.x, term.c.y, 1, sl); + width = (term.line[term.c.y][curx].mode & ATTR_WIDE)\ + ? 2 : 1; + xdraws(g.c, g, term.c.x, term.c.y, width, sl); } else { XftDrawRect(xw.draw, &dc.col[defaultcs], - borderpx + term.c.x * xw.cw, + borderpx + curx * xw.cw, borderpx + term.c.y * xw.ch, xw.cw - 1, 1); XftDrawRect(xw.draw, &dc.col[defaultcs], - borderpx + term.c.x * xw.cw, + borderpx + curx * xw.cw, borderpx + term.c.y * xw.ch, 1, xw.ch - 1); XftDrawRect(xw.draw, &dc.col[defaultcs], - borderpx + (term.c.x + 1) * xw.cw - 1, + borderpx + (curx + 1) * xw.cw - 1, borderpx + term.c.y * xw.ch, 1, xw.ch - 1); XftDrawRect(xw.draw, &dc.col[defaultcs], - borderpx + term.c.x * xw.cw, + borderpx + curx * xw.cw, borderpx + (term.c.y + 1) * xw.ch - 1, xw.cw, 1); } - oldx = term.c.x, oldy = term.c.y; + oldx = curx, oldy = term.c.y; } } @@ -3252,6 +3349,7 @@ xsettitle(char *p) { Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop); XSetWMName(xw.dpy, xw.win, &prop); + XFree(prop.value); } void @@ -3288,6 +3386,7 @@ drawregion(int x1, int y1, int x2, int y2) { Glyph base, new; char buf[DRAW_BUF_SIZ]; bool ena_sel = sel.ob.x != -1; + long u8char; if(sel.alt ^ IS_SET(MODE_ALTSCREEN)) ena_sel = 0; @@ -3305,6 +3404,8 @@ drawregion(int x1, int y1, int x2, int y2) { ic = ib = ox = 0; for(x = x1; x < x2; x++) { new = term.line[y][x]; + if(new.mode == ATTR_WDUMMY) + continue; if(ena_sel && selected(x, y)) new.mode ^= ATTR_REVERSE; if(ib > 0 && (ATTRCMP(base, new) @@ -3317,10 +3418,10 @@ drawregion(int x1, int y1, int x2, int y2) { base = new; } - sl = utf8size(new.c); + sl = utf8decode(new.c, &u8char); memcpy(buf+ib, new.c, sl); ib += sl; - ++ic; + ic += (new.mode & ATTR_WIDE)? 2 : 1; } if(ib > 0) xdraws(buf, base, ox, y, ic, ib); @@ -3600,8 +3701,8 @@ run(void) { ttyread(); if(blinktimeout) { blinkset = tattrset(ATTR_BLINK); - if(!blinkset && term.mode & ATTR_BLINK) - term.mode &= ~(MODE_BLINK); + if(!blinkset) + MODBIT(term.mode, 0, MODE_BLINK); } } @@ -3707,7 +3808,7 @@ main(int argc, char *argv[]) { xw.fh = (int)hr; if(bitm & XNegative && xw.fx == 0) xw.fx = -1; - if(bitm & XNegative && xw.fy == 0) + if(bitm & YNegative && xw.fy == 0) xw.fy = -1; if(xw.fh != 0 && xw.fw != 0)