X-Git-Url: https://jasonwoof.com/gitweb/?p=st.git;a=blobdiff_plain;f=st.c;h=0e228a7a6b1132f41145a2e75571aaad361e7d10;hp=70caf8294cd4773b19e483bb78597572a2f1c2c1;hb=177d888dff2fdf987dfa7fc3eb8495fa107879ad;hpb=b8d6171cb00c28e4c90e58e101554097003b59d3 diff --git a/st.c b/st.c index 70caf82..0e228a7 100644 --- a/st.c +++ b/st.c @@ -36,10 +36,6 @@ char *argv0; #define Glyph Glyph_ #define Font Font_ -#define Draw XftDraw * -#define Colour XftColor -#define Colourmap Colormap -#define Rectangle XRectangle #if defined(__linux) #include @@ -69,17 +65,19 @@ char *argv0; #define REDRAW_TIMEOUT (80*1000) /* 80 ms */ /* macros */ -#define SERRNO strerror(errno) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) < (b) ? (b) : (a)) -#define LEN(a) (sizeof(a) / sizeof(a[0])) +#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) -#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 TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) #define IS_TRUECOL(x) (1 << 24 & (x)) @@ -88,19 +86,19 @@ char *argv0; #define TRUEBLUE(x) (((x) & 0xff) << 8) -#define VT102ID "\033[?6c" - enum glyph_attribute { ATTR_NULL = 0, - ATTR_REVERSE = 1, - ATTR_UNDERLINE = 2, - ATTR_BOLD = 4, - ATTR_GFX = 8, - ATTR_ITALIC = 16, - ATTR_BLINK = 32, - ATTR_WRAP = 64, - ATTR_WIDE = 128, - ATTR_WDUMMY = 256, + ATTR_BOLD = 1, + ATTR_FAINT = 2, + ATTR_ITALIC = 4, + ATTR_UNDERLINE = 8, + ATTR_BLINK = 16, + ATTR_REVERSE = 32, + ATTR_INVISIBLE = 64, + ATTR_STRUCK = 128, + ATTR_WRAP = 256, + ATTR_WIDE = 512, + ATTR_WDUMMY = 1024, }; enum cursor_movement { @@ -153,7 +151,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 */ @@ -180,6 +178,9 @@ typedef unsigned int uint; typedef unsigned long ulong; typedef unsigned short ushort; +typedef XftDraw *Draw; +typedef XftColor Color; + typedef struct { char c[UTF_SIZ]; /* character code */ ushort mode; /* attribute flags */ @@ -239,7 +240,7 @@ typedef struct { /* Purely graphic info */ typedef struct { Display *dpy; - Colourmap cmap; + Colormap cmap; Window win; Drawable buf; Atom xembed, wmdeletewin, netwmname, netwmpid; @@ -250,7 +251,8 @@ typedef struct { XSetWindowAttributes attrs; int scr; bool isfixed; /* is fixed geometry? */ - int fx, fy, fw, fh; /* fixed geometry */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ int tw, th; /* tty width and height */ int w, h; /* window width and height */ int ch; /* char height */ @@ -292,19 +294,19 @@ typedef struct { char *clip; Atom xtarget; bool alt; - struct timeval tclick1; - struct timeval tclick2; + struct timespec tclick1; + struct timespec tclick2; } Selection; typedef union { int i; - unsigned int ui; + uint ui; float f; const void *v; } Arg; typedef struct { - unsigned int mod; + uint mod; KeySym keysym; void (*func)(const Arg *); const Arg arg; @@ -337,7 +339,7 @@ typedef struct { /* Drawing Context */ typedef struct { - Colour col[LEN(colorname) < 256 ? 256 : LEN(colorname)]; + Color col[MAX(LEN(colorname), 256)]; Font font, bfont, ifont, ibfont; GC gc; } DC; @@ -360,7 +362,7 @@ static void strparse(void); static void strreset(void); static int tattrset(int); -static void tprinter(char *s, size_t len); +static void tprinter(char *, size_t); static void tdumpsel(void); static void tdumpline(int); static void tdump(void); @@ -370,17 +372,18 @@ static void tdeletechar(int); static void tdeleteline(int); static void tinsertblank(int); static void tinsertblankline(int); +static int tlinelen(int); static void tmoveto(int, int); -static void tmoveato(int x, int y); +static void tmoveato(int, int); static void tnew(int, int); static void tnewline(int); -static void tputtab(bool); +static void tputtab(int); static void tputc(char *, int); static void treset(void); -static int tresize(int, int); +static void tresize(int, int); static void tscrollup(int, int); static void tscrolldown(int, int); -static void tsetattr(int*, int); +static void tsetattr(int *, int); static void tsetchar(char *, Glyph *, int, int); static void tsetscroll(int, int); static void tswapscreen(void); @@ -389,8 +392,9 @@ static void tsetdirtattr(int); static void tsetmode(bool, bool, int *, int); static void tfulldirt(void); static void techo(char *, int); +static void tcontrolcode(uchar ); +static void tdectest(char ); static int32_t tdefcolor(int *, int *, int); -static void tselcs(void); static void tdeftran(char); static inline bool match(uint, uint); static void ttynew(void); @@ -398,6 +402,7 @@ static void ttyread(void); static void ttyresize(void); static void ttysend(char *, size_t); static void ttywrite(const char *, size_t); +static void tstrsequence(uchar c); static void xdraws(char *, Glyph, int, int, int, int); static void xhints(void); @@ -406,6 +411,7 @@ static void xdrawcursor(void); static void xinit(void); static void xloadcols(void); static int xsetcolorname(int, const char *); +static int xgeommasktogravity(int); static int xloadfont(Font *, FcPattern *); static void xloadfonts(char *, double); static int xloadfontset(Font *); @@ -413,9 +419,9 @@ static void xsettitle(char *); static void xresettitle(void); static void xsetpointermotion(int); static void xseturgency(int); -static void xsetsel(char*); +static void xsetsel(char *); static void xtermclear(int, int, int, int); -static void xunloadfont(Font *f); +static void xunloadfont(Font *); static void xunloadfonts(void); static void xresize(int, int); @@ -436,12 +442,14 @@ static void selclear(XEvent *); static void selrequest(XEvent *); static void selinit(void); -static void selsort(void); +static void selnormalize(void); static inline bool selected(int, int); 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 *); @@ -450,10 +458,12 @@ 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 *s); +static char *xstrdup(char *); + +static void usage(void); static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, @@ -516,7 +526,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) { @@ -549,12 +559,10 @@ xrealloc(void *p, size_t len) { char * xstrdup(char *s) { - char *p = strdup(s); - - if (!p) + if((s = strdup(s)) == NULL) die("Out of memory\n"); - return p; + return s; } size_t @@ -650,9 +658,20 @@ y2row(int y) { return LIMIT(y, 0, term.row-1); } +static int tlinelen(int y) { + int i = term.col; + + while (i > 0 && term.line[y][i - 1].c[0] == ' ') + --i; + + return i; +} + static void -selsort(void) { - if(sel.ob.y == sel.oe.y) { +selnormalize(void) { + int i; + + if(sel.ob.y == sel.oe.y || sel.type == SEL_RECTANGULAR) { sel.nb.x = MIN(sel.ob.x, sel.oe.x); sel.ne.x = MAX(sel.ob.x, sel.oe.x); } else { @@ -661,27 +680,32 @@ selsort(void) { } sel.nb.y = MIN(sel.ob.y, sel.oe.y); sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; } 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 selsnap(int mode, int *x, int *y, int direction) { - int i; + int newx, newy, xt, yt; + Glyph *gp; switch(mode) { case SNAP_WORD: @@ -690,36 +714,31 @@ selsnap(int mode, int *x, int *y, int direction) { * beginning of a line. */ for(;;) { - if(direction < 0 && *x <= 0) { - if(*y > 0 && term.line[*y - 1][term.col-1].mode - & ATTR_WRAP) { - *y -= 1; - *x = term.col-1; - } else { + newx = *x + direction; + newy = *y; + if(!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) break; - } - } - if(direction > 0 && *x >= term.col-1) { - if(*y < term.row-1 && term.line[*y][*x].mode - & ATTR_WRAP) { - *y += 1; - *x = 0; - } else { + + if(direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if(!(term.line[yt][xt].mode & ATTR_WRAP)) break; - } } - if(term.line[*y][*x+direction].mode & ATTR_WDUMMY) { - *x += direction; - continue; - } + if (newx >= tlinelen(newy)) + break; - if(strchr(worddelimiters, - term.line[*y][*x+direction].c[0])) { + gp = &term.line[newy][newx]; + if (!(gp->mode & ATTR_WDUMMY) && strchr(worddelimiters, gp->c[0])) break; - } - *x += direction; + *x = newx; + *y = newy; } break; case SNAP_LINE: @@ -745,25 +764,13 @@ selsnap(int mode, int *x, int *y, int direction) { } } break; - default: - /* - * Select the whole line when the end of line is reached. - */ - if(direction > 0) { - i = term.col; - while(--i > 0 && term.line[*y][i].c[0] == ' ') - /* nothing */; - if(i > 0 && i < *x) - *x = term.col - 1; - } - break; } } void getbuttoninfo(XEvent *e) { int type; - uint state = e->xbutton.state &~Button1Mask; + uint state = e->xbutton.state & ~(Button1Mask | forceselmod); sel.alt = IS_SET(MODE_ALTSCREEN); @@ -778,7 +785,7 @@ getbuttoninfo(XEvent *e) { selsnap(sel.snap, &sel.oe.x, &sel.oe.y, -1); selsnap(sel.snap, &sel.ob.x, &sel.ob.y, +1); } - selsort(); + selnormalize(); sel.type = SEL_REGULAR; for(type = 1; type < LEN(selmasks); ++type) { @@ -827,6 +834,8 @@ mousereport(XEvent *e) { /* MODE_MOUSEX10: no button release reporting */ if(IS_SET(MODE_MOUSEX10)) return; + if (button == 64 || button == 65) + return; } } @@ -853,10 +862,10 @@ mousereport(XEvent *e) { void bpress(XEvent *e) { - struct timeval now; + struct timespec now; Mousekey *mk; - if(IS_SET(MODE_MOUSE)) { + if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { mousereport(e); return; } @@ -870,7 +879,7 @@ bpress(XEvent *e) { } if(e->xbutton.button == Button1) { - gettimeofday(&now, NULL); + clock_gettime(CLOCK_MONOTONIC, &now); /* Clear previous selection, logically and visually. */ selclear(NULL); @@ -892,7 +901,7 @@ bpress(XEvent *e) { } selsnap(sel.snap, &sel.ob.x, &sel.ob.y, -1); selsnap(sel.snap, &sel.oe.x, &sel.oe.y, +1); - selsort(); + selnormalize(); /* * Draw selection, unless it's regular and we don't want to @@ -910,63 +919,50 @@ bpress(XEvent *e) { char * getsel(void) { char *str, *ptr; - int x, y, bufsize, size, i, ex; + int y, bufsize, size, lastx, linelen; 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); - - /* 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]; + if(sel.ob.x == -1) + return NULL; - while(last >= gp && !(selected(last - gp, y) && - strcmp(last->c, " ") != 0)) { - --last; - } + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); - for(x = 0; gp <= last; x++, ++gp) { - if(!selected(x, y) || (gp->mode & ATTR_WDUMMY)) - continue; + /* append every set & selected glyph to the selection */ + for(y = sel.nb.y; y < sel.ne.y + 1; y++) { + linelen = tlinelen(y); - size = utf8len(gp->c); - memcpy(ptr, gp->c, size); - ptr += size; - } + if(sel.type == SEL_RECTANGULAR) { + gp = &term.line[y][sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &term.line[y][MIN(lastx, linelen-1)]; - /* - * 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'; + for( ; gp <= last; ++gp) { + if(gp->mode & ATTR_WDUMMY) + continue; - /* - * 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'; - } + size = utf8len(gp->c); + memcpy(ptr, gp->c, size); + ptr += size; } - *ptr = 0; + + /* + * 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(sel.ne.y > y || lastx >= linelen) + *ptr++ = '\n'; } + *ptr = 0; return str; } @@ -992,7 +988,7 @@ selnotify(XEvent *e) { } /* - * As seen in selcopy: + * As seen in getsel: * Line endings are inconsistent in the terminal and GUI world * copy and pasting. When receiving some selection data, * replace all '\n' with '\r'. @@ -1089,7 +1085,7 @@ xsetsel(char *str) { void brelease(XEvent *e) { - if(IS_SET(MODE_MOUSE)) { + if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { mousereport(e); return; } @@ -1112,7 +1108,7 @@ void bmotion(XEvent *e) { int oldey, oldex, oldsby, oldsey; - if(IS_SET(MODE_MOUSE)) { + if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { mousereport(e); return; } @@ -1178,16 +1174,15 @@ execsh(void) { void sigchld(int a) { - int stat = 0; + int stat, ret; if(waitpid(pid, &stat, 0) < 0) - die("Waiting for pid %hd failed: %s\n", pid, SERRNO); + die("Waiting for pid %hd failed: %s\n", pid, strerror(errno)); - if(WIFEXITED(stat)) { - exit(WEXITSTATUS(stat)); - } else { - exit(EXIT_FAILURE); - } + ret = WIFEXITED(stat) ? WEXITSTATUS(stat) : EXIT_FAILURE; + if (ret != EXIT_SUCCESS) + die("child finished with error '%d'\n", stat); + exit(EXIT_SUCCESS); } void @@ -1197,7 +1192,7 @@ ttynew(void) { /* seems to work fine on linux, openbsd and freebsd */ if(openpty(&m, &s, NULL, NULL, &w) < 0) - die("openpty failed: %s\n", SERRNO); + die("openpty failed: %s\n", strerror(errno)); switch(pid = fork()) { case -1: @@ -1209,7 +1204,7 @@ ttynew(void) { dup2(s, STDOUT_FILENO); dup2(s, STDERR_FILENO); if(ioctl(s, TIOCSCTTY, NULL) < 0) - die("ioctl TIOCSCTTY failed: %s\n", SERRNO); + die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); close(s); close(m); execsh(); @@ -1228,19 +1223,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; @@ -1252,7 +1239,7 @@ ttyread(void) { /* append read bytes to unprocessed bytes */ if((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) - die("Couldn't read from shell: %s\n", SERRNO); + die("Couldn't read from shell: %s\n", strerror(errno)); /* process every complete utf8 char */ buflen += ret; @@ -1270,8 +1257,8 @@ ttyread(void) { void ttywrite(const char *s, size_t n) { - if(write(cmdfd, s, n) == -1) - die("write error on tty: %s\n", SERRNO); + if(xwrite(cmdfd, s, n) == -1) + die("write error on tty: %s\n", strerror(errno)); } void @@ -1290,7 +1277,7 @@ ttyresize(void) { w.ws_xpixel = xw.tw; w.ws_ypixel = xw.th; if(ioctl(cmdfd, TIOCSWINSZ, &w) < 0) - fprintf(stderr, "Couldn't set window size: %s\n", SERRNO); + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); } int @@ -1369,9 +1356,12 @@ treset(void) { memset(term.trantbl, sizeof(term.trantbl), CS_USA); term.charset = 0; - tclearregion(0, 0, term.col-1, term.row-1); - tmoveto(0, 0); - tcursor(CURSOR_SAVE); + for(i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } } void @@ -1400,15 +1390,13 @@ tscrolldown(int orig, int n) { LIMIT(n, 0, term.bot-orig+1); + tsetdirt(orig, term.bot-n); tclearregion(0, term.bot-n+1, term.col-1, term.bot); for(i = term.bot; i >= orig+n; i--) { temp = term.line[i]; term.line[i] = term.line[i-n]; term.line[i-n] = temp; - - term.dirty[i] = 1; - term.dirty[i-n] = 1; } selscroll(orig, n); @@ -1418,17 +1406,16 @@ void tscrollup(int orig, int n) { int i; Line temp; + LIMIT(n, 0, term.bot-orig+1); tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); for(i = orig; i <= term.bot-n; i++) { - temp = term.line[i]; - term.line[i] = term.line[i+n]; - term.line[i+n] = temp; - - term.dirty[i] = 1; - term.dirty[i+n] = 1; + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; } selscroll(orig, -n); @@ -1459,7 +1446,7 @@ selscroll(int orig, int n) { sel.oe.x = term.col; } } - selsort(); + selnormalize(); } } @@ -1543,9 +1530,8 @@ tsetchar(char *c, Glyph *attr, int x, int y) { /* * The table is proudly stolen from rxvt. */ - if(attr->mode & ATTR_GFX) { - if(c[0] >= 0x41 && c[0] <= 0x7e - && vt100_0[c[0] - 0x41]) { + if(term.trantbl[term.charset] == CS_GRAPHIC0) { + if(BETWEEN(c[0], 0x41, 0x7e) && vt100_0[c[0] - 0x41]) { c = vt100_0[c[0] - 0x41]; } } @@ -1568,6 +1554,7 @@ tsetchar(char *c, Glyph *attr, int x, int y) { void tclearregion(int x1, int y1, int x2, int y2) { int x, y, temp; + Glyph *gp; if(x1 > x2) temp = x1, x1 = x2, x2 = temp; @@ -1582,64 +1569,59 @@ tclearregion(int x1, int y1, int x2, int y2) { for(y = y1; y <= y2; y++) { term.dirty[y] = 1; for(x = x1; x <= x2; x++) { + gp = &term.line[y][x]; if(selected(x, y)) selclear(NULL); - term.line[y][x] = term.c.attr; - memcpy(term.line[y][x].c, " ", 2); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + memcpy(gp->c, " ", 2); } } } void tdeletechar(int n) { - int src = term.c.x + n; - int dst = term.c.x; - int size = term.col - src; + int dst, src, size; + Glyph *line; - term.dirty[term.c.y] = 1; + LIMIT(n, 0, term.col - term.c.x); - if(src >= term.col) { - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); - return; - } + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; - memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], - size * sizeof(Glyph)); + memmove(&line[dst], &line[src], size * sizeof(Glyph)); tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); } void tinsertblank(int n) { - int src = term.c.x; - int dst = src + n; - int size = term.col - dst; + int dst, src, size; + Glyph *line; - term.dirty[term.c.y] = 1; + LIMIT(n, 0, term.col - term.c.x); - if(dst >= term.col) { - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); - return; - } + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; - memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], - size * sizeof(Glyph)); + memmove(&line[dst], &line[src], size * sizeof(Glyph)); tclearregion(src, term.c.y, dst - 1, term.c.y); } void tinsertblankline(int n) { - if(term.c.y < term.top || term.c.y > term.bot) - return; - - tscrolldown(term.c.y, n); + if(BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n); } void tdeleteline(int n) { - if(term.c.y < term.top || term.c.y > term.bot) - return; - - tscrollup(term.c.y, n); + if(BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n); } int32_t @@ -1648,7 +1630,7 @@ tdefcolor(int *attr, int *npar, int l) { uint r, g, b; switch (attr[*npar + 1]) { - case 2: /* direct colour in RGB space */ + case 2: /* direct color in RGB space */ if (*npar + 4 >= l) { fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n", @@ -1665,7 +1647,7 @@ tdefcolor(int *attr, int *npar, int l) { else idx = TRUECOLOR(r, g, b); break; - case 5: /* indexed colour */ + case 5: /* indexed color */ if (*npar + 2 >= l) { fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n", @@ -1680,11 +1662,12 @@ tdefcolor(int *attr, int *npar, int l) { break; case 0: /* implemented defined (only foreground) */ case 1: /* transparent */ - case 3: /* direct colour in CMY space */ - case 4: /* direct colour in CMYK space */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ default: fprintf(stderr, "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; } return idx; @@ -1698,15 +1681,24 @@ tsetattr(int *attr, int l) { for(i = 0; i < l; i++) { switch(attr[i]) { case 0: - term.c.attr.mode &= ~(ATTR_REVERSE | ATTR_UNDERLINE \ - | ATTR_BOLD | ATTR_ITALIC \ - | ATTR_BLINK); + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; break; case 1: term.c.attr.mode |= ATTR_BOLD; break; + case 2: + term.c.attr.mode |= ATTR_FAINT; + break; case 3: term.c.attr.mode |= ATTR_ITALIC; break; @@ -1714,15 +1706,21 @@ tsetattr(int *attr, int l) { term.c.attr.mode |= ATTR_UNDERLINE; break; case 5: /* slow blink */ + /* FALLTHROUGH */ case 6: /* rapid blink */ term.c.attr.mode |= ATTR_BLINK; break; case 7: term.c.attr.mode |= ATTR_REVERSE; break; - case 21: + case 8: + term.c.attr.mode |= ATTR_INVISIBLE; + break; + case 9: + term.c.attr.mode |= ATTR_STRUCK; + break; case 22: - term.c.attr.mode &= ~ATTR_BOLD; + term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); break; case 23: term.c.attr.mode &= ~ATTR_ITALIC; @@ -1731,12 +1729,17 @@ tsetattr(int *attr, int l) { term.c.attr.mode &= ~ATTR_UNDERLINE; break; case 25: - case 26: term.c.attr.mode &= ~ATTR_BLINK; break; case 27: term.c.attr.mode &= ~ATTR_REVERSE; break; + case 28: + term.c.attr.mode &= ~ATTR_INVISIBLE; + break; + case 29: + term.c.attr.mode &= ~ATTR_STRUCK; + break; case 38: if ((idx = tdefcolor(attr, &i, l)) >= 0) term.c.attr.fg = idx; @@ -1785,8 +1788,6 @@ tsetscroll(int t, int b) { term.bot = b; } -#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) - void tsetmode(bool priv, bool set, int *args, int narg) { int *lim, mode; @@ -1795,7 +1796,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; @@ -1858,7 +1858,7 @@ tsetmode(bool priv, bool set, int *args, int narg) { if (!allowaltscreen) break; tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); - /* FALLTHRU */ + /* FALLTHROUGH */ case 47: /* swap screen */ case 1047: if (!allowaltscreen) @@ -1872,7 +1872,7 @@ tsetmode(bool priv, bool set, int *args, int narg) { tswapscreen(); if(*args != 1049) break; - /* FALLTRU */ + /* FALLTHROUGH */ case 1048: tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); break; @@ -1966,7 +1966,7 @@ csihandle(void) { break; case 'c': /* DA -- Device Attributes */ if(csiescseq.arg[0] == 0) - ttywrite(VT102ID, sizeof(VT102ID) - 1); + ttywrite(vtiden, sizeof(vtiden) - 1); break; case 'C': /* CUF -- Cursor Forward */ case 'a': /* HPR -- Cursor Forward */ @@ -2010,8 +2010,7 @@ csihandle(void) { break; case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ DEFAULT(csiescseq.arg[0], 1); - while(csiescseq.arg[0]--) - tputtab(1); + tputtab(csiescseq.arg[0]); break; case 'J': /* ED -- Clear screen */ selclear(NULL); @@ -2079,8 +2078,7 @@ csihandle(void) { break; case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ DEFAULT(csiescseq.arg[0], 1); - while(csiescseq.arg[0]--) - tputtab(0); + tputtab(-csiescseq.arg[0]); break; case 'd': /* VPA -- Move to */ DEFAULT(csiescseq.arg[0], 1); @@ -2097,8 +2095,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; @@ -2151,6 +2149,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]); @@ -2168,10 +2167,10 @@ strhandle(void) { if(narg < 3) break; p = strescseq.args[2]; - /* fall through */ + /* FALLTHROUGH */ case 104: /* color reset, here p = NULL */ j = (narg > 1) ? atoi(strescseq.args[1]) : -1; - if (!xsetcolorname(j, p)) { + if(xsetcolorname(j, p)) { fprintf(stderr, "erresc: invalid color %s\n", p); } else { /* @@ -2186,7 +2185,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; @@ -2262,8 +2261,7 @@ printsel(const Arg *arg) { } void -tdumpsel(void) -{ +tdumpsel(void) { char *ptr; if((ptr = getsel())) { @@ -2277,12 +2275,10 @@ tdumpline(int n) { Glyph *bp, *end; bp = &term.line[n][0]; - end = &bp[term.col-1]; - while(end > bp && !strcmp(" ", end->c)) - --end; - if(bp != end || strcmp(bp->c, " ")) { + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if(bp != end || bp->c[0] != ' ') { for( ;bp <= end; ++bp) - tprinter(bp->c, strlen(bp->c)); + tprinter(bp->c, utf8len(bp->c)); } tprinter("\n", 1); } @@ -2296,19 +2292,17 @@ tdump(void) { } void -tputtab(bool forward) { +tputtab(int n) { uint x = term.c.x; - if(forward) { - if(x == term.col) - return; - for(++x; x < term.col && !term.tabs[x]; ++x) - /* nothing */ ; - } else { - if(x == 0) - return; - for(--x; x > 0 && !term.tabs[x]; --x) - /* nothing */ ; + if(n > 0) { + while(x < term.col && n--) + for(++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if(n < 0) { + while(x > 0 && n++) + for(--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; } tmoveto(x, term.c.y); } @@ -2318,12 +2312,13 @@ techo(char *buf, int len) { for(; len > 0; buf++, len--) { char c = *buf; - if(c == '\033') { /* escape */ - tputc("^", 1); - tputc("[", 1); - } else if(c < '\x20') { /* control code */ - if(c != '\n' && c != '\r' && c != '\t') { - c |= '\x40'; + if(ISCONTROL((uchar) c)) { /* control code */ + if(c & 0x80) { + c &= 0x7f; + tputc("^", 1); + tputc("[", 1); + } else if(c != '\n' && c != '\r' && c != '\t') { + c ^= 0x40; tputc("^", 1); } tputc(&c, 1); @@ -2337,82 +2332,193 @@ 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} - }; + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; - for (bp = &tbl[0]; (c = (*bp)[0]) && c != ascii; ++bp) - /* nothing */; - - if (c == 0) + if((p = strchr(cs, ascii)) == NULL) { fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); - else - term.trantbl[term.icharset] = (*bp)[1]; + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } } void -tselcs(void) { - if (term.trantbl[term.charset] == CS_GRAPHIC0) - term.c.attr.mode |= ATTR_GFX; - else - term.c.attr.mode &= ~ATTR_GFX; +tstrsequence(uchar c) { + if (c & 0x80) { + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; + return; +} + +void +tcontrolcode(uchar ascii) { + static char question[UTF_SIZ] = "?"; + + 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(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; + case '\016': /* SO */ + term.charset = 0; + return; + case '\017': /* SI */ + term.charset = 1; + return; + 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) */ + return; + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x98: /* TODO: SOS */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, sizeof(vtiden) - 1); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9f: /* APC -- Application Program Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9d: /* OSC -- Operating System Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); + return; +} + +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_START|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; } /* @@ -2421,51 +2527,11 @@ 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) */ - return; - } + tcontrolcode(ascii); + /* + * control codes are not shown ever + */ + return; } else if(term.esc & ESC_START) { if(term.esc & ESC_CSI) { csiescseq.buf[csiescseq.len++] = ascii; @@ -2476,64 +2542,45 @@ 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 */ case ']': /* OSC -- Operating System Command */ case 'k': /* old title set compatibility */ - strreset(); - strescseq.type = ascii; - term.esc |= ESC_STR; - break; + tstrsequence(ascii); + 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) { @@ -2541,66 +2588,55 @@ 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; + ttywrite(vtiden, sizeof(vtiden) - 1); 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 */ return; } - /* - * Display control codes only if we are in graphic mode - */ - if(control && !(term.c.attr.mode & ATTR_GFX)) - 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); @@ -2608,10 +2644,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) { @@ -2621,17 +2657,20 @@ tputc(char *c, int len) { } } -int +void tresize(int col, int row) { int i; int minrow = MIN(row, term.row); int mincol = MIN(col, term.col); int slide = term.c.y - row + 1; bool *bp; - Line *orig; + TCursor c; - if(col < 1 || row < 1) - return 0; + if(col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } /* free unneeded rows */ i = 0; @@ -2661,14 +2700,12 @@ tresize(int col, int row) { /* resize each row to new width, zero-pad if needed */ for(i = 0; i < minrow; i++) { - term.dirty[i] = 1; term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); } /* allocate any new rows */ for(/* i == minrow */; i < row; i++) { - term.dirty[i] = 1; term.line[i] = xmalloc(col * sizeof(Glyph)); term.alt[i] = xmalloc(col * sizeof(Glyph)); } @@ -2688,9 +2725,9 @@ tresize(int col, int row) { tsetscroll(0, row-1); /* make use of the LIMIT in tmoveto */ tmoveto(term.c.x, term.c.y); - /* Clearing both screens */ - orig = term.line; - do { + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for(i = 0; i < 2; i++) { if(mincol < col && 0 < minrow) { tclearregion(mincol, 0, col - 1, minrow - 1); } @@ -2698,9 +2735,9 @@ tresize(int col, int row) { tclearregion(0, minrow, col - 1, row - 1); } tswapscreen(); - } while(orig != term.line); - - return (slide > 0); + tcursor(CURSOR_LOAD); + } + term.c = c; } void @@ -2722,17 +2759,17 @@ sixd_to_16bit(int x) { void xloadcols(void) { - int i, r, g, b; + int i; XRenderColor color = { .alpha = 0xffff }; static bool loaded; - Colour *cp; + Color *cp; if(loaded) { for (cp = dc.col; cp < dc.col + LEN(dc.col); ++cp) XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); } - /* load colors [0-15] colors and [256-LEN(colorname)[ (config.h) */ + /* load colors [0-15] and [256-LEN(colorname)] (config.h) */ for(i = 0; i < LEN(colorname); i++) { if(!colorname[i]) continue; @@ -2741,27 +2778,20 @@ xloadcols(void) { } } - /* load colors [16-255] ; same colors as xterm */ - for(i = 16, r = 0; r < 6; r++) { - for(g = 0; g < 6; g++) { - for(b = 0; b < 6; b++) { - color.red = sixd_to_16bit(r); - color.green = sixd_to_16bit(g); - color.blue = sixd_to_16bit(b); - if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i])) { - die("Could not allocate color %d\n", i); - } - i++; - } - } + /* load colors [16-231] ; same colors as xterm */ + for(i = 16; i < 6*6*6+16; i++) { + color.red = sixd_to_16bit( ((i-16)/36)%6 ); + color.green = sixd_to_16bit( ((i-16)/6) %6 ); + color.blue = sixd_to_16bit( ((i-16)/1) %6 ); + if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i])) + die("Could not allocate color %d\n", i); } - for(r = 0; r < 24; r++, i++) { - color.red = color.green = color.blue = 0x0808 + 0x0a0a * r; - if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, - &dc.col[i])) { + /* load colors [232-255] ; grayscale */ + for(; i < 256; i++) { + color.red = color.green = color.blue = 0x0808 + 0x0a0a * (i-(6*6*6+16)); + if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &dc.col[i])) die("Could not allocate color %d\n", i); - } } loaded = true; } @@ -2769,33 +2799,45 @@ xloadcols(void) { int xsetcolorname(int x, const char *name) { XRenderColor color = { .alpha = 0xffff }; - Colour colour; - if (x < 0 || x > LEN(colorname)) - return -1; + Color ncolor; + + if(!BETWEEN(x, 0, LEN(colorname))) + return 1; + if(!name) { - if(16 <= x && x < 16 + 216) { - int r = (x - 16) / 36, g = ((x - 16) % 36) / 6, b = (x - 16) % 6; - color.red = sixd_to_16bit(r); - color.green = sixd_to_16bit(g); - color.blue = sixd_to_16bit(b); - if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &colour)) - return 0; /* something went wrong */ - dc.col[x] = colour; - return 1; - } else if (16 + 216 <= x && x < 256) { - color.red = color.green = color.blue = 0x0808 + 0x0a0a * (x - (16 + 216)); - if(!XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, &colour)) - return 0; /* something went wrong */ - dc.col[x] = colour; - return 1; - } else { + if(BETWEEN(x, 16, 16 + 215)) { /* 256 color */ + color.red = sixd_to_16bit( ((x-16)/36)%6 ); + color.green = sixd_to_16bit( ((x-16)/6) %6 ); + color.blue = sixd_to_16bit( ((x-16)/1) %6 ); + if(!XftColorAllocValue(xw.dpy, xw.vis, + xw.cmap, &color, &ncolor)) { + return 1; + } + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + return 0; + } else if(BETWEEN(x, 16 + 216, 255)) { /* greyscale */ + color.red = color.green = color.blue = \ + 0x0808 + 0x0a0a * (x - (16 + 216)); + if(!XftColorAllocValue(xw.dpy, xw.vis, + xw.cmap, &color, &ncolor)) { + return 1; + } + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + return 0; + } else { /* system colors */ name = colorname[x]; } } - if(!XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, &colour)) - return 0; - dc.col[x] = colour; - return 1; + if(!XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, &ncolor)) + return 1; + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + return 0; } void @@ -2825,18 +2867,24 @@ xhints(void) { XSizeHints *sizeh = NULL; sizeh = XAllocSizeHints(); - if(xw.isfixed == False) { - sizeh->flags = PSize | PResizeInc | PBaseSize; - sizeh->height = xw.h; - sizeh->width = xw.w; - sizeh->height_inc = xw.ch; - sizeh->width_inc = xw.cw; - sizeh->base_height = 2 * borderpx; - sizeh->base_width = 2 * borderpx; - } else { - sizeh->flags = PMaxSize | PMinSize; - sizeh->min_width = sizeh->max_width = xw.fw; - sizeh->min_height = sizeh->max_height = xw.fh; + + sizeh->flags = PSize | PResizeInc | PBaseSize; + sizeh->height = xw.h; + sizeh->width = xw.w; + sizeh->height_inc = xw.ch; + sizeh->width_inc = xw.cw; + sizeh->base_height = 2 * borderpx; + sizeh->base_width = 2 * borderpx; + if(xw.isfixed == True) { + sizeh->flags |= PMaxSize | PMinSize; + sizeh->min_width = sizeh->max_width = xw.w; + sizeh->min_height = sizeh->max_height = xw.h; + } + if(xw.gm & (XValue|YValue)) { + sizeh->flags |= USPosition | PWinGravity; + sizeh->x = xw.l; + sizeh->y = xw.t; + sizeh->win_gravity = xgeommasktogravity(xw.gm); } XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, @@ -2845,6 +2893,19 @@ xhints(void) { } int +xgeommasktogravity(int mask) { + switch(mask & (XNegative|YNegative)) { + case 0: + return NorthWestGravity; + case XNegative: + return NorthEastGravity; + case YNegative: + return SouthWestGravity; + } + return SouthEastGravity; +} + +int xloadfont(Font *f, FcPattern *pattern) { FcPattern *match; FcResult result; @@ -2877,6 +2938,7 @@ xloadfonts(char *fontstr, double fontsize) { FcPattern *pattern; FcResult r_sz, r_psz; double fontval; + float ceilf(float); if(fontstr[0] == '-') { pattern = XftXlfdParse(fontstr, False, False); @@ -2922,8 +2984,8 @@ xloadfonts(char *fontstr, double fontsize) { } /* Setting character width and height. */ - xw.cw = CEIL(dc.font.width * cwscale); - xw.ch = CEIL(dc.font.height * chscale); + xw.cw = ceilf(dc.font.width * cwscale); + xw.ch = ceilf(dc.font.height * chscale); FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); @@ -2962,13 +3024,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); @@ -2982,6 +3040,7 @@ xzoom(const Arg *arg) { xloadfonts(usedfont, usedfontsize + arg->i); cresize(0, 0); redraw(0); + xhints(); } void @@ -2989,7 +3048,6 @@ xinit(void) { XGCValues gcvalues; Cursor cursor; Window parent; - int sw, sh; pid_t thispid = getpid(); if(!(xw.dpy = XOpenDisplay(NULL))) @@ -3009,23 +3067,12 @@ xinit(void) { xloadcols(); /* adjust fixed window geometry */ - if(xw.isfixed) { - sw = DisplayWidth(xw.dpy, xw.scr); - sh = DisplayHeight(xw.dpy, xw.scr); - if(xw.fx < 0) - xw.fx = sw + xw.fx - xw.fw - 1; - if(xw.fy < 0) - xw.fy = sh + xw.fy - xw.fh - 1; - - xw.h = xw.fh; - xw.w = xw.fw; - } else { - /* window - default size */ - xw.h = 2 * borderpx + term.row * xw.ch; - xw.w = 2 * borderpx + term.col * xw.cw; - xw.fx = 0; - xw.fy = 0; - } + xw.w = 2 * borderpx + term.col * xw.cw; + xw.h = 2 * borderpx + term.row * xw.ch; + if(xw.gm & XNegative) + xw.l += DisplayWidth(xw.dpy, xw.scr) - xw.w - 2; + if(xw.gm & YNegative) + xw.t += DisplayWidth(xw.dpy, xw.scr) - xw.h - 2; /* Events */ xw.attrs.background_pixel = dc.col[defaultbg].pixel; @@ -3038,7 +3085,7 @@ xinit(void) { parent = opt_embed ? strtol(opt_embed, NULL, 0) : \ XRootWindow(xw.dpy, xw.scr); - xw.win = XCreateWindow(xw.dpy, parent, xw.fx, xw.fy, + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask | CWColormap, &xw.attrs); @@ -3087,7 +3134,7 @@ xinit(void) { xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, - PropModeReplace, (unsigned char *)&thispid, 1); + PropModeReplace, (uchar *)&thispid, 1); xresettitle(); XMapWindow(xw.dpy, xw.win); @@ -3108,9 +3155,9 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { FcPattern *fcpattern, *fontpattern; FcFontSet *fcsets[] = { NULL }; FcCharSet *fccharset; - Colour *fg, *bg, *temp, revfg, revbg, truefg, truebg; + Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; XRenderColor colfg, colbg; - Rectangle r; + XRectangle r; int oneatatime; frcflags = FRC_NORMAL; @@ -3153,24 +3200,20 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { } if(base.mode & ATTR_BOLD) { - if(BETWEEN(base.fg, 0, 7)) { - /* basic system colors */ - fg = &dc.col[base.fg + 8]; - } else if(BETWEEN(base.fg, 16, 195)) { - /* 256 colors */ - fg = &dc.col[base.fg + 36]; - } else if(BETWEEN(base.fg, 232, 251)) { - /* greyscale */ - fg = &dc.col[base.fg + 4]; - } /* - * Those ranges will not be brightened: - * 8 - 15 – bright system colors - * 196 - 231 – highest 256 color cube - * 252 - 255 – brightest colors in greyscale + * change basic system colors [0-7] + * to bright system colors [8-15] */ - font = &dc.bfont; - frcflags = FRC_BOLD; + if(BETWEEN(base.fg, 0, 7) && !(base.mode & ATTR_FAINT)) + fg = &dc.col[base.fg + 8]; + + if(base.mode & ATTR_ITALIC) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else { + font = &dc.bfont; + frcflags = FRC_BOLD; + } } if(IS_SET(MODE_REVERSE)) { @@ -3205,9 +3248,20 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { bg = temp; } + if(base.mode & ATTR_FAINT && !(base.mode & ATTR_BOLD)) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + if(base.mode & ATTR_BLINK && term.mode & MODE_BLINK) fg = bg; + if(base.mode & ATTR_INVISIBLE) + fg = bg; + /* Intelligent cleaning up of the borders. */ if(x == 0) { xclear(0, (y == 0)? 0 : winy, borderpx, @@ -3250,28 +3304,22 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { bytelen -= u8cblen; doesexist = XftCharExists(xw.dpy, font->match, unicodep); - if(oneatatime || !doesexist || bytelen <= 0) { - if(oneatatime || bytelen <= 0) { - if(doesexist) { - u8fl++; - u8fblen += u8cblen; - } - } - - if(u8fl > 0) { - XftDrawStringUtf8(xw.draw, fg, - font->match, xp, - winy + font->ascent, - (FcChar8 *)u8fs, - u8fblen); - xp += xw.cw * u8fl; - - } - break; + if(doesexist) { + u8fl++; + u8fblen += u8cblen; + if(!oneatatime && bytelen > 0) + continue; } - u8fl++; - u8fblen += u8cblen; + if(u8fl > 0) { + XftDrawStringUtf8(xw.draw, fg, + font->match, xp, + winy + font->ascent, + (FcChar8 *)u8fs, + u8fblen); + xp += xw.cw * u8fl; + } + break; } if(doesexist) { if(oneatatime) @@ -3358,6 +3406,11 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { width, 1); } + if(base.mode & ATTR_STRUCK) { + XftDrawRect(xw.draw, fg, winx, winy + 2 * font->ascent / 3, + width, 1); + } + /* Reset clip to none. */ XftDrawSetClip(xw.draw, 0); } @@ -3443,6 +3496,7 @@ void redraw(int timeout) { struct timespec tv = {0, timeout * 1000}; + tfulldirt(); draw(); if(timeout > 0) { @@ -3466,12 +3520,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; @@ -3548,7 +3599,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); } @@ -3721,11 +3772,14 @@ run(void) { int w = xw.w, h = xw.h; fd_set rfd; int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; - struct timeval drawtimeout, *tv = NULL, now, last, lastblink; + struct timespec drawtimeout, *tv = NULL, now, last, lastblink; + long deltatime; /* Waiting for window mapping */ while(1) { XNextEvent(xw.dpy, &ev); + if(XFilterEvent(&ev, None)) + continue; if(ev.type == ConfigureNotify) { w = ev.xconfigure.width; h = ev.xconfigure.height; @@ -3735,25 +3789,20 @@ run(void) { } ttynew(); - if(!xw.isfixed) - cresize(w, h); - else - cresize(xw.fw, xw.fh); + cresize(w, h); - gettimeofday(&last, NULL); + clock_gettime(CLOCK_MONOTONIC, &last); lastblink = last; for(xev = actionfps;;) { - long deltatime; - FD_ZERO(&rfd); FD_SET(cmdfd, &rfd); FD_SET(xfd, &rfd); - if(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv) < 0) { + if(pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { if(errno == EINTR) continue; - die("select failed: %s\n", SERRNO); + die("select failed: %s\n", strerror(errno)); } if(FD_ISSET(cmdfd, &rfd)) { ttyread(); @@ -3767,9 +3816,9 @@ run(void) { if(FD_ISSET(xfd, &rfd)) xev = actionfps; - gettimeofday(&now, NULL); + clock_gettime(CLOCK_MONOTONIC, &now); drawtimeout.tv_sec = 0; - drawtimeout.tv_usec = (1000/xfps) * 1000; + drawtimeout.tv_nsec = (1000/xfps) * 1E6; tv = &drawtimeout; dodraw = 0; @@ -3804,9 +3853,9 @@ run(void) { if(blinkset) { if(TIMEDIFF(now, lastblink) \ > blinktimeout) { - drawtimeout.tv_usec = 1; + drawtimeout.tv_nsec = 1000; } else { - drawtimeout.tv_usec = (1000 * \ + drawtimeout.tv_nsec = (1E6 * \ (blinktimeout - \ TIMEDIFF(now, lastblink))); @@ -3821,18 +3870,17 @@ run(void) { void usage(void) { - die("%s " VERSION " (c) 2010-2013 st engineers\n" \ - "usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]" \ - " [-t title] [-w windowid] [-e command ...]\n", argv0); + die("%s " VERSION " (c) 2010-2014 st engineers\n" \ + "usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n" + " [-i] [-t title] [-w windowid] [-e command ...]\n", argv0); } int main(int argc, char *argv[]) { - int bitm, xr, yr; - uint wr, hr; char *titles; + uint cols = 80, rows = 24; - xw.fw = xw.fh = xw.fx = xw.fy = 0; + xw.l = xw.t = 0; xw.isfixed = False; ARGBEGIN { @@ -3856,22 +3904,11 @@ main(int argc, char *argv[]) { opt_font = EARGF(usage()); break; case 'g': - bitm = XParseGeometry(EARGF(usage()), &xr, &yr, &wr, &hr); - if(bitm & XValue) - xw.fx = xr; - if(bitm & YValue) - xw.fy = yr; - if(bitm & WidthValue) - xw.fw = (int)wr; - if(bitm & HeightValue) - xw.fh = (int)hr; - if(bitm & XNegative && xw.fx == 0) - xw.fx = -1; - if(bitm & YNegative && xw.fy == 0) - xw.fy = -1; - - if(xw.fh != 0 && xw.fw != 0) - xw.isfixed = True; + xw.gm = XParseGeometry(EARGF(usage()), + &xw.l, &xw.t, &cols, &rows); + break; + case 'i': + xw.isfixed = True; break; case 'o': opt_io = EARGF(usage()); @@ -3890,7 +3927,7 @@ main(int argc, char *argv[]) { run: setlocale(LC_CTYPE, ""); XSetLocaleModifiers(""); - tnew(80, 24); + tnew(cols? cols : 1, rows? rows : 1); xinit(); selinit(); run();