X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=st.c;h=8065ebee9f1b517d3b0bb3bbc1a705c917ce0141;hb=1e09726518b84091e80dfaf96632c122f6f446a6;hp=fc9ed70d435c03434e5a1fb64898a667d5b4caeb;hpb=c6b89f23e7546c30dea42a3c49f99682c5818190;p=st.git diff --git a/st.c b/st.c index fc9ed70..8065ebe 100644 --- a/st.c +++ b/st.c @@ -27,11 +27,16 @@ #include #include +#include "arg.h" + +char *argv0; + #define Glyph Glyph_ #define Font Font_ #define Draw XftDraw * #define Colour XftColor #define Colourmap Colormap +#define Rectangle XRectangle #if defined(__linux) #include @@ -41,10 +46,6 @@ #include #endif -#define USAGE \ - "st " VERSION " (c) 2010-2013 st engineers\n" \ - "usage: st [-v] [-c class] [-f font] [-g geometry] [-o file]" \ - " [-t title] [-w windowid] [-e command ...]\n" /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 @@ -85,6 +86,7 @@ enum glyph_attribute { ATTR_GFX = 8, ATTR_ITALIC = 16, ATTR_BLINK = 32, + ATTR_WRAP = 64, }; enum cursor_movement { @@ -98,11 +100,6 @@ enum cursor_state { CURSOR_ORIGIN = 2 }; -enum glyph_state { - GLYPH_SET = 1, - GLYPH_DIRTY = 2 -}; - enum term_mode { MODE_WRAP = 1, MODE_INSERT = 2, @@ -118,6 +115,9 @@ enum term_mode { MODE_ECHO = 1024, MODE_APPCURSOR = 2048, MODE_MOUSESGR = 4096, + MODE_8BIT = 8192, + MODE_BLINK = 16384, + MODE_FBLINK = 32768, }; enum escape_state { @@ -140,6 +140,11 @@ enum selection_type { SEL_RECTANGULAR = 2 }; +enum selection_snap { + SNAP_WORD = 1, + SNAP_LINE = 2 +}; + /* bit macro */ #undef B0 enum { B0=1, B1=2, B2=4, B3=8, B4=16, B5=32, B6=64, B7=128 }; @@ -154,7 +159,6 @@ typedef struct { uchar mode; /* attribute flags */ ushort fg; /* foreground */ ushort bg; /* background */ - uchar state; /* state flags */ } Glyph; typedef Glyph *Line; @@ -238,6 +242,7 @@ typedef struct { typedef struct { int mode; int type; + int snap; int bx, by; int ex, ey; struct { @@ -310,7 +315,8 @@ static void strhandle(void); static void strparse(void); static void strreset(void); -static void tclearregion(int, int, int, int, int); +static int tattrset(int); +static void tclearregion(int, int, int, int); static void tcursor(int); static void tdeletechar(int); static void tdeleteline(int); @@ -331,6 +337,7 @@ static void tsetchar(char *, Glyph *, int, int); static void tsetscroll(int, int); static void tswapscreen(void); static void tsetdirt(int, int); +static void tsetdirtattr(int); static void tsetmode(bool, bool, int *, int); static void tfulldirt(void); static void techo(char *, int); @@ -378,6 +385,7 @@ static void selinit(void); static inline bool selected(int, int); static void selcopy(void); static void selscroll(int, int); +static void selsnap(int, int *, int *, int); static int utf8decode(char *, long *); static int utf8encode(long *, char *); @@ -649,23 +657,35 @@ selected(int x, int y) { if(sel.ey == y && sel.by == y) { bx = MIN(sel.bx, sel.ex); ex = MAX(sel.bx, sel.ex); + return BETWEEN(x, bx, ex); } - return ((sel.b.y < y && y < sel.e.y) - || (y == sel.e.y && x <= sel.e.x)) - || (y == sel.b.y && x >= sel.b.x - && (x <= sel.e.x || sel.b.y != sel.e.y)); - switch(sel.type) { - case SEL_REGULAR: - return ((sel.b.y < y && y < sel.e.y) - || (y == sel.e.y && x <= sel.e.x)) - || (y == sel.b.y && x >= sel.b.x - && (x <= sel.e.x || sel.b.y != sel.e.y)); - case SEL_RECTANGULAR: + if(sel.type == SEL_RECTANGULAR) { return ((sel.b.y <= y && y <= sel.e.y) && (sel.b.x <= x && x <= sel.e.x)); - }; + } + return ((sel.b.y < y && y < sel.e.y) + || (y == sel.e.y && x <= sel.e.x)) + || (y == sel.b.y && x >= sel.b.x + && (x <= sel.e.x || sel.b.y != sel.e.y)); +} + +void +selsnap(int mode, int *x, int *y, int direction) { + switch(mode) { + case SNAP_WORD: + while(*x > 0 && *x < term.col-1 + && term.line[*y][*x + direction].c[0] != ' ') { + *x += direction; + } + break; + case SNAP_LINE: + *x = (direction < 0) ? 0 : term.col - 1; + break; + default: + break; + } } void @@ -678,6 +698,15 @@ getbuttoninfo(XEvent *e) { sel.ex = x2col(e->xbutton.x); sel.ey = y2row(e->xbutton.y); + if (sel.by < sel.ey + || (sel.by == sel.ey && sel.bx < sel.ex)) { + selsnap(sel.snap, &sel.bx, &sel.by, -1); + selsnap(sel.snap, &sel.ex, &sel.ey, +1); + } else { + selsnap(sel.snap, &sel.ex, &sel.ey, -1); + selsnap(sel.snap, &sel.bx, &sel.by, +1); + } + sel.b.x = sel.by < sel.ey ? sel.bx : sel.ex; sel.b.y = MIN(sel.by, sel.ey); sel.e.x = sel.by < sel.ey ? sel.ex : sel.bx; @@ -741,9 +770,14 @@ mousereport(XEvent *e) { void bpress(XEvent *e) { + struct timeval now; + if(IS_SET(MODE_MOUSE)) { mousereport(e); } else if(e->xbutton.button == Button1) { + gettimeofday(&now, NULL); + + /* Clear previous selection, logically and visually. */ if(sel.bx != -1) { sel.bx = -1; tsetdirt(sel.b.y, sel.e.y); @@ -753,6 +787,36 @@ bpress(XEvent *e) { sel.type = SEL_REGULAR; sel.ex = sel.bx = x2col(e->xbutton.x); sel.ey = sel.by = y2row(e->xbutton.y); + + /* + * If the user clicks below predefined timeouts specific + * snapping behaviour is exposed. + */ + if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { + sel.snap = SNAP_LINE; + } else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { + sel.snap = SNAP_WORD; + } else { + sel.snap = 0; + } + selsnap(sel.snap, &sel.bx, &sel.by, -1); + selsnap(sel.snap, &sel.ex, &sel.ey, 1); + sel.b.x = sel.bx; + sel.b.y = sel.by; + sel.e.x = sel.ex; + sel.e.y = sel.ey; + + /* + * Draw selection, unless it's regular and we don't want to + * make clicks visible + */ + if(sel.snap != 0) { + sel.mode++; + tsetdirt(sel.b.y, sel.e.y); + draw(); + } + sel.tclick2 = sel.tclick1; + sel.tclick1 = now; } else if(e->xbutton.button == Button4) { ttywrite("\031", 1); } else if(e->xbutton.button == Button5) { @@ -762,8 +826,8 @@ bpress(XEvent *e) { void selcopy(void) { - char *str, *ptr, *p; - int x, y, bufsize, is_selected = 0, size; + char *str, *ptr; + int x, y, bufsize, size; Glyph *gp, *last; if(sel.bx == -1) { @@ -774,28 +838,33 @@ selcopy(void) { /* append every set & selected glyph to the selection */ for(y = sel.b.y; y < sel.e.y + 1; y++) { - is_selected = 0; gp = &term.line[y][0]; last = gp + term.col; - while(--last >= gp && !(last->state & GLYPH_SET)) + while(--last >= gp && !(selected(last - gp, y) && \ + strcmp(last->c, " ") != 0)) /* nothing */; for(x = 0; gp <= last; x++, ++gp) { - if(!selected(x, y)) { + if(!selected(x, y)) continue; - } else { - is_selected = 1; - } - p = (gp->state & GLYPH_SET) ? gp->c : " "; - size = utf8size(p); - memcpy(ptr, p, size); + size = utf8size(gp->c); + memcpy(ptr, gp->c, size); ptr += size; } - /* \n at the end of every selected line except for the last one */ - if(is_selected && y < sel.e.y) - *ptr++ = '\r'; + + /* + * 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.e.y && !((gp-1)->mode & ATTR_WRAP)) + *ptr++ = '\n'; } *ptr = 0; } @@ -806,7 +875,7 @@ void selnotify(XEvent *e) { ulong nitems, ofs, rem; int format; - uchar *data; + uchar *data, *last, *repl; Atom type; ofs = 0; @@ -817,7 +886,21 @@ selnotify(XEvent *e) { fprintf(stderr, "Clipboard allocation failed\n"); return; } - ttywrite((const char *) data, nitems * format / 8); + + /* + * As seen in selcopy: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + ttywrite((const char *)data, nitems * format / 8); XFree(data); /* number of 32-bit chunks returned */ ofs += nitems * format / 32; @@ -898,8 +981,6 @@ xsetsel(char *str) { void brelease(XEvent *e) { - struct timeval now; - if(IS_SET(MODE_MOUSE)) { mousereport(e); return; @@ -908,42 +989,15 @@ brelease(XEvent *e) { if(e->xbutton.button == Button2) { selpaste(NULL); } else if(e->xbutton.button == Button1) { - sel.mode = 0; - getbuttoninfo(e); - term.dirty[sel.ey] = 1; - if(sel.bx == sel.ex && sel.by == sel.ey) { + if(sel.mode < 2) { sel.bx = -1; - gettimeofday(&now, NULL); - - if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { - /* triple click on the line */ - sel.b.x = sel.bx = 0; - sel.e.x = sel.ex = term.col; - sel.b.y = sel.e.y = sel.ey; - selcopy(); - } else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { - /* double click to select word */ - sel.bx = sel.ex; - while(sel.bx > 0 && term.line[sel.ey][sel.bx-1].state & GLYPH_SET && - term.line[sel.ey][sel.bx-1].c[0] != ' ') { - sel.bx--; - } - sel.b.x = sel.bx; - while(sel.ex < term.col-1 && term.line[sel.ey][sel.ex+1].state & GLYPH_SET && - term.line[sel.ey][sel.ex+1].c[0] != ' ') { - sel.ex++; - } - sel.e.x = sel.ex; - sel.b.y = sel.e.y = sel.ey; - selcopy(); - } } else { + getbuttoninfo(e); selcopy(); } + sel.mode = 0; + term.dirty[sel.ey] = 1; } - - memcpy(&sel.tclick2, &sel.tclick1, sizeof(struct timeval)); - gettimeofday(&sel.tclick1, NULL); } void @@ -958,6 +1012,7 @@ bmotion(XEvent *e) { if(!sel.mode) return; + sel.mode++; oldey = sel.ey; oldex = sel.ex; oldsby = sel.b.y; @@ -1124,6 +1179,20 @@ ttyresize(void) { fprintf(stderr, "Couldn't set window size: %s\n", SERRNO); } +int +tattrset(int attr) { + int i, j; + + for(i = 0; i < term.row-1; i++) { + for(j = 0; j < term.col-1; j++) { + if(term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + void tsetdirt(int top, int bot) { int i; @@ -1136,6 +1205,20 @@ tsetdirt(int top, int bot) { } void +tsetdirtattr(int attr) { + int i, j; + + for(i = 0; i < term.row-1; i++) { + for(j = 0; j < term.col-1; j++) { + if(term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void tfulldirt(void) { tsetdirt(0, term.row-1); } @@ -1169,30 +1252,17 @@ treset(void) { term.bot = term.row - 1; term.mode = MODE_WRAP; - tclearregion(0, 0, term.col-1, term.row-1, 0); + tclearregion(0, 0, term.col-1, term.row-1); tmoveto(0, 0); tcursor(CURSOR_SAVE); } void tnew(int col, int row) { - /* set screen size */ - term.row = row; - term.col = col; - term.line = xmalloc(term.row * sizeof(Line)); - term.alt = xmalloc(term.row * sizeof(Line)); - term.dirty = xmalloc(term.row * sizeof(*term.dirty)); - term.tabs = xmalloc(term.col * sizeof(*term.tabs)); - - for(row = 0; row < term.row; row++) { - term.line[row] = xmalloc(term.col * sizeof(Glyph)); - term.alt [row] = xmalloc(term.col * sizeof(Glyph)); - term.dirty[row] = 0; - } - + memset(&term, 0, sizeof(Term)); + tresize(col, row); term.numlock = 1; - memset(term.tabs, 0, term.col * sizeof(*term.tabs)); - /* setup screen */ + treset(); } @@ -1213,7 +1283,7 @@ tscrolldown(int orig, int n) { LIMIT(n, 0, term.bot-orig+1); - tclearregion(0, term.bot-n+1, term.col-1, term.bot, 0); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); for(i = term.bot; i >= orig+n; i--) { temp = term.line[i]; @@ -1233,7 +1303,7 @@ tscrollup(int orig, int n) { Line temp; LIMIT(n, 0, term.bot-orig+1); - tclearregion(0, orig, term.col-1, orig+n-1, 0); + tclearregion(0, orig, term.col-1, orig+n-1); for(i = orig; i <= term.bot-n; i++) { temp = term.line[i]; @@ -1257,8 +1327,12 @@ selscroll(int orig, int n) { sel.bx = -1; return; } - switch(sel.type) { - case SEL_REGULAR: + if(sel.type == SEL_RECTANGULAR) { + if(sel.by < term.top) + sel.by = term.top; + if(sel.ey > term.bot) + sel.ey = term.bot; + } else { if(sel.by < term.top) { sel.by = term.top; sel.bx = 0; @@ -1267,14 +1341,7 @@ selscroll(int orig, int n) { sel.ey = term.bot; sel.ex = term.col; } - break; - case SEL_RECTANGULAR: - if(sel.by < term.top) - sel.by = term.top; - if(sel.ey > term.bot) - sel.ey = term.bot; - break; - }; + } sel.b.y = sel.by, sel.b.x = sel.bx; sel.e.y = sel.ey, sel.e.x = sel.ex; } @@ -1370,11 +1437,10 @@ tsetchar(char *c, Glyph *attr, int x, int y) { term.dirty[y] = 1; term.line[y][x] = *attr; memcpy(term.line[y][x].c, c, UTF_SIZ); - term.line[y][x].state |= GLYPH_SET; } void -tclearregion(int x1, int y1, int x2, int y2, int bce) { +tclearregion(int x1, int y1, int x2, int y2) { int x, y, temp; if(x1 > x2) @@ -1390,13 +1456,10 @@ tclearregion(int x1, int y1, int x2, int y2, int bce) { for(y = y1; y <= y2; y++) { term.dirty[y] = 1; for(x = x1; x <= x2; x++) { - if(bce) { - term.line[y][x] = term.c.attr; - memcpy(term.line[y][x].c, " ", 2); - term.line[y][x].state |= GLYPH_SET; - } else { - term.line[y][x].state = 0; - } + if(selected(x, y)) + selclear(NULL); + term.line[y][x] = term.c.attr; + memcpy(term.line[y][x].c, " ", 2); } } } @@ -1410,13 +1473,13 @@ tdeletechar(int n) { term.dirty[term.c.y] = 1; if(src >= term.col) { - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 0); + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); return; } memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph)); - tclearregion(term.col-n, term.c.y, term.col-1, term.c.y, 0); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); } void @@ -1428,13 +1491,13 @@ tinsertblank(int n) { term.dirty[term.c.y] = 1; if(dst >= term.col) { - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 0); + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); return; } memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], size * sizeof(Glyph)); - tclearregion(src, term.c.y, dst - 1, term.c.y, 0); + tclearregion(src, term.c.y, dst - 1, term.c.y); } void @@ -1460,8 +1523,9 @@ 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_REVERSE | ATTR_UNDERLINE \ + | ATTR_BOLD | ATTR_ITALIC \ + | ATTR_BLINK); term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; break; @@ -1621,20 +1685,25 @@ tsetmode(bool priv, bool set, int *args, int narg) { case 1006: MODBIT(term.mode, set, MODE_MOUSESGR); break; + case 1034: + MODBIT(term.mode, set, MODE_8BIT); + break; case 1049: /* = 1047 and 1048 */ case 47: - case 1047: { + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(MODE_ALTSCREEN); if(alt) { tclearregion(0, 0, term.col-1, - term.row-1, 0); + term.row-1); } if(set ^ alt) /* set is always 1 or 0 */ tswapscreen(); if(*args != 1049) break; - } - /* pass through */ + /* FALLTRU */ case 1048: tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); break; @@ -1747,19 +1816,19 @@ csihandle(void) { sel.bx = -1; switch(csiescseq.arg[0]) { case 0: /* below */ - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1); + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); if(term.c.y < term.row-1) { tclearregion(0, term.c.y+1, term.col-1, - term.row-1, 1); + term.row-1); } break; case 1: /* above */ if(term.c.y > 1) - tclearregion(0, 0, term.col-1, term.c.y-1, 1); - tclearregion(0, term.c.y, term.c.x, term.c.y, 1); + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); break; case 2: /* all */ - tclearregion(0, 0, term.col-1, term.row-1, 1); + tclearregion(0, 0, term.col-1, term.row-1); break; default: goto unknown; @@ -1769,13 +1838,13 @@ csihandle(void) { switch(csiescseq.arg[0]) { case 0: /* right */ tclearregion(term.c.x, term.c.y, term.col-1, - term.c.y, 1); + term.c.y); break; case 1: /* left */ - tclearregion(0, term.c.y, term.c.x, term.c.y, 1); + tclearregion(0, term.c.y, term.c.x, term.c.y); break; case 2: /* all */ - tclearregion(0, term.c.y, term.col-1, term.c.y, 1); + tclearregion(0, term.c.y, term.col-1, term.c.y); break; } break; @@ -1801,7 +1870,7 @@ csihandle(void) { case 'X': /* ECH -- Erase char */ DEFAULT(csiescseq.arg[0], 1); tclearregion(term.c.x, term.c.y, - term.c.x + csiescseq.arg[0] - 1, term.c.y, 1); + term.c.x + csiescseq.arg[0] - 1, term.c.y); break; case 'P': /* DCH -- Delete char */ DEFAULT(csiescseq.arg[0], 1); @@ -2242,8 +2311,10 @@ tputc(char *c, int len) { return; if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey)) sel.bx = -1; - if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT) - tnewline(1); /* always go to first col */ + if(IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + term.line[term.c.y][term.c.x].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], @@ -2261,11 +2332,12 @@ tputc(char *c, int len) { int tresize(int col, int row) { - int i, x; + 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; if(col < 1 || row < 1) return 0; @@ -2301,10 +2373,6 @@ tresize(int col, int row) { term.dirty[i] = 1; term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); - for(x = mincol; x < col; x++) { - term.line[i][x].state = 0; - term.alt[i][x].state = 0; - } } /* allocate any new rows */ @@ -2329,6 +2397,17 @@ 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 { + if(mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if(0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + } while(orig != term.line); return (slide > 0); } @@ -2341,10 +2420,8 @@ xresize(int col, int row) { XFreePixmap(xw.dpy, xw.buf); xw.buf = XCreatePixmap(xw.dpy, xw.win, xw.w, xw.h, DefaultDepth(xw.dpy, xw.scr)); - XSetForeground(xw.dpy, dc.gc, dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg].pixel); - XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, xw.w, xw.h); - XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, xw.w, xw.h); } static inline ushort @@ -2493,7 +2570,7 @@ xloadfont(Font *f, FcPattern *pattern) { f->lbearing = 0; f->rbearing = f->match->max_advance_width; - f->height = f->match->height; + f->height = f->ascent + f->descent; f->width = f->lbearing + f->rbearing; return 0; @@ -2719,12 +2796,29 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { FcPattern *fcpattern, *fontpattern; FcFontSet *fcsets[] = { NULL }; FcCharSet *fccharset; - Colour *fg = &dc.col[base.fg], *bg = &dc.col[base.bg], - *temp, revfg, revbg; + Colour *fg, *bg, *temp, revfg, revbg; XRenderColor colfg, colbg; + Rectangle r; frcflags = FRC_NORMAL; + if(base.mode & ATTR_ITALIC) { + if(base.fg == defaultfg) + base.fg = defaultitalic; + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD)) { + if(base.fg == defaultfg) + base.fg = defaultitalic; + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if(base.mode & ATTR_UNDERLINE) { + if(base.fg == defaultfg) + base.fg = defaultunderline; + } + fg = &dc.col[base.fg]; + bg = &dc.col[base.bg]; + if(base.mode & ATTR_BOLD) { if(BETWEEN(base.fg, 0, 7)) { /* basic system colors */ @@ -2746,15 +2840,6 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { frcflags = FRC_BOLD; } - if(base.mode & ATTR_ITALIC) { - font = &dc.ifont; - frcflags = FRC_ITALIC; - } - if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD)) { - font = &dc.ibfont; - frcflags = FRC_ITALICBOLD; - } - if(IS_SET(MODE_REVERSE)) { if(fg == &dc.col[defaultfg]) { fg = &dc.col[defaultbg]; @@ -2785,6 +2870,9 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { bg = temp; } + if(base.mode & ATTR_BLINK && term.mode & MODE_BLINK) + fg = bg; + /* Intelligent cleaning up of the borders. */ if(x == 0) { xclear(0, (y == 0)? 0 : winy, borderpx, @@ -2802,6 +2890,13 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { /* Clean up the region we want to draw to. */ XftDrawRect(xw.draw, bg, winx, winy, width, xw.ch); + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0; + r.y = 0; + r.height = xw.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + fcsets[0] = font->set; for(xp = winx; bytelen > 0;) { /* @@ -2835,6 +2930,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { (FcChar8 *)u8fs, u8fblen); xp += font->width * u8fl; + } break; } @@ -2881,8 +2977,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { FcTrue, fcpattern, &fcres); /* - * Overwrite or create the new cache entry - * entry. + * Overwrite or create the new cache entry. */ frccur++; frclen++; @@ -2920,39 +3015,56 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { XftDrawRect(xw.draw, fg, winx, winy + font->ascent + 1, width, 1); } + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); } void xdrawcursor(void) { static int oldx = 0, oldy = 0; int sl; - Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs, 0}; + Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs}; LIMIT(oldx, 0, term.col-1); LIMIT(oldy, 0, term.row-1); - if(term.line[term.c.y][term.c.x].state & GLYPH_SET) - memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ); + memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ); /* remove the old cursor */ - if(term.line[oldy][oldx].state & GLYPH_SET) { - sl = utf8size(term.line[oldy][oldx].c); - xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx, - oldy, 1, sl); - } else { - xtermclear(oldx, oldy, oldx, oldy); - } + sl = utf8size(term.line[oldy][oldx].c); + xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx, + oldy, 1, sl); /* draw the new one */ if(!(IS_SET(MODE_HIDE))) { - if(!(xw.state & WIN_FOCUSED)) - g.bg = defaultucs; - - if(IS_SET(MODE_REVERSE)) - g.mode |= ATTR_REVERSE, g.fg = defaultcs, g.bg = defaultfg; + if(xw.state & WIN_FOCUSED) { + if(IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + g.fg = defaultcs; + g.bg = defaultfg; + } - sl = utf8size(g.c); - xdraws(g.c, g, term.c.x, term.c.y, 1, sl); + sl = utf8size(g.c); + xdraws(g.c, g, term.c.x, term.c.y, 1, sl); + } else { + XftDrawRect(xw.draw, &dc.col[defaultcs], + borderpx + term.c.x * 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 + term.c.y * xw.ch, + 1, xw.ch - 1); + XftDrawRect(xw.draw, &dc.col[defaultcs], + borderpx + (term.c.x + 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 + (term.c.y + 1) * xw.ch - 1, + xw.cw, 1); + } oldx = term.c.x, oldy = term.c.y; } } @@ -3018,25 +3130,22 @@ 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(ena_sel && *(new.c) && selected(x, y)) + if(ena_sel && selected(x, y)) new.mode ^= ATTR_REVERSE; - if(ib > 0 && (!(new.state & GLYPH_SET) - || ATTRCMP(base, new) + if(ib > 0 && (ATTRCMP(base, new) || ib >= DRAW_BUF_SIZ-UTF_SIZ)) { xdraws(buf, base, ox, y, ic, ib); ic = ib = 0; } - if(new.state & GLYPH_SET) { - if(ib == 0) { - ox = x; - base = new; - } - - sl = utf8size(new.c); - memcpy(buf+ib, new.c, sl); - ib += sl; - ++ic; + if(ib == 0) { + ox = x; + base = new; } + + sl = utf8size(new.c); + memcpy(buf+ib, new.c, sl); + ib += sl; + ++ic; } if(ib > 0) xdraws(buf, base, ox, y, ic, ib); @@ -3172,7 +3281,8 @@ kpress(XEvent *ev) { XKeyEvent *e = &ev->xkey; KeySym ksym; char xstr[31], buf[32], *customkey, *cp = buf; - int len; + int len, ret; + long c; Status status; Shortcut *bp; @@ -3193,13 +3303,23 @@ kpress(XEvent *ev) { if((customkey = kmap(ksym, e->state))) { len = strlen(customkey); memcpy(buf, customkey, len); - /* 2. hardcoded (overrides X lookup) */ + /* 3. hardcoded (overrides X lookup) */ } else { if(len == 0) return; - if(len == 1 && e->state & Mod1Mask) - *cp++ = '\033'; + if(len == 1 && e->state & Mod1Mask) { + if(IS_SET(MODE_8BIT)) { + if(*xstr < 0177) { + c = *xstr | B7; + ret = utf8encode(&c, cp); + cp += ret; + len = 0; + } + } else { + *cp++ = '\033'; + } + } memcpy(cp, xstr, len); len = cp - buf + len; @@ -3260,34 +3380,55 @@ void run(void) { XEvent ev; fd_set rfd; - int xfd = XConnectionNumber(xw.dpy), xev; - struct timeval drawtimeout, *tv = NULL, now, last; + int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; + struct timeval drawtimeout, *tv = NULL, now, last, lastblink; + gettimeofday(&lastblink, NULL); gettimeofday(&last, NULL); for(xev = actionfps;;) { FD_ZERO(&rfd); FD_SET(cmdfd, &rfd); FD_SET(xfd, &rfd); - if(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv) < 0) { + + switch(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv) < 0) { + case -1: if(errno == EINTR) continue; die("select failed: %s\n", SERRNO); - } + default: + if(FD_ISSET(cmdfd, &rfd)) { + ttyread(); + if(blinktimeout) { + blinkset = tattrset(ATTR_BLINK); + if(!blinkset && term.mode & ATTR_BLINK) + term.mode &= ~(MODE_BLINK); + } + } + if(FD_ISSET(xfd, &rfd)) + xev = actionfps; + break; + } gettimeofday(&now, NULL); drawtimeout.tv_sec = 0; drawtimeout.tv_usec = (1000/xfps) * 1000; tv = &drawtimeout; - if(FD_ISSET(cmdfd, &rfd)) - ttyread(); - - if(FD_ISSET(xfd, &rfd)) - xev = actionfps; + dodraw = 0; + if(blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) { + tsetdirtattr(ATTR_BLINK); + term.mode ^= MODE_BLINK; + gettimeofday(&lastblink, NULL); + dodraw = 1; + } + if(TIMEDIFF(now, last) \ + > (xev? (1000/xfps) : (1000/actionfps))) { + dodraw = 1; + last = now; + } - if(TIMEDIFF(now, last) > \ - (xev ? (1000/xfps) : (1000/actionfps))) { + if(dodraw) { while(XPending(xw.dpy)) { XNextEvent(xw.dpy, &ev); if(XFilterEvent(&ev, None)) @@ -3298,77 +3439,78 @@ run(void) { draw(); XFlush(xw.dpy); - last = now; if(xev && !FD_ISSET(xfd, &rfd)) xev--; - if(!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) + if(!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd) \ + && !blinkset) { tv = NULL; + } } } } +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); +} + int main(int argc, char *argv[]) { - int i, bitm, xr, yr; + int bitm, xr, yr; uint wr, hr; xw.fw = xw.fh = xw.fx = xw.fy = 0; xw.isfixed = False; - for(i = 1; i < argc; i++) { - switch(argv[i][0] != '-' || argv[i][2] ? -1 : argv[i][1]) { - case 'c': - if(++i < argc) - opt_class = argv[i]; - break; - case 'e': - /* eat all remaining arguments */ - if(++i < argc) - opt_cmd = &argv[i]; - goto run; - case 'f': - if(++i < argc) - opt_font = argv[i]; - break; - case 'g': - if(++i >= argc) - break; - - bitm = XParseGeometry(argv[i], &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 & XNegative && xw.fy == 0) - xw.fy = -1; - - if(xw.fh != 0 && xw.fw != 0) - xw.isfixed = True; - break; - case 'o': - if(++i < argc) - opt_io = argv[i]; - break; - case 't': - if(++i < argc) - opt_title = argv[i]; - break; - case 'v': - default: - die(USAGE); - case 'w': - if(++i < argc) - opt_embed = argv[i]; - break; - } - } + ARGBEGIN { + case 'a': + allowaltscreen = false; + break; + case 'c': + opt_class = EARGF(usage()); + break; + case 'e': + /* eat all remaining arguments */ + if(argc > 1) + opt_cmd = &argv[1]; + goto run; + case 'f': + 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 & XNegative && xw.fy == 0) + xw.fy = -1; + + if(xw.fh != 0 && xw.fw != 0) + xw.isfixed = True; + break; + case 'o': + opt_io = EARGF(usage()); + break; + case 't': + opt_title = EARGF(usage()); + break; + case 'w': + opt_embed = EARGF(usage()); + break; + case 'v': + default: + usage(); + } ARGEND; run: setlocale(LC_CTYPE, ""); @@ -3377,6 +3519,8 @@ run: xinit(); ttynew(); selinit(); + if(xw.isfixed) + cresize(xw.h, xw.w); run(); return 0;