X-Git-Url: https://git.armaanb.net/?p=st.git;a=blobdiff_plain;f=x.c;h=9b26b96fba538e627738e74a3415b089fd19d73e;hp=03555d139069469650b5929fd382913098853868;hb=HEAD;hpb=75c9a0ee1d232a1a177746d97a13cf92b03da44a diff --git a/x.c b/x.c index 03555d1..9b26b96 100644 --- a/x.c +++ b/x.c @@ -1,32 +1,65 @@ /* See LICENSE for license details. */ #include +#include +#include #include #include -#include #include #include #include #include #include #include -#include #include #include #include #include -static char *argv0; +char *argv0; #include "arg.h" #include "st.h" #include "win.h" +#include "hb.h" + +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +typedef struct { + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; + uint release; +} MouseShortcut; + +typedef struct { + KeySym k; + uint mask; + char *s; + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ +} Key; + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13) /* function definitions used in config.h */ static void clipcopy(const Arg *); static void clippaste(const Arg *); +static void numlock(const Arg *); static void selpaste(const Arg *); static void zoom(const Arg *); static void zoomabs(const Arg *); static void zoomreset(const Arg *); +static void ttysend(const Arg *); /* config.h for applying patches and the configuration. */ #include "config.h" @@ -36,6 +69,7 @@ static void zoomreset(const Arg *); #define XEMBED_FOCUS_OUT 5 /* macros */ +#define IS_SET(flag) ((win.mode & (flag)) != 0) #define TRUERED(x) (((x) & 0xff0000) >> 8) #define TRUEGREEN(x) (((x) & 0xff00)) #define TRUEBLUE(x) (((x) & 0xff) << 8) @@ -45,15 +79,28 @@ typedef XftColor Color; typedef XftGlyphFontSpec GlyphFontSpec; /* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + typedef struct { Display *dpy; Colormap cmap; Window win; Drawable buf; GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ - Atom xembed, wmdeletewin, netwmname, netwmpid; - XIM xim; - XIC xic; + Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; Draw draw; Visual *vis; XSetWindowAttributes attrs; @@ -65,6 +112,9 @@ typedef struct { typedef struct { Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; } XSelection; /* Font structure */ @@ -96,19 +146,24 @@ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int) static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); -static void xdrawcursor(void); static int xgeommasktogravity(int); -static void xinit(void); +static int ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); +static int xicdestroy(XIC, XPointer, XPointer); +static void xinit(int, int); static void cresize(int, int); static void xresize(int, int); +static void xhints(void); +static int xloadcolor(int, const char *, Color *); static int xloadfont(Font *, FcPattern *); -static void xloadfonts(char *, double); +static void xloadfonts(const char *, double); static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); static void xseturgency(int); -static int x2col(int); -static int y2row(int); +static int evcol(XEvent *); +static int evrow(XEvent *); static void expose(XEvent *); static void visibility(XEvent *); @@ -117,6 +172,8 @@ static void kpress(XEvent *); static void cmessage(XEvent *); static void resize(XEvent *); static void focus(XEvent *); +static uint buttonmask(uint); +static int mouseaction(XEvent *, uint); static void brelease(XEvent *); static void bpress(XEvent *); static void bmotion(XEvent *); @@ -124,8 +181,8 @@ static void propnotify(XEvent *); static void selnotify(XEvent *); static void selclear_(XEvent *); static void selrequest(XEvent *); -static void selcopy(Time); -static void getbuttoninfo(XEvent *); +static void setsel(char *, Time); +static void mousesel(XEvent *, int); static void mousereport(XEvent *); static char *kmap(KeySym, uint); static int match(uint, uint); @@ -163,11 +220,7 @@ static void (*handler[LASTEvent])(XEvent *) = { static DC dc; static XWindow xw; static XSelection xsel; - -enum window_state { - WIN_VISIBLE = 1, - WIN_FOCUSED = 2 -}; +static TermWindow win; /* Font Ring Cache */ enum { @@ -184,8 +237,9 @@ typedef struct { } Fontcache; /* Fontcache is an array now. A new font will be appended to the array. */ -static Fontcache frc[16]; +static Fontcache *frc = NULL; static int frclen = 0; +static int frccap = 0; static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; @@ -199,16 +253,19 @@ static char *opt_line = NULL; static char *opt_name = NULL; static char *opt_title = NULL; +static int oldbutton = 3; /* button event on startup: 3 = release */ +static int cursorblinks = 0; + void clipcopy(const Arg *dummy) { Atom clipboard; - if (sel.clipboard != NULL) - free(sel.clipboard); + free(xsel.clipboard); + xsel.clipboard = NULL; - if (sel.primary != NULL) { - sel.clipboard = xstrdup(sel.primary); + if (xsel.primary != NULL) { + xsel.clipboard = xstrdup(xsel.primary); clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); } @@ -231,6 +288,12 @@ selpaste(const Arg *dummy) xw.win, CurrentTime); } +void +numlock(const Arg *dummy) +{ + win.mode ^= MODE_NUMLOCK; +} + void zoom(const Arg *arg) { @@ -246,7 +309,6 @@ zoomabs(const Arg *arg) xunloadfonts(); xloadfonts(usedfont, arg->f); cresize(0, 0); - ttyresize(win.tw, win.th); redraw(); xhints(); } @@ -262,51 +324,50 @@ zoomreset(const Arg *arg) } } -int -x2col(int x) +void +ttysend(const Arg *arg) { - x -= borderpx; - x /= win.cw; - - return LIMIT(x, 0, term.col-1); + ttywrite(arg->s, strlen(arg->s), 1); } int -y2row(int y) +evcol(XEvent *e) { - y -= borderpx; - y /= win.ch; + int x = e->xbutton.x - borderpx; + LIMIT(x, 0, win.tw - 1); + return x / win.cw; +} - return LIMIT(y, 0, term.row-1); +int +evrow(XEvent *e) +{ + int y = e->xbutton.y - borderpx; + LIMIT(y, 0, win.th - 1); + return y / win.ch; } void -getbuttoninfo(XEvent *e) +mousesel(XEvent *e, int done) { - int type; - uint state = e->xbutton.state & ~(Button1Mask | forceselmod); - - sel.alt = IS_SET(MODE_ALTSCREEN); - - sel.oe.x = x2col(e->xbutton.x); - sel.oe.y = y2row(e->xbutton.y); - selnormalize(); + int type, seltype = SEL_REGULAR; + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); - sel.type = SEL_REGULAR; for (type = 1; type < LEN(selmasks); ++type) { if (match(selmasks[type], state)) { - sel.type = type; + seltype = type; break; } } + selextend(evcol(e), evrow(e), seltype, done); + if (done) + setsel(getsel(), e->xbutton.time); } void mousereport(XEvent *e) { - int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y), - button = e->xbutton.button, state = e->xbutton.state, - len; + int len, x = evcol(e), y = evrow(e), + button = e->xbutton.button, state = e->xbutton.state; char buf[40]; static int ox, oy; @@ -328,7 +389,9 @@ mousereport(XEvent *e) button = 3; } else { button -= Button1; - if (button >= 3) + if (button >= 7) + button += 128 - 7; + else if (button >= 3) button += 64 - 3; } if (e->xbutton.type == ButtonPress) { @@ -362,65 +425,75 @@ mousereport(XEvent *e) return; } - ttywrite(buf, len); + ttywrite(buf, len, 0); +} + +uint +buttonmask(uint button) +{ + return button == Button1 ? Button1Mask + : button == Button2 ? Button2Mask + : button == Button3 ? Button3Mask + : button == Button4 ? Button4Mask + : button == Button5 ? Button5Mask + : 0; +} + +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + + /* ignore Buttonmask for Button - it's set on release */ + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { + ms->func(&(ms->arg)); + return 1; + } + } + + return 0; } void bpress(XEvent *e) { struct timespec now; - MouseShortcut *ms; + int snap; - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } - for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { - if (e->xbutton.button == ms->b - && match(ms->mask, e->xbutton.state)) { - ttysend(ms->s, strlen(ms->s)); - return; - } - } + if (mouseaction(e, 0)) + return; if (e->xbutton.button == Button1) { - clock_gettime(CLOCK_MONOTONIC, &now); - - /* Clear previous selection, logically and visually. */ - selclear_(NULL); - sel.mode = SEL_EMPTY; - sel.type = SEL_REGULAR; - sel.oe.x = sel.ob.x = x2col(e->xbutton.x); - sel.oe.y = sel.ob.y = 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; + clock_gettime(CLOCK_MONOTONIC, &now); + if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { + snap = SNAP_LINE; + } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { + snap = SNAP_WORD; } else { - sel.snap = 0; + snap = 0; } - selnormalize(); + xsel.tclick2 = xsel.tclick1; + xsel.tclick1 = now; - if (sel.snap != 0) - sel.mode = SEL_READY; - tsetdirt(sel.nb.y, sel.ne.y); - sel.tclick2 = sel.tclick1; - sel.tclick1 = now; + selstart(evcol(e), evrow(e), snap); } } -void -selcopy(Time t) -{ - xsetsel(getsel(), t); -} - void propnotify(XEvent *e) { @@ -441,18 +514,16 @@ selnotify(XEvent *e) ulong nitems, ofs, rem; int format; uchar *data, *last, *repl; - Atom type, incratom, property; + Atom type, incratom, property = None; incratom = XInternAtom(xw.dpy, "INCR", 0); ofs = 0; - if (e->type == SelectionNotify) { + if (e->type == SelectionNotify) property = e->xselection.property; - } else if(e->type == PropertyNotify) { + else if (e->type == PropertyNotify) property = e->xproperty.atom; - } else { - return; - } + if (property == None) return; @@ -508,10 +579,10 @@ selnotify(XEvent *e) } if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) - ttywrite("\033[200~", 6); - ttysend((char *)data, nitems * format / 8); + ttywrite("\033[200~", 6, 0); + ttywrite((char *)data, nitems * format / 8, 1); if (IS_SET(MODE_BRCKTPASTE) && rem == 0) - ttywrite("\033[201~", 6); + ttywrite("\033[201~", 6, 0); XFree(data); /* number of 32-bit chunks returned */ ofs += nitems * format / 32; @@ -571,9 +642,9 @@ selrequest(XEvent *e) */ clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); if (xsre->selection == XA_PRIMARY) { - seltext = sel.primary; + seltext = xsel.primary; } else if (xsre->selection == clipboard) { - seltext = sel.clipboard; + seltext = xsel.clipboard; } else { fprintf(stderr, "Unhandled clipboard selection 0x%lx\n", @@ -595,59 +666,48 @@ selrequest(XEvent *e) } void -xsetsel(char *str, Time t) +setsel(char *str, Time t) { - free(sel.primary); - sel.primary = str; + if (!str) + return; + + free(xsel.primary); + xsel.primary = str; XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) - selclear_(NULL); + selclear(); +} + +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); } void brelease(XEvent *e) { - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } - if (e->xbutton.button == Button2) { - selpaste(NULL); - } else if (e->xbutton.button == Button1) { - if (sel.mode == SEL_READY) { - getbuttoninfo(e); - selcopy(e->xbutton.time); - } else - selclear_(NULL); - sel.mode = SEL_IDLE; - tsetdirt(sel.nb.y, sel.ne.y); - } + if (mouseaction(e, 1)) + return; + if (e->xbutton.button == Button1) + mousesel(e, 1); } void bmotion(XEvent *e) { - int oldey, oldex, oldsby, oldsey; - - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } - if (!sel.mode) - return; - - sel.mode = SEL_READY; - oldey = sel.oe.y; - oldex = sel.oe.x; - oldsby = sel.nb.y; - oldsey = sel.ne.y; - getbuttoninfo(e); - - if (oldey != sel.oe.y || oldex != sel.oe.x) - tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + mousesel(e, 0); } void @@ -662,16 +722,19 @@ cresize(int width, int height) col = (win.w - 2 * borderpx) / win.cw; row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); tresize(col, row); xresize(col, row); + ttyresize(win.tw, win.th); } void xresize(int col, int row) { - win.tw = MAX(1, col * win.cw); - win.th = MAX(1, row * win.ch); + win.tw = col * win.cw; + win.th = row * win.ch; XFreePixmap(xw.dpy, xw.buf); xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, @@ -720,20 +783,20 @@ xloadcols(void) static int loaded; Color *cp; - dc.collen = MAX(LEN(colorname), 256); - dc.col = xmalloc(dc.collen * sizeof(Color)); - if (loaded) { for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); } for (i = 0; i < dc.collen; i++) if (!xloadcolor(i, NULL, &dc.col[i])) { if (colorname[i]) - die("Could not allocate color '%s'\n", colorname[i]); + die("could not allocate color '%s'\n", colorname[i]); else - die("Could not allocate color %d\n", i); + die("could not allocate color %d\n", i); } loaded = 1; } @@ -746,7 +809,6 @@ xsetcolorname(int x, const char *name) if (!BETWEEN(x, 0, dc.collen)) return 1; - if (!xloadcolor(x, name, &ncolor)) return 1; @@ -773,19 +835,21 @@ xhints(void) XClassHint class = {opt_name ? opt_name : termname, opt_class ? opt_class : termname}; XWMHints wm = {.flags = InputHint, .input = 1}; - XSizeHints *sizeh = NULL; + XSizeHints *sizeh; sizeh = XAllocSizeHints(); - sizeh->flags = PSize | PResizeInc | PBaseSize; + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->height = win.h; sizeh->width = win.w; sizeh->height_inc = win.ch; sizeh->width_inc = win.cw; sizeh->base_height = 2 * borderpx; sizeh->base_width = 2 * borderpx; + sizeh->min_height = win.ch + 2 * borderpx; + sizeh->min_width = win.cw + 2 * borderpx; if (xw.isfixed) { - sizeh->flags |= PMaxSize | PMinSize; + sizeh->flags |= PMaxSize; sizeh->min_width = sizeh->max_width = win.w; sizeh->min_height = sizeh->max_height = win.h; } @@ -858,7 +922,7 @@ xloadfont(Font *f, FcPattern *pattern) if ((XftPatternGetInteger(f->match->pattern, "slant", 0, &haveattr) != XftResultMatch) || haveattr < wantattr) { f->badslant = 1; - fputs("st: font slant does not match\n", stderr); + fputs("font slant does not match\n", stderr); } } @@ -867,7 +931,7 @@ xloadfont(Font *f, FcPattern *pattern) if ((XftPatternGetInteger(f->match->pattern, "weight", 0, &haveattr) != XftResultMatch) || haveattr != wantattr) { f->badweight = 1; - fputs("st: font weight does not match\n", stderr); + fputs("font weight does not match\n", stderr); } } @@ -890,20 +954,18 @@ xloadfont(Font *f, FcPattern *pattern) } void -xloadfonts(char *fontstr, double fontsize) +xloadfonts(const char *fontstr, double fontsize) { FcPattern *pattern; double fontval; - float ceilf(float); - if (fontstr[0] == '-') { + if (fontstr[0] == '-') pattern = XftXlfdParse(fontstr, False, False); - } else { - pattern = FcNameParse((FcChar8 *)fontstr); - } + else + pattern = FcNameParse((const FcChar8 *)fontstr); if (!pattern) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); if (fontsize > 1) { FcPatternDel(pattern, FC_PIXEL_SIZE); @@ -929,7 +991,7 @@ xloadfonts(char *fontstr, double fontsize) } if (xloadfont(&dc.font, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); if (usedfontsize < 0) { FcPatternGetDouble(dc.font.match->pattern, @@ -946,17 +1008,17 @@ xloadfonts(char *fontstr, double fontsize) FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); if (xloadfont(&dc.ifont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_WEIGHT); FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); if (xloadfont(&dc.ibfont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); if (xloadfont(&dc.bfont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDestroy(pattern); } @@ -973,6 +1035,9 @@ xunloadfont(Font *f) void xunloadfonts(void) { + /* Clear Harfbuzz font cache. */ + hbunloadfonts(); + /* Free the loaded fonts in the font cache. */ while (frclen > 0) XftFontClose(xw.dpy, frc[--frclen].font); @@ -983,8 +1048,62 @@ xunloadfonts(void) xunloadfont(&dc.ibfont); } +int +ximopen(Display *dpy) +{ + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; + + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if (xw.ime.xim == NULL) + return 0; + + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); + + if (xw.ime.xic == NULL) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNDestroyCallback, &icdestroy, + NULL); + } + if (xw.ime.xic == NULL) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); + + return 1; +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + XFree(xw.ime.spotlist); +} + +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = NULL; + return 1; +} + void -xinit(void) +xinit(int cols, int rows) { XGCValues gcvalues; Cursor cursor; @@ -993,13 +1112,13 @@ xinit(void) XColor xmousefg, xmousebg; if (!(xw.dpy = XOpenDisplay(NULL))) - die("Can't open display\n"); + die("can't open display\n"); xw.scr = XDefaultScreen(xw.dpy); xw.vis = XDefaultVisual(xw.dpy, xw.scr); /* font */ if (!FcInit()) - die("Could not init fontconfig.\n"); + die("could not init fontconfig.\n"); usedfont = (opt_font == NULL)? font : opt_font; xloadfonts(usedfont, 0); @@ -1009,8 +1128,8 @@ xinit(void) xloadcols(); /* adjust fixed window geometry */ - win.w = 2 * borderpx + term.col * win.cw; - win.h = 2 * borderpx + term.row * win.ch; + win.w = 2 * borderpx + cols * win.cw; + win.h = 2 * borderpx + rows * win.ch; if (xw.gm & XNegative) xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; if (xw.gm & YNegative) @@ -1020,7 +1139,7 @@ xinit(void) xw.attrs.background_pixel = dc.col[defaultbg].pixel; xw.attrs.border_pixel = dc.col[defaultbg].pixel; xw.attrs.bit_gravity = NorthWestGravity; - xw.attrs.event_mask = FocusChangeMask | KeyPressMask + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | ExposureMask | VisibilityChangeMask | StructureNotifyMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; xw.attrs.colormap = xw.cmap; @@ -1042,28 +1161,16 @@ xinit(void) XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); /* font spec buffer */ - xw.specbuf = xmalloc(term.col * sizeof(GlyphFontSpec)); + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); /* Xft rendering context */ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); /* input methods */ - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im=local"); - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im="); - if ((xw.xim = XOpenIM(xw.dpy, - NULL, NULL, NULL)) == NULL) { - die("XOpenIM failed. Could not open input" - " device.\n"); - } - } + if (!ximopen(xw.dpy)) { + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); } - xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing - | XIMStatusNothing, XNClientWindow, xw.win, - XNFocusWindow, xw.win, NULL); - if (xw.xic == NULL) - die("XCreateIC failed. Could not obtain input method.\n"); /* white cursor, black outline */ cursor = XCreateFontCursor(xw.dpy, mouseshape); @@ -1086,17 +1193,23 @@ xinit(void) xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); + xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, PropModeReplace, (uchar *)&thispid, 1); + win.mode = MODE_NUMLOCK; resettitle(); - XMapWindow(xw.dpy, xw.win); xhints(); + XMapWindow(xw.dpy, xw.win); XSync(xw.dpy, False); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = NULL; + xsel.clipboard = NULL; xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); if (xsel.xtarget == None) xsel.xtarget = XA_STRING; @@ -1124,7 +1237,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x mode = glyphs[i].mode; /* Skip dummy wide-character spacing. */ - if (mode == ATTR_WDUMMY) + if (mode & ATTR_WDUMMY) continue; /* Determine font for glyph if different from previous glyph. */ @@ -1200,13 +1313,10 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); - /* - * Overwrite or create the new cache entry. - */ - if (frclen >= LEN(frc)) { - frclen = LEN(frc) - 1; - XftFontClose(xw.dpy, frc[frclen].font); - frc[frclen].unicodep = 0; + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); } frc[frclen].font = XftFontOpenPattern(xw.dpy, @@ -1234,6 +1344,9 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x numspecs++; } + /* Harfbuzz transformation for ligatures. */ + hbtransform(specs, glyphs, len, x, y); + return numspecs; } @@ -1317,14 +1430,13 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i fg = &revfg; } - if (base.mode & ATTR_REVERSE) { temp = fg; fg = bg; bg = temp; } - if (base.mode & ATTR_BLINK && term.mode & MODE_BLINK) + if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) fg = bg; if (base.mode & ATTR_INVISIBLE) @@ -1333,15 +1445,16 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i /* Intelligent cleaning up of the borders. */ if (x == 0) { xclear(0, (y == 0)? 0 : winy, borderpx, - winy + win.ch + ((y >= term.row-1)? win.h : 0)); + winy + win.ch + + ((winy + win.ch >= borderpx + win.th)? win.h : 0)); } - if (x + charlen >= term.col) { + if (winx + width >= borderpx + win.tw) { xclear(winx + width, (y == 0)? 0 : winy, win.w, - ((y >= term.row-1)? win.h : (winy + win.ch))); + ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); } if (y == 0) xclear(winx, 0, winx + width, borderpx); - if (y == term.row-1) + if (winy + win.ch >= borderpx + win.th) xclear(winx, winy + win.ch, winx + width, win.h); /* Clean up the region we want to draw to. */ @@ -1383,42 +1496,30 @@ xdrawglyph(Glyph g, int x, int y) } void -xdrawcursor(void) +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) { - static int oldx = 0, oldy = 0; - int curx; - Glyph g = {' ', ATTR_NULL, defaultbg, defaultcs}, og; - int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); Color drawcol; - 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--; - /* remove the old cursor */ - og = term.line[oldy][oldx]; - if (ena_sel && selected(oldx, oldy)) + if (selected(ox, oy)) og.mode ^= ATTR_REVERSE; - xdrawglyph(og, oldx, oldy); - g.u = term.line[term.c.y][term.c.x].u; - g.mode |= term.line[term.c.y][term.c.x].mode & - (ATTR_BOLD | ATTR_ITALIC | ATTR_UNDERLINE | ATTR_STRUCK); + /* Redraw the line where cursor was previously. + * It will restore the ligatures broken by the cursor. */ + xdrawline(line, 0, oy, len); + + if (IS_SET(MODE_HIDE)) + return; /* * Select the right color for the right mode. */ + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; g.bg = defaultfg; - if (ena_sel && selected(term.c.x, term.c.y)) { + if (selected(cx, cy)) { drawcol = dc.col[defaultcs]; g.fg = defaultrcs; } else { @@ -1426,64 +1527,75 @@ xdrawcursor(void) g.fg = defaultcs; } } else { - if (ena_sel && selected(term.c.x, term.c.y)) { - drawcol = dc.col[defaultrcs]; + if (selected(cx, cy)) { g.fg = defaultfg; g.bg = defaultrcs; } else { - drawcol = dc.col[defaultcs]; + g.fg = defaultbg; + g.bg = defaultcs; } + drawcol = dc.col[g.bg]; } - if (IS_SET(MODE_HIDE)) - return; - /* draw the new one */ - if (win.state & WIN_FOCUSED) { + if (IS_SET(MODE_FOCUSED)) { switch (win.cursor) { - case 7: /* st extension: snowman */ - utf8decode("☃", &g.u, UTF_SIZ); - case 0: /* Blinking Block */ - case 1: /* Blinking Block (Default) */ - case 2: /* Steady Block */ - g.mode |= term.line[term.c.y][curx].mode & ATTR_WIDE; - xdrawglyph(g, term.c.x, term.c.y); + case 0: /* Blinking block */ + case 1: /* Blinking block (default) */ + if (IS_SET(MODE_BLINK)) + break; + /* FALLTHROUGH */ + case 2: /* Steady block */ + xdrawglyph(g, cx, cy); break; - case 3: /* Blinking Underline */ - case 4: /* Steady Underline */ + case 3: /* Blinking underline */ + if (IS_SET(MODE_BLINK)) + break; + /* FALLTHROUGH */ + case 4: /* Steady underline */ XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + (term.c.y + 1) * win.ch - \ + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - \ cursorthickness, win.cw, cursorthickness); break; case 5: /* Blinking bar */ + if (IS_SET(MODE_BLINK)) + break; + /* FALLTHROUGH */ case 6: /* Steady bar */ XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + term.c.y * win.ch, + borderpx + cx * win.cw, + borderpx + cy * win.ch, cursorthickness, win.ch); break; + case 7: /* Blinking st cursor */ + if (IS_SET(MODE_BLINK)) + break; + /* FALLTHROUGH */ + case 8: /* Steady st cursor */ + g.u = stcursor; + xdrawglyph(g, cx, cy); + break; } } else { XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + term.c.y * win.ch, + borderpx + cx * win.cw, + borderpx + cy * win.ch, win.cw - 1, 1); XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + term.c.y * win.ch, + borderpx + cx * win.cw, + borderpx + cy * win.ch, 1, win.ch - 1); XftDrawRect(xw.draw, &drawcol, - borderpx + (curx + 1) * win.cw - 1, - borderpx + term.c.y * win.ch, + borderpx + (cx + 1) * win.cw - 1, + borderpx + cy * win.ch, 1, win.ch - 1); XftDrawRect(xw.draw, &drawcol, - borderpx + curx * win.cw, - borderpx + (term.c.y + 1) * win.ch - 1, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - 1, win.cw, 1); } - oldx = curx, oldy = term.c.y; } void @@ -1495,11 +1607,24 @@ xsetenv(void) setenv("WINDOWID", buf, 1); } +void +xseticontitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop); + XSetWMIconName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); + XFree(prop.value); +} + void xsettitle(char *p) { XTextProperty prop; - DEFAULT(p, "st"); + DEFAULT(p, opt_title); Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop); @@ -1508,10 +1633,46 @@ xsettitle(char *p) XFree(prop.value); } +int +xstartdraw(void) +{ + return IS_SET(MODE_VISIBLE); +} + +void +xdrawline(Line line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + Glyph base, new; + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); +} + void -draw(void) +xfinishdraw(void) { - drawregion(0, 0, term.col, term.row); XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0); XSetForeground(xw.dpy, dc.gc, @@ -1520,48 +1681,15 @@ draw(void) } void -drawregion(int x1, int y1, int x2, int y2) +xximspot(int x, int y) { - int i, x, y, ox, numspecs; - Glyph base, new; - XftGlyphFontSpec *specs; - int ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN); - - if (!(win.state & WIN_VISIBLE)) + if (xw.ime.xic == NULL) return; - for (y = y1; y < y2; y++) { - if (!term.dirty[y]) - continue; - - term.dirty[y] = 0; + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; - specs = xw.specbuf; - numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y); - - i = ox = 0; - for (x = x1; x < x2 && i < numspecs; x++) { - new = term.line[y][x]; - if (new.mode == ATTR_WDUMMY) - continue; - if (ena_sel && selected(x, y)) - new.mode ^= ATTR_REVERSE; - if (i > 0 && ATTRCMP(base, new)) { - xdrawglyphfontspecs(specs, base, i, ox, y); - specs += i; - numspecs -= i; - i = 0; - } - if (i == 0) { - ox = x; - base = new; - } - i++; - } - if (i > 0) - xdrawglyphfontspecs(specs, base, i, ox, y); - } - xdrawcursor(); + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); } void @@ -1575,13 +1703,13 @@ visibility(XEvent *ev) { XVisibilityEvent *e = &ev->xvisibility; - MODBIT(win.state, e->state != VisibilityFullyObscured, WIN_VISIBLE); + MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); } void unmap(XEvent *ev) { - win.state &= ~WIN_VISIBLE; + win.mode &= ~MODE_VISIBLE; } void @@ -1591,6 +1719,27 @@ xsetpointermotion(int set) XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); } +void +xsetmode(int set, unsigned int flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); +} + +int +xsetcursor(int cursor) +{ + if (!BETWEEN(cursor, 0, 8)) /* 7-8: st extensions */ + return 1; + win.cursor = cursor; + cursorblinks = win.cursor == 0 || win.cursor == 1 || + win.cursor == 3 || win.cursor == 5 || + win.cursor == 7; + return 0; +} + void xseturgency(int add) { @@ -1604,7 +1753,7 @@ xseturgency(int add) void xbell(void) { - if (!(win.state & WIN_FOCUSED)) + if (!(IS_SET(MODE_FOCUSED))) xseturgency(1); if (bellvolume) XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); @@ -1619,16 +1768,18 @@ focus(XEvent *ev) return; if (ev->type == FocusIn) { - XSetICFocus(xw.xic); - win.state |= WIN_FOCUSED; + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); + win.mode |= MODE_FOCUSED; xseturgency(0); if (IS_SET(MODE_FOCUS)) - ttywrite("\033[I", 3); + ttywrite("\033[I", 3, 0); } else { - XUnsetICFocus(xw.xic); - win.state &= ~WIN_FOCUSED; + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); + win.mode &= ~MODE_FOCUSED; if (IS_SET(MODE_FOCUS)) - ttywrite("\033[O", 3); + ttywrite("\033[O", 3, 0); } } @@ -1663,15 +1814,12 @@ kmap(KeySym k, uint state) if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) continue; - if (term.numlock && kp->appkey == 2) + if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) continue; if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) continue; - if (IS_SET(MODE_CRLF) ? kp->crlf < 0 : kp->crlf > 0) - continue; - return kp->s; } @@ -1683,7 +1831,7 @@ kpress(XEvent *ev) { XKeyEvent *e = &ev->xkey; KeySym ksym; - char buf[32], *customkey; + char buf[64], *customkey; int len; Rune c; Status status; @@ -1692,7 +1840,10 @@ kpress(XEvent *ev) if (IS_SET(MODE_KBDLOCK)) return; - len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); + if (xw.ime.xic) + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + else + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); /* 1. shortcuts */ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { if (ksym == bp->keysym && match(bp->mod, e->state)) { @@ -1703,7 +1854,7 @@ kpress(XEvent *ev) /* 2. custom keys from config.h */ if ((customkey = kmap(ksym, e->state))) { - ttysend(customkey, strlen(customkey)); + ttywrite(customkey, strlen(customkey), 1); return; } @@ -1722,10 +1873,9 @@ kpress(XEvent *ev) len = 2; } } - ttysend(buf, len); + ttywrite(buf, len, 1); } - void cmessage(XEvent *e) { @@ -1735,14 +1885,13 @@ cmessage(XEvent *e) */ if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { - win.state |= WIN_FOCUSED; + win.mode |= MODE_FOCUSED; xseturgency(0); } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { - win.state &= ~WIN_FOCUSED; + win.mode &= ~MODE_FOCUSED; } } else if (e->xclient.data.l[0] == xw.wmdeletewin) { - /* Send SIGHUP to shell */ - kill(pid, SIGHUP); + ttyhangup(); exit(0); } } @@ -1754,7 +1903,6 @@ resize(XEvent *e) return; cresize(e->xconfigure.width, e->xconfigure.height); - ttyresize(win.tw, win.th); } void @@ -1763,9 +1911,9 @@ run(void) XEvent ev; int w = win.w, h = win.h; fd_set rfd; - int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0; - struct timespec drawtimeout, *tv = NULL, now, last, lastblink; - long deltatime; + int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; + struct timespec seltv, *tv, now, lastblink, trigger; + double timeout; /* Waiting for window mapping */ do { @@ -1783,86 +1931,84 @@ run(void) } } while (ev.type != MapNotify); + ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); cresize(w, h); - ttynew(opt_line, opt_io, opt_cmd); - ttyresize(win.tw, win.th); - clock_gettime(CLOCK_MONOTONIC, &last); - lastblink = last; - - for (xev = actionfps;;) { + for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { FD_ZERO(&rfd); - FD_SET(cmdfd, &rfd); + FD_SET(ttyfd, &rfd); FD_SET(xfd, &rfd); - if (pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (XPending(xw.dpy)) + timeout = 0; /* existing events might not set xfd */ + + seltv.tv_sec = timeout / 1E3; + seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); + tv = timeout >= 0 ? &seltv : NULL; + + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { if (errno == EINTR) continue; die("select failed: %s\n", strerror(errno)); } - if (FD_ISSET(cmdfd, &rfd)) { - ttyread(); - if (blinktimeout) { - blinkset = tattrset(ATTR_BLINK); - if (!blinkset) - MODBIT(term.mode, 0, MODE_BLINK); - } - } + clock_gettime(CLOCK_MONOTONIC, &now); - if (FD_ISSET(xfd, &rfd)) - xev = actionfps; + if (FD_ISSET(ttyfd, &rfd)) + ttyread(); - clock_gettime(CLOCK_MONOTONIC, &now); - drawtimeout.tv_sec = 0; - drawtimeout.tv_nsec = (1000 * 1E6)/ xfps; - tv = &drawtimeout; - - dodraw = 0; - if (blinktimeout && TIMEDIFF(now, lastblink) > blinktimeout) { - tsetdirtattr(ATTR_BLINK); - term.mode ^= MODE_BLINK; - lastblink = now; - dodraw = 1; - } - deltatime = TIMEDIFF(now, last); - if (deltatime > 1000 / (xev ? xfps : actionfps)) { - dodraw = 1; - last = now; + xev = 0; + while (XPending(xw.dpy)) { + xev = 1; + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); } - if (dodraw) { - while (XPending(xw.dpy)) { - XNextEvent(xw.dpy, &ev); - if (XFilterEvent(&ev, None)) - continue; - if (handler[ev.type]) - (handler[ev.type])(&ev); + /* + * To reduce flicker and tearing, when new content or event + * triggers drawing, we first wait a bit to ensure we got + * everything, and if nothing new arrives - we draw. + * We start with trying to wait minlatency ms. If more content + * arrives sooner, we retry with shorter and shorter periods, + * and eventually draw even without idle after maxlatency ms. + * Typically this results in low latency while interacting, + * maximum latency intervals during `cat huge.txt`, and perfect + * sync with periodic updates from animations/key-repeats/etc. + */ + if (FD_ISSET(ttyfd, &rfd) || xev) { + if (!drawing) { + trigger = now; + if (IS_SET(MODE_BLINK)) { + win.mode ^= MODE_BLINK; + } + lastblink = now; + drawing = 1; } + timeout = (maxlatency - TIMEDIFF(now, trigger)) \ + / maxlatency * minlatency; + if (timeout > 0) + continue; /* we have time, try to find idle */ + } - draw(); - XFlush(xw.dpy); - - if (xev && !FD_ISSET(xfd, &rfd)) - xev--; - if (!FD_ISSET(cmdfd, &rfd) && !FD_ISSET(xfd, &rfd)) { - if (blinkset) { - if (TIMEDIFF(now, lastblink) \ - > blinktimeout) { - drawtimeout.tv_nsec = 1000; - } else { - drawtimeout.tv_nsec = (1E6 * \ - (blinktimeout - \ - TIMEDIFF(now, - lastblink))); - } - drawtimeout.tv_sec = \ - drawtimeout.tv_nsec / 1E9; - drawtimeout.tv_nsec %= (long)1E9; - } else { - tv = NULL; - } + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK))) { + timeout = blinktimeout - TIMEDIFF(now, lastblink); + if (timeout <= 0) { + if (-timeout > blinktimeout) /* start visible */ + win.mode |= MODE_BLINK; + win.mode ^= MODE_BLINK; + tsetdirtattr(ATTR_BLINK); + lastblink = now; + timeout = blinktimeout; } } + + draw(); + XFlush(xw.dpy); + drawing = 0; } } @@ -1884,7 +2030,7 @@ main(int argc, char *argv[]) { xw.l = xw.t = 0; xw.isfixed = False; - win.cursor = cursorshape; + xsetcursor(cursorstyle); ARGBEGIN { case 'a': @@ -1924,23 +2070,25 @@ main(int argc, char *argv[]) opt_embed = EARGF(usage()); break; case 'v': - die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0); + die("%s " VERSION "\n", argv0); break; default: usage(); } ARGEND; run: - if (argc > 0) { - /* eat all remaining arguments */ + if (argc > 0) /* eat all remaining arguments */ opt_cmd = argv; - if (!opt_title && !opt_line) - opt_title = basename(xstrdup(argv[0])); - } + + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; + setlocale(LC_CTYPE, ""); XSetLocaleModifiers(""); - tnew(MAX(cols, 1), MAX(rows, 1)); - xinit(); + cols = MAX(cols, 1); + rows = MAX(rows, 1); + tnew(cols, rows); + xinit(cols, rows); xsetenv(); selinit(); run();