tx.c - st - [fork] customized build of st, the simple terminal
 (HTM) git clone git://src.adamsgaard.dk/st
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tx.c (48848B)
       ---
            1 /* See LICENSE for license details. */
            2 #include <errno.h>
            3 #include <math.h>
            4 #include <limits.h>
            5 #include <locale.h>
            6 #include <signal.h>
            7 #include <sys/select.h>
            8 #include <time.h>
            9 #include <unistd.h>
           10 #include <libgen.h>
           11 #include <X11/Xatom.h>
           12 #include <X11/Xlib.h>
           13 #include <X11/cursorfont.h>
           14 #include <X11/keysym.h>
           15 #include <X11/Xft/Xft.h>
           16 #include <X11/XKBlib.h>
           17 
           18 char *argv0;
           19 #include "arg.h"
           20 #include "st.h"
           21 #include "win.h"
           22 
           23 /* types used in config.h */
           24 typedef struct {
           25         uint mod;
           26         KeySym keysym;
           27         void (*func)(const Arg *);
           28         const Arg arg;
           29 } Shortcut;
           30 
           31 typedef struct {
           32         uint mod;
           33         uint button;
           34         void (*func)(const Arg *);
           35         const Arg arg;
           36         uint  release;
           37 } MouseShortcut;
           38 
           39 typedef struct {
           40         KeySym k;
           41         uint mask;
           42         char *s;
           43         /* three-valued logic variables: 0 indifferent, 1 on, -1 off */
           44         signed char appkey;    /* application keypad */
           45         signed char appcursor; /* application cursor */
           46 } Key;
           47 
           48 /* X modifiers */
           49 #define XK_ANY_MOD    UINT_MAX
           50 #define XK_NO_MOD     0
           51 #define XK_SWITCH_MOD (1<<13|1<<14)
           52 
           53 /* function definitions used in config.h */
           54 static void clipcopy(const Arg *);
           55 static void clippaste(const Arg *);
           56 static void numlock(const Arg *);
           57 static void selpaste(const Arg *);
           58 static void zoom(const Arg *);
           59 static void zoomabs(const Arg *);
           60 static void zoomreset(const Arg *);
           61 static void invert(const Arg *);
           62 static void ttysend(const Arg *);
           63 
           64 /* config.h for applying patches and the configuration. */
           65 #include "config.h"
           66 
           67 /* XEMBED messages */
           68 #define XEMBED_FOCUS_IN  4
           69 #define XEMBED_FOCUS_OUT 5
           70 
           71 /* macros */
           72 #define IS_SET(flag)                ((win.mode & (flag)) != 0)
           73 #define TRUERED(x)                (((x) & 0xff0000) >> 8)
           74 #define TRUEGREEN(x)                (((x) & 0xff00))
           75 #define TRUEBLUE(x)                (((x) & 0xff) << 8)
           76 
           77 typedef XftDraw *Draw;
           78 typedef XftColor Color;
           79 typedef XftGlyphFontSpec GlyphFontSpec;
           80 
           81 /* Purely graphic info */
           82 typedef struct {
           83         int tw, th; /* tty width and height */
           84         int w, h; /* window width and height */
           85         int ch; /* char height */
           86         int cw; /* char width  */
           87         int mode; /* window state/mode flags */
           88         int cursor; /* cursor style */
           89 } TermWindow;
           90 
           91 typedef struct {
           92         Display *dpy;
           93         Colormap cmap;
           94         Window win;
           95         Drawable buf;
           96         GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
           97         Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid;
           98         struct {
           99                 XIM xim;
          100                 XIC xic;
          101                 XPoint spot;
          102                 XVaNestedList spotlist;
          103         } ime;
          104         Draw draw;
          105         Visual *vis;
          106         XSetWindowAttributes attrs;
          107         int scr;
          108         int isfixed; /* is fixed geometry? */
          109         int l, t; /* left and top offset */
          110         int gm; /* geometry mask */
          111 } XWindow;
          112 
          113 typedef struct {
          114         Atom xtarget;
          115         char *primary, *clipboard;
          116         struct timespec tclick1;
          117         struct timespec tclick2;
          118 } XSelection;
          119 
          120 /* Font structure */
          121 #define Font Font_
          122 typedef struct {
          123         int height;
          124         int width;
          125         int ascent;
          126         int descent;
          127         int badslant;
          128         int badweight;
          129         short lbearing;
          130         short rbearing;
          131         XftFont *match;
          132         FcFontSet *set;
          133         FcPattern *pattern;
          134 } Font;
          135 
          136 /* Drawing Context */
          137 typedef struct {
          138         Color *col;
          139         size_t collen;
          140         Font font, bfont, ifont, ibfont;
          141         GC gc;
          142 } DC;
          143 
          144 static inline ushort sixd_to_16bit(int);
          145 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
          146 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
          147 static void xdrawglyph(Glyph, int, int);
          148 static void xclear(int, int, int, int);
          149 static int xgeommasktogravity(int);
          150 static int ximopen(Display *);
          151 static void ximinstantiate(Display *, XPointer, XPointer);
          152 static void ximdestroy(XIM, XPointer, XPointer);
          153 static int xicdestroy(XIC, XPointer, XPointer);
          154 static void xinit(int, int);
          155 static void cresize(int, int);
          156 static void xresize(int, int);
          157 static void xhints(void);
          158 static int xloadcolor(int, const char *, Color *);
          159 static int xloadfont(Font *, FcPattern *);
          160 static void xloadfonts(const char *, double);
          161 static void xunloadfont(Font *);
          162 static void xunloadfonts(void);
          163 static void xsetenv(void);
          164 static void xseturgency(int);
          165 static int evcol(XEvent *);
          166 static int evrow(XEvent *);
          167 
          168 static void expose(XEvent *);
          169 static void visibility(XEvent *);
          170 static void unmap(XEvent *);
          171 static void kpress(XEvent *);
          172 static void cmessage(XEvent *);
          173 static void resize(XEvent *);
          174 static void focus(XEvent *);
          175 static uint buttonmask(uint);
          176 static int mouseaction(XEvent *, uint);
          177 static void brelease(XEvent *);
          178 static void bpress(XEvent *);
          179 static void bmotion(XEvent *);
          180 static void propnotify(XEvent *);
          181 static void selnotify(XEvent *);
          182 static void selclear_(XEvent *);
          183 static void selrequest(XEvent *);
          184 static void setsel(char *, Time);
          185 static void mousesel(XEvent *, int);
          186 static void mousereport(XEvent *);
          187 static char *kmap(KeySym, uint);
          188 static int match(uint, uint);
          189 
          190 static void run(void);
          191 static void usage(void);
          192 
          193 static void (*handler[LASTEvent])(XEvent *) = {
          194         [KeyPress] = kpress,
          195         [ClientMessage] = cmessage,
          196         [ConfigureNotify] = resize,
          197         [VisibilityNotify] = visibility,
          198         [UnmapNotify] = unmap,
          199         [Expose] = expose,
          200         [FocusIn] = focus,
          201         [FocusOut] = focus,
          202         [MotionNotify] = bmotion,
          203         [ButtonPress] = bpress,
          204         [ButtonRelease] = brelease,
          205 /*
          206  * Uncomment if you want the selection to disappear when you select something
          207  * different in another window.
          208  */
          209 /*        [SelectionClear] = selclear_, */
          210         [SelectionNotify] = selnotify,
          211 /*
          212  * PropertyNotify is only turned on when there is some INCR transfer happening
          213  * for the selection retrieval.
          214  */
          215         [PropertyNotify] = propnotify,
          216         [SelectionRequest] = selrequest,
          217 };
          218 
          219 /* Globals */
          220 static DC dc;
          221 static XWindow xw;
          222 static XSelection xsel;
          223 static TermWindow win;
          224 
          225 /* Font Ring Cache */
          226 enum {
          227         FRC_NORMAL,
          228         FRC_ITALIC,
          229         FRC_BOLD,
          230         FRC_ITALICBOLD
          231 };
          232 
          233 typedef struct {
          234         XftFont *font;
          235         int flags;
          236         Rune unicodep;
          237 } Fontcache;
          238 
          239 /* Fontcache is an array now. A new font will be appended to the array. */
          240 static Fontcache *frc = NULL;
          241 static int frclen = 0;
          242 static int frccap = 0;
          243 static char *usedfont = NULL;
          244 static double usedfontsize = 0;
          245 static double defaultfontsize = 0;
          246 
          247 static char *opt_class = NULL;
          248 static char **opt_cmd  = NULL;
          249 static char *opt_embed = NULL;
          250 static char *opt_font  = NULL;
          251 static char *opt_io    = NULL;
          252 static char *opt_line  = NULL;
          253 static char *opt_name  = NULL;
          254 static char *opt_title = NULL;
          255 
          256 static int invertcolors = 0;
          257 static uint buttons; /* bit field of pressed buttons */
          258 
          259 void
          260 invert(const Arg *dummy)
          261 {
          262         /*unsigned int tmp;
          263         tmp = defaultfg;
          264         defaultfg = defaultbg;
          265         defaultbg = tmp;*/
          266 
          267         invertcolors = !invertcolors;
          268         xloadcols();
          269         redraw();
          270 }
          271 
          272 /*Color
          273 invertedcolor(Color *clr)
          274 {
          275         XRenderColor rc;
          276         Color inverted;
          277         rc.red = ~clr->color.red;
          278         rc.green = ~clr->color.green;
          279         rc.blue = ~clr->color.blue;
          280         rc.alpha = clr->color.alpha;
          281         XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &rc, &inverted);
          282         return inverted;
          283 }*/
          284 
          285 void
          286 clipcopy(const Arg *dummy)
          287 {
          288         Atom clipboard;
          289 
          290         free(xsel.clipboard);
          291         xsel.clipboard = NULL;
          292 
          293         if (xsel.primary != NULL) {
          294                 xsel.clipboard = xstrdup(xsel.primary);
          295                 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
          296                 XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
          297         }
          298 }
          299 
          300 void
          301 clippaste(const Arg *dummy)
          302 {
          303         Atom clipboard;
          304 
          305         clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
          306         XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard,
          307                         xw.win, CurrentTime);
          308 }
          309 
          310 void
          311 selpaste(const Arg *dummy)
          312 {
          313         XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY,
          314                         xw.win, CurrentTime);
          315 }
          316 
          317 void
          318 numlock(const Arg *dummy)
          319 {
          320         win.mode ^= MODE_NUMLOCK;
          321 }
          322 
          323 void
          324 zoom(const Arg *arg)
          325 {
          326         Arg larg;
          327 
          328         larg.f = usedfontsize + arg->f;
          329         zoomabs(&larg);
          330 }
          331 
          332 void
          333 zoomabs(const Arg *arg)
          334 {
          335         xunloadfonts();
          336         xloadfonts(usedfont, arg->f);
          337         cresize(0, 0);
          338         redraw();
          339         xhints();
          340 }
          341 
          342 void
          343 zoomreset(const Arg *arg)
          344 {
          345         Arg larg;
          346 
          347         if (defaultfontsize > 0) {
          348                 larg.f = defaultfontsize;
          349                 zoomabs(&larg);
          350         }
          351 }
          352 
          353 void
          354 ttysend(const Arg *arg)
          355 {
          356         ttywrite(arg->s, strlen(arg->s), 1);
          357 }
          358 
          359 int
          360 evcol(XEvent *e)
          361 {
          362         int x = e->xbutton.x - borderpx;
          363         LIMIT(x, 0, win.tw - 1);
          364         return x / win.cw;
          365 }
          366 
          367 int
          368 evrow(XEvent *e)
          369 {
          370         int y = e->xbutton.y - borderpx;
          371         LIMIT(y, 0, win.th - 1);
          372         return y / win.ch;
          373 }
          374 
          375 void
          376 mousesel(XEvent *e, int done)
          377 {
          378         int type, seltype = SEL_REGULAR;
          379         uint state = e->xbutton.state & ~(Button1Mask | forcemousemod);
          380 
          381         for (type = 1; type < LEN(selmasks); ++type) {
          382                 if (match(selmasks[type], state)) {
          383                         seltype = type;
          384                         break;
          385                 }
          386         }
          387         selextend(evcol(e), evrow(e), seltype, done);
          388         if (done)
          389                 setsel(getsel(), e->xbutton.time);
          390 }
          391 
          392 void
          393 mousereport(XEvent *e)
          394 {
          395         int len, btn, code;
          396         int x = evcol(e), y = evrow(e);
          397         int state = e->xbutton.state;
          398         char buf[40];
          399         static int ox, oy;
          400 
          401         if (e->type == MotionNotify) {
          402                 if (x == ox && y == oy)
          403                         return;
          404                 if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY))
          405                         return;
          406                 /* MODE_MOUSEMOTION: no reporting if no button is pressed */
          407                 if (IS_SET(MODE_MOUSEMOTION) && buttons == 0)
          408                         return;
          409                 /* Set btn to lowest-numbered pressed button, or 12 if no
          410                  * buttons are pressed. */
          411                 for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++)
          412                         ;
          413                 code = 32;
          414         } else {
          415                 btn = e->xbutton.button;
          416                 /* Only buttons 1 through 11 can be encoded */
          417                 if (btn < 1 || btn > 11)
          418                         return;
          419                 if (e->type == ButtonRelease) {
          420                         /* MODE_MOUSEX10: no button release reporting */
          421                         if (IS_SET(MODE_MOUSEX10))
          422                                 return;
          423                         /* Don't send release events for the scroll wheel */
          424                         if (btn == 4 || btn == 5)
          425                                 return;
          426                 }
          427                 code = 0;
          428         }
          429 
          430         ox = x;
          431         oy = y;
          432 
          433         /* Encode btn into code. If no button is pressed for a motion event in
          434          * MODE_MOUSEMANY, then encode it as a release. */
          435         if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12)
          436                 code += 3;
          437         else if (btn >= 8)
          438                 code += 128 + btn - 8;
          439         else if (btn >= 4)
          440                 code += 64 + btn - 4;
          441         else
          442                 code += btn - 1;
          443 
          444         if (!IS_SET(MODE_MOUSEX10)) {
          445                 code += ((state & ShiftMask  ) ?  4 : 0)
          446                       + ((state & Mod1Mask   ) ?  8 : 0) /* meta key: alt */
          447                       + ((state & ControlMask) ? 16 : 0);
          448         }
          449 
          450         if (IS_SET(MODE_MOUSESGR)) {
          451                 len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c",
          452                                 code, x+1, y+1,
          453                                 e->type == ButtonRelease ? 'm' : 'M');
          454         } else if (x < 223 && y < 223) {
          455                 len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
          456                                 32+code, 32+x+1, 32+y+1);
          457         } else {
          458                 return;
          459         }
          460 
          461         ttywrite(buf, len, 0);
          462 }
          463 
          464 uint
          465 buttonmask(uint button)
          466 {
          467         return button == Button1 ? Button1Mask
          468              : button == Button2 ? Button2Mask
          469              : button == Button3 ? Button3Mask
          470              : button == Button4 ? Button4Mask
          471              : button == Button5 ? Button5Mask
          472              : 0;
          473 }
          474 
          475 int
          476 mouseaction(XEvent *e, uint release)
          477 {
          478         MouseShortcut *ms;
          479 
          480         /* ignore Button<N>mask for Button<N> - it's set on release */
          481         uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
          482 
          483         for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
          484                 if (ms->release == release &&
          485                     ms->button == e->xbutton.button &&
          486                     (match(ms->mod, state) ||  /* exact or forced */
          487                      match(ms->mod, state & ~forcemousemod))) {
          488                         ms->func(&(ms->arg));
          489                         return 1;
          490                 }
          491         }
          492 
          493         return 0;
          494 }
          495 
          496 void
          497 bpress(XEvent *e)
          498 {
          499         int btn = e->xbutton.button;
          500         struct timespec now;
          501         int snap;
          502 
          503         if (1 <= btn && btn <= 11)
          504                 buttons |= 1 << (btn-1);
          505 
          506         if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
          507                 mousereport(e);
          508                 return;
          509         }
          510 
          511         if (mouseaction(e, 0))
          512                 return;
          513 
          514         if (btn == Button1) {
          515                 /*
          516                  * If the user clicks below predefined timeouts specific
          517                  * snapping behaviour is exposed.
          518                  */
          519                 clock_gettime(CLOCK_MONOTONIC, &now);
          520                 if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) {
          521                         snap = SNAP_LINE;
          522                 } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) {
          523                         snap = SNAP_WORD;
          524                 } else {
          525                         snap = 0;
          526                 }
          527                 xsel.tclick2 = xsel.tclick1;
          528                 xsel.tclick1 = now;
          529 
          530                 selstart(evcol(e), evrow(e), snap);
          531         }
          532 }
          533 
          534 void
          535 propnotify(XEvent *e)
          536 {
          537         XPropertyEvent *xpev;
          538         Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
          539 
          540         xpev = &e->xproperty;
          541         if (xpev->state == PropertyNewValue &&
          542                         (xpev->atom == XA_PRIMARY ||
          543                          xpev->atom == clipboard)) {
          544                 selnotify(e);
          545         }
          546 }
          547 
          548 void
          549 selnotify(XEvent *e)
          550 {
          551         ulong nitems, ofs, rem;
          552         int format;
          553         uchar *data, *last, *repl;
          554         Atom type, incratom, property = None;
          555 
          556         incratom = XInternAtom(xw.dpy, "INCR", 0);
          557 
          558         ofs = 0;
          559         if (e->type == SelectionNotify)
          560                 property = e->xselection.property;
          561         else if (e->type == PropertyNotify)
          562                 property = e->xproperty.atom;
          563 
          564         if (property == None)
          565                 return;
          566 
          567         do {
          568                 if (XGetWindowProperty(xw.dpy, xw.win, property, ofs,
          569                                         BUFSIZ/4, False, AnyPropertyType,
          570                                         &type, &format, &nitems, &rem,
          571                                         &data)) {
          572                         fprintf(stderr, "Clipboard allocation failed\n");
          573                         return;
          574                 }
          575 
          576                 if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
          577                         /*
          578                          * If there is some PropertyNotify with no data, then
          579                          * this is the signal of the selection owner that all
          580                          * data has been transferred. We won't need to receive
          581                          * PropertyNotify events anymore.
          582                          */
          583                         MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask);
          584                         XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
          585                                         &xw.attrs);
          586                 }
          587 
          588                 if (type == incratom) {
          589                         /*
          590                          * Activate the PropertyNotify events so we receive
          591                          * when the selection owner does send us the next
          592                          * chunk of data.
          593                          */
          594                         MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
          595                         XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
          596                                         &xw.attrs);
          597 
          598                         /*
          599                          * Deleting the property is the transfer start signal.
          600                          */
          601                         XDeleteProperty(xw.dpy, xw.win, (int)property);
          602                         continue;
          603                 }
          604 
          605                 /*
          606                  * As seen in getsel:
          607                  * Line endings are inconsistent in the terminal and GUI world
          608                  * copy and pasting. When receiving some selection data,
          609                  * replace all '\n' with '\r'.
          610                  * FIXME: Fix the computer world.
          611                  */
          612                 repl = data;
          613                 last = data + nitems * format / 8;
          614                 while ((repl = memchr(repl, '\n', last - repl))) {
          615                         *repl++ = '\r';
          616                 }
          617 
          618                 if (IS_SET(MODE_BRCKTPASTE) && ofs == 0)
          619                         ttywrite("\033[200~", 6, 0);
          620                 ttywrite((char *)data, nitems * format / 8, 1);
          621                 if (IS_SET(MODE_BRCKTPASTE) && rem == 0)
          622                         ttywrite("\033[201~", 6, 0);
          623                 XFree(data);
          624                 /* number of 32-bit chunks returned */
          625                 ofs += nitems * format / 32;
          626         } while (rem > 0);
          627 
          628         /*
          629          * Deleting the property again tells the selection owner to send the
          630          * next data chunk in the property.
          631          */
          632         XDeleteProperty(xw.dpy, xw.win, (int)property);
          633 }
          634 
          635 void
          636 xclipcopy(void)
          637 {
          638         clipcopy(NULL);
          639 }
          640 
          641 void
          642 selclear_(XEvent *e)
          643 {
          644         selclear();
          645 }
          646 
          647 void
          648 selrequest(XEvent *e)
          649 {
          650         XSelectionRequestEvent *xsre;
          651         XSelectionEvent xev;
          652         Atom xa_targets, string, clipboard;
          653         char *seltext;
          654 
          655         xsre = (XSelectionRequestEvent *) e;
          656         xev.type = SelectionNotify;
          657         xev.requestor = xsre->requestor;
          658         xev.selection = xsre->selection;
          659         xev.target = xsre->target;
          660         xev.time = xsre->time;
          661         if (xsre->property == None)
          662                 xsre->property = xsre->target;
          663 
          664         /* reject */
          665         xev.property = None;
          666 
          667         xa_targets = XInternAtom(xw.dpy, "TARGETS", 0);
          668         if (xsre->target == xa_targets) {
          669                 /* respond with the supported type */
          670                 string = xsel.xtarget;
          671                 XChangeProperty(xsre->display, xsre->requestor, xsre->property,
          672                                 XA_ATOM, 32, PropModeReplace,
          673                                 (uchar *) &string, 1);
          674                 xev.property = xsre->property;
          675         } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) {
          676                 /*
          677                  * xith XA_STRING non ascii characters may be incorrect in the
          678                  * requestor. It is not our problem, use utf8.
          679                  */
          680                 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
          681                 if (xsre->selection == XA_PRIMARY) {
          682                         seltext = xsel.primary;
          683                 } else if (xsre->selection == clipboard) {
          684                         seltext = xsel.clipboard;
          685                 } else {
          686                         fprintf(stderr,
          687                                 "Unhandled clipboard selection 0x%lx\n",
          688                                 xsre->selection);
          689                         return;
          690                 }
          691                 if (seltext != NULL) {
          692                         XChangeProperty(xsre->display, xsre->requestor,
          693                                         xsre->property, xsre->target,
          694                                         8, PropModeReplace,
          695                                         (uchar *)seltext, strlen(seltext));
          696                         xev.property = xsre->property;
          697                 }
          698         }
          699 
          700         /* all done, send a notification to the listener */
          701         if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev))
          702                 fprintf(stderr, "Error sending SelectionNotify event\n");
          703 }
          704 
          705 void
          706 setsel(char *str, Time t)
          707 {
          708         if (!str)
          709                 return;
          710 
          711         free(xsel.primary);
          712         xsel.primary = str;
          713 
          714         XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
          715         if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
          716                 selclear();
          717 }
          718 
          719 void
          720 xsetsel(char *str)
          721 {
          722         setsel(str, CurrentTime);
          723 }
          724 
          725 void
          726 brelease(XEvent *e)
          727 {
          728         int btn = e->xbutton.button;
          729 
          730         if (1 <= btn && btn <= 11)
          731                 buttons &= ~(1 << (btn-1));
          732 
          733         if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
          734                 mousereport(e);
          735                 return;
          736         }
          737 
          738         if (mouseaction(e, 1))
          739                 return;
          740         if (btn == Button1)
          741                 mousesel(e, 1);
          742 }
          743 
          744 void
          745 bmotion(XEvent *e)
          746 {
          747         if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) {
          748                 mousereport(e);
          749                 return;
          750         }
          751 
          752         mousesel(e, 0);
          753 }
          754 
          755 void
          756 cresize(int width, int height)
          757 {
          758         int col, row;
          759 
          760         if (width != 0)
          761                 win.w = width;
          762         if (height != 0)
          763                 win.h = height;
          764 
          765         col = (win.w - 2 * borderpx) / win.cw;
          766         row = (win.h - 2 * borderpx) / win.ch;
          767         col = MAX(1, col);
          768         row = MAX(1, row);
          769 
          770         tresize(col, row);
          771         xresize(col, row);
          772         ttyresize(win.tw, win.th);
          773 }
          774 
          775 void
          776 xresize(int col, int row)
          777 {
          778         win.tw = col * win.cw;
          779         win.th = row * win.ch;
          780 
          781         XFreePixmap(xw.dpy, xw.buf);
          782         xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
          783                         DefaultDepth(xw.dpy, xw.scr));
          784         XftDrawChange(xw.draw, xw.buf);
          785         xclear(0, 0, win.w, win.h);
          786 
          787         /* resize to new width */
          788         xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
          789 }
          790 
          791 ushort
          792 sixd_to_16bit(int x)
          793 {
          794         return x == 0 ? 0 : 0x3737 + 0x2828 * x;
          795 }
          796 
          797 int
          798 xloadcolor(int i, const char *name, Color *ncolor)
          799 {
          800         XRenderColor color = { .alpha = 0xffff };
          801 
          802         if (!name) {
          803                 if (BETWEEN(i, 16, 255)) { /* 256 color */
          804                         if (i < 6*6*6+16) { /* same colors as xterm */
          805                                 color.red   = sixd_to_16bit( ((i-16)/36)%6 );
          806                                 color.green = sixd_to_16bit( ((i-16)/6) %6 );
          807                                 color.blue  = sixd_to_16bit( ((i-16)/1) %6 );
          808                         } else { /* greyscale */
          809                                 color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16));
          810                                 color.green = color.blue = color.red;
          811                         }
          812                         return XftColorAllocValue(xw.dpy, xw.vis,
          813                                                   xw.cmap, &color, ncolor);
          814                 } else
          815                         if (invertcolors)
          816                                 name = altcolorname[i];
          817                         else
          818                                 name = colorname[i];
          819         }
          820 
          821         return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
          822 }
          823 
          824 void
          825 xloadcols(void)
          826 {
          827         int i;
          828         static int loaded;
          829         Color *cp;
          830 
          831         if (loaded) {
          832                 for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp)
          833                         XftColorFree(xw.dpy, xw.vis, xw.cmap, cp);
          834         } else {
          835                 dc.collen = MAX(LEN(colorname), 256);
          836                 dc.col = xmalloc(dc.collen * sizeof(Color));
          837         }
          838 
          839         for (i = 0; i < dc.collen; i++)
          840                 if (!xloadcolor(i, NULL, &dc.col[i])) {
          841                         if (colorname[i])
          842                                 die("could not allocate color '%s'\n", colorname[i]);
          843                         else
          844                                 die("could not allocate color %d\n", i);
          845                 }
          846         loaded = 1;
          847 }
          848 
          849 int
          850 xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b)
          851 {
          852         if (!BETWEEN(x, 0, dc.collen))
          853                 return 1;
          854 
          855         *r = dc.col[x].color.red >> 8;
          856         *g = dc.col[x].color.green >> 8;
          857         *b = dc.col[x].color.blue >> 8;
          858 
          859         return 0;
          860 }
          861 
          862 int
          863 xsetcolorname(int x, const char *name)
          864 {
          865         Color ncolor;
          866 
          867         if (!BETWEEN(x, 0, dc.collen))
          868                 return 1;
          869 
          870         if (!xloadcolor(x, name, &ncolor))
          871                 return 1;
          872 
          873         XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
          874         dc.col[x] = ncolor;
          875 
          876         return 0;
          877 }
          878 
          879 /*
          880  * Absolute coordinates.
          881  */
          882 void
          883 xclear(int x1, int y1, int x2, int y2)
          884 {
          885         Color c;
          886         c = dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg];
          887         /*if (invertcolors)
          888                 c = invertedcolor(&c);*/
          889         XftDrawRect(xw.draw, &c, x1, y1, x2-x1, y2-y1);
          890 }
          891 
          892 void
          893 xhints(void)
          894 {
          895         XClassHint class = {opt_name ? opt_name : termname,
          896                             opt_class ? opt_class : termname};
          897         XWMHints wm = {.flags = InputHint, .input = 1};
          898         XSizeHints *sizeh;
          899 
          900         sizeh = XAllocSizeHints();
          901 
          902         sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize;
          903         sizeh->height = win.h;
          904         sizeh->width = win.w;
          905         sizeh->height_inc = win.ch;
          906         sizeh->width_inc = win.cw;
          907         sizeh->base_height = 2 * borderpx;
          908         sizeh->base_width = 2 * borderpx;
          909         sizeh->min_height = win.ch + 2 * borderpx;
          910         sizeh->min_width = win.cw + 2 * borderpx;
          911         if (xw.isfixed) {
          912                 sizeh->flags |= PMaxSize;
          913                 sizeh->min_width = sizeh->max_width = win.w;
          914                 sizeh->min_height = sizeh->max_height = win.h;
          915         }
          916         if (xw.gm & (XValue|YValue)) {
          917                 sizeh->flags |= USPosition | PWinGravity;
          918                 sizeh->x = xw.l;
          919                 sizeh->y = xw.t;
          920                 sizeh->win_gravity = xgeommasktogravity(xw.gm);
          921         }
          922 
          923         XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm,
          924                         &class);
          925         XFree(sizeh);
          926 }
          927 
          928 int
          929 xgeommasktogravity(int mask)
          930 {
          931         switch (mask & (XNegative|YNegative)) {
          932         case 0:
          933                 return NorthWestGravity;
          934         case XNegative:
          935                 return NorthEastGravity;
          936         case YNegative:
          937                 return SouthWestGravity;
          938         }
          939 
          940         return SouthEastGravity;
          941 }
          942 
          943 int
          944 xloadfont(Font *f, FcPattern *pattern)
          945 {
          946         FcPattern *configured;
          947         FcPattern *match;
          948         FcResult result;
          949         XGlyphInfo extents;
          950         int wantattr, haveattr;
          951 
          952         /*
          953          * Manually configure instead of calling XftMatchFont
          954          * so that we can use the configured pattern for
          955          * "missing glyph" lookups.
          956          */
          957         configured = FcPatternDuplicate(pattern);
          958         if (!configured)
          959                 return 1;
          960 
          961         FcConfigSubstitute(NULL, configured, FcMatchPattern);
          962         XftDefaultSubstitute(xw.dpy, xw.scr, configured);
          963 
          964         match = FcFontMatch(NULL, configured, &result);
          965         if (!match) {
          966                 FcPatternDestroy(configured);
          967                 return 1;
          968         }
          969 
          970         if (!(f->match = XftFontOpenPattern(xw.dpy, match))) {
          971                 FcPatternDestroy(configured);
          972                 FcPatternDestroy(match);
          973                 return 1;
          974         }
          975 
          976         if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) ==
          977             XftResultMatch)) {
          978                 /*
          979                  * Check if xft was unable to find a font with the appropriate
          980                  * slant but gave us one anyway. Try to mitigate.
          981                  */
          982                 if ((XftPatternGetInteger(f->match->pattern, "slant", 0,
          983                     &haveattr) != XftResultMatch) || haveattr < wantattr) {
          984                         f->badslant = 1;
          985                         fputs("font slant does not match\n", stderr);
          986                 }
          987         }
          988 
          989         if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) ==
          990             XftResultMatch)) {
          991                 if ((XftPatternGetInteger(f->match->pattern, "weight", 0,
          992                     &haveattr) != XftResultMatch) || haveattr != wantattr) {
          993                         f->badweight = 1;
          994                         fputs("font weight does not match\n", stderr);
          995                 }
          996         }
          997 
          998         XftTextExtentsUtf8(xw.dpy, f->match,
          999                 (const FcChar8 *) ascii_printable,
         1000                 strlen(ascii_printable), &extents);
         1001 
         1002         f->set = NULL;
         1003         f->pattern = configured;
         1004 
         1005         f->ascent = f->match->ascent;
         1006         f->descent = f->match->descent;
         1007         f->lbearing = 0;
         1008         f->rbearing = f->match->max_advance_width;
         1009 
         1010         f->height = f->ascent + f->descent;
         1011         f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
         1012 
         1013         return 0;
         1014 }
         1015 
         1016 void
         1017 xloadfonts(const char *fontstr, double fontsize)
         1018 {
         1019         FcPattern *pattern;
         1020         double fontval;
         1021 
         1022         if (fontstr[0] == '-')
         1023                 pattern = XftXlfdParse(fontstr, False, False);
         1024         else
         1025                 pattern = FcNameParse((const FcChar8 *)fontstr);
         1026 
         1027         if (!pattern)
         1028                 die("can't open font %s\n", fontstr);
         1029 
         1030         if (fontsize > 1) {
         1031                 FcPatternDel(pattern, FC_PIXEL_SIZE);
         1032                 FcPatternDel(pattern, FC_SIZE);
         1033                 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize);
         1034                 usedfontsize = fontsize;
         1035         } else {
         1036                 if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) ==
         1037                                 FcResultMatch) {
         1038                         usedfontsize = fontval;
         1039                 } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) ==
         1040                                 FcResultMatch) {
         1041                         usedfontsize = -1;
         1042                 } else {
         1043                         /*
         1044                          * Default font size is 12, if none given. This is to
         1045                          * have a known usedfontsize value.
         1046                          */
         1047                         FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
         1048                         usedfontsize = 12;
         1049                 }
         1050                 defaultfontsize = usedfontsize;
         1051         }
         1052 
         1053         if (xloadfont(&dc.font, pattern))
         1054                 die("can't open font %s\n", fontstr);
         1055 
         1056         if (usedfontsize < 0) {
         1057                 FcPatternGetDouble(dc.font.match->pattern,
         1058                                    FC_PIXEL_SIZE, 0, &fontval);
         1059                 usedfontsize = fontval;
         1060                 if (fontsize == 0)
         1061                         defaultfontsize = fontval;
         1062         }
         1063 
         1064         /* Setting character width and height. */
         1065         win.cw = ceilf(dc.font.width * cwscale);
         1066         win.ch = ceilf(dc.font.height * chscale);
         1067 
         1068         FcPatternDel(pattern, FC_SLANT);
         1069         FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
         1070         if (xloadfont(&dc.ifont, pattern))
         1071                 die("can't open font %s\n", fontstr);
         1072 
         1073         FcPatternDel(pattern, FC_WEIGHT);
         1074         FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
         1075         if (xloadfont(&dc.ibfont, pattern))
         1076                 die("can't open font %s\n", fontstr);
         1077 
         1078         FcPatternDel(pattern, FC_SLANT);
         1079         FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
         1080         if (xloadfont(&dc.bfont, pattern))
         1081                 die("can't open font %s\n", fontstr);
         1082 
         1083         FcPatternDestroy(pattern);
         1084 }
         1085 
         1086 void
         1087 xunloadfont(Font *f)
         1088 {
         1089         XftFontClose(xw.dpy, f->match);
         1090         FcPatternDestroy(f->pattern);
         1091         if (f->set)
         1092                 FcFontSetDestroy(f->set);
         1093 }
         1094 
         1095 void
         1096 xunloadfonts(void)
         1097 {
         1098         /* Free the loaded fonts in the font cache.  */
         1099         while (frclen > 0)
         1100                 XftFontClose(xw.dpy, frc[--frclen].font);
         1101 
         1102         xunloadfont(&dc.font);
         1103         xunloadfont(&dc.bfont);
         1104         xunloadfont(&dc.ifont);
         1105         xunloadfont(&dc.ibfont);
         1106 }
         1107 
         1108 int
         1109 ximopen(Display *dpy)
         1110 {
         1111         XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy };
         1112         XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy };
         1113 
         1114         xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL);
         1115         if (xw.ime.xim == NULL)
         1116                 return 0;
         1117 
         1118         if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL))
         1119                 fprintf(stderr, "XSetIMValues: "
         1120                                 "Could not set XNDestroyCallback.\n");
         1121 
         1122         xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot,
         1123                                               NULL);
         1124 
         1125         if (xw.ime.xic == NULL) {
         1126                 xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle,
         1127                                        XIMPreeditNothing | XIMStatusNothing,
         1128                                        XNClientWindow, xw.win,
         1129                                        XNDestroyCallback, &icdestroy,
         1130                                        NULL);
         1131         }
         1132         if (xw.ime.xic == NULL)
         1133                 fprintf(stderr, "XCreateIC: Could not create input context.\n");
         1134 
         1135         return 1;
         1136 }
         1137 
         1138 void
         1139 ximinstantiate(Display *dpy, XPointer client, XPointer call)
         1140 {
         1141         if (ximopen(dpy))
         1142                 XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
         1143                                                  ximinstantiate, NULL);
         1144 }
         1145 
         1146 void
         1147 ximdestroy(XIM xim, XPointer client, XPointer call)
         1148 {
         1149         xw.ime.xim = NULL;
         1150         XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
         1151                                        ximinstantiate, NULL);
         1152         XFree(xw.ime.spotlist);
         1153 }
         1154 
         1155 int
         1156 xicdestroy(XIC xim, XPointer client, XPointer call)
         1157 {
         1158         xw.ime.xic = NULL;
         1159         return 1;
         1160 }
         1161 
         1162 void
         1163 xinit(int cols, int rows)
         1164 {
         1165         XGCValues gcvalues;
         1166         Cursor cursor;
         1167         Window parent;
         1168         pid_t thispid = getpid();
         1169         XColor xmousefg, xmousebg;
         1170 
         1171         if (!(xw.dpy = XOpenDisplay(NULL)))
         1172                 die("can't open display\n");
         1173         xw.scr = XDefaultScreen(xw.dpy);
         1174         xw.vis = XDefaultVisual(xw.dpy, xw.scr);
         1175 
         1176         /* font */
         1177         if (!FcInit())
         1178                 die("could not init fontconfig.\n");
         1179 
         1180         usedfont = (opt_font == NULL)? font : opt_font;
         1181         xloadfonts(usedfont, 0);
         1182 
         1183         /* colors */
         1184         xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
         1185         xloadcols();
         1186 
         1187         /* adjust fixed window geometry */
         1188         win.w = 2 * borderpx + cols * win.cw;
         1189         win.h = 2 * borderpx + rows * win.ch;
         1190         if (xw.gm & XNegative)
         1191                 xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
         1192         if (xw.gm & YNegative)
         1193                 xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2;
         1194 
         1195         /* Events */
         1196         xw.attrs.background_pixel = dc.col[defaultbg].pixel;
         1197         xw.attrs.border_pixel = dc.col[defaultbg].pixel;
         1198         xw.attrs.bit_gravity = NorthWestGravity;
         1199         xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask
         1200                 | ExposureMask | VisibilityChangeMask | StructureNotifyMask
         1201                 | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
         1202         xw.attrs.colormap = xw.cmap;
         1203 
         1204         if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
         1205                 parent = XRootWindow(xw.dpy, xw.scr);
         1206         xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t,
         1207                         win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
         1208                         xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity
         1209                         | CWEventMask | CWColormap, &xw.attrs);
         1210 
         1211         memset(&gcvalues, 0, sizeof(gcvalues));
         1212         gcvalues.graphics_exposures = False;
         1213         dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures,
         1214                         &gcvalues);
         1215         xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h,
         1216                         DefaultDepth(xw.dpy, xw.scr));
         1217         XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
         1218         XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
         1219 
         1220         /* font spec buffer */
         1221         xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
         1222 
         1223         /* Xft rendering context */
         1224         xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
         1225 
         1226         /* input methods */
         1227         if (!ximopen(xw.dpy)) {
         1228                 XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL,
         1229                                                ximinstantiate, NULL);
         1230         }
         1231 
         1232         /* white cursor, black outline */
         1233         cursor = XCreateFontCursor(xw.dpy, mouseshape);
         1234         XDefineCursor(xw.dpy, xw.win, cursor);
         1235 
         1236         if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
         1237                 xmousefg.red   = 0xffff;
         1238                 xmousefg.green = 0xffff;
         1239                 xmousefg.blue  = 0xffff;
         1240         }
         1241 
         1242         if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) {
         1243                 xmousebg.red   = 0x0000;
         1244                 xmousebg.green = 0x0000;
         1245                 xmousebg.blue  = 0x0000;
         1246         }
         1247 
         1248         XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
         1249 
         1250         xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
         1251         xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
         1252         xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False);
         1253         xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False);
         1254         XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1);
         1255 
         1256         xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
         1257         XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
         1258                         PropModeReplace, (uchar *)&thispid, 1);
         1259 
         1260         win.mode = MODE_NUMLOCK;
         1261         resettitle();
         1262         xhints();
         1263         XMapWindow(xw.dpy, xw.win);
         1264         XSync(xw.dpy, False);
         1265 
         1266         clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1);
         1267         clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2);
         1268         xsel.primary = NULL;
         1269         xsel.clipboard = NULL;
         1270         xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
         1271         if (xsel.xtarget == None)
         1272                 xsel.xtarget = XA_STRING;
         1273 }
         1274 
         1275 int
         1276 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
         1277 {
         1278         float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
         1279         ushort mode, prevmode = USHRT_MAX;
         1280         Font *font = &dc.font;
         1281         int frcflags = FRC_NORMAL;
         1282         float runewidth = win.cw;
         1283         Rune rune;
         1284         FT_UInt glyphidx;
         1285         FcResult fcres;
         1286         FcPattern *fcpattern, *fontpattern;
         1287         FcFontSet *fcsets[] = { NULL };
         1288         FcCharSet *fccharset;
         1289         int i, f, numspecs = 0;
         1290 
         1291         for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
         1292                 /* Fetch rune and mode for current glyph. */
         1293                 rune = glyphs[i].u;
         1294                 mode = glyphs[i].mode;
         1295 
         1296                 /* Skip dummy wide-character spacing. */
         1297                 if (mode == ATTR_WDUMMY)
         1298                         continue;
         1299 
         1300                 /* Determine font for glyph if different from previous glyph. */
         1301                 if (prevmode != mode) {
         1302                         prevmode = mode;
         1303                         font = &dc.font;
         1304                         frcflags = FRC_NORMAL;
         1305                         runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
         1306                         if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
         1307                                 font = &dc.ibfont;
         1308                                 frcflags = FRC_ITALICBOLD;
         1309                         } else if (mode & ATTR_ITALIC) {
         1310                                 font = &dc.ifont;
         1311                                 frcflags = FRC_ITALIC;
         1312                         } else if (mode & ATTR_BOLD) {
         1313                                 font = &dc.bfont;
         1314                                 frcflags = FRC_BOLD;
         1315                         }
         1316                         yp = winy + font->ascent;
         1317                 }
         1318 
         1319                 /* Lookup character index with default font. */
         1320                 glyphidx = XftCharIndex(xw.dpy, font->match, rune);
         1321                 if (glyphidx) {
         1322                         specs[numspecs].font = font->match;
         1323                         specs[numspecs].glyph = glyphidx;
         1324                         specs[numspecs].x = (short)xp;
         1325                         specs[numspecs].y = (short)yp;
         1326                         xp += runewidth;
         1327                         numspecs++;
         1328                         continue;
         1329                 }
         1330 
         1331                 /* Fallback on font cache, search the font cache for match. */
         1332                 for (f = 0; f < frclen; f++) {
         1333                         glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
         1334                         /* Everything correct. */
         1335                         if (glyphidx && frc[f].flags == frcflags)
         1336                                 break;
         1337                         /* We got a default font for a not found glyph. */
         1338                         if (!glyphidx && frc[f].flags == frcflags
         1339                                         && frc[f].unicodep == rune) {
         1340                                 break;
         1341                         }
         1342                 }
         1343 
         1344                 /* Nothing was found. Use fontconfig to find matching font. */
         1345                 if (f >= frclen) {
         1346                         if (!font->set)
         1347                                 font->set = FcFontSort(0, font->pattern,
         1348                                                        1, 0, &fcres);
         1349                         fcsets[0] = font->set;
         1350 
         1351                         /*
         1352                          * Nothing was found in the cache. Now use
         1353                          * some dozen of Fontconfig calls to get the
         1354                          * font for one single character.
         1355                          *
         1356                          * Xft and fontconfig are design failures.
         1357                          */
         1358                         fcpattern = FcPatternDuplicate(font->pattern);
         1359                         fccharset = FcCharSetCreate();
         1360 
         1361                         FcCharSetAddChar(fccharset, rune);
         1362                         FcPatternAddCharSet(fcpattern, FC_CHARSET,
         1363                                         fccharset);
         1364                         FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
         1365 
         1366                         FcConfigSubstitute(0, fcpattern,
         1367                                         FcMatchPattern);
         1368                         FcDefaultSubstitute(fcpattern);
         1369 
         1370                         fontpattern = FcFontSetMatch(0, fcsets, 1,
         1371                                         fcpattern, &fcres);
         1372 
         1373                         /* Allocate memory for the new cache entry. */
         1374                         if (frclen >= frccap) {
         1375                                 frccap += 16;
         1376                                 frc = xrealloc(frc, frccap * sizeof(Fontcache));
         1377                         }
         1378 
         1379                         frc[frclen].font = XftFontOpenPattern(xw.dpy,
         1380                                         fontpattern);
         1381                         if (!frc[frclen].font)
         1382                                 die("XftFontOpenPattern failed seeking fallback font: %s\n",
         1383                                         strerror(errno));
         1384                         frc[frclen].flags = frcflags;
         1385                         frc[frclen].unicodep = rune;
         1386 
         1387                         glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
         1388 
         1389                         f = frclen;
         1390                         frclen++;
         1391 
         1392                         FcPatternDestroy(fcpattern);
         1393                         FcCharSetDestroy(fccharset);
         1394                 }
         1395 
         1396                 specs[numspecs].font = frc[f].font;
         1397                 specs[numspecs].glyph = glyphidx;
         1398                 specs[numspecs].x = (short)xp;
         1399                 specs[numspecs].y = (short)yp;
         1400                 xp += runewidth;
         1401                 numspecs++;
         1402         }
         1403 
         1404         return numspecs;
         1405 }
         1406 
         1407 void
         1408 xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
         1409 {
         1410         int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
         1411         int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
         1412             width = charlen * win.cw;
         1413         Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
         1414         XRenderColor colfg, colbg;
         1415         XRectangle r;
         1416 
         1417         /* Fallback on color display for attributes not supported by the font */
         1418         if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
         1419                 if (dc.ibfont.badslant || dc.ibfont.badweight)
         1420                         base.fg = defaultattr;
         1421         } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) ||
         1422             (base.mode & ATTR_BOLD && dc.bfont.badweight)) {
         1423                 base.fg = defaultattr;
         1424         }
         1425 
         1426         if (IS_TRUECOL(base.fg)) {
         1427                 colfg.alpha = 0xffff;
         1428                 colfg.red = TRUERED(base.fg);
         1429                 colfg.green = TRUEGREEN(base.fg);
         1430                 colfg.blue = TRUEBLUE(base.fg);
         1431                 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg);
         1432                 fg = &truefg;
         1433         } else {
         1434                 fg = &dc.col[base.fg];
         1435         }
         1436 
         1437         if (IS_TRUECOL(base.bg)) {
         1438                 colbg.alpha = 0xffff;
         1439                 colbg.green = TRUEGREEN(base.bg);
         1440                 colbg.red = TRUERED(base.bg);
         1441                 colbg.blue = TRUEBLUE(base.bg);
         1442                 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg);
         1443                 bg = &truebg;
         1444         } else {
         1445                 bg = &dc.col[base.bg];
         1446         }
         1447 
         1448         /* Change basic system colors [0-7] to bright system colors [8-15] */
         1449         if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
         1450                 fg = &dc.col[base.fg + 8];
         1451 
         1452         if (IS_SET(MODE_REVERSE)) {
         1453                 if (fg == &dc.col[defaultfg]) {
         1454                         fg = &dc.col[defaultbg];
         1455                 } else {
         1456                         colfg.red = ~fg->color.red;
         1457                         colfg.green = ~fg->color.green;
         1458                         colfg.blue = ~fg->color.blue;
         1459                         colfg.alpha = fg->color.alpha;
         1460                         XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg,
         1461                                         &revfg);
         1462                         fg = &revfg;
         1463                 }
         1464 
         1465                 if (bg == &dc.col[defaultbg]) {
         1466                         bg = &dc.col[defaultfg];
         1467                 } else {
         1468                         colbg.red = ~bg->color.red;
         1469                         colbg.green = ~bg->color.green;
         1470                         colbg.blue = ~bg->color.blue;
         1471                         colbg.alpha = bg->color.alpha;
         1472                         XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg,
         1473                                         &revbg);
         1474                         bg = &revbg;
         1475                 }
         1476         }
         1477 
         1478         if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) {
         1479                 colfg.red = fg->color.red / 2;
         1480                 colfg.green = fg->color.green / 2;
         1481                 colfg.blue = fg->color.blue / 2;
         1482                 colfg.alpha = fg->color.alpha;
         1483                 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg);
         1484                 fg = &revfg;
         1485         }
         1486 
         1487         if (base.mode & ATTR_REVERSE) {
         1488                 temp = fg;
         1489                 fg = bg;
         1490                 bg = temp;
         1491         }
         1492 
         1493         if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK)
         1494                 fg = bg;
         1495 
         1496         if (base.mode & ATTR_INVISIBLE)
         1497                 fg = bg;
         1498 
         1499         /*if (invertcolors) {
         1500                 revfg = invertedcolor(fg);
         1501                 revbg = invertedcolor(bg);
         1502                 fg = &revfg;
         1503                 bg = &revbg;
         1504         }*/
         1505 
         1506         /* Intelligent cleaning up of the borders. */
         1507         if (x == 0) {
         1508                 xclear(0, (y == 0)? 0 : winy, borderpx,
         1509                         winy + win.ch +
         1510                         ((winy + win.ch >= borderpx + win.th)? win.h : 0));
         1511         }
         1512         if (winx + width >= borderpx + win.tw) {
         1513                 xclear(winx + width, (y == 0)? 0 : winy, win.w,
         1514                         ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch)));
         1515         }
         1516         if (y == 0)
         1517                 xclear(winx, 0, winx + width, borderpx);
         1518         if (winy + win.ch >= borderpx + win.th)
         1519                 xclear(winx, winy + win.ch, winx + width, win.h);
         1520 
         1521         /* Clean up the region we want to draw to. */
         1522         XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
         1523 
         1524         /* Set the clip region because Xft is sometimes dirty. */
         1525         r.x = 0;
         1526         r.y = 0;
         1527         r.height = win.ch;
         1528         r.width = width;
         1529         XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
         1530 
         1531         /* Render the glyphs. */
         1532         XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
         1533 
         1534         /* Render underline and strikethrough. */
         1535         if (base.mode & ATTR_UNDERLINE) {
         1536                 XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1,
         1537                                 width, 1);
         1538         }
         1539 
         1540         if (base.mode & ATTR_STRUCK) {
         1541                 XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3,
         1542                                 width, 1);
         1543         }
         1544 
         1545         /* Reset clip to none. */
         1546         XftDrawSetClip(xw.draw, 0);
         1547 }
         1548 
         1549 void
         1550 xdrawglyph(Glyph g, int x, int y)
         1551 {
         1552         int numspecs;
         1553         XftGlyphFontSpec spec;
         1554 
         1555         numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
         1556         xdrawglyphfontspecs(&spec, g, numspecs, x, y);
         1557 }
         1558 
         1559 void
         1560 xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
         1561 {
         1562         Color drawcol;
         1563 
         1564         /* remove the old cursor */
         1565         if (selected(ox, oy))
         1566                 og.mode ^= ATTR_REVERSE;
         1567         xdrawglyph(og, ox, oy);
         1568 
         1569         if (IS_SET(MODE_HIDE))
         1570                 return;
         1571 
         1572         /*
         1573          * Select the right color for the right mode.
         1574          */
         1575         g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE;
         1576 
         1577         if (IS_SET(MODE_REVERSE)) {
         1578                 g.mode |= ATTR_REVERSE;
         1579                 g.bg = defaultfg;
         1580                 if (selected(cx, cy)) {
         1581                         drawcol = dc.col[defaultcs];
         1582                         g.fg = defaultrcs;
         1583                 } else {
         1584                         drawcol = dc.col[defaultrcs];
         1585                         g.fg = defaultcs;
         1586                 }
         1587         } else {
         1588                 if (selected(cx, cy)) {
         1589                         g.fg = defaultfg;
         1590                         g.bg = defaultrcs;
         1591                 } else {
         1592                         g.fg = defaultbg;
         1593                         g.bg = defaultcs;
         1594                 }
         1595                 drawcol = dc.col[g.bg];
         1596         }
         1597 
         1598         /* draw the new one */
         1599         if (IS_SET(MODE_FOCUSED)) {
         1600                 switch (win.cursor) {
         1601                 case 7: /* st extension */
         1602                         g.u = 0x2603; /* snowman (U+2603) */
         1603                         /* FALLTHROUGH */
         1604                 case 0: /* Blinking Block */
         1605                 case 1: /* Blinking Block (Default) */
         1606                 case 2: /* Steady Block */
         1607                         xdrawglyph(g, cx, cy);
         1608                         break;
         1609                 case 3: /* Blinking Underline */
         1610                 case 4: /* Steady Underline */
         1611                         XftDrawRect(xw.draw, &drawcol,
         1612                                         borderpx + cx * win.cw,
         1613                                         borderpx + (cy + 1) * win.ch - \
         1614                                                 cursorthickness,
         1615                                         win.cw, cursorthickness);
         1616                         break;
         1617                 case 5: /* Blinking bar */
         1618                 case 6: /* Steady bar */
         1619                         XftDrawRect(xw.draw, &drawcol,
         1620                                         borderpx + cx * win.cw,
         1621                                         borderpx + cy * win.ch,
         1622                                         cursorthickness, win.ch);
         1623                         break;
         1624                 }
         1625         } else {
         1626                 XftDrawRect(xw.draw, &drawcol,
         1627                                 borderpx + cx * win.cw,
         1628                                 borderpx + cy * win.ch,
         1629                                 win.cw - 1, 1);
         1630                 XftDrawRect(xw.draw, &drawcol,
         1631                                 borderpx + cx * win.cw,
         1632                                 borderpx + cy * win.ch,
         1633                                 1, win.ch - 1);
         1634                 XftDrawRect(xw.draw, &drawcol,
         1635                                 borderpx + (cx + 1) * win.cw - 1,
         1636                                 borderpx + cy * win.ch,
         1637                                 1, win.ch - 1);
         1638                 XftDrawRect(xw.draw, &drawcol,
         1639                                 borderpx + cx * win.cw,
         1640                                 borderpx + (cy + 1) * win.ch - 1,
         1641                                 win.cw, 1);
         1642         }
         1643 }
         1644 
         1645 void
         1646 xsetenv(void)
         1647 {
         1648         char buf[sizeof(long) * 8 + 1];
         1649 
         1650         snprintf(buf, sizeof(buf), "%lu", xw.win);
         1651         setenv("WINDOWID", buf, 1);
         1652 }
         1653 
         1654 void
         1655 xseticontitle(char *p)
         1656 {
         1657         XTextProperty prop;
         1658         DEFAULT(p, opt_title);
         1659 
         1660         if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
         1661                                         &prop) != Success)
         1662                 return;
         1663         XSetWMIconName(xw.dpy, xw.win, &prop);
         1664         XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname);
         1665         XFree(prop.value);
         1666 }
         1667 
         1668 void
         1669 xsettitle(char *p)
         1670 {
         1671         XTextProperty prop;
         1672         DEFAULT(p, opt_title);
         1673 
         1674         if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle,
         1675                                         &prop) != Success)
         1676                 return;
         1677         XSetWMName(xw.dpy, xw.win, &prop);
         1678         XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname);
         1679         XFree(prop.value);
         1680 }
         1681 
         1682 int
         1683 xstartdraw(void)
         1684 {
         1685         return IS_SET(MODE_VISIBLE);
         1686 }
         1687 
         1688 void
         1689 xdrawline(Line line, int x1, int y1, int x2)
         1690 {
         1691         int i, x, ox, numspecs;
         1692         Glyph base, new;
         1693         XftGlyphFontSpec *specs = xw.specbuf;
         1694 
         1695         numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
         1696         i = ox = 0;
         1697         for (x = x1; x < x2 && i < numspecs; x++) {
         1698                 new = line[x];
         1699                 if (new.mode == ATTR_WDUMMY)
         1700                         continue;
         1701                 if (selected(x, y1))
         1702                         new.mode ^= ATTR_REVERSE;
         1703                 if (i > 0 && ATTRCMP(base, new)) {
         1704                         xdrawglyphfontspecs(specs, base, i, ox, y1);
         1705                         specs += i;
         1706                         numspecs -= i;
         1707                         i = 0;
         1708                 }
         1709                 if (i == 0) {
         1710                         ox = x;
         1711                         base = new;
         1712                 }
         1713                 i++;
         1714         }
         1715         if (i > 0)
         1716                 xdrawglyphfontspecs(specs, base, i, ox, y1);
         1717 }
         1718 
         1719 void
         1720 xfinishdraw(void)
         1721 {
         1722         XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w,
         1723                         win.h, 0, 0);
         1724         XSetForeground(xw.dpy, dc.gc,
         1725                         dc.col[IS_SET(MODE_REVERSE)?
         1726                                 defaultfg : defaultbg].pixel);
         1727 }
         1728 
         1729 void
         1730 xximspot(int x, int y)
         1731 {
         1732         if (xw.ime.xic == NULL)
         1733                 return;
         1734 
         1735         xw.ime.spot.x = borderpx + x * win.cw;
         1736         xw.ime.spot.y = borderpx + (y + 1) * win.ch;
         1737 
         1738         XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL);
         1739 }
         1740 
         1741 void
         1742 expose(XEvent *ev)
         1743 {
         1744         redraw();
         1745 }
         1746 
         1747 void
         1748 visibility(XEvent *ev)
         1749 {
         1750         XVisibilityEvent *e = &ev->xvisibility;
         1751 
         1752         MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE);
         1753 }
         1754 
         1755 void
         1756 unmap(XEvent *ev)
         1757 {
         1758         win.mode &= ~MODE_VISIBLE;
         1759 }
         1760 
         1761 void
         1762 xsetpointermotion(int set)
         1763 {
         1764         MODBIT(xw.attrs.event_mask, set, PointerMotionMask);
         1765         XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
         1766 }
         1767 
         1768 void
         1769 xsetmode(int set, unsigned int flags)
         1770 {
         1771         int mode = win.mode;
         1772         MODBIT(win.mode, set, flags);
         1773         if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE))
         1774                 redraw();
         1775 }
         1776 
         1777 int
         1778 xsetcursor(int cursor)
         1779 {
         1780         if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */
         1781                 return 1;
         1782         win.cursor = cursor;
         1783         return 0;
         1784 }
         1785 
         1786 void
         1787 xseturgency(int add)
         1788 {
         1789         XWMHints *h = XGetWMHints(xw.dpy, xw.win);
         1790 
         1791         MODBIT(h->flags, add, XUrgencyHint);
         1792         XSetWMHints(xw.dpy, xw.win, h);
         1793         XFree(h);
         1794 }
         1795 
         1796 void
         1797 xbell(void)
         1798 {
         1799         if (!(IS_SET(MODE_FOCUSED)))
         1800                 xseturgency(1);
         1801         if (bellvolume)
         1802                 XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL);
         1803 }
         1804 
         1805 void
         1806 focus(XEvent *ev)
         1807 {
         1808         XFocusChangeEvent *e = &ev->xfocus;
         1809 
         1810         if (e->mode == NotifyGrab)
         1811                 return;
         1812 
         1813         if (ev->type == FocusIn) {
         1814                 if (xw.ime.xic)
         1815                         XSetICFocus(xw.ime.xic);
         1816                 win.mode |= MODE_FOCUSED;
         1817                 xseturgency(0);
         1818                 if (IS_SET(MODE_FOCUS))
         1819                         ttywrite("\033[I", 3, 0);
         1820         } else {
         1821                 if (xw.ime.xic)
         1822                         XUnsetICFocus(xw.ime.xic);
         1823                 win.mode &= ~MODE_FOCUSED;
         1824                 if (IS_SET(MODE_FOCUS))
         1825                         ttywrite("\033[O", 3, 0);
         1826         }
         1827 }
         1828 
         1829 int
         1830 match(uint mask, uint state)
         1831 {
         1832         return mask == XK_ANY_MOD || mask == (state & ~ignoremod);
         1833 }
         1834 
         1835 char*
         1836 kmap(KeySym k, uint state)
         1837 {
         1838         Key *kp;
         1839         int i;
         1840 
         1841         /* Check for mapped keys out of X11 function keys. */
         1842         for (i = 0; i < LEN(mappedkeys); i++) {
         1843                 if (mappedkeys[i] == k)
         1844                         break;
         1845         }
         1846         if (i == LEN(mappedkeys)) {
         1847                 if ((k & 0xFFFF) < 0xFD00)
         1848                         return NULL;
         1849         }
         1850 
         1851         for (kp = key; kp < key + LEN(key); kp++) {
         1852                 if (kp->k != k)
         1853                         continue;
         1854 
         1855                 if (!match(kp->mask, state))
         1856                         continue;
         1857 
         1858                 if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0)
         1859                         continue;
         1860                 if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2)
         1861                         continue;
         1862 
         1863                 if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0)
         1864                         continue;
         1865 
         1866                 return kp->s;
         1867         }
         1868 
         1869         return NULL;
         1870 }
         1871 
         1872 void
         1873 kpress(XEvent *ev)
         1874 {
         1875         XKeyEvent *e = &ev->xkey;
         1876         KeySym ksym;
         1877         char buf[64], *customkey;
         1878         int len;
         1879         Rune c;
         1880         Status status;
         1881         Shortcut *bp;
         1882 
         1883         if (IS_SET(MODE_KBDLOCK))
         1884                 return;
         1885 
         1886         if (xw.ime.xic)
         1887                 len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
         1888         else
         1889                 len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
         1890         /* 1. shortcuts */
         1891         for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
         1892                 if (ksym == bp->keysym && match(bp->mod, e->state)) {
         1893                         bp->func(&(bp->arg));
         1894                         return;
         1895                 }
         1896         }
         1897 
         1898         /* 2. custom keys from config.h */
         1899         if ((customkey = kmap(ksym, e->state))) {
         1900                 ttywrite(customkey, strlen(customkey), 1);
         1901                 return;
         1902         }
         1903 
         1904         /* 3. composed string from input method */
         1905         if (len == 0)
         1906                 return;
         1907         if (len == 1 && e->state & Mod1Mask) {
         1908                 if (IS_SET(MODE_8BIT)) {
         1909                         if (*buf < 0177) {
         1910                                 c = *buf | 0x80;
         1911                                 len = utf8encode(c, buf);
         1912                         }
         1913                 } else {
         1914                         buf[1] = buf[0];
         1915                         buf[0] = '\033';
         1916                         len = 2;
         1917                 }
         1918         }
         1919         ttywrite(buf, len, 1);
         1920 }
         1921 
         1922 void
         1923 cmessage(XEvent *e)
         1924 {
         1925         /*
         1926          * See xembed specs
         1927          *  http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
         1928          */
         1929         if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) {
         1930                 if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) {
         1931                         win.mode |= MODE_FOCUSED;
         1932                         xseturgency(0);
         1933                 } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) {
         1934                         win.mode &= ~MODE_FOCUSED;
         1935                 }
         1936         } else if (e->xclient.data.l[0] == xw.wmdeletewin) {
         1937                 ttyhangup();
         1938                 exit(0);
         1939         }
         1940 }
         1941 
         1942 void
         1943 resize(XEvent *e)
         1944 {
         1945         if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
         1946                 return;
         1947 
         1948         cresize(e->xconfigure.width, e->xconfigure.height);
         1949 }
         1950 
         1951 void
         1952 run(void)
         1953 {
         1954         XEvent ev;
         1955         int w = win.w, h = win.h;
         1956         fd_set rfd;
         1957         int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing;
         1958         struct timespec seltv, *tv, now, lastblink, trigger;
         1959         double timeout;
         1960 
         1961         /* Waiting for window mapping */
         1962         do {
         1963                 XNextEvent(xw.dpy, &ev);
         1964                 /*
         1965                  * This XFilterEvent call is required because of XOpenIM. It
         1966                  * does filter out the key event and some client message for
         1967                  * the input method too.
         1968                  */
         1969                 if (XFilterEvent(&ev, None))
         1970                         continue;
         1971                 if (ev.type == ConfigureNotify) {
         1972                         w = ev.xconfigure.width;
         1973                         h = ev.xconfigure.height;
         1974                 }
         1975         } while (ev.type != MapNotify);
         1976 
         1977         ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd);
         1978         cresize(w, h);
         1979 
         1980         for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) {
         1981                 FD_ZERO(&rfd);
         1982                 FD_SET(ttyfd, &rfd);
         1983                 FD_SET(xfd, &rfd);
         1984 
         1985                 if (XPending(xw.dpy))
         1986                         timeout = 0;  /* existing events might not set xfd */
         1987 
         1988                 seltv.tv_sec = timeout / 1E3;
         1989                 seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec);
         1990                 tv = timeout >= 0 ? &seltv : NULL;
         1991 
         1992                 if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
         1993                         if (errno == EINTR)
         1994                                 continue;
         1995                         die("select failed: %s\n", strerror(errno));
         1996                 }
         1997                 clock_gettime(CLOCK_MONOTONIC, &now);
         1998 
         1999                 if (FD_ISSET(ttyfd, &rfd))
         2000                         ttyread();
         2001 
         2002                 xev = 0;
         2003                 while (XPending(xw.dpy)) {
         2004                         xev = 1;
         2005                         XNextEvent(xw.dpy, &ev);
         2006                         if (XFilterEvent(&ev, None))
         2007                                 continue;
         2008                         if (handler[ev.type])
         2009                                 (handler[ev.type])(&ev);
         2010                 }
         2011 
         2012                 /*
         2013                  * To reduce flicker and tearing, when new content or event
         2014                  * triggers drawing, we first wait a bit to ensure we got
         2015                  * everything, and if nothing new arrives - we draw.
         2016                  * We start with trying to wait minlatency ms. If more content
         2017                  * arrives sooner, we retry with shorter and shorter periods,
         2018                  * and eventually draw even without idle after maxlatency ms.
         2019                  * Typically this results in low latency while interacting,
         2020                  * maximum latency intervals during `cat huge.txt`, and perfect
         2021                  * sync with periodic updates from animations/key-repeats/etc.
         2022                  */
         2023                 if (FD_ISSET(ttyfd, &rfd) || xev) {
         2024                         if (!drawing) {
         2025                                 trigger = now;
         2026                                 drawing = 1;
         2027                         }
         2028                         timeout = (maxlatency - TIMEDIFF(now, trigger)) \
         2029                                   / maxlatency * minlatency;
         2030                         if (timeout > 0)
         2031                                 continue;  /* we have time, try to find idle */
         2032                 }
         2033 
         2034                 /* idle detected or maxlatency exhausted -> draw */
         2035                 timeout = -1;
         2036                 if (blinktimeout && tattrset(ATTR_BLINK)) {
         2037                         timeout = blinktimeout - TIMEDIFF(now, lastblink);
         2038                         if (timeout <= 0) {
         2039                                 if (-timeout > blinktimeout) /* start visible */
         2040                                         win.mode |= MODE_BLINK;
         2041                                 win.mode ^= MODE_BLINK;
         2042                                 tsetdirtattr(ATTR_BLINK);
         2043                                 lastblink = now;
         2044                                 timeout = blinktimeout;
         2045                         }
         2046                 }
         2047 
         2048                 draw();
         2049                 XFlush(xw.dpy);
         2050                 drawing = 0;
         2051         }
         2052 }
         2053 
         2054 void
         2055 usage(void)
         2056 {
         2057         die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]"
         2058             " [-n name] [-o file]\n"
         2059             "          [-T title] [-t title] [-w windowid]"
         2060             " [[-e] command [args ...]]\n"
         2061             "       %s [-aiv] [-c class] [-f font] [-g geometry]"
         2062             " [-n name] [-o file]\n"
         2063             "          [-T title] [-t title] [-w windowid] -l line"
         2064             " [stty_args ...]\n", argv0, argv0);
         2065 }
         2066 
         2067 int
         2068 main(int argc, char *argv[])
         2069 {
         2070         xw.l = xw.t = 0;
         2071         xw.isfixed = False;
         2072         xsetcursor(cursorshape);
         2073 
         2074         ARGBEGIN {
         2075         case 'a':
         2076                 allowaltscreen = 0;
         2077                 break;
         2078         case 'c':
         2079                 opt_class = EARGF(usage());
         2080                 break;
         2081         case 'e':
         2082                 if (argc > 0)
         2083                         --argc, ++argv;
         2084                 goto run;
         2085         case 'f':
         2086                 opt_font = EARGF(usage());
         2087                 break;
         2088         case 'g':
         2089                 xw.gm = XParseGeometry(EARGF(usage()),
         2090                                 &xw.l, &xw.t, &cols, &rows);
         2091                 break;
         2092         case 'i':
         2093                 xw.isfixed = 1;
         2094                 break;
         2095         case 'o':
         2096                 opt_io = EARGF(usage());
         2097                 break;
         2098         case 'l':
         2099                 opt_line = EARGF(usage());
         2100                 break;
         2101         case 'n':
         2102                 opt_name = EARGF(usage());
         2103                 break;
         2104         case 't':
         2105         case 'T':
         2106                 opt_title = EARGF(usage());
         2107                 break;
         2108         case 'w':
         2109                 opt_embed = EARGF(usage());
         2110                 break;
         2111         case 'v':
         2112                 die("%s " VERSION "\n", argv0);
         2113                 break;
         2114         default:
         2115                 usage();
         2116         } ARGEND;
         2117 
         2118 run:
         2119         if (argc > 0) /* eat all remaining arguments */
         2120                 opt_cmd = argv;
         2121 
         2122         if (!opt_title)
         2123                 opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0];
         2124 
         2125         setlocale(LC_CTYPE, "");
         2126         XSetLocaleModifiers("");
         2127         cols = MAX(cols, 1);
         2128         rows = MAX(rows, 1);
         2129         tnew(cols, rows);
         2130         xinit(cols, rows);
         2131         xsetenv();
         2132         selinit();
         2133         run();
         2134 
         2135         return 0;
         2136 }