1 /* See LICENSE for license details. */ 
   7 #include <sys/select.h> 
  11 #include <X11/Xatom.h> 
  13 #include <X11/cursorfont.h> 
  14 #include <X11/keysym.h> 
  15 #include <X11/Xft/Xft.h> 
  16 #include <X11/XKBlib.h> 
  23 /* types used in config.h */ 
  27         void (*func
)(const Arg 
*); 
  34         void (*func
)(const Arg 
*); 
  43         /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ 
  44         signed char appkey
;    /* application keypad */ 
  45         signed char appcursor
; /* application cursor */ 
  49 #define XK_ANY_MOD    UINT_MAX 
  51 #define XK_SWITCH_MOD (1<<13|1<<14) 
  53 /* function definitions used in config.h */ 
  54 static void clipcopy(const Arg 
*); 
  55 static void clippaste(const Arg 
*); 
  56 static void numlock(const Arg 
*); 
  57 static void selpaste(const Arg 
*); 
  58 static void zoom(const Arg 
*); 
  59 static void zoomabs(const Arg 
*); 
  60 static void zoomreset(const Arg 
*); 
  61 static void ttysend(const Arg 
*); 
  63 /* config.h for applying patches and the configuration. */ 
  67 #define XEMBED_FOCUS_IN  4 
  68 #define XEMBED_FOCUS_OUT 5 
  71 #define IS_SET(flag)            ((win.mode & (flag)) != 0) 
  72 #define TRUERED(x)              (((x) & 0xff0000) >> 8) 
  73 #define TRUEGREEN(x)            (((x) & 0xff00)) 
  74 #define TRUEBLUE(x)             (((x) & 0xff) << 8) 
  76 typedef XftDraw 
*Draw
; 
  77 typedef XftColor Color
; 
  78 typedef XftGlyphFontSpec GlyphFontSpec
; 
  80 /* Purely graphic info */ 
  82         int tw
, th
; /* tty width and height */ 
  83         int w
, h
; /* window width and height */ 
  84         int hborderpx
, vborderpx
; 
  85         int ch
; /* char height */ 
  86         int cw
; /* char width  */ 
  87         int mode
; /* window state/mode flags */ 
  88         int cursor
; /* cursor style */ 
  96         GlyphFontSpec 
*specbuf
; /* font spec buffer used for rendering */ 
  97         Atom xembed
, wmdeletewin
, netwmname
, netwmiconname
, netwmpid
; 
 102                 XVaNestedList spotlist
; 
 106         XSetWindowAttributes attrs
; 
 108         int isfixed
; /* is fixed geometry? */ 
 109         int depth
; /* bit depth */ 
 110         int l
, t
; /* left and top offset */ 
 111         int gm
; /* geometry mask */ 
 116         char *primary
, *clipboard
; 
 117         struct timespec tclick1
; 
 118         struct timespec tclick2
; 
 137 /* Drawing Context */ 
 141         Font font
, bfont
, ifont
, ibfont
; 
 145 static inline ushort 
sixd_to_16bit(int); 
 146 static int xmakeglyphfontspecs(XftGlyphFontSpec 
*, const Glyph 
*, int, int, int); 
 147 static void xdrawglyphfontspecs(const XftGlyphFontSpec 
*, Glyph
, int, int, int); 
 148 static void xdrawglyph(Glyph
, int, int); 
 149 static void xclear(int, int, int, int); 
 150 static int xgeommasktogravity(int); 
 151 static int ximopen(Display 
*); 
 152 static void ximinstantiate(Display 
*, XPointer
, XPointer
); 
 153 static void ximdestroy(XIM
, XPointer
, XPointer
); 
 154 static int xicdestroy(XIC
, XPointer
, XPointer
); 
 155 static void xinit(int, int); 
 156 static void cresize(int, int); 
 157 static void xresize(int, int); 
 158 static void xhints(void); 
 159 static int xloadcolor(int, const char *, Color 
*); 
 160 static int xloadfont(Font 
*, FcPattern 
*); 
 161 static void xloadfonts(const char *, double); 
 162 static void xunloadfont(Font 
*); 
 163 static void xunloadfonts(void); 
 164 static void xsetenv(void); 
 165 static void xseturgency(int); 
 166 static int evcol(XEvent 
*); 
 167 static int evrow(XEvent 
*); 
 169 static void expose(XEvent 
*); 
 170 static void visibility(XEvent 
*); 
 171 static void unmap(XEvent 
*); 
 172 static void kpress(XEvent 
*); 
 173 static void cmessage(XEvent 
*); 
 174 static void resize(XEvent 
*); 
 175 static void focus(XEvent 
*); 
 176 static uint 
