Advertisement
Guest User

Untitled

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