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) 
  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 ch
; /* char height */ 
  85         int cw
; /* char width  */ 
  86         int mode
; /* window state/mode flags */ 
  87         int cursor
; /* cursor style */ 
  95         GlyphFontSpec 
*specbuf
; /* font spec buffer used for rendering */ 
  96         Atom xembed
, wmdeletewin
, netwmname
, netwmpid
; 
 101                 XVaNestedList spotlist
; 
 105         XSetWindowAttributes attrs
; 
 107         int isfixed
; /* is fixed geometry? */ 
 108         int l
, t
; /* left and top offset */ 
 109         int gm
; /* geometry mask */ 
 114         char *primary
, *clipboard
; 
 115         struct timespec tclick1
; 
 116         struct timespec tclick2
; 
 135 /* Drawing Context */ 
 139         Font font
, bfont
, ifont
, ibfont
; 
 143 static inline ushort 
sixd_to_16bit(int); 
 144 static int xmakeglyphfontspecs(XftGlyphFontSpec 
*, const Glyph 
*, int, int, int); 
 145 static void xdrawglyphfontspecs(const XftGlyphFontSpec 
*, Glyph
, int, int, int); 
 146 static void xdrawglyph(Glyph
, int, int); 
 147 static void xclear(int, int, int, int); 
 148 static int xgeommasktogravity(int); 
 149 static int ximopen(Display 
*); 
 150 static void ximinstantiate(Display 
*, XPointer
, XPointer
); 
 151 static void ximdestroy(XIM
, XPointer
, XPointer
); 
 152 static int xicdestroy(XIC
, XPointer
, XPointer
); 
 153 static void xinit(int, int); 
 154 static void cresize(int, int); 
 155 static void xresize(int, int); 
 156 static void xhints(void); 
 157 static int xloadcolor(int, const char *, Color 
*); 
 158 static int xloadfont(Font 
*, FcPattern 
*); 
 159 static void xloadfonts(char *, double); 
 160 static void xunloadfont(Font 
*); 
 161 static void xunloadfonts(void); 
 162 static void xsetenv(void); 
 163 static void xseturgency(int); 
 164 static int evcol(XEvent 
*); 
 165 static int evrow(XEvent 
*); 
 167 static void expose(XEvent 
*); 
 168 static void visibility(XEvent 
*); 
 169 static void unmap(XEvent 
*); 
 170 static void kpress(XEvent 
*); 
 171 static void cmessage(XEvent 
*); 
 172 static void resize(XEvent 
*); 
 173 static void focus(XEvent 
*); 
 174 static int mouseaction(XEvent 
*, uint
); 
 175 static void brelease(XEvent 
*); 
 176 static void bpress(XEvent 
*); 
 177 static void bmotion(XEvent 
*); 
 178 static void propnotify(XEvent 
*); 
 179 static void selnotify(XEvent 
*); 
 180 static void selclear_(XEvent 
*); 
 181 static void selrequest(XEvent 
*); 
 182 static void setsel(char *, Time
); 
 183 static void mousesel(XEvent 
*, int); 
 184 static void mousereport(XEvent 
*); 
 185 static char *kmap(KeySym
, uint
); 
 186 static int match(uint
, uint
); 
 188 static void run(void); 
 189 static void usage(void); 
 191 static void (*handler
[LASTEvent
])(XEvent 
*) = { 
 193         [ClientMessage
] = cmessage
, 
 194         [ConfigureNotify
] = resize
, 
 195         [VisibilityNotify
] = visibility
, 
 196         [UnmapNotify
] = unmap
, 
 200         [MotionNotify
] = bmotion
, 
 201         [ButtonPress
] = bpress
, 
 202         [ButtonRelease
] = brelease
, 
 204  * Uncomment if you want the selection to disappear when you select something 
 205  * different in another window. 
 207 /*      [SelectionClear] = selclear_, */ 
 208         [SelectionNotify
] = selnotify
, 
 210  * PropertyNotify is only turned on when there is some INCR transfer happening 
 211  * for the selection retrieval. 
 213         [PropertyNotify
] = propnotify
, 
 214         [SelectionRequest
] = selrequest
, 
 220 static XSelection xsel
; 
 221 static TermWindow win
; 
 223 /* Font Ring Cache */ 
 237 /* Fontcache is an array now. A new font will be appended to the array. */ 
 238 static Fontcache 
*frc 
= NULL
; 
 239 static int frclen 
= 0; 
 240 static int frccap 
= 0; 
 241 static char *usedfont 
= NULL
; 
 242 static double usedfontsize 
= 0; 
 243 static double defaultfontsize 
= 0; 
 245 static char *opt_class 
= NULL
; 
 246 static char **opt_cmd  
= NULL
; 
 247 static char *opt_embed 
= NULL
; 
 248 static char *opt_font  
= NULL
; 
 249 static char *opt_io    
= NULL
; 
 250 static char *opt_line  
= NULL
; 
 251 static char *opt_name  
= NULL
; 
 252 static char *opt_title 
= NULL
; 
 254 static int oldbutton 
= 3; /* button event on startup: 3 = release */ 
 257 clipcopy(const Arg 
*dummy
) 
 261         free(xsel
.clipboard
); 
 262         xsel
.clipboard 
= NULL
; 
 264         if (xsel
.primary 
!= NULL
) { 
 265                 xsel
.clipboard 
= xstrdup(xsel
.primary
); 
 266                 clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
 267                 XSetSelectionOwner(xw
.dpy
, clipboard
, xw
.win
, CurrentTime
); 
 272 clippaste(const Arg 
*dummy
) 
 276         clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
 277         XConvertSelection(xw
.dpy
, clipboard
, xsel
.xtarget
, clipboard
, 
 278                         xw
.win
, CurrentTime
); 
 282 selpaste(const Arg 
*dummy
) 
 284         XConvertSelection(xw
.dpy
, XA_PRIMARY
, xsel
.xtarget
, XA_PRIMARY
, 
 285                         xw
.win
, CurrentTime
); 
 289 numlock(const Arg 
*dummy
) 
 291         win
.mode 
^= MODE_NUMLOCK
; 
 299         larg
.f 
= usedfontsize 
+ arg
->f
; 
 304 zoomabs(const Arg 
*arg
) 
 307         xloadfonts(usedfont
, arg
->f
); 
 314 zoomreset(const Arg 
*arg
) 
 318         if (defaultfontsize 
> 0) { 
 319                 larg
.f 
= defaultfontsize
; 
 325 ttysend(const Arg 
*arg
) 
 327         ttywrite(arg
->s
, strlen(arg
->s
), 1); 
 333         int x 
= e
->xbutton
.x 
- borderpx
; 
 334         LIMIT(x
, 0, win
.tw 
- 1); 
 341         int y 
= e
->xbutton
.y 
- borderpx
; 
 342         LIMIT(y
, 0, win
.th 
- 1); 
 347 mousesel(XEvent 
*e
, int done
) 
 349         int type
, seltype 
= SEL_REGULAR
; 
 350         uint state 
= e
->xbutton
.state 
& ~(Button1Mask 
| forcemousemod
); 
 352         for (type 
= 1; type 
< LEN(selmasks
); ++type
) { 
 353                 if (match(selmasks
[type
], state
)) { 
 358         selextend(evcol(e
), evrow(e
), seltype
, done
); 
 360                 setsel(getsel(), e
->xbutton
.time
); 
 364 mousereport(XEvent 
*e
) 
 366         int len
, x 
= evcol(e
), y 
= evrow(e
), 
 367             button 
= e
->xbutton
.button
, state 
= e
->xbutton
.state
; 
 372         if (e
->xbutton
.type 
== MotionNotify
) { 
 373                 if (x 
== ox 
&& y 
== oy
) 
 375                 if (!IS_SET(MODE_MOUSEMOTION
) && !IS_SET(MODE_MOUSEMANY
)) 
 377                 /* MOUSE_MOTION: no reporting if no button is pressed */ 
 378                 if (IS_SET(MODE_MOUSEMOTION
) && oldbutton 
== 3) 
 381                 button 
= oldbutton 
+ 32; 
 385                 if (!IS_SET(MODE_MOUSESGR
) && e
->xbutton
.type 
== ButtonRelease
) { 
 392                 if (e
->xbutton
.type 
== ButtonPress
) { 
 396                 } else if (e
->xbutton
.type 
== ButtonRelease
) { 
 398                         /* MODE_MOUSEX10: no button release reporting */ 
 399                         if (IS_SET(MODE_MOUSEX10
)) 
 401                         if (button 
== 64 || button 
== 65) 
 406         if (!IS_SET(MODE_MOUSEX10
)) { 
 407                 button 
+= ((state 
& ShiftMask  
) ? 4  : 0) 
 408                         + ((state 
& Mod4Mask   
) ? 8  : 0) 
 409                         + ((state 
& ControlMask
) ? 16 : 0); 
 412         if (IS_SET(MODE_MOUSESGR
)) { 
 413                 len 
= snprintf(buf
, sizeof(buf
), "\033[<%d;%d;%d%c", 
 415                                 e
->xbutton
.type 
== ButtonRelease 
? 'm' : 'M'); 
 416         } else if (x 
< 223 && y 
< 223) { 
 417                 len 
= snprintf(buf
, sizeof(buf
), "\033[M%c%c%c", 
 418                                 32+button
, 32+x
+1, 32+y
+1); 
 423         ttywrite(buf
, len
, 0); 
 427 mouseaction(XEvent 
*e
, uint release
) 
 431         for (ms 
= mshortcuts
; ms 
< mshortcuts 
+ LEN(mshortcuts
); ms
++) { 
 432                 if (ms
->release 
== release 
&& 
 433                     ms
->button 
== e
->xbutton
.button 
&& 
 434                     (match(ms
->mod
, e
->xbutton
.state
) ||  /* exact or forced */ 
 435                      match(ms
->mod
, e
->xbutton
.state 
& ~forcemousemod
))) { 
 436                         ms
->func(&(ms
->arg
)); 
 450         if (IS_SET(MODE_MOUSE
) && !(e
->xbutton
.state 
& forcemousemod
)) { 
 455         if (mouseaction(e
, 0)) 
 458         if (e
->xbutton
.button 
== Button1
) { 
 460                  * If the user clicks below predefined timeouts specific 
 461                  * snapping behaviour is exposed. 
 463                 clock_gettime(CLOCK_MONOTONIC
, &now
); 
 464                 if (TIMEDIFF(now
, xsel
.tclick2
) <= tripleclicktimeout
) { 
 466                 } else if (TIMEDIFF(now
, xsel
.tclick1
) <= doubleclicktimeout
) { 
 471                 xsel
.tclick2 
= xsel
.tclick1
; 
 474                 selstart(evcol(e
), evrow(e
), snap
); 
 479 propnotify(XEvent 
*e
) 
 481         XPropertyEvent 
*xpev
; 
 482         Atom clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
 484         xpev 
= &e
->xproperty
; 
 485         if (xpev
->state 
== PropertyNewValue 
&& 
 486                         (xpev
->atom 
== XA_PRIMARY 
|| 
 487                          xpev
->atom 
== clipboard
)) { 
 495         ulong nitems
, ofs
, rem
; 
 497         uchar 
*data
, *last
, *repl
; 
 498         Atom type
, incratom
, property 
= None
; 
 500         incratom 
= XInternAtom(xw
.dpy
, "INCR", 0); 
 503         if (e
->type 
== SelectionNotify
) 
 504                 property 
= e
->xselection
.property
; 
 505         else if (e
->type 
== PropertyNotify
) 
 506                 property 
= e
->xproperty
.atom
; 
 508         if (property 
== None
) 
 512                 if (XGetWindowProperty(xw
.dpy
, xw
.win
, property
, ofs
, 
 513                                         BUFSIZ
/4, False
, AnyPropertyType
, 
 514                                         &type
, &format
, &nitems
, &rem
, 
 516                         fprintf(stderr
, "Clipboard allocation failed\n"); 
 520                 if (e
->type 
== PropertyNotify 
&& nitems 
== 0 && rem 
== 0) { 
 522                          * If there is some PropertyNotify with no data, then 
 523                          * this is the signal of the selection owner that all 
 524                          * data has been transferred. We won't need to receive 
 525                          * PropertyNotify events anymore. 
 527                         MODBIT(xw
.attrs
.event_mask
, 0, PropertyChangeMask
); 
 528                         XChangeWindowAttributes(xw
.dpy
, xw
.win
, CWEventMask
, 
 532                 if (type 
== incratom
) { 
 534                          * Activate the PropertyNotify events so we receive 
 535                          * when the selection owner does send us the next 
 538                         MODBIT(xw
.attrs
.event_mask
, 1, PropertyChangeMask
); 
 539                         XChangeWindowAttributes(xw
.dpy
, xw
.win
, CWEventMask
, 
 543                          * Deleting the property is the transfer start signal. 
 545                         XDeleteProperty(xw
.dpy
, xw
.win
, (int)property
); 
 551                  * Line endings are inconsistent in the terminal and GUI world 
 552                  * copy and pasting. When receiving some selection data, 
 553                  * replace all '\n' with '\r'. 
 554                  * FIXME: Fix the computer world. 
 557                 last 
= data 
+ nitems 
* format 
/ 8; 
 558                 while ((repl 
= memchr(repl
, '\n', last 
- repl
))) { 
 562                 if (IS_SET(MODE_BRCKTPASTE
) && ofs 
== 0) 
 563                         ttywrite("\033[200~", 6, 0); 
 564                 ttywrite((char *)data
, nitems 
* format 
/ 8, 1); 
 565                 if (IS_SET(MODE_BRCKTPASTE
) && rem 
== 0) 
 566                         ttywrite("\033[201~", 6, 0); 
 568                 /* number of 32-bit chunks returned */ 
 569                 ofs 
+= nitems 
* format 
/ 32; 
 573          * Deleting the property again tells the selection owner to send the 
 574          * next data chunk in the property. 
 576         XDeleteProperty(xw
.dpy
, xw
.win
, (int)property
); 
 592 selrequest(XEvent 
*e
) 
 594         XSelectionRequestEvent 
*xsre
; 
 596         Atom xa_targets
, string
, clipboard
; 
 599         xsre 
= (XSelectionRequestEvent 
*) e
; 
 600         xev
.type 
= SelectionNotify
; 
 601         xev
.requestor 
= xsre
->requestor
; 
 602         xev
.selection 
= xsre
->selection
; 
 603         xev
.target 
= xsre
->target
; 
 604         xev
.time 
= xsre
->time
; 
 605         if (xsre
->property 
== None
) 
 606                 xsre
->property 
= xsre
->target
; 
 611         xa_targets 
= XInternAtom(xw
.dpy
, "TARGETS", 0); 
 612         if (xsre
->target 
== xa_targets
) { 
 613                 /* respond with the supported type */ 
 614                 string 
= xsel
.xtarget
; 
 615                 XChangeProperty(xsre
->display
, xsre
->requestor
, xsre
->property
, 
 616                                 XA_ATOM
, 32, PropModeReplace
, 
 617                                 (uchar 
*) &string
, 1); 
 618                 xev
.property 
= xsre
->property
; 
 619         } else if (xsre
->target 
== xsel
.xtarget 
|| xsre
->target 
== XA_STRING
) { 
 621                  * xith XA_STRING non ascii characters may be incorrect in the 
 622                  * requestor. It is not our problem, use utf8. 
 624                 clipboard 
= XInternAtom(xw
.dpy
, "CLIPBOARD", 0); 
 625                 if (xsre
->selection 
== XA_PRIMARY
) { 
 626                         seltext 
= xsel
.primary
; 
 627                 } else if (xsre
->selection 
== clipboard
) { 
 628                         seltext 
= xsel
.clipboard
; 
 631                                 "Unhandled clipboard selection 0x%lx\n", 
 635                 if (seltext 
!= NULL
) { 
 636                         XChangeProperty(xsre
->display
, xsre
->requestor
, 
 637                                         xsre
->property
, xsre
->target
, 
 639                                         (uchar 
*)seltext
, strlen(seltext
)); 
 640                         xev
.property 
= xsre
->property
; 
 644         /* all done, send a notification to the listener */ 
 645         if (!XSendEvent(xsre
->display
, xsre
->requestor
, 1, 0, (XEvent 
*) &xev
)) 
 646                 fprintf(stderr
, "Error sending SelectionNotify event\n"); 
 650 setsel(char *str
, Time t
) 
 658         XSetSelectionOwner(xw
.dpy
, XA_PRIMARY
, xw
.win
, t
); 
 659         if (XGetSelectionOwner(xw
.dpy
, XA_PRIMARY
) != xw
.win
) 
 666         setsel(str
, CurrentTime
); 
 672         if (IS_SET(MODE_MOUSE
) && !(e
->xbutton
.state 
& forcemousemod
)) { 
 677         if (mouseaction(e
, 1)) 
 679         if (e
->xbutton
.button 
== Button1
) 
 686         if (IS_SET(MODE_MOUSE
) && !(e
->xbutton
.state 
& forcemousemod
)) { 
 695 cresize(int width
, int height
) 
 704         col 
= (win
.w 
- 2 * borderpx
) / win
.cw
; 
 705         row 
= (win
.h 
- 2 * borderpx
) / win
.ch
; 
 711         ttyresize(win
.tw
, win
.th
); 
 715 xresize(int col
, int row
) 
 717         win
.tw 
= col 
* win
.cw
; 
 718         win
.th 
= row 
* win
.ch
; 
 720         XFreePixmap(xw
.dpy
, xw
.buf
); 
 721         xw
.buf 
= XCreatePixmap(xw
.dpy
, xw
.win
, win
.w
, win
.h
, 
 722                         DefaultDepth(xw
.dpy
, xw
.scr
)); 
 723         XftDrawChange(xw
.draw
, xw
.buf
); 
 724         xclear(0, 0, win
.w
, win
.h
); 
 726         /* resize to new width */ 
 727         xw
.specbuf 
= xrealloc(xw
.specbuf
, col 
* sizeof(GlyphFontSpec
)); 
 733         return x 
== 0 ? 0 : 0x3737 + 0x2828 * x
; 
 737 xloadcolor(int i
, const char *name
, Color 
*ncolor
) 
 739         XRenderColor color 
= { .alpha 
= 0xffff }; 
 742                 if (BETWEEN(i
, 16, 255)) { /* 256 color */ 
 743                         if (i 
< 6*6*6+16) { /* same colors as xterm */ 
 744                                 color
.red   
= sixd_to_16bit( ((i
-16)/36)%6 ); 
 745                                 color
.green 
= sixd_to_16bit( ((i
-16)/6) %6 ); 
 746                                 color
.blue  
= sixd_to_16bit( ((i
-16)/1) %6 ); 
 747                         } else { /* greyscale */ 
 748                                 color
.red 
= 0x0808 + 0x0a0a * (i 
- (6*6*6+16)); 
 749                                 color
.green 
= color
.blue 
= color
.red
; 
 751                         return XftColorAllocValue(xw
.dpy
, xw
.vis
, 
 752                                                   xw
.cmap
, &color
, ncolor
); 
 757         return XftColorAllocName(xw
.dpy
, xw
.vis
, xw
.cmap
, name
, ncolor
); 
 768                 for (cp 
= dc
.col
; cp 
< &dc
.col
[dc
.collen
]; ++cp
) 
 769                         XftColorFree(xw
.dpy
, xw
.vis
, xw
.cmap
, cp
); 
 771                 dc
.collen 
= MAX(LEN(colorname
), 256); 
 772                 dc
.col 
= xmalloc(dc
.collen 
* sizeof(Color
)); 
 775         for (i 
= 0; i 
< dc
.collen
; i
++) 
 776                 if (!xloadcolor(i
, NULL
, &dc
.col
[i
])) { 
 778                                 die("could not allocate color '%s'\n", colorname
[i
]); 
 780                                 die("could not allocate color %d\n", i
); 
 786 xsetcolorname(int x
, const char *name
) 
 790         if (!BETWEEN(x
, 0, dc
.collen
)) 
 793         if (!xloadcolor(x
, name
, &ncolor
)) 
 796         XftColorFree(xw
.dpy
, xw
.vis
, xw
.cmap
, &dc
.col
[x
]); 
 803  * Absolute coordinates. 
 806 xclear(int x1
, int y1
, int x2
, int y2
) 
 809                         &dc
.col
[IS_SET(MODE_REVERSE
)? defaultfg 
: defaultbg
], 
 810                         x1
, y1
, x2
-x1
, y2
-y1
); 
 816         XClassHint 
class = {opt_name 
? opt_name 
: termname
, 
 817                             opt_class 
? opt_class 
: termname
}; 
 818         XWMHints wm 
= {.flags 
= InputHint
, .input 
= 1}; 
 821         sizeh 
= XAllocSizeHints(); 
 823         sizeh
->flags 
= PSize 
| PResizeInc 
| PBaseSize 
| PMinSize
; 
 824         sizeh
->height 
= win
.h
; 
 825         sizeh
->width 
= win
.w
; 
 826         sizeh
->height_inc 
= win
.ch
; 
 827         sizeh
->width_inc 
= win
.cw
; 
 828         sizeh
->base_height 
= 2 * borderpx
; 
 829         sizeh
->base_width 
= 2 * borderpx
; 
 830         sizeh
->min_height 
= win
.ch 
+ 2 * borderpx
; 
 831         sizeh
->min_width 
= win
.cw 
+ 2 * borderpx
; 
 833                 sizeh
->flags 
|= PMaxSize
; 
 834                 sizeh
->min_width 
= sizeh
->max_width 
= win
.w
; 
 835                 sizeh
->min_height 
= sizeh
->max_height 
= win
.h
; 
 837         if (xw
.gm 
& (XValue
|YValue
)) { 
 838                 sizeh
->flags 
|= USPosition 
| PWinGravity
; 
 841                 sizeh
->win_gravity 
= xgeommasktogravity(xw
.gm
); 
 844         XSetWMProperties(xw
.dpy
, xw
.win
, NULL
, NULL
, NULL
, 0, sizeh
, &wm
, 
 850 xgeommasktogravity(int mask
) 
 852         switch (mask 
& (XNegative
|YNegative
)) { 
 854                 return NorthWestGravity
; 
 856                 return NorthEastGravity
; 
 858                 return SouthWestGravity
; 
 861         return SouthEastGravity
; 
 865 xloadfont(Font 
*f
, FcPattern 
*pattern
) 
 867         FcPattern 
*configured
; 
 871         int wantattr
, haveattr
; 
 874          * Manually configure instead of calling XftMatchFont 
 875          * so that we can use the configured pattern for 
 876          * "missing glyph" lookups. 
 878         configured 
= FcPatternDuplicate(pattern
); 
 882         FcConfigSubstitute(NULL
, configured
, FcMatchPattern
); 
 883         XftDefaultSubstitute(xw
.dpy
, xw
.scr
, configured
); 
 885         match 
= FcFontMatch(NULL
, configured
, &result
); 
 887                 FcPatternDestroy(configured
); 
 891         if (!(f
->match 
= XftFontOpenPattern(xw
.dpy
, match
))) { 
 892                 FcPatternDestroy(configured
); 
 893                 FcPatternDestroy(match
); 
 897         if ((XftPatternGetInteger(pattern
, "slant", 0, &wantattr
) == 
 900                  * Check if xft was unable to find a font with the appropriate 
 901                  * slant but gave us one anyway. Try to mitigate. 
 903                 if ((XftPatternGetInteger(f
->match
->pattern
, "slant", 0, 
 904                     &haveattr
) != XftResultMatch
) || haveattr 
< wantattr
) { 
 906                         fputs("font slant does not match\n", stderr
); 
 910         if ((XftPatternGetInteger(pattern
, "weight", 0, &wantattr
) == 
 912                 if ((XftPatternGetInteger(f
->match
->pattern
, "weight", 0, 
 913                     &haveattr
) != XftResultMatch
) || haveattr 
!= wantattr
) { 
 915                         fputs("font weight does not match\n", stderr
); 
 919         XftTextExtentsUtf8(xw
.dpy
, f
->match
, 
 920                 (const FcChar8 
*) ascii_printable
, 
 921                 strlen(ascii_printable
), &extents
); 
 924         f
->pattern 
= configured
; 
 926         f
->ascent 
= f
->match
->ascent
; 
 927         f
->descent 
= f
->match
->descent
; 
 929         f
->rbearing 
= f
->match
->max_advance_width
; 
 931         f
->height 
= f
->ascent 
+ f
->descent
; 
 932         f
->width 
= DIVCEIL(extents
.xOff
, strlen(ascii_printable
)); 
 938 xloadfonts(char *fontstr
, double fontsize
) 
 943         if (fontstr
[0] == '-') 
 944                 pattern 
= XftXlfdParse(fontstr
, False
, False
); 
 946                 pattern 
= FcNameParse((FcChar8 
*)fontstr
); 
 949                 die("can't open font %s\n", fontstr
); 
 952                 FcPatternDel(pattern
, FC_PIXEL_SIZE
); 
 953                 FcPatternDel(pattern
, FC_SIZE
); 
 954                 FcPatternAddDouble(pattern
, FC_PIXEL_SIZE
, (double)fontsize
); 
 955                 usedfontsize 
= fontsize
; 
 957                 if (FcPatternGetDouble(pattern
, FC_PIXEL_SIZE
, 0, &fontval
) == 
 959                         usedfontsize 
= fontval
; 
 960                 } else if (FcPatternGetDouble(pattern
, FC_SIZE
, 0, &fontval
) == 
 965                          * Default font size is 12, if none given. This is to 
 966                          * have a known usedfontsize value. 
 968                         FcPatternAddDouble(pattern
, FC_PIXEL_SIZE
, 12); 
 971                 defaultfontsize 
= usedfontsize
; 
 974         if (xloadfont(&dc
.font
, pattern
)) 
 975                 die("can't open font %s\n", fontstr
); 
 977         if (usedfontsize 
< 0) { 
 978                 FcPatternGetDouble(dc
.font
.match
->pattern
, 
 979                                    FC_PIXEL_SIZE
, 0, &fontval
); 
 980                 usedfontsize 
= fontval
; 
 982                         defaultfontsize 
= fontval
; 
 985         /* Setting character width and height. */ 
 986         win
.cw 
= ceilf(dc
.font
.width 
* cwscale
); 
 987         win
.ch 
= ceilf(dc
.font
.height 
* chscale
); 
 989         FcPatternDel(pattern
, FC_SLANT
); 
 990         FcPatternAddInteger(pattern
, FC_SLANT
, FC_SLANT_ITALIC
); 
 991         if (xloadfont(&dc
.ifont
, pattern
)) 
 992                 die("can't open font %s\n", fontstr
); 
 994         FcPatternDel(pattern
, FC_WEIGHT
); 
 995         FcPatternAddInteger(pattern
, FC_WEIGHT
, FC_WEIGHT_BOLD
); 
 996         if (xloadfont(&dc
.ibfont
, pattern
)) 
 997                 die("can't open font %s\n", fontstr
); 
 999         FcPatternDel(pattern
, FC_SLANT
); 
1000         FcPatternAddInteger(pattern
, FC_SLANT
, FC_SLANT_ROMAN
); 
1001         if (xloadfont(&dc
.bfont
, pattern
)) 
1002                 die("can't open font %s\n", fontstr
); 
1004         FcPatternDestroy(pattern
); 
1008 xunloadfont(Font 
*f
) 
1010         XftFontClose(xw
.dpy
, f
->match
); 
1011         FcPatternDestroy(f
->pattern
); 
1013                 FcFontSetDestroy(f
->set
); 
1019         /* Free the loaded fonts in the font cache.  */ 
1021                 XftFontClose(xw
.dpy
, frc
[--frclen
].font
); 
1023         xunloadfont(&dc
.font
); 
1024         xunloadfont(&dc
.bfont
); 
1025         xunloadfont(&dc
.ifont
); 
1026         xunloadfont(&dc
.ibfont
); 
1030 ximopen(Display 
*dpy
) 
1032         XIMCallback imdestroy 
= { .client_data 
= NULL
, .callback 
= ximdestroy 
}; 
1033         XICCallback icdestroy 
= { .client_data 
= NULL
, .callback 
= xicdestroy 
}; 
1035         xw
.ime
.xim 
= XOpenIM(xw
.dpy
, NULL
, NULL
, NULL
); 
1036         if (xw
.ime
.xim 
== NULL
) 
1039         if (XSetIMValues(xw
.ime
.xim
, XNDestroyCallback
, &imdestroy
, NULL
)) 
1040                 fprintf(stderr
, "XSetIMValues: " 
1041                                 "Could not set XNDestroyCallback.\n"); 
1043         xw
.ime
.spotlist 
= XVaCreateNestedList(0, XNSpotLocation
, &xw
.ime
.spot
, 
1046         if (xw
.ime
.xic 
== NULL
) { 
1047                 xw
.ime
.xic 
= XCreateIC(xw
.ime
.xim
, XNInputStyle
, 
1048                                        XIMPreeditNothing 
| XIMStatusNothing
, 
1049                                        XNClientWindow
, xw
.win
, 
1050                                        XNFocusWindow
, xw
.win
, 
1051                                        XNDestroyCallback
, &icdestroy
, 
1054         if (xw
.ime
.xic 
== NULL
) 
1055                 fprintf(stderr
, "XCreateIC: Could not create input context.\n"); 
1061 ximinstantiate(Display 
*dpy
, XPointer client
, XPointer call
) 
1064                 XUnregisterIMInstantiateCallback(xw
.dpy
, NULL
, NULL
, NULL
, 
1065                                                  ximinstantiate
, NULL
); 
1069 ximdestroy(XIM xim
, XPointer client
, XPointer call
) 
1072         XRegisterIMInstantiateCallback(xw
.dpy
, NULL
, NULL
, NULL
, 
1073                                        ximinstantiate
, NULL
); 
1074         XFree(xw
.ime
.spotlist
); 
1078 xicdestroy(XIC xim
, XPointer client
, XPointer call
) 
1085 xinit(int cols
, int rows
) 
1090         pid_t thispid 
= getpid(); 
1091         XColor xmousefg
, xmousebg
; 
1093         if (!(xw
.dpy 
= XOpenDisplay(NULL
))) 
1094                 die("can't open display\n"); 
1095         xw
.scr 
= XDefaultScreen(xw
.dpy
); 
1096         xw
.vis 
= XDefaultVisual(xw
.dpy
, xw
.scr
); 
1100                 die("could not init fontconfig.\n"); 
1102         usedfont 
= (opt_font 
== NULL
)? font 
: opt_font
; 
1103         xloadfonts(usedfont
, 0); 
1106         xw
.cmap 
= XDefaultColormap(xw
.dpy
, xw
.scr
); 
1109         /* adjust fixed window geometry */ 
1110         win
.w 
= 2 * borderpx 
+ cols 
* win
.cw
; 
1111         win
.h 
= 2 * borderpx 
+ rows 
* win
.ch
; 
1112         if (xw
.gm 
& XNegative
) 
1113                 xw
.l 
+= DisplayWidth(xw
.dpy
, xw
.scr
) - win
.w 
- 2; 
1114         if (xw
.gm 
& YNegative
) 
1115                 xw
.t 
+= DisplayHeight(xw
.dpy
, xw
.scr
) - win
.h 
- 2; 
1118         xw
.attrs
.background_pixel 
= dc
.col
[defaultbg
].pixel
; 
1119         xw
.attrs
.border_pixel 
= dc
.col
[defaultbg
].pixel
; 
1120         xw
.attrs
.bit_gravity 
= NorthWestGravity
; 
1121         xw
.attrs
.event_mask 
= FocusChangeMask 
| KeyPressMask 
| KeyReleaseMask
 
1122                 | ExposureMask 
| VisibilityChangeMask 
| StructureNotifyMask
 
1123                 | ButtonMotionMask 
| ButtonPressMask 
| ButtonReleaseMask
; 
1124         xw
.attrs
.colormap 
= xw
.cmap
; 
1126         if (!(opt_embed 
&& (parent 
= strtol(opt_embed
, NULL
, 0)))) 
1127                 parent 
= XRootWindow(xw
.dpy
, xw
.scr
); 
1128         xw
.win 
= XCreateWindow(xw
.dpy
, parent
, xw
.l
, xw
.t
, 
1129                         win
.w
, win
.h
, 0, XDefaultDepth(xw
.dpy
, xw
.scr
), InputOutput
, 
1130                         xw
.vis
, CWBackPixel 
| CWBorderPixel 
| CWBitGravity
 
1131                         | CWEventMask 
| CWColormap
, &xw
.attrs
); 
1133         memset(&gcvalues
, 0, sizeof(gcvalues
)); 
1134         gcvalues
.graphics_exposures 
= False
; 
1135         dc
.gc 
= XCreateGC(xw
.dpy
, parent
, GCGraphicsExposures
, 
1137         xw
.buf 
= XCreatePixmap(xw
.dpy
, xw
.win
, win
.w
, win
.h
, 
1138                         DefaultDepth(xw
.dpy
, xw
.scr
)); 
1139         XSetForeground(xw
.dpy
, dc
.gc
, dc
.col
[defaultbg
].pixel
); 
1140         XFillRectangle(xw
.dpy
, xw
.buf
, dc
.gc
, 0, 0, win
.w
, win
.h
); 
1142         /* font spec buffer */ 
1143         xw
.specbuf 
= xmalloc(cols 
* sizeof(GlyphFontSpec
)); 
1145         /* Xft rendering context */ 
1146         xw
.draw 
= XftDrawCreate(xw
.dpy
, xw
.buf
, xw
.vis
, xw
.cmap
); 
1149         if (!ximopen(xw
.dpy
)) { 
1150                 XRegisterIMInstantiateCallback(xw
.dpy
, NULL
, NULL
, NULL
, 
1151                                                ximinstantiate
, NULL
); 
1154         /* white cursor, black outline */ 
1155         cursor 
= XCreateFontCursor(xw
.dpy
, mouseshape
); 
1156         XDefineCursor(xw
.dpy
, xw
.win
, cursor
); 
1158         if (XParseColor(xw
.dpy
, xw
.cmap
, colorname
[mousefg
], &xmousefg
) == 0) { 
1159                 xmousefg
.red   
= 0xffff; 
1160                 xmousefg
.green 
= 0xffff; 
1161                 xmousefg
.blue  
= 0xffff; 
1164         if (XParseColor(xw
.dpy
, xw
.cmap
, colorname
[mousebg
], &xmousebg
) == 0) { 
1165                 xmousebg
.red   
= 0x0000; 
1166                 xmousebg
.green 
= 0x0000; 
1167                 xmousebg
.blue  
= 0x0000; 
1170         XRecolorCursor(xw
.dpy
, cursor
, &xmousefg
, &xmousebg
); 
1172         xw
.xembed 
= XInternAtom(xw
.dpy
, "_XEMBED", False
); 
1173         xw
.wmdeletewin 
= XInternAtom(xw
.dpy
, "WM_DELETE_WINDOW", False
); 
1174         xw
.netwmname 
= XInternAtom(xw
.dpy
, "_NET_WM_NAME", False
); 
1175         XSetWMProtocols(xw
.dpy
, xw
.win
, &xw
.wmdeletewin
, 1); 
1177         xw
.netwmpid 
= XInternAtom(xw
.dpy
, "_NET_WM_PID", False
); 
1178         XChangeProperty(xw
.dpy
, xw
.win
, xw
.netwmpid
, XA_CARDINAL
, 32, 
1179                         PropModeReplace
, (uchar 
*)&thispid
, 1); 
1181         win
.mode 
= MODE_NUMLOCK
; 
1184         XMapWindow(xw
.dpy
, xw
.win
); 
1185         XSync(xw
.dpy
, False
); 
1187         clock_gettime(CLOCK_MONOTONIC
, &xsel
.tclick1
); 
1188         clock_gettime(CLOCK_MONOTONIC
, &xsel
.tclick2
); 
1189         xsel
.primary 
= NULL
; 
1190         xsel
.clipboard 
= NULL
; 
1191         xsel
.xtarget 
= XInternAtom(xw
.dpy
, "UTF8_STRING", 0); 
1192         if (xsel
.xtarget 
== None
) 
1193                 xsel
.xtarget 
= XA_STRING
; 
1197 xmakeglyphfontspecs(XftGlyphFontSpec 
*specs
, const Glyph 
*glyphs
, int len
, int x
, int y
) 
1199         float winx 
= borderpx 
+ x 
* win
.cw
, winy 
= borderpx 
+ y 
* win
.ch
, xp
, yp
; 
1200         ushort mode
, prevmode 
= USHRT_MAX
; 
1201         Font 
*font 
= &dc
.font
; 
1202         int frcflags 
= FRC_NORMAL
; 
1203         float runewidth 
= win
.cw
; 
1207         FcPattern 
*fcpattern
, *fontpattern
; 
1208         FcFontSet 
*fcsets
[] = { NULL 
}; 
1209         FcCharSet 
*fccharset
; 
1210         int i
, f
, numspecs 
= 0; 
1212         for (i 
= 0, xp 
= winx
, yp 
= winy 
+ font
->ascent
; i 
< len
; ++i
) { 
1213                 /* Fetch rune and mode for current glyph. */ 
1215                 mode 
= glyphs
[i
].mode
; 
1217                 /* Skip dummy wide-character spacing. */ 
1218                 if (mode 
== ATTR_WDUMMY
) 
1221                 /* Determine font for glyph if different from previous glyph. */ 
1222                 if (prevmode 
!= mode
) { 
1225                         frcflags 
= FRC_NORMAL
; 
1226                         runewidth 
= win
.cw 
* ((mode 
& ATTR_WIDE
) ? 2.0f 
: 1.0f
); 
1227                         if ((mode 
& ATTR_ITALIC
) && (mode 
& ATTR_BOLD
)) { 
1229                                 frcflags 
= FRC_ITALICBOLD
; 
1230                         } else if (mode 
& ATTR_ITALIC
) { 
1232                                 frcflags 
= FRC_ITALIC
; 
1233                         } else if (mode 
& ATTR_BOLD
) { 
1235                                 frcflags 
= FRC_BOLD
; 
1237                         yp 
= winy 
+ font
->ascent
; 
1240                 /* Lookup character index with default font. */ 
1241                 glyphidx 
= XftCharIndex(xw
.dpy
, font
->match
, rune
); 
1243                         specs
[numspecs
].font 
= font
->match
; 
1244                         specs
[numspecs
].glyph 
= glyphidx
; 
1245                         specs
[numspecs
].x 
= (short)xp
; 
1246                         specs
[numspecs
].y 
= (short)yp
; 
1252                 /* Fallback on font cache, search the font cache for match. */ 
1253                 for (f 
= 0; f 
< frclen
; f
++) { 
1254                         glyphidx 
= XftCharIndex(xw
.dpy
, frc
[f
].font
, rune
); 
1255                         /* Everything correct. */ 
1256                         if (glyphidx 
&& frc
[f
].flags 
== frcflags
) 
1258                         /* We got a default font for a not found glyph. */ 
1259                         if (!glyphidx 
&& frc
[f
].flags 
== frcflags
 
1260                                         && frc
[f
].unicodep 
== rune
) { 
1265                 /* Nothing was found. Use fontconfig to find matching font. */ 
1268                                 font
->set 
= FcFontSort(0, font
->pattern
, 
1270                         fcsets
[0] = font
->set
; 
1273                          * Nothing was found in the cache. Now use 
1274                          * some dozen of Fontconfig calls to get the 
1275                          * font for one single character. 
1277                          * Xft and fontconfig are design failures. 
1279                         fcpattern 
= FcPatternDuplicate(font
->pattern
); 
1280                         fccharset 
= FcCharSetCreate(); 
1282                         FcCharSetAddChar(fccharset
, rune
); 
1283                         FcPatternAddCharSet(fcpattern
, FC_CHARSET
, 
1285                         FcPatternAddBool(fcpattern
, FC_SCALABLE
, 1); 
1287                         FcConfigSubstitute(0, fcpattern
, 
1289                         FcDefaultSubstitute(fcpattern
); 
1291                         fontpattern 
= FcFontSetMatch(0, fcsets
, 1, 
1294                         /* Allocate memory for the new cache entry. */ 
1295                         if (frclen 
>= frccap
) { 
1297                                 frc 
= xrealloc(frc
, frccap 
* sizeof(Fontcache
)); 
1300                         frc
[frclen
].font 
= XftFontOpenPattern(xw
.dpy
, 
1302                         if (!frc
[frclen
].font
) 
1303                                 die("XftFontOpenPattern failed seeking fallback font: %s\n", 
1305                         frc
[frclen
].flags 
= frcflags
; 
1306                         frc
[frclen
].unicodep 
= rune
; 
1308                         glyphidx 
= XftCharIndex(xw
.dpy
, frc
[frclen
].font
, rune
); 
1313                         FcPatternDestroy(fcpattern
); 
1314                         FcCharSetDestroy(fccharset
); 
1317                 specs
[numspecs
].font 
= frc
[f
].font
; 
1318                 specs
[numspecs
].glyph 
= glyphidx
; 
1319                 specs
[numspecs
].x 
= (short)xp
; 
1320                 specs
[numspecs
].y 
= (short)yp
; 
1329 xdrawglyphfontspecs(const XftGlyphFontSpec 
*specs
, Glyph base
, int len
, int x
, int y
) 
1331         int charlen 
= len 
* ((base
.mode 
& ATTR_WIDE
) ? 2 : 1); 
1332         int winx 
= borderpx 
+ x 
* win
.cw
, winy 
= borderpx 
+ y 
* win
.ch
, 
1333             width 
= charlen 
* win
.cw
; 
1334         Color 
*fg
, *bg
, *temp
, revfg
, revbg
, truefg
, truebg
; 
1335         XRenderColor colfg
, colbg
; 
1338         /* Fallback on color display for attributes not supported by the font */ 
1339         if (base
.mode 
& ATTR_ITALIC 
&& base
.mode 
& ATTR_BOLD
) { 
1340                 if (dc
.ibfont
.badslant 
|| dc
.ibfont
.badweight
) 
1341                         base
.fg 
= defaultattr
; 
1342         } else if ((base
.mode 
& ATTR_ITALIC 
&& dc
.ifont
.badslant
) || 
1343             (base
.mode 
& ATTR_BOLD 
&& dc
.bfont
.badweight
)) { 
1344                 base
.fg 
= defaultattr
; 
1347         if (IS_TRUECOL(base
.fg
)) { 
1348                 colfg
.alpha 
= 0xffff; 
1349                 colfg
.red 
= TRUERED(base
.fg
); 
1350                 colfg
.green 
= TRUEGREEN(base
.fg
); 
1351                 colfg
.blue 
= TRUEBLUE(base
.fg
); 
1352                 XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colfg
, &truefg
); 
1355                 fg 
= &dc
.col
[base
.fg
]; 
1358         if (IS_TRUECOL(base
.bg
)) { 
1359                 colbg
.alpha 
= 0xffff; 
1360                 colbg
.green 
= TRUEGREEN(base
.bg
); 
1361                 colbg
.red 
= TRUERED(base
.bg
); 
1362                 colbg
.blue 
= TRUEBLUE(base
.bg
); 
1363                 XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colbg
, &truebg
); 
1366                 bg 
= &dc
.col
[base
.bg
]; 
1369         /* Change basic system colors [0-7] to bright system colors [8-15] */ 
1370         if ((base
.mode 
& ATTR_BOLD_FAINT
) == ATTR_BOLD 
&& BETWEEN(base
.fg
, 0, 7)) 
1371                 fg 
= &dc
.col
[base
.fg 
+ 8]; 
1373         if (IS_SET(MODE_REVERSE
)) { 
1374                 if (fg 
== &dc
.col
[defaultfg
]) { 
1375                         fg 
= &dc
.col
[defaultbg
]; 
1377                         colfg
.red 
= ~fg
->color
.red
; 
1378                         colfg
.green 
= ~fg
->color
.green
; 
1379                         colfg
.blue 
= ~fg
->color
.blue
; 
1380                         colfg
.alpha 
= fg
->color
.alpha
; 
1381                         XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colfg
, 
1386                 if (bg 
== &dc
.col
[defaultbg
]) { 
1387                         bg 
= &dc
.col
[defaultfg
]; 
1389                         colbg
.red 
= ~bg
->color
.red
; 
1390                         colbg
.green 
= ~bg
->color
.green
; 
1391                         colbg
.blue 
= ~bg
->color
.blue
; 
1392                         colbg
.alpha 
= bg
->color
.alpha
; 
1393                         XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colbg
, 
1399         if ((base
.mode 
& ATTR_BOLD_FAINT
) == ATTR_FAINT
) { 
1400                 colfg
.red 
= fg
->color
.red 
/ 2; 
1401                 colfg
.green 
= fg
->color
.green 
/ 2; 
1402                 colfg
.blue 
= fg
->color
.blue 
/ 2; 
1403                 colfg
.alpha 
= fg
->color
.alpha
; 
1404                 XftColorAllocValue(xw
.dpy
, xw
.vis
, xw
.cmap
, &colfg
, &revfg
); 
1408         if (base
.mode 
& ATTR_REVERSE
) { 
1414         if (base
.mode 
& ATTR_BLINK 
&& win
.mode 
& MODE_BLINK
) 
1417         if (base
.mode 
& ATTR_INVISIBLE
) 
1420         /* Intelligent cleaning up of the borders. */ 
1422                 xclear(0, (y 
== 0)? 0 : winy
, borderpx
, 
1424                         ((winy 
+ win
.ch 
>= borderpx 
+ win
.th
)? win
.h 
: 0)); 
1426         if (winx 
+ width 
>= borderpx 
+ win
.tw
) { 
1427                 xclear(winx 
+ width
, (y 
== 0)? 0 : winy
, win
.w
, 
1428                         ((winy 
+ win
.ch 
>= borderpx 
+ win
.th
)? win
.h 
: (winy 
+ win
.ch
))); 
1431                 xclear(winx
, 0, winx 
+ width
, borderpx
); 
1432         if (winy 
+ win
.ch 
>= borderpx 
+ win
.th
) 
1433                 xclear(winx
, winy 
+ win
.ch
, winx 
+ width
, win
.h
); 
1435         /* Clean up the region we want to draw to. */ 
1436         XftDrawRect(xw
.draw
, bg
, winx
, winy
, width
, win
.ch
); 
1438         /* Set the clip region because Xft is sometimes dirty. */ 
1443         XftDrawSetClipRectangles(xw
.draw
, winx
, winy
, &r
, 1); 
1445         /* Render the glyphs. */ 
1446         XftDrawGlyphFontSpec(xw
.draw
, fg
, specs
, len
); 
1448         /* Render underline and strikethrough. */ 
1449         if (base
.mode 
& ATTR_UNDERLINE
) { 
1450                 XftDrawRect(xw
.draw
, fg
, winx
, winy 
+ dc
.font
.ascent 
+ 1, 
1454         if (base
.mode 
& ATTR_STRUCK
) { 
1455                 XftDrawRect(xw
.draw
, fg
, winx
, winy 
+ 2 * dc
.font
.ascent 
/ 3, 
1459         /* Reset clip to none. */ 
1460         XftDrawSetClip(xw
.draw
, 0); 
1464 xdrawglyph(Glyph g
, int x
, int y
) 
1467         XftGlyphFontSpec spec
; 
1469         numspecs 
= xmakeglyphfontspecs(&spec
, &g
, 1, x
, y
); 
1470         xdrawglyphfontspecs(&spec
, g
, numspecs
, x
, y
); 
1474 xdrawcursor(int cx
, int cy
, Glyph g
, int ox
, int oy
, Glyph og
) 
1478         /* remove the old cursor */ 
1479         if (selected(ox
, oy
)) 
1480                 og
.mode 
^= ATTR_REVERSE
; 
1481         xdrawglyph(og
, ox
, oy
); 
1483         if (IS_SET(MODE_HIDE
)) 
1487          * Select the right color for the right mode. 
1489         g
.mode 
&= ATTR_BOLD
|ATTR_ITALIC
|ATTR_UNDERLINE
|ATTR_STRUCK
|ATTR_WIDE
; 
1491         if (IS_SET(MODE_REVERSE
)) { 
1492                 g
.mode 
|= ATTR_REVERSE
; 
1494                 if (selected(cx
, cy
)) { 
1495                         drawcol 
= dc
.col
[defaultcs
]; 
1498                         drawcol 
= dc
.col
[defaultrcs
]; 
1502                 if (selected(cx
, cy
)) { 
1509                 drawcol 
= dc
.col
[g
.bg
]; 
1512         /* draw the new one */ 
1513         if (IS_SET(MODE_FOCUSED
)) { 
1514                 switch (win
.cursor
) { 
1515                 case 7: /* st extension: snowman (U+2603) */ 
1517                 case 0: /* Blinking Block */ 
1518                 case 1: /* Blinking Block (Default) */ 
1519                 case 2: /* Steady Block */ 
1520                         xdrawglyph(g
, cx
, cy
); 
1522                 case 3: /* Blinking Underline */ 
1523                 case 4: /* Steady Underline */ 
1524                         XftDrawRect(xw
.draw
, &drawcol
, 
1525                                         borderpx 
+ cx 
* win
.cw
, 
1526                                         borderpx 
+ (cy 
+ 1) * win
.ch 
- \
 
1528                                         win
.cw
, cursorthickness
); 
1530                 case 5: /* Blinking bar */ 
1531                 case 6: /* Steady bar */ 
1532                         XftDrawRect(xw
.draw
, &drawcol
, 
1533                                         borderpx 
+ cx 
* win
.cw
, 
1534                                         borderpx 
+ cy 
* win
.ch
, 
1535                                         cursorthickness
, win
.ch
); 
1539                 XftDrawRect(xw
.draw
, &drawcol
, 
1540                                 borderpx 
+ cx 
* win
.cw
, 
1541                                 borderpx 
+ cy 
* win
.ch
, 
1543                 XftDrawRect(xw
.draw
, &drawcol
, 
1544                                 borderpx 
+ cx 
* win
.cw
, 
1545                                 borderpx 
+ cy 
* win
.ch
, 
1547                 XftDrawRect(xw
.draw
, &drawcol
, 
1548                                 borderpx 
+ (cx 
+ 1) * win
.cw 
- 1, 
1549                                 borderpx 
+ cy 
* win
.ch
, 
1551                 XftDrawRect(xw
.draw
, &drawcol
, 
1552                                 borderpx 
+ cx 
* win
.cw
, 
1553                                 borderpx 
+ (cy 
+ 1) * win
.ch 
- 1, 
1561         char buf
[sizeof(long) * 8 + 1]; 
1563         snprintf(buf
, sizeof(buf
), "%lu", xw
.win
); 
1564         setenv("WINDOWID", buf
, 1); 
1571         DEFAULT(p
, opt_title
); 
1573         Xutf8TextListToTextProperty(xw
.dpy
, &p
, 1, XUTF8StringStyle
, 
1575         XSetWMName(xw
.dpy
, xw
.win
, &prop
); 
1576         XSetTextProperty(xw
.dpy
, xw
.win
, &prop
, xw
.netwmname
); 
1583         return IS_SET(MODE_VISIBLE
); 
1587 xdrawline(Line line
, int x1
, int y1
, int x2
) 
1589         int i
, x
, ox
, numspecs
; 
1591         XftGlyphFontSpec 
*specs 
= xw
.specbuf
; 
1593         numspecs 
= xmakeglyphfontspecs(specs
, &line
[x1
], x2 
- x1
, x1
, y1
); 
1595         for (x 
= x1
; x 
< x2 
&& i 
< numspecs
; x
++) { 
1597                 if (new.mode 
== ATTR_WDUMMY
) 
1599                 if (selected(x
, y1
)) 
1600                         new.mode 
^= ATTR_REVERSE
; 
1601                 if (i 
> 0 && ATTRCMP(base
, new)) { 
1602                         xdrawglyphfontspecs(specs
, base
, i
, ox
, y1
); 
1614                 xdrawglyphfontspecs(specs
, base
, i
, ox
, y1
); 
1620         XCopyArea(xw
.dpy
, xw
.buf
, xw
.win
, dc
.gc
, 0, 0, win
.w
, 
1622         XSetForeground(xw
.dpy
, dc
.gc
, 
1623                         dc
.col
[IS_SET(MODE_REVERSE
)? 
1624                                 defaultfg 
: defaultbg
].pixel
); 
1628 xximspot(int x
, int y
) 
1630         if (xw
.ime
.xic 
== NULL
) 
1633         xw
.ime
.spot
.x 
= borderpx 
+ x 
* win
.cw
; 
1634         xw
.ime
.spot
.y 
= borderpx 
+ (y 
+ 1) * win
.ch
; 
1636         XSetICValues(xw
.ime
.xic
, XNPreeditAttributes
, xw
.ime
.spotlist
, NULL
); 
1646 visibility(XEvent 
*ev
) 
1648         XVisibilityEvent 
*e 
= &ev
->xvisibility
; 
1650         MODBIT(win
.mode
, e
->state 
!= VisibilityFullyObscured
, MODE_VISIBLE
); 
1656         win
.mode 
&= ~MODE_VISIBLE
; 
1660 xsetpointermotion(int set
) 
1662         MODBIT(xw
.attrs
.event_mask
, set
, PointerMotionMask
); 
1663         XChangeWindowAttributes(xw
.dpy
, xw
.win
, CWEventMask
, &xw
.attrs
); 
1667 xsetmode(int set
, unsigned int flags
) 
1669         int mode 
= win
.mode
; 
1670         MODBIT(win
.mode
, set
, flags
); 
1671         if ((win
.mode 
& MODE_REVERSE
) != (mode 
& MODE_REVERSE
)) 
1676 xsetcursor(int cursor
) 
1679         if (!BETWEEN(cursor
, 0, 6)) 
1681         win
.cursor 
= cursor
; 
1686 xseturgency(int add
) 
1688         XWMHints 
*h 
= XGetWMHints(xw
.dpy
, xw
.win
); 
1690         MODBIT(h
->flags
, add
, XUrgencyHint
); 
1691         XSetWMHints(xw
.dpy
, xw
.win
, h
); 
1698         if (!(IS_SET(MODE_FOCUSED
))) 
1701                 XkbBell(xw
.dpy
, xw
.win
, bellvolume
, (Atom
)NULL
); 
1707         XFocusChangeEvent 
*e 
= &ev
->xfocus
; 
1709         if (e
->mode 
== NotifyGrab
) 
1712         if (ev
->type 
== FocusIn
) { 
1714                         XSetICFocus(xw
.ime
.xic
); 
1715                 win
.mode 
|= MODE_FOCUSED
; 
1717                 if (IS_SET(MODE_FOCUS
)) 
1718                         ttywrite("\033[I", 3, 0); 
1721                         XUnsetICFocus(xw
.ime
.xic
); 
1722                 win
.mode 
&= ~MODE_FOCUSED
; 
1723                 if (IS_SET(MODE_FOCUS
)) 
1724                         ttywrite("\033[O", 3, 0); 
1729 match(uint mask
, uint state
) 
1731         return mask 
== XK_ANY_MOD 
|| mask 
== (state 
& ~ignoremod
); 
1735 kmap(KeySym k
, uint state
) 
1740         /* Check for mapped keys out of X11 function keys. */ 
1741         for (i 
= 0; i 
< LEN(mappedkeys
); i
++) { 
1742                 if (mappedkeys
[i
] == k
) 
1745         if (i 
== LEN(mappedkeys
)) { 
1746                 if ((k 
& 0xFFFF) < 0xFD00) 
1750         for (kp 
= key
; kp 
< key 
+ LEN(key
); kp
++) { 
1754                 if (!match(kp
->mask
, state
)) 
1757                 if (IS_SET(MODE_APPKEYPAD
) ? kp
->appkey 
< 0 : kp
->appkey 
> 0) 
1759                 if (IS_SET(MODE_NUMLOCK
) && kp
->appkey 
== 2) 
1762                 if (IS_SET(MODE_APPCURSOR
) ? kp
->appcursor 
< 0 : kp
->appcursor 
> 0) 
1774         XKeyEvent 
*e 
= &ev
->xkey
; 
1776         char buf
[64], *customkey
; 
1782         if (IS_SET(MODE_KBDLOCK
)) 
1786                 len 
= XmbLookupString(xw
.ime
.xic
, e
, buf
, sizeof buf
, &ksym
, &status
); 
1788                 len 
= XLookupString(e
, buf
, sizeof buf
, &ksym
, NULL
); 
1790         for (bp 
= shortcuts
; bp 
< shortcuts 
+ LEN(shortcuts
); bp
++) { 
1791                 if (ksym 
== bp
->keysym 
&& match(bp
->mod
, e
->state
)) { 
1792                         bp
->func(&(bp
->arg
)); 
1797         /* 2. custom keys from config.h */ 
1798         if ((customkey 
= kmap(ksym
, e
->state
))) { 
1799                 ttywrite(customkey
, strlen(customkey
), 1); 
1803         /* 3. composed string from input method */ 
1806         if (len 
== 1 && e
->state 
& Mod1Mask
) { 
1807                 if (IS_SET(MODE_8BIT
)) { 
1810                                 len 
= utf8encode(c
, buf
); 
1818         ttywrite(buf
, len
, 1); 
1826          *  http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html 
1828         if (e
->xclient
.message_type 
== xw
.xembed 
&& e
->xclient
.format 
== 32) { 
1829                 if (e
->xclient
.data
.l
[1] == XEMBED_FOCUS_IN
) { 
1830                         win
.mode 
|= MODE_FOCUSED
; 
1832                 } else if (e
->xclient
.data
.l
[1] == XEMBED_FOCUS_OUT
) { 
1833                         win
.mode 
&= ~MODE_FOCUSED
; 
1835         } else if (e
->xclient
.data
.l
[0] == xw
.wmdeletewin
) { 
1844         if (e
->xconfigure
.width 
== win
.w 
&& e
->xconfigure
.height 
== win
.h
) 
1847         cresize(e
->xconfigure
.width
, e
->xconfigure
.height
); 
1854         int w 
= win
.w
, h 
= win
.h
; 
1856         int xfd 
= XConnectionNumber(xw
.dpy
), xev
, blinkset 
= 0, dodraw 
= 0; 
1858         struct timespec drawtimeout
, *tv 
= NULL
, now
, last
, lastblink
; 
1861         /* Waiting for window mapping */ 
1863                 XNextEvent(xw
.dpy
, &ev
); 
1865                  * This XFilterEvent call is required because of XOpenIM. It 
1866                  * does filter out the key event and some client message for 
1867                  * the input method too. 
1869                 if (XFilterEvent(&ev
, None
)) 
1871                 if (ev
.type 
== ConfigureNotify
) { 
1872                         w 
= ev
.xconfigure
.width
; 
1873                         h 
= ev
.xconfigure
.height
; 
1875         } while (ev
.type 
!= MapNotify
); 
1877         ttyfd 
= ttynew(opt_line
, shell
, opt_io
, opt_cmd
); 
1880         clock_gettime(CLOCK_MONOTONIC
, &last
); 
1883         for (xev 
= actionfps
;;) { 
1885                 FD_SET(ttyfd
, &rfd
); 
1888                 if (pselect(MAX(xfd
, ttyfd
)+1, &rfd
, NULL
, NULL
, tv
, NULL
) < 0) { 
1891                         die("select failed: %s\n", strerror(errno
)); 
1893                 if (FD_ISSET(ttyfd
, &rfd
)) { 
1896                                 blinkset 
= tattrset(ATTR_BLINK
); 
1898                                         MODBIT(win
.mode
, 0, MODE_BLINK
); 
1902                 if (FD_ISSET(xfd
, &rfd
)) 
1905                 clock_gettime(CLOCK_MONOTONIC
, &now
); 
1906                 drawtimeout
.tv_sec 
= 0; 
1907                 drawtimeout
.tv_nsec 
=  (1000 * 1E6
)/ xfps
; 
1911                 if (blinktimeout 
&& TIMEDIFF(now
, lastblink
) > blinktimeout
) { 
1912                         tsetdirtattr(ATTR_BLINK
); 
1913                         win
.mode 
^= MODE_BLINK
; 
1917                 deltatime 
= TIMEDIFF(now
, last
); 
1918                 if (deltatime 
> 1000 / (xev 
? xfps 
: actionfps
)) { 
1924                         while (XPending(xw
.dpy
)) { 
1925                                 XNextEvent(xw
.dpy
, &ev
); 
1926                                 if (XFilterEvent(&ev
, None
)) 
1928                                 if (handler
[ev
.type
]) 
1929                                         (handler
[ev
.type
])(&ev
); 
1935                         if (xev 
&& !FD_ISSET(xfd
, &rfd
)) 
1937                         if (!FD_ISSET(ttyfd
, &rfd
) && !FD_ISSET(xfd
, &rfd
)) { 
1939                                         if (TIMEDIFF(now
, lastblink
) \
 
1941                                                 drawtimeout
.tv_nsec 
= 1000; 
1943                                                 drawtimeout
.tv_nsec 
= (1E6 
* \
 
1948                                         drawtimeout
.tv_sec 
= \
 
1949                                             drawtimeout
.tv_nsec 
/ 1E9
; 
1950                                         drawtimeout
.tv_nsec 
%= (long)1E9
; 
1962         die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" 
1963             " [-n name] [-o file]\n" 
1964             "          [-T title] [-t title] [-w windowid]" 
1965             " [[-e] command [args ...]]\n" 
1966             "       %s [-aiv] [-c class] [-f font] [-g geometry]" 
1967             " [-n name] [-o file]\n" 
1968             "          [-T title] [-t title] [-w windowid] -l line" 
1969             " [stty_args ...]\n", argv0
, argv0
); 
1973 main(int argc
, char *argv
[]) 
1977         win
.cursor 
= cursorshape
; 
1984                 opt_class 
= EARGF(usage()); 
1991                 opt_font 
= EARGF(usage()); 
1994                 xw
.gm 
= XParseGeometry(EARGF(usage()), 
1995                                 &xw
.l
, &xw
.t
, &cols
, &rows
); 
2001                 opt_io 
= EARGF(usage()); 
2004                 opt_line 
= EARGF(usage()); 
2007                 opt_name 
= EARGF(usage()); 
2011                 opt_title 
= EARGF(usage()); 
2014                 opt_embed 
= EARGF(usage()); 
2017                 die("%s " VERSION 
"\n", argv0
); 
2024         if (argc 
> 0) /* eat all remaining arguments */ 
2028                 opt_title 
= (opt_line 
|| !opt_cmd
) ? "st" : opt_cmd
[0]; 
2030         setlocale(LC_CTYPE
, ""); 
2031         XSetLocaleModifiers(""); 
2032         cols 
= MAX(cols
, 1); 
2033         rows 
= MAX(rows
, 1);