buttonmask(uint
); 
 177 static int mouseaction(XEvent 
*, uint
); 
 178 static void brelease(XEvent 
*); 
 179 static void bpress(XEvent 
*); 
 180 static void bmotion(XEvent 
*); 
 181 static void propnotify(XEvent 
*); 
 182 static void selnotify(XEvent 
*); 
 183 static void selclear_(XEvent 
*); 
 184 static void selrequest(XEvent 
*); 
 185 static void setsel(char *, Time
); 
 186 static void mousesel(XEvent 
*, int); 
 187 static void mousereport(XEvent 
*); 
 188 static char *kmap(KeySym
, uint
); 
 189 static int match(uint
, uint
); 
 191 static void run(void); 
 192 static void usage(void); 
 194 static void (*handler
[LASTEvent
])(XEvent 
*) = { 
 196         [ClientMessage
] = cmessage
, 
 197         [ConfigureNotify
] = resize
, 
 198         [VisibilityNotify
] = visibility
, 
 199         [UnmapNotify
] = unmap
, 
 203         [MotionNotify
] = bmotion
, 
 204         [ButtonPress
] = bpress
, 
 205         [ButtonRelease
] = brelease
, 
 207  * Uncomment if you want the selection to disappear when you select something 
 208  * different in another window. 
 210 /*      [SelectionClear] = selclear_, */ 
 211         [SelectionNotify
] = selnotify
, 
 213  * PropertyNotify is only turned on when there is some INCR transfer happening 
 214  * for the selection retrieval. 
 216         [PropertyNotify
] = propnotify
, 
 217         [SelectionRequest
] = selrequest
, 
 223 static XSelection xsel
; 
 224 static TermWindow win
; 
 226 /* Font Ring Cache */ 
 240 /* Fontcache is an array now. A new font will be appended to the array. */ 
 241 static Fontcache 
*frc 
= NULL
; 
 242 static int frclen 
= 0; 
 243 static int frccap 
= 0; 
 244 static char *usedfont 
= NULL
; 
 245 static double usedfontsize 
= 0; 
 246 static double defaultfontsize 
= 0; 
 248 static char *opt_alpha 
= NULL
; 
 249 static char *opt_class 
= NULL
; 
 250 static char **opt_cmd  
= NULL
; 
 251 static char *opt_embed 
= NULL
; 
 252 static char *opt_font  
= NULL
; 
 253 static char *opt_io    
= NULL
; 
 254 static char *opt_line  
= NULL
; 
 255 static char *opt_name  
= NULL
; 
 256 static char *opt_title 
= NULL
; 
 258 static uint buttons
; /* bit field of pressed buttons */ 
 261 clipcopy(const Arg 
*dummy
) 
 265         free(xsel
.clipboard
); 
 266         xsel
.clipboard 
= NULL
; 
 268         if (xsel
.primary 
!= NULL
) { 
 269                 xsel
.clipboard 
= xstrdup(xsel
.primary
); 
 270                 clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
 271                 XSetSelectionOwner(xw
.dpy
, clipboard
, xw
.win
, CurrentTime
); 
 276 clippaste(const Arg 
*dummy
) 
 280         clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
 281         XConvertSelection(xw
.dpy
, clipboard
, xsel
.xtarget
, clipboard
, 
 282                         xw
.win
, CurrentTime
); 
 286 selpaste(const Arg 
*dummy
) 
 288         XConvertSelection(xw
.dpy
, XA_PRIMARY
, xsel
.xtarget
, XA_PRIMARY
, 
 289                         xw
.win
, CurrentTime
); 
 293 numlock(const Arg 
*dummy
) 
 295         win
.mode 
^= MODE_NUMLOCK
; 
 303         larg
.f 
= usedfontsize 
+ arg
->f
; 
 308 zoomabs(const Arg 
*arg
) 
 311         xloadfonts(usedfont
, arg
->f
); 
 318 zoomreset(const Arg 
*arg
) 
 322         if (defaultfontsize 
> 0) { 
 323                 larg
.f 
= defaultfontsize
; 
 329 ttysend(const Arg 
*arg
) 
 331         ttywrite(arg
->s
, strlen(arg
->s
), 1); 
 337         int x 
= e
->xbutton
.x 
- win
.hborderpx
; 
 338         LIMIT(x
, 0, win
.tw 
- 1); 
 345         int y 
= e
->xbutton
.y 
- win
.vborderpx
; 
 346         LIMIT(y
, 0, win
.th 
- 1); 
 351 mousesel(XEvent 
*e
, int done
) 
 353         int type
, seltype 
= SEL_REGULAR
; 
 354         uint state 
= e
->xbutton
.state 
& ~(Button1Mask 
| forcemousemod
); 
 356         for (type 
= 1; type 
< LEN(selmasks
); ++type
) { 
 357                 if (match(selmasks
[type
], state
)) { 
 362         selextend(evcol(e
), evrow(e
), seltype
, done
); 
 364                 setsel(getsel(), e
->xbutton
.time
); 
 368 mousereport(XEvent 
*e
) 
 371         int x 
= evcol(e
), y 
= evrow(e
); 
 372         int state 
= e
->xbutton
.state
; 
 376         if (e
->type 
== MotionNotify
) { 
 377                 if (x 
== ox 
&& y 
== oy
) 
 379                 if (!IS_SET(MODE_MOUSEMOTION
) && !IS_SET(MODE_MOUSEMANY
)) 
 381                 /* MODE_MOUSEMOTION: no reporting if no button is pressed */ 
 382                 if (IS_SET(MODE_MOUSEMOTION
) && buttons 
== 0) 
 384                 /* Set btn to lowest-numbered pressed button, or 12 if no 
 385                  * buttons are pressed. */ 
 386                 for (btn 
= 1; btn 
<= 11 && !(buttons 
& (1<<(btn
-1))); btn
++) 
 390                 btn 
= e
->xbutton
.button
; 
 391                 /* Only buttons 1 through 11 can be encoded */ 
 392                 if (btn 
< 1 || btn 
> 11) 
 394                 if (e
->type 
== ButtonRelease
) { 
 395                         /* MODE_MOUSEX10: no button release reporting */ 
 396                         if (IS_SET(MODE_MOUSEX10
)) 
 398                         /* Don't send release events for the scroll wheel */ 
 399                         if (btn 
== 4 || btn 
== 5) 
 408         /* Encode btn into code. If no button is pressed for a motion event in 
 409          * MODE_MOUSEMANY, then encode it as a release. */ 
 410         if ((!IS_SET(MODE_MOUSESGR
) && e
->type 
== ButtonRelease
) || btn 
== 12) 
 413                 code 
+= 128 + btn 
- 8; 
 415                 code 
+= 64 + btn 
- 4; 
 419         if (!IS_SET(MODE_MOUSEX10
)) { 
 420                 code 
+= ((state 
& ShiftMask  
) ?  4 : 0) 
 421                       + ((state 
& Mod1Mask   
) ?  8 : 0) /* meta key: alt */ 
 422                       + ((state 
& ControlMask
) ? 16 : 0); 
 425         if (IS_SET(MODE_MOUSESGR
)) { 
 426                 len 
= snprintf(buf
, sizeof(buf
), "\033[<%d;%d;%d%c", 
 428                                 e
->type 
== ButtonRelease 
? 'm' : 'M'); 
 429         } else if (x 
< 223 && y 
< 223) { 
 430                 len 
= snprintf(buf
, sizeof(buf
), "\033[M%c%c%c", 
 431                                 32+code
, 32+x
+1, 32+y
+1); 
 436         ttywrite(buf
, len
, 0); 
 440 buttonmask(uint button
) 
 442         return button 
== Button1 
? Button1Mask
 
 443              : button 
== Button2 
? Button2Mask
 
 444              : button 
== Button3 
? Button3Mask
 
 445              : button 
== Button4 
? Button4Mask
 
 446              : button 
== Button5 
? Button5Mask
 
 451 mouseaction(XEvent 
*e
, uint release
) 
 455         /* ignore Button<N>mask for Button<N> - it's set on release */ 
 456         uint state 
= e
->xbutton
.state 
& ~buttonmask(e
->xbutton
.button
); 
 458         for (ms 
= mshortcuts
; ms 
< mshortcuts 
+ LEN(mshortcuts
); ms
++) { 
 459                 if (ms
->release 
== release 
&& 
 460                     ms
->button 
== e
->xbutton
.button 
&& 
 461                     (match(ms
->mod
, state
) ||  /* exact or forced */ 
 462                      match(ms
->mod
, state 
& ~forcemousemod
))) { 
 463                         ms
->func(&(ms
->arg
)); 
 474         int btn 
= e
->xbutton
.button
; 
 478         if (1 <= btn 
&& btn 
<= 11) 
 479                 buttons 
|= 1 << (btn
-1); 
 481         if (IS_SET(MODE_MOUSE
) && !(e
->xbutton
.state 
& forcemousemod
)) { 
 486         if (mouseaction(e
, 0)) 
 489         if (btn 
== Button1
) { 
 491                  * If the user clicks below predefined timeouts specific 
 492                  * snapping behaviour is exposed. 
 494                 clock_gettime(CLOCK_MONOTONIC
, &now
); 
 495                 if (TIMEDIFF(now
, xsel
.tclick2
) <= tripleclicktimeout
) { 
 497                 } else if (TIMEDIFF(now
, xsel
.tclick1
) <= doubleclicktimeout
) { 
 502                 xsel
.tclick2 
= xsel
.tclick1
; 
 505                 selstart(evcol(e
), evrow(e
), snap
); 
 510 propnotify(XEvent 
*e
) 
 512         XPropertyEvent 
*xpev
; 
 513         Atom clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
 515         xpev 
= &e
->xproperty
; 
 516         if (xpev
->state 
== PropertyNewValue 
&& 
 517                         (xpev
->atom 
== XA_PRIMARY 
|| 
 518                          xpev
->atom 
== clipboard
)) { 
 526         ulong nitems
, ofs
, rem
; 
 528         uchar 
*data
, *last
, *repl
; 
 529         Atom type
, incratom
, property 
= None
; 
 531         incratom 
= XInternAtom(xw
.dpy
, "INCR", 0); 
 534         if (e
->type 
== SelectionNotify
) 
 535                 property 
= e
->xselection
.property
; 
 536         else if (e
->type 
== PropertyNotify
) 
 537                 property 
= e
->xproperty
.atom
; 
 539         if (property 
== None
) 
 543                 if (XGetWindowProperty(xw
.dpy
, xw
.win
, property
, ofs
, 
 544                                         BUFSIZ
/4, False
, AnyPropertyType
, 
 545                                         &type
, &format
, &nitems
, &rem
, 
 547                         fprintf(stderr
, "Clipboard allocation failed\n"); 
 551                 if (e
->type 
== PropertyNotify 
&& nitems 
== 0 && rem 
== 0) { 
 553                          * If there is some PropertyNotify with no data, then 
 554                          * this is the signal of the selection owner that all 
 555                          * data has been transferred. We won't need to receive 
 556                          * PropertyNotify events anymore. 
 558                         MODBIT(xw
.attrs
.event_mask
, 0, PropertyChangeMask
); 
 559                         XChangeWindowAttributes(xw
.dpy
, xw
.win
, CWEventMask
, 
 563                 if (type 
== incratom
) { 
 565                          * Activate the PropertyNotify events so we receive 
 566                          * when the selection owner does send us the next 
 569                         MODBIT(xw
.attrs
.event_mask
, 1, PropertyChangeMask
); 
 570                         XChangeWindowAttributes(xw
.dpy
, xw
.win
, CWEventMask
, 
 574                          * Deleting the property is the transfer start signal. 
 576                         XDeleteProperty(xw
.dpy
, xw
.win
, (int)property
); 
 582                  * Line endings are inconsistent in the terminal and GUI world 
 583                  * copy and pasting. When receiving some selection data, 
 584                  * replace all '\n' with '\r'. 
 585                  * FIXME: Fix the computer world. 
 588                 last 
= data 
+ nitems 
* format 
/ 8; 
 589                 while ((repl 
= memchr(repl
, '\n', last 
- repl
))) { 
 593                 if (IS_SET(MODE_BRCKTPASTE
) && ofs 
== 0) 
 594                         ttywrite("\033[200~", 6, 0); 
 595                 ttywrite((char *)data
, nitems 
* format 
/ 8, 1); 
 596                 if (IS_SET(MODE_BRCKTPASTE
) && rem 
== 0) 
 597                         ttywrite("\033[201~", 6, 0); 
 599                 /* number of 32-bit chunks returned */ 
 600                 ofs 
+= nitems 
* format 
/ 32; 
 604          * Deleting the property again tells the selection owner to send the 
 605          * next data chunk in the property. 
 607         XDeleteProperty(xw
.dpy
, xw
.win
, (int)property
); 
 623 selrequest(XEvent 
*e
) 
 625         XSelectionRequestEvent 
*xsre
; 
 627         Atom xa_targets
, string
, clipboard
; 
 630         xsre 
= (XSelectionRequestEvent 
*) e
; 
 631         xev
.type 
= SelectionNotify
; 
 632         xev
.requestor 
= xsre
->requestor
; 
 633         xev
.selection 
= xsre
->selection
; 
 634         xev
.target 
= xsre
->target
; 
 635         xev
.time 
= xsre
->time
; 
 636         if (xsre
->property 
== None
) 
 637                 xsre
->property 
= xsre
->target
; 
 642         xa_targets 
= XInternAtom(xw
.dpy
, "TARGETS", 0); 
 643         if (xsre
->target 
== xa_targets
) { 
 644                 /* respond with the supported type */ 
 645                 string 
= xsel
.xtarget
; 
 646                 XChangeProperty(xsre
->display
, xsre
->requestor
, xsre
->property
, 
 647                                 XA_ATOM
, 32, PropModeReplace
, 
 648                                 (uchar 
*) &string
, 1); 
 649                 xev
.property 
= xsre
->property
; 
 650         } else if (xsre
->target 
== xsel
.xtarget 
|| xsre
->target 
== XA_STRING
) { 
 652                  * xith XA_STRING non ascii characters may be incorrect in the 
 653                  * requestor. It is not our problem, use utf8. 
 655                 clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
 656                 if (xsre
->selection 
== XA_PRIMARY
) { 
 657                         seltext 
= xsel
.primary
; 
 658                 } else if (xsre
->selection 
== clipboard
) { 
 659                         seltext 
= xsel
.clipboard
; 
 662                                 "Unhandled clipboard selection 0x%lx\n", 
 666                 if (seltext 
!= NULL
) { 
 667                         XChangeProperty(xsre
->display
, xsre
->requestor
, 
 668                                         xsre
->property
, xsre
->target
, 
 670                                         (uchar 
*)seltext
, strlen(seltext
)); 
 671                         xev
.property 
= xsre
->property
; 
 675         /* all done, send a notification to the listener */ 
 676         if (!XSendEvent(xsre
->display
, xsre
->requestor
, 1, 0, (XEvent 
*) &xev
)) 
 677                 fprintf(stderr
, "Error sending SelectionNotify event\n"); 
 681 setsel(char *str
, Time t
) 
 689         XSetSelectionOwner(xw
.dpy
, XA_PRIMARY
, xw
.win
, t
); 
 690         if (XGetSelectionOwner(xw
.dpy
, XA_PRIMARY
) != xw
.win
) 
 697         setsel(str
, CurrentTime
); 
 703         int btn 
= e
->xbutton
.button
; 
 705         if (1 <= btn 
&& btn 
<= 11) 
 706                 buttons 
&= ~(1 << (btn
-1)); 
 708         if (IS_SET(MODE_MOUSE
) && !(e
->xbutton
.state 
& forcemousemod
)) { 
 713         if (mouseaction(e
, 1)) 
 722         if (IS_SET(MODE_MOUSE
) && !(e
->xbutton
.state 
& forcemousemod
)) { 
 731 cresize(int width
, int height
) 
 740         col 
= (win
.w 
- 2 * borderpx
) / win
.cw
; 
 741         row 
= (win
.h 
- 2 * borderpx
) / win
.ch
; 
 745         win
.hborderpx 
= (win
.w 
- col 
* win
.cw
) / 2; 
 746         win
.vborderpx 
= (win
.h 
- row 
* win
.ch
) / 2; 
 750         ttyresize(win
.tw
, win
.th
); 
 754 xresize(int col
, int row
) 
 756         win
.tw 
= col 
* win
.cw
; 
 757         win
.th 
= row 
* win
.ch
; 
 759         XFreePixmap(xw
.dpy
, xw
.buf
); 
 760         xw
.buf 
= XCreatePixmap(xw
.dpy
, xw
.win
, win
.w
, win
.h
, 
 762         XftDrawChange(xw
.draw
, xw
.buf
); 
 763         xclear(0, 0, win
.w
, win
.h
); 
 765         /* resize to new width */ 
 766         xw
.specbuf 
= xrealloc(xw
.specbuf
, col 
* sizeof(GlyphFontSpec
)); 
 772         return x 
== 0 ? 0 : 0x3737 + 0x2828 * x
; 
 776 xloadcolor(int i
, const char *name
, Color 
*ncolor
) 
 778         XRenderColor color 
= { .alpha 
= 0xffff }; 
 781                 if (BETWEEN(i
, 16, 255)) { /* 256 color */ 
 782                         if (i 
< 6*6*6+16) { /* same colors as xterm */ 
 783                                 color
.red   
= sixd_to_16bit( ((i
-16)/36)%6 ); 
 784                                 color
.green 
= sixd_to_16bit( ((i
-16)/6) %6 ); 
 785                                 color
.blue  
= sixd_to_16bit( ((i
-16)/1) %6 ); 
 786                         } else { /* greyscale */ 
 787                                 color
.red 
= 0x0808 + 0x0a0a * (i 
- (6*6*6+16)); 
 788                                 color
.green 
= color
.blue 
= color
.red
; 
 790                         return XftColorAllocValue(xw
.dpy
, xw
.vis
, 
 791                                                   xw
.cmap
, &color
, ncolor
); 
 796         return XftColorAllocName(xw
.dpy
, xw
.vis
, xw
.cmap
, name
, ncolor
); 
 807                 for (cp 
= dc
.col
; cp 
< &dc
.col
[dc
.collen
]; ++cp
) 
 808                         XftColorFree(xw
.dpy
, xw
.vis
, xw
.cmap
, cp
); 
 810                 dc
.collen 
= MAX(LEN(colorname
), 256); 
 811                 dc
.col 
= xmalloc(dc
.collen 
* sizeof(Color
)); 
 814         for (i 
= 0; i 
< dc
.collen
; i
++) 
 815                 if (!xloadcolor(i
, NULL
, &dc
.col
[i
])) { 
 817                                 die("could not allocate color '%s'\n", colorname
[i
]); 
 819                                 die("could not allocate color %d\n", i
); 
 822         /* set alpha value of bg color */ 
 824                 alpha 
= strtof(opt_alpha
, NULL
); 
 825         dc
.col
[defaultbg
].color
.alpha 
= (unsigned short)(0xffff * alpha
); 
 826         dc
.col
[defaultbg
].pixel 
&= 0x00FFFFFF; 
 827         dc
.col
[defaultbg
].pixel 
|= (unsigned char)(0xff * alpha
) << 24; 
 832 xgetcolor(int x
, unsigned char *r
, unsigned char *g
, unsigned char *b
) 
 834         if (!BETWEEN(x
, 0, dc
.collen
)) 
 837         *r 
= dc
.col
[x
].color
.red 
>> 8; 
 838         *g 
= dc
.col
[x
].color
.green 
>> 8; 
 839         *b 
= dc
.col
[x
].color
.blue 
>> 8; 
 845 xsetcolorname(int x
, const char *name
) 
 849         if (!BETWEEN(x
, 0, dc
.collen
)) 
 852         if (!xloadcolor(x
, name
, &ncolor
)) 
 855         XftColorFree(xw
.dpy
, xw
.vis
, xw
.cmap
, &dc
.col
[x
]); 
 862  * Absolute coordinates. 
 865 xclear(int x1
, int y1
, int x2
, int y2
) 
 868                         &dc
.col
[IS_SET(MODE_REVERSE
)? defaultfg 
: defaultbg
], 
 869                         x1
, y1
, x2
-x1
, y2
-y1
); 
 875         XClassHint 
class = {opt_name 
? opt_name 
: termname
, 
 876                             opt_class 
? opt_class 
: termname
}; 
 877         XWMHints wm 
= {.flags 
= InputHint
, .input 
= 1}; 
 880         sizeh 
= XAllocSizeHints(); 
 882         sizeh
->flags 
= PSize 
| PResizeInc 
| PBaseSize 
| PMinSize
; 
 883         sizeh
->height 
= win
.h
; 
 884         sizeh
->width 
= win
.w
; 
 885         sizeh
->height_inc 
= 1; 
 886         sizeh
->width_inc 
= 1; 
 887         sizeh
->base_height 
= 2 * borderpx
; 
 888         sizeh
->base_width 
= 2 * borderpx
; 
 889         sizeh
->min_height 
= win
.ch 
+ 2 * borderpx
; 
 890         sizeh
->min_width 
= win
.cw 
+ 2 * borderpx
; 
 892                 sizeh
->flags 
|= PMaxSize
; 
 893                 sizeh
->min_width 
= sizeh
->max_width 
= win
.w
; 
 894                 sizeh
->min_height 
= sizeh
->max_height 
= win
.h
; 
 896         if (xw
.gm 
& (XValue
|YValue
)) { 
 897                 sizeh
->flags 
|= USPosition 
| PWinGravity
; 
 900                 sizeh
->win_gravity 
= xgeommasktogravity(xw
.gm
); 
 903         XSetWMProperties(xw
.dpy
, xw
.win
, NULL
, NULL
, NULL
, 0, sizeh
, &wm
, 
 909 xgeommasktogravity(int mask
) 
 911         switch (mask 
& (XNegative
|YNegative
)) { 
 913                 return NorthWestGravity
; 
 915                 return NorthEastGravity
; 
 917                 return SouthWestGravity
; 
 920         return SouthEastGravity
; 
 924 xloadfont(Font 
*f
, FcPattern 
*pattern
) 
 926         FcPattern 
*configured
; 
 930         int wantattr
, haveattr
; 
 933          * Manually configure instead of calling XftMatchFont 
 934          * so that we can use the configured pattern for 
 935          * "missing glyph" lookups. 
 937         configured 
= FcPatternDuplicate(pattern
); 
 941         FcConfigSubstitute(NULL
, configured
, FcMatchPattern
); 
 942         XftDefaultSubstitute(xw
.dpy
, xw
.scr
, configured
); 
 944         match 
= FcFontMatch(NULL
, configured
, &result
); 
 946                 FcPatternDestroy(configured
); 
 950         if (!(f
->match 
= XftFontOpenPattern(xw
.dpy
, match
))) { 
 951                 FcPatternDestroy(configured
); 
 952                 FcPatternDestroy(match
); 
 956         if ((XftPatternGetInteger(pattern
, "slant", 0, &wantattr
) == 
 959                  * Check if xft was unable to find a font with the appropriate 
 960                  * slant but gave us one anyway. Try to mitigate. 
 962                 if ((XftPatternGetInteger(f
->match
->pattern
, "slant", 0, 
 963                     &haveattr
) != XftResultMatch
) || haveattr 
< wantattr
) { 
 965                         fputs("font slant does not match\n", stderr
); 
 969         if ((XftPatternGetInteger(pattern
, "weight", 0, &wantattr
) == 
 971                 if ((XftPatternGetInteger(f
->match
->pattern
, "weight", 0, 
 972                     &haveattr
) != XftResultMatch
) || haveattr 
!= wantattr
) { 
 974                         fputs("font weight does not match\n", stderr
); 
 978         XftTextExtentsUtf8(xw
.dpy
, f
->match
, 
 979                 (const FcChar8 
*) ascii_printable
, 
 980                 strlen(ascii_printable
), &extents
); 
 983         f
->pattern 
= configured
; 
 985         f
->ascent 
= f
->match
->ascent
; 
 986         f
->descent 
= f
->match
->descent
; 
 988         f
->rbearing 
= f
->match
->max_advance_width
; 
 990         f
->height 
= f
->ascent 
+ f
->descent
; 
 991         f
->width 
= DIVCEIL(extents
.xOff
, strlen(ascii_printable
)); 
 997 xloadfonts(const char *fontstr
, double fontsize
) 
1002         if (fontstr
[0] == '-') 
1003                 pattern 
= XftXlfdParse(fontstr
, False
, False
); 
1005                 pattern 
= FcNameParse((const FcChar8 
*)fontstr
); 
1008                 die("can't open font %s\n", fontstr
); 
1011                 FcPatternDel(pattern
, FC_PIXEL_SIZE
); 
1012                 FcPatternDel(pattern
, FC_SIZE
); 
1013                 FcPatternAddDouble(pattern
, FC_PIXEL_SIZE
, (double)fontsize
); 
1014                 usedfontsize 
= fontsize
; 
1016                 if (FcPatternGetDouble(pattern
, FC_PIXEL_SIZE
, 0, &fontval
) == 
1018                         usedfontsize 
= fontval
; 
1019                 } else if (FcPatternGetDouble(pattern
, FC_SIZE
, 0, &fontval
) == 
1024                          * Default font size is 12, if none given. This is to 
1025                          * have a known usedfontsize value. 
1027                         FcPatternAddDouble(pattern
, FC_PIXEL_SIZE
, 12); 
1030                 defaultfontsize 
= usedfontsize
; 
1033         if (xloadfont(&dc
.font
, pattern
)) 
1034                 die("can't open font %s\n", fontstr
); 
1036         if (usedfontsize 
< 0) { 
1037                 FcPatternGetDouble(dc
.font
.match
->pattern
, 
1038                                    FC_PIXEL_SIZE
, 0, &fontval
); 
1039                 usedfontsize 
= fontval
; 
1041                         defaultfontsize 
= fontval
; 
1044         /* Setting character width and height. */ 
1045         win
.cw 
= ceilf(dc
.font
.width 
* cwscale
); 
1046         win
.ch 
= ceilf(dc
.font
.height 
* chscale
); 
1048         FcPatternDel(pattern
, FC_SLANT
); 
1049         FcPatternAddInteger(pattern
, FC_SLANT
, FC_SLANT_ITALIC
); 
1050         if (xloadfont(&dc
.ifont
, pattern
)) 
1051                 die("can't open font %s\n", fontstr
); 
1053         FcPatternDel(pattern
, FC_WEIGHT
); 
1054         FcPatternAddInteger(pattern
, FC_WEIGHT
, FC_WEIGHT_BOLD
); 
1055         if (xloadfont(&dc
.ibfont
, pattern
)) 
1056                 die("can't open font %s\n", fontstr
); 
1058         FcPatternDel(pattern
, FC_SLANT
); 
1059         FcPatternAddInteger(pattern
, FC_SLANT
, FC_SLANT_ROMAN
); 
1060         if (xloadfont(&dc
.bfont
, pattern
)) 
1061                 die("can't open font %s\n", fontstr
); 
1063         FcPatternDestroy(pattern
); 
1067 xunloadfont(Font 
*f
) 
1069         XftFontClose(xw
.dpy
, f
->match
); 
1070         FcPatternDestroy(f
->pattern
); 
1072                 FcFontSetDestroy(f
->set
); 
1078         /* Free the loaded fonts in the font cache.  */ 
1080                 XftFontClose(xw
.dpy
, frc
[--frclen
].font
); 
1082         xunloadfont(&dc
.font
); 
1083         xunloadfont(&dc
.bfont
); 
1084         xunloadfont(&dc
.ifont
); 
1085         xunloadfont(&dc
.ibfont
); 
1089 ximopen(Display 
*dpy
) 
1091         XIMCallback imdestroy 
= { .client_data 
= NULL
, .callback 
= ximdestroy 
}; 
1092         XICCallback icdestroy 
= { .client_data 
= NULL
, .callback 
= xicdestroy 
}; 
1094         xw
.ime
.xim 
= XOpenIM(xw
.dpy
, NULL
, NULL
, NULL
); 
1095         if (xw
.ime
.xim 
== NULL
) 
1098         if (XSetIMValues(xw
.ime
.xim
, XNDestroyCallback
, &imdestroy
, NULL
)) 
1099                 fprintf(stderr
, "XSetIMValues: " 
1100                                 "Could not set XNDestroyCallback.\n"); 
1102         xw
.ime
.spotlist 
= XVaCreateNestedList(0, XNSpotLocation
, &xw
.ime
.spot
, 
1105         if (xw
.ime
.xic 
== NULL
) { 
1106                 xw
.ime
.xic 
= XCreateIC(xw
.ime
.xim
, XNInputStyle
, 
1107                                        XIMPreeditNothing 
| XIMStatusNothing
, 
1108                                        XNClientWindow
, xw
.win
, 
1109                                        XNDestroyCallback
, &icdestroy
, 
1112         if (xw
.ime
.xic 
== NULL
) 
1113                 fprintf(stderr
, "XCreateIC: Could not create input context.\n"); 
1119 ximinstantiate(Display 
*dpy
, XPointer client
, XPointer call
) 
1122                 XUnregisterIMInstantiateCallback(xw
.dpy
, NULL
, NULL
, NULL
, 
1123                                                  ximinstantiate
, NULL
); 
1127 ximdestroy(XIM xim
, XPointer client
, XPointer call
) 
1130         XRegisterIMInstantiateCallback(xw
.dpy
, NULL
, NULL
, NULL
, 
1131                                        ximinstantiate
, NULL
); 
1132         XFree(xw
.ime
.spotlist
); 
1136 xicdestroy(XIC xim
, XPointer client
, XPointer call
) 
1143 xinit(int cols
, int rows
) 
1148         pid_t thispid 
= getpid(); 
1149         XColor xmousefg
, xmousebg
; 
1150         XWindowAttributes attr
; 
1153         if (!(xw
.dpy 
= XOpenDisplay(NULL
))) 
1154                 die("can't open display\n"); 
1155         xw
.scr 
= XDefaultScreen(xw
.dpy
); 
1157         if (!(opt_embed 
&& (parent 
= strtol(opt_embed
, NULL
, 0)))) { 
1158                 parent 
= XRootWindow(xw
.dpy
, xw
.scr
); 
1161                 XGetWindowAttributes(xw
.dpy
, parent
, &attr
); 
1162                 xw
.depth 
= attr
.depth
; 
1165         XMatchVisualInfo(xw
.dpy
, xw
.scr
, xw
.depth
, TrueColor
, &vis
); 
1166         xw
.vis 
= vis
.visual
; 
1170                 die("could not init fontconfig.\n"); 
1172         usedfont 
= (opt_font 
== NULL
)? font 
: opt_font
; 
1173         xloadfonts(usedfont
, 0); 
1176         xw
.cmap 
= XCreateColormap(xw
.dpy
, parent
, xw
.vis
, None
); 
1179         /* adjust fixed window geometry */ 
1180         win
.w 
= 2 * win
.hborderpx 
+ 2 * borderpx 
+ cols 
* win
.cw
; 
1181         win
.h 
= 2 * win
.vborderpx 
+ 2 * borderpx 
+ rows 
* win
.ch
; 
1182         if (xw
.gm 
& XNegative
) 
1183                 xw
.l 
+= DisplayWidth(xw
.dpy
, xw
.scr
) - win
.w 
- 2; 
1184         if (xw
.gm 
& YNegative
) 
1185                 xw
.t 
+= DisplayHeight(xw
.dpy
, xw
.scr
) - win
.h 
- 2; 
1188         xw
.attrs
.background_pixel 
= dc
.col
[defaultbg
].pixel
; 
1189         xw
.attrs
.border_pixel 
= dc
.col
[defaultbg
].pixel
; 
1190         xw
.attrs
.bit_gravity 
= NorthWestGravity
; 
1191         xw
.attrs
.event_mask 
= FocusChangeMask 
| KeyPressMask 
| KeyReleaseMask
 
1192                 | ExposureMask 
| VisibilityChangeMask 
| StructureNotifyMask
 
1193                 | ButtonMotionMask 
| ButtonPressMask 
| ButtonReleaseMask
; 
1194         xw
.attrs
.colormap 
= xw
.cmap
; 
1196         xw
.win 
= XCreateWindow(xw
.dpy
, parent
, xw
.l
, xw
.t
, 
1197                         win
.w
, win
.h
, 0, xw
.depth
, InputOutput
, 
1198                         xw
.vis
, CWBackPixel 
| CWBorderPixel 
| CWBitGravity
 
1199                         | CWEventMask 
| CWColormap
, &xw
.attrs
); 
1201         memset(&gcvalues
, 0, sizeof(gcvalues
)); 
1202         gcvalues
.graphics_exposures 
= False
; 
1203         xw
.buf 
= XCreatePixmap(xw
.dpy
, xw
.win
, win
.w
, win
.h
, xw
.depth
); 
1204         dc
.gc 
= XCreateGC(xw
.dpy
, xw
.buf
, GCGraphicsExposures
, &gcvalues
); 
1205         XSetForeground(xw
.dpy
, dc
.gc
, dc
.col
[defaultbg
].pixel
); 
1206         XFillRectangle(xw
.dpy
, xw
.buf
, dc
.gc
, 0, 0, win
.w
, win
.h
); 
1208         /* font spec buffer */ 
1209         xw
.specbuf 
= xmalloc(cols 
* sizeof(GlyphFontSpec
)); 
1211         /* Xft rendering context */ 
1212         xw
.draw 
= XftDrawCreate(xw
.dpy
, xw
.buf
, xw
.vis
, xw
.cmap
); 
1215         if (!ximopen(xw
.dpy
)) { 
1216                 XRegisterIMInstantiateCallback(xw
.dpy
, NULL
, NULL
, NULL
, 
1217                                                ximinstantiate
, NULL
); 
1220         /* white cursor, black outline */ 
1221         cursor 
= XCreateFontCursor(xw
.dpy
, mouseshape
); 
1222         XDefineCursor(xw
.dpy
, xw
.win
, cursor
); 
1224         if (XParseColor(xw
.dpy
, xw
.cmap
, colorname
[mousefg
], &xmousefg
) == 0) { 
1225                 xmousefg
.red   
= 0xffff; 
1226                 xmousefg
.green 
= 0xffff; 
1227                 xmousefg
.blue  
= 0xffff; 
1230         if (XParseColor(xw
.dpy
, xw
.cmap
, colorname
[mousebg
], &xmousebg
) == 0) { 
1231                 xmousebg
.red   
= 0x0000; 
1232                 xmousebg
.green 
= 0x0000; 
1233                 xmousebg
.blue  
= 0x0000; 
1236         XRecolorCursor(xw
.dpy
, cursor
, &xmousefg
, &xmousebg
); 
1238         xw
.xembed 
= XInternAtom(xw
.dpy
, "_XEMBED", False
); 
1239         xw
.wmdeletewin 
= XInternAtom(xw
.dpy
, "WM_DELETE_WINDOW", False
); 
1240         xw
.netwmname 
= XInternAtom(xw
.dpy
, "_NET_WM_NAME", False
); 
1241         xw
.netwmiconname 
= XInternAtom(xw
.dpy
, "_NET_WM_ICON_NAME", False
); 
1242         XSetWMProtocols(xw
.dpy
, xw
.win
, &xw
.wmdeletewin
, 1); 
1244         xw
.netwmpid 
= XInternAtom(xw
.dpy
, "_NET_WM_PID", False
); 
1245         XChangeProperty(xw
.dpy
, xw
.win
, xw
.netwmpid
, XA_CARDINAL
, 32, 
1246                         PropModeReplace
, (uchar 
*)&thispid
, 1); 
1248         win
.mode 
= MODE_NUMLOCK
; 
1251         XMapWindow(xw
.dpy
, xw
.win
); 
1252         XSync(xw
.dpy
, False
); 
1254         clock_gettime(CLOCK_MONOTONIC
, &xsel
.tclick1
); 
1255         clock_gettime(CLOCK_MONOTONIC
, &xsel
.tclick2
); 
1256         xsel
.primary 
= NULL
; 
1257         xsel
.clipboard 
= NULL
; 
1258         xsel
.xtarget 
= XInternAtom(xw
.dpy
, "UTF8_STRING", 0); 
1259         if (xsel
.xtarget 
== None
) 
1260                 xsel
.xtarget 
= XA_STRING
; 
1264 xmakeglyphfontspecs(XftGlyphFontSpec 
*specs
, const Glyph 
*glyphs
, int len
, int x
, int y
) 
1266         float winx 
= win
.hborderpx 
+ x 
* win
.cw
, winy 
= win
.vborderpx 
+ y 
* win
.ch
, xp
, yp
; 
1267         ushort mode
, prevmode 
= USHRT_MAX
; 
1268         Font 
*font 
= &dc
.font
; 
1269         int frcflags 
= FRC_NORMAL
; 
1270         float runewidth 
= win
.cw
; 
1274         FcPattern 
*fcpattern
, *fontpattern
; 
1275         FcFontSet 
*fcsets
[] = { NULL 
}; 
1276         FcCharSet 
*fccharset
; 
1277         int i
, f
, numspecs 
= 0; 
1279         for (i 
= 0, xp 
= winx
, yp 
= winy 
+ font
->ascent
; i 
< len
; ++i
) { 
1280                 /* Fetch rune and mode for current glyph. */ 
1282                 mode 
= glyphs
[i
].mode
; 
1284                 /* Skip dummy wide-character spacing. */ 
1285                 if (mode 
== ATTR_WDUMMY
) 
1288                 /* Determine font for glyph if different from previous glyph. */ 
1289                 if (prevmode 
!= mode
) { 
1292                         frcflags 
= FRC_NORMAL
; 
1293                         runewidth 
= win
.cw 
* ((mode 
& ATTR_WIDE
) ? 2.0f 
: 1.0f
); 
1294                         if ((mode 
& ATTR_ITALIC
) && (mode 
& ATTR_BOLD
)) { 
1296                                 frcflags 
= FRC_ITALICBOLD
; 
1297                         } else if (mode 
& ATTR_ITALIC
) { 
1299                                 frcflags 
= FRC_ITALIC
; 
1300                         } else if (mode 
& ATTR_BOLD
) { 
1302                                 frcflags 
= FRC_BOLD
; 
1304                         yp 
= winy 
+ font
->ascent
; 
1307                 /* Lookup character index with default font. */ 
1308                 glyphidx 
= XftCharIndex(xw
.dpy
, font
->match
, rune
); 
1310                         specs
[numspecs
].font 
= font
->match
; 
1311                         specs
[numspecs
].glyph 
= glyphidx
; 
1312                         specs
[numspecs
].x 
= (short)xp
; 
1313                         specs
[numspecs
].y 
= (short)yp
; 
1319                 /* Fallback on font cache, search the font cache for match. */ 
1320                 for (f 
= 0; f 
< frclen
; f
++) { 
1321                         glyphidx 
= XftCharIndex(xw
.dpy
, frc
[f
].font
, rune
); 
1322                         /* Everything correct. */ 
1323                         if (glyphidx 
&& frc
[f
].flags 
== frcflags
) 
1325                         /* We got a default font for a not found glyph. */ 
1326                         if (!glyphidx 
&& frc
[f
].flags 
== frcflags
 
1327                                         && frc
[f
].unicodep 
== rune
) { 
1332                 /* Nothing was found. Use fontconfig to find matching font. */ 
1335                                 font
->set 
= FcFontSort(0, font
->pattern
, 
1337                         fcsets
[0] = font
->set
; 
1340                          * Nothing was found in the cache. Now use 
1341                          * some dozen of Fontconfig calls to get the 
1342                          * font for one single character. 
1344                          * Xft and fontconfig are design failures. 
1346                         fcpattern 
= FcPatternDuplicate(font
->pattern
); 
1347                         fccharset 
= FcCharSetCreate(); 
1349                         FcCharSetAddChar(fccharset
, rune
); 
1350                         FcPatternAddCharSet(fcpattern
, FC_CHARSET
, 
1352                         FcPatternAddBool(fcpattern
, FC_SCALABLE
, 1); 
1354                         FcConfigSubstitute(0, fcpattern
, 
1356                         FcDefaultSubstitute(fcpattern
); 
1358                         fontpattern 
= FcFontSetMatch(0, fcsets
, 1, 
1361                         /* Allocate memory for the new cache entry. */ 
1362                         if (frclen 
>= frccap
) { 
1364                                 frc 
= xrealloc(frc
, frccap 
* sizeof(Fontcache
)); 
1367                         frc
[frclen
].font 
= XftFontOpenPattern(xw
.dpy
, 
1369                         if (!frc
[frclen
].font
) 
1370                                 die("XftFontOpenPattern failed seeking fallback font: %s\n", 
1372                         frc
[frclen
].flags 
= frcflags
; 
1373                         frc
[frclen
].unicodep 
= rune
; 
1375                         glyphidx 
= XftCharIndex(xw
.dpy
, frc
[frclen
].font
, rune
); 
1380                         FcPatternDestroy(fcpattern
); 
1381                         FcCharSetDestroy(fccharset
); 
1384                 specs
[numspecs
].font 
= frc
[f
].font
; 
1385                 specs
[numspecs
].glyph 
= glyphidx
; 
1386                 specs
[numspecs
].x 
= (short)xp
; 
1387                 specs
[numspecs
].y 
= (short)yp
; 
1396 xdrawglyphfontspecs(const XftGlyphFontSpec 
*specs
, Glyph base
, int len
, int x
, int y
) 
1398         int charlen 
= len 
* ((base
.mode 
& ATTR_WIDE
) ? 2 : 1); 
1399         int winx 
= win
.hborderpx 
+ x 
* win
.cw
, winy 
= win
.vborderpx 
+ y 
* win
.ch
, 
1400             width 
= charlen 
* win
.cw
; 
1401         Color 
*fg
, *bg
, *temp
, revfg
, revbg
, truefg
, truebg
; 
1402         XRenderColor colfg
, colbg
; 
1405         /* Fallback on color display for attributes not supported by the font */ 
1406         if (base
.mode 
& ATTR_ITALIC 
&& base
.mode 
& ATTR_BOLD
) { 
1407                 if (dc
.ibfont
.badslant 
|| dc
.ibfont
.badweight
) 
1408                         base
.fg 
= defaultattr
; 
1409         } else if ((base
.mode 
& ATTR_ITALIC 
&& dc
.ifont
.badslant
) || 
1410             (base
.mode 
& ATTR_BOLD 
&& dc
.bfont
.badweight
)) { 
1411                 base
.fg 
= defaultattr
; 
1414         if (IS_TRUECOL(base
.fg
)) { 
1415                 colfg
.alpha 
= 0xffff; 
1416                 colfg
.red 
= TRUERED(base
.fg
); 
1417                 colfg
.green 
= TRUEGREEN(base
.fg
); 
1418                 colfg
.blue 
= TRUEBLUE(base
.fg
); 
1419                 XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colfg
, &truefg
); 
1422                 fg 
= &dc
.col
[base
.fg
]; 
1425         if (IS_TRUECOL(base
.bg
)) { 
1426                 colbg
.alpha 
= 0xffff; 
1427                 colbg
.green 
= TRUEGREEN(base
.bg
); 
1428                 colbg
.red 
= TRUERED(base
.bg
); 
1429                 colbg
.blue 
= TRUEBLUE(base
.bg
); 
1430                 XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colbg
, &truebg
); 
1433                 bg 
= &dc
.col
[base
.bg
]; 
1436         /* Change basic system colors [0-7] to bright system colors [8-15] */ 
1437         if ((base
.mode 
& ATTR_BOLD_FAINT
) == ATTR_BOLD 
&& BETWEEN(base
.fg
, 0, 7)) 
1438                 fg 
= &dc
.col
[base
.fg 
+ 8]; 
1440         if (IS_SET(MODE_REVERSE
)) { 
1441                 if (fg 
== &dc
.col
[defaultfg
]) { 
1442                         fg 
= &dc
.col
[defaultbg
]; 
1444                         colfg
.red 
= ~fg
->color
.red
; 
1445                         colfg
.green 
= ~fg
->color
.green
; 
1446                         colfg
.blue 
= ~fg
->color
.blue
; 
1447                         colfg
.alpha 
= fg
->color
.alpha
; 
1448                         XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colfg
, 
1453                 if (bg 
== &dc
.col
[defaultbg
]) { 
1454                         bg 
= &dc
.col
[defaultfg
]; 
1456                         colbg
.red 
= ~bg
->color
.red
; 
1457                         colbg
.green 
= ~bg
->color
.green
; 
1458                         colbg
.blue 
= ~bg
->color
.blue
; 
1459                         colbg
.alpha 
= bg
->color
.alpha
; 
1460                         XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colbg
, 
1466         if ((base
.mode 
& ATTR_BOLD_FAINT
) == ATTR_FAINT
) { 
1467                 colfg
.red 
= fg
->color
.red 
/ 2; 
1468                 colfg
.green 
= fg
->color
.green 
/ 2; 
1469                 colfg
.blue 
= fg
->color
.blue 
/ 2; 
1470                 colfg
.alpha 
= fg
->color
.alpha
; 
1471                 XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colfg
, &revfg
); 
1475         if (base
.mode 
& ATTR_REVERSE
) { 
1481         if (base
.mode 
& ATTR_BLINK 
&& win
.mode 
& MODE_BLINK
) 
1484         if (base
.mode 
& ATTR_INVISIBLE
) 
1487         /* Intelligent cleaning up of the borders. */ 
1489                 xclear(0, (y 
== 0)? 0 : winy
, win
.vborderpx
, 
1491                         ((winy 
+ win
.ch 
>= win
.vborderpx 
+ win
.th
)? win
.h 
: 0)); 
1493         if (winx 
+ width 
>= win
.hborderpx 
+ win
.tw
) { 
1494                 xclear(winx 
+ width
, (y 
== 0)? 0 : winy
, win
.w
, 
1495                         ((winy 
+ win
.ch 
>= win
.vborderpx 
+ win
.th
)? win
.h 
: (winy 
+ win
.ch
))); 
1498                 xclear(winx
, 0, winx 
+ width
, win
.vborderpx
); 
1499         if (winy 
+ win
.ch 
>= win
.vborderpx 
+ win
.th
) 
1500                 xclear(winx
, winy 
+ win
.ch
, winx 
+ width
, win
.h
); 
1502         /* Clean up the region we want to draw to. */ 
1503         XftDrawRect(xw
.draw
, bg
, winx
, winy
, width
, win
.ch
); 
1505         /* Set the clip region because Xft is sometimes dirty. */ 
1510         XftDrawSetClipRectangles(xw
.draw
, winx
, winy
, &r
, 1); 
1512         /* Render the glyphs. */ 
1513         XftDrawGlyphFontSpec(xw
.draw
, fg
, specs
, len
); 
1515         /* Render underline and strikethrough. */ 
1516         if (base
.mode 
& ATTR_UNDERLINE
) { 
1517                 XftDrawRect(xw
.draw
, fg
, winx
, winy 
+ dc
.font
.ascent 
* chscale 
+ 1, 
1521         if (base
.mode 
& ATTR_STRUCK
) { 
1522                 XftDrawRect(xw
.draw
, fg
, winx
, winy 
+ 2 * dc
.font
.ascent 
* chscale 
/ 3, 
1526         /* Reset clip to none. */ 
1527         XftDrawSetClip(xw
.draw
, 0); 
1531 xdrawglyph(Glyph g
, int x
, int y
) 
1534         XftGlyphFontSpec spec
; 
1536         numspecs 
= xmakeglyphfontspecs(&spec
, &g
, 1, x
, y
); 
1537         xdrawglyphfontspecs(&spec
, g
, numspecs
, x
, y
); 
1541 xdrawcursor(int cx
, int cy
, Glyph g
, int ox
, int oy
, Glyph og
) 
1545         /* remove the old cursor */ 
1546         if (selected(ox
, oy
)) 
1547                 og
.mode 
^= ATTR_REVERSE
; 
1548         xdrawglyph(og
, ox
, oy
); 
1550         if (IS_SET(MODE_HIDE
)) 
1554          * Select the right color for the right mode. 
1556         g
.mode 
&= ATTR_BOLD
|ATTR_ITALIC
|ATTR_UNDERLINE
|ATTR_STRUCK
|ATTR_WIDE
; 
1558         if (IS_SET(MODE_REVERSE
)) { 
1559                 g
.mode 
|= ATTR_REVERSE
; 
1561                 if (selected(cx
, cy
)) { 
1562                         drawcol 
= dc
.col
[defaultcs
]; 
1565                         drawcol 
= dc
.col
[defaultrcs
]; 
1569                 if (selected(cx
, cy
)) { 
1576                 drawcol 
= dc
.col
[g
.bg
]; 
1579         /* draw the new one */ 
1580         if (IS_SET(MODE_FOCUSED
)) { 
1581                 switch (win
.cursor
) { 
1582                 case 7: /* st extension */ 
1583                         g
.u 
= 0x2603; /* snowman (U+2603) */ 
1585                 case 0: /* Blinking Block */ 
1586                 case 1: /* Blinking Block (Default) */ 
1587                 case 2: /* Steady Block */ 
1588                         xdrawglyph(g
, cx
, cy
); 
1590                 case 3: /* Blinking Underline */ 
1591                 case 4: /* Steady Underline */ 
1592                         XftDrawRect(xw
.draw
, &drawcol
, 
1593                                         win
.hborderpx 
+ cx 
* win
.cw
, 
1594                                         win
.vborderpx 
+ (cy 
+ 1) * win
.ch 
- \
 
1596                                         win
.cw
, cursorthickness
); 
1598                 case 5: /* Blinking bar */ 
1599                 case 6: /* Steady bar */ 
1600                         XftDrawRect(xw
.draw
, &drawcol
, 
1601                                         win
.hborderpx 
+ cx 
* win
.cw
, 
1602                                         win
.vborderpx 
+ cy 
* win
.ch
, 
1603                                         cursorthickness
, win
.ch
); 
1607                 XftDrawRect(xw
.draw
, &drawcol
, 
1608                                 win
.hborderpx 
+ cx 
* win
.cw
, 
1609                                 win
.vborderpx 
+ cy 
* win
.ch
, 
1611                 XftDrawRect(xw
.draw
, &drawcol
, 
1612                                 win
.hborderpx 
+ cx 
* win
.cw
, 
1613                                 win
.vborderpx 
+ cy 
* win
.ch
, 
1615                 XftDrawRect(xw
.draw
, &drawcol
, 
1616                                 win
.hborderpx 
+ (cx 
+ 1) * win
.cw 
- 1, 
1617                                 win
.vborderpx 
+ cy 
* win
.ch
, 
1619                 XftDrawRect(xw
.draw
, &drawcol
, 
1620                                 win
.hborderpx 
+ cx 
* win
.cw
, 
1621                                 win
.vborderpx 
+ (cy 
+ 1) * win
.ch 
- 1, 
1629         char buf
[sizeof(long) * 8 + 1]; 
1631         snprintf(buf
, sizeof(buf
), "%lu", xw
.win
); 
1632         setenv("WINDOWID", buf
, 1); 
1636 xseticontitle(char *p
) 
1639         DEFAULT(p
, opt_title
); 
1641         if (Xutf8TextListToTextProperty(xw
.dpy
, &p
, 1, XUTF8StringStyle
, 
1644         XSetWMIconName(xw
.dpy
, xw
.win
, &prop
); 
1645         XSetTextProperty(xw
.dpy
, xw
.win
, &prop
, xw
.netwmiconname
); 
1653         DEFAULT(p
, opt_title
); 
1655         if (Xutf8TextListToTextProperty(xw
.dpy
, &p
, 1, XUTF8StringStyle
, 
1658         XSetWMName(xw
.dpy
, xw
.win
, &prop
); 
1659         XSetTextProperty(xw
.dpy
, xw
.win
, &prop
, xw
.netwmname
); 
1666         return IS_SET(MODE_VISIBLE
); 
1670 xdrawline(Line line
, int x1
, int y1
, int x2
) 
1672         int i
, x
, ox
, numspecs
; 
1674         XftGlyphFontSpec 
*specs 
= xw
.specbuf
; 
1676         numspecs 
= xmakeglyphfontspecs(specs
, &line
[x1
], x2 
- x1
, x1
, y1
); 
1678         for (x 
= x1
; x 
< x2 
&& i 
< numspecs
; x
++) { 
1680                 if (new.mode 
== ATTR_WDUMMY
) 
1682                 if (selected(x
, y1
)) 
1683                         new.mode 
^= ATTR_REVERSE
; 
1684                 if (i 
> 0 && ATTRCMP(base
, new)) { 
1685                         xdrawglyphfontspecs(specs
, base
, i
, ox
, y1
); 
1697                 xdrawglyphfontspecs(specs
, base
, i
, ox
, y1
); 
1703         XCopyArea(xw
.dpy
, xw
.buf
, xw
.win
, dc
.gc
, 0, 0, win
.w
, 
1705         XSetForeground(xw
.dpy
, dc
.gc
, 
1706                         dc
.col
[IS_SET(MODE_REVERSE
)? 
1707                                 defaultfg 
: defaultbg
].pixel
); 
1711 xximspot(int x
, int y
) 
1713         if (xw
.ime
.xic 
== NULL
) 
1716         xw
.ime
.spot
.x 
= borderpx 
+ x 
* win
.cw
; 
1717         xw
.ime
.spot
.y 
= borderpx 
+ (y 
+ 1) * win
.ch
; 
1719         XSetICValues(xw
.ime
.xic
, XNPreeditAttributes
, xw
.ime
.spotlist
, NULL
); 
1729 visibility(XEvent 
*ev
) 
1731         XVisibilityEvent 
*e 
= &ev
->xvisibility
; 
1733         MODBIT(win
.mode
, e
->state 
!= VisibilityFullyObscured
, MODE_VISIBLE
); 
1739         win
.mode 
&= ~MODE_VISIBLE
; 
1743 xsetpointermotion(int set
) 
1745         MODBIT(xw
.attrs
.event_mask
, set
, PointerMotionMask
); 
1746         XChangeWindowAttributes(xw
.dpy
, xw
.win
, CWEventMask
, &xw
.attrs
); 
1750 xsetmode(int set
, unsigned int flags
) 
1752         int mode 
= win
.mode
; 
1753         MODBIT(win
.mode
, set
, flags
); 
1754         if ((win
.mode 
& MODE_REVERSE
) != (mode 
& MODE_REVERSE
)) 
1759 xsetcursor(int cursor
) 
1761         if (!BETWEEN(cursor
, 0, 7)) /* 7: st extension */ 
1763         win
.cursor 
= cursor
; 
1768 xseturgency(int add
) 
1770         XWMHints 
*h 
= XGetWMHints(xw
.dpy
, xw
.win
); 
1772         MODBIT(h
->flags
, add
, XUrgencyHint
); 
1773         XSetWMHints(xw
.dpy
, xw
.win
, h
); 
1780         if (!(IS_SET(MODE_FOCUSED
))) 
1783                 XkbBell(xw
.dpy
, xw
.win
, bellvolume
, (Atom
)NULL
); 
1789         XFocusChangeEvent 
*e 
= &ev
->xfocus
; 
1791         if (e
->mode 
== NotifyGrab
) 
1794         if (ev
->type 
== FocusIn
) { 
1796                         XSetICFocus(xw
.ime
.xic
); 
1797                 win
.mode 
|= MODE_FOCUSED
; 
1799                 if (IS_SET(MODE_FOCUS
)) 
1800                         ttywrite("\033[I", 3, 0); 
1803                         XUnsetICFocus(xw
.ime
.xic
); 
1804                 win
.mode 
&= ~MODE_FOCUSED
; 
1805                 if (IS_SET(MODE_FOCUS
)) 
1806                         ttywrite("\033[O", 3, 0); 
1811 match(uint mask
, uint state
) 
1813         return mask 
== XK_ANY_MOD 
|| mask 
== (state 
& ~ignoremod
); 
1817 kmap(KeySym k
, uint state
) 
1822         /* Check for mapped keys out of X11 function keys. */ 
1823         for (i 
= 0; i 
< LEN(mappedkeys
); i
++) { 
1824                 if (mappedkeys
[i
] == k
) 
1827         if (i 
== LEN(mappedkeys
)) { 
1828                 if ((k 
& 0xFFFF) < 0xFD00) 
1832         for (kp 
= key
; kp 
< key 
+ LEN(key
); kp
++) { 
1836                 if (!match(kp
->mask
, state
)) 
1839                 if (IS_SET(MODE_APPKEYPAD
) ? kp
->appkey 
< 0 : kp
->appkey 
> 0) 
1841                 if (IS_SET(MODE_NUMLOCK
) && kp
->appkey 
== 2) 
1844                 if (IS_SET(MODE_APPCURSOR
) ? kp
->appcursor 
< 0 : kp
->appcursor 
> 0) 
1856         XKeyEvent 
*e 
= &ev
->xkey
; 
1858         char buf
[64], *customkey
; 
1864         if (IS_SET(MODE_KBDLOCK
)) 
1868                 len 
= XmbLookupString(xw
.ime
.xic
, e
, buf
, sizeof buf
, &ksym
, &status
); 
1870                 len 
= XLookupString(e
, buf
, sizeof buf
, &ksym
, NULL
); 
1872         for (bp 
= shortcuts
; bp 
< shortcuts 
+ LEN(shortcuts
); bp
++) { 
1873                 if (ksym 
== bp
->keysym 
&& match(bp
->mod
, e
->state
)) { 
1874                         bp
->func(&(bp
->arg
)); 
1879         /* 2. custom keys from config.h */ 
1880         if ((customkey 
= kmap(ksym
, e
->state
))) { 
1881                 ttywrite(customkey
, strlen(customkey
), 1); 
1885         /* 3. composed string from input method */ 
1888         if (len 
== 1 && e
->state 
& Mod1Mask
) { 
1889                 if (IS_SET(MODE_8BIT
)) { 
1892                                 len 
= utf8encode(c
, buf
); 
1900         ttywrite(buf
, len
, 1); 
1908          *  http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html 
1910         if (e
->xclient
.message_type 
== xw
.xembed 
&& e
->xclient
.format 
== 32) { 
1911                 if (e
->xclient
.data
.l
[1] == XEMBED_FOCUS_IN
) { 
1912                         win
.mode 
|= MODE_FOCUSED
; 
1914                 } else if (e
->xclient
.data
.l
[1] == XEMBED_FOCUS_OUT
) { 
1915                         win
.mode 
&= ~MODE_FOCUSED
; 
1917         } else if (e
->xclient
.data
.l
[0] == xw
.wmdeletewin
) { 
1926         if (e
->xconfigure
.width 
== win
.w 
&& e
->xconfigure
.height 
== win
.h
) 
1929         cresize(e
->xconfigure
.width
, e
->xconfigure
.height
); 
1936         int w 
= win
.w
, h 
= win
.h
; 
1938         int xfd 
= XConnectionNumber(xw
.dpy
), ttyfd
, xev
, drawing
; 
1939         struct timespec seltv
, *tv
, now
, lastblink
, trigger
; 
1942         /* Waiting for window mapping */ 
1944                 XNextEvent(xw
.dpy
, &ev
); 
1946                  * This XFilterEvent call is required because of XOpenIM. It 
1947                  * does filter out the key event and some client message for 
1948                  * the input method too. 
1950                 if (XFilterEvent(&ev
, None
)) 
1952                 if (ev
.type 
== ConfigureNotify
) { 
1953                         w 
= ev
.xconfigure
.width
; 
1954                         h 
= ev
.xconfigure
.height
; 
1956         } while (ev
.type 
!= MapNotify
); 
1958         ttyfd 
= ttynew(opt_line
, shell
, opt_io
, opt_cmd
); 
1961         for (timeout 
= -1, drawing 
= 0, lastblink 
= (struct timespec
){0};;) { 
1963                 FD_SET(ttyfd
, &rfd
); 
1966                 if (XPending(xw
.dpy
)) 
1967                         timeout 
= 0;  /* existing events might not set xfd */ 
1969                 seltv
.tv_sec 
= timeout 
/ 1E3
; 
1970                 seltv
.tv_nsec 
= 1E6 
* (timeout 
- 1E3 
* seltv
.tv_sec
); 
1971                 tv 
= timeout 
>= 0 ? &seltv 
: NULL
; 
1973                 if (pselect(MAX(xfd
, ttyfd
)+1, &rfd
, NULL
, NULL
, tv
, NULL
) < 0) { 
1976                         die("select failed: %s\n", strerror(errno
)); 
1978                 clock_gettime(CLOCK_MONOTONIC
, &now
); 
1980                 if (FD_ISSET(ttyfd
, &rfd
)) 
1984                 while (XPending(xw
.dpy
)) { 
1986                         XNextEvent(xw
.dpy
, &ev
); 
1987                         if (XFilterEvent(&ev
, None
)) 
1989                         if (handler
[ev
.type
]) 
1990                                 (handler
[ev
.type
])(&ev
); 
1994                  * To reduce flicker and tearing, when new content or event 
1995                  * triggers drawing, we first wait a bit to ensure we got 
1996                  * everything, and if nothing new arrives - we draw. 
1997                  * We start with trying to wait minlatency ms. If more content 
1998                  * arrives sooner, we retry with shorter and shorter periods, 
1999                  * and eventually draw even without idle after maxlatency ms. 
2000                  * Typically this results in low latency while interacting, 
2001                  * maximum latency intervals during `cat huge.txt`, and perfect 
2002                  * sync with periodic updates from animations/key-repeats/etc. 
2004                 if (FD_ISSET(ttyfd
, &rfd
) || xev
) { 
2009                         timeout 
= (maxlatency 
- TIMEDIFF(now
, trigger
)) \
 
2010                                   / maxlatency 
* minlatency
; 
2012                                 continue;  /* we have time, try to find idle */ 
2015                 /* idle detected or maxlatency exhausted -> draw */ 
2017                 if (blinktimeout 
&& tattrset(ATTR_BLINK
)) { 
2018                         timeout 
= blinktimeout 
- TIMEDIFF(now
, lastblink
); 
2020                                 if (-timeout 
> blinktimeout
) /* start visible */ 
2021                                         win
.mode 
|= MODE_BLINK
; 
2022                                 win
.mode 
^= MODE_BLINK
; 
2023                                 tsetdirtattr(ATTR_BLINK
); 
2025                                 timeout 
= blinktimeout
; 
2038         die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" 
2039             " [-n name] [-o file]\n" 
2040             "          [-T title] [-t title] [-w windowid]" 
2041             " [[-e] command [args ...]]\n" 
2042             "       %s [-aiv] [-c class] [-f font] [-g geometry]" 
2043             " [-n name] [-o file]\n" 
2044             "          [-T title] [-t title] [-w windowid] -l line" 
2045             " [stty_args ...]\n", argv0
, argv0
); 
2049 main(int argc
, char *argv
[]) 
2053         xsetcursor(cursorshape
); 
2060                 opt_alpha 
= EARGF(usage()); 
2063                 opt_class 
= EARGF(usage()); 
2070                 opt_font 
= EARGF(usage()); 
2073                 xw
.gm 
= XParseGeometry(EARGF(usage()), 
2074                                 &xw
.l
, &xw
.t
, &cols
, &rows
); 
2080                 opt_io 
= EARGF(usage()); 
2083                 opt_line 
= EARGF(usage()); 
2086                 opt_name 
= EARGF(usage()); 
2090                 opt_title 
= EARGF(usage()); 
2093                 opt_embed 
= EARGF(usage()); 
2096                 die("%s " VERSION 
"\n", argv0
); 
2103         if (argc 
> 0) /* eat all remaining arguments */ 
2107                 opt_title 
= (opt_line 
|| !opt_cmd
) ? "st" : opt_cmd
[0]; 
2109         setlocale(LC_CTYPE
, ""); 
2110         XSetLocaleModifiers(""); 
2111         cols 
= MAX(cols
, 1); 
2112         rows 
= MAX(rows
, 1);