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