st

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