x.c (100819B)
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 #if LIGATURES_PATCH 23 #include "hb.h" 24 #endif // LIGATURES_PATCH 25 26 #if THEMED_CURSOR_PATCH 27 #include <X11/Xcursor/Xcursor.h> 28 #endif // THEMED_CURSOR_PATCH 29 30 #if SIXEL_PATCH 31 #include <Imlib2.h> 32 #include "sixel.h" 33 #endif // SIXEL_PATCH 34 35 #if UNDERCURL_PATCH 36 /* Undercurl slope types */ 37 enum undercurl_slope_type { 38 UNDERCURL_SLOPE_ASCENDING = 0, 39 UNDERCURL_SLOPE_TOP_CAP = 1, 40 UNDERCURL_SLOPE_DESCENDING = 2, 41 UNDERCURL_SLOPE_BOTTOM_CAP = 3 42 }; 43 #endif // UNDERCURL_PATCH 44 45 #if ANYGEOMETRY_PATCH 46 typedef enum { 47 PixelGeometry, 48 CellGeometry 49 } Geometry; 50 #endif // ANYGEOMETRY_PATCH 51 52 /* X modifiers */ 53 #define XK_ANY_MOD UINT_MAX 54 #define XK_NO_MOD 0 55 #define XK_SWITCH_MOD (1<<13|1<<14) 56 57 /* function definitions used in config.h */ 58 static void clipcopy(const Arg *); 59 static void clippaste(const Arg *); 60 static void numlock(const Arg *); 61 static void selpaste(const Arg *); 62 static void ttysend(const Arg *); 63 static void zoom(const Arg *); 64 static void zoomabs(const Arg *); 65 static void zoomreset(const Arg *); 66 67 #include "patch/st_include.h" 68 #include "patch/x_include.h" 69 70 /* config.h for applying patches and the configuration. */ 71 #include "config.h" 72 73 #if CSI_22_23_PATCH 74 /* size of title stack */ 75 #define TITLESTACKSIZE 8 76 #endif // CSI_22_23_PATCH 77 78 /* XEMBED messages */ 79 #define XEMBED_FOCUS_IN 4 80 #define XEMBED_FOCUS_OUT 5 81 82 /* macros */ 83 #define IS_SET(flag) ((win.mode & (flag)) != 0) 84 #define TRUERED(x) (((x) & 0xff0000) >> 8) 85 #define TRUEGREEN(x) (((x) & 0xff00)) 86 #define TRUEBLUE(x) (((x) & 0xff) << 8) 87 88 static inline ushort sixd_to_16bit(int); 89 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); 90 #if LIGATURES_PATCH && WIDE_GLYPHS_PATCH 91 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int, int); 92 #elif LIGATURES_PATCH || WIDE_GLYPHS_PATCH 93 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int); 94 #else 95 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); 96 #endif // WIDE_GLYPHS_PATCH | LIGATURES_PATCH 97 #if LIGATURES_PATCH 98 static inline void xresetfontsettings(uint32_t mode, Font **font, int *frcflags); 99 #endif // LIGATURES_PATCH 100 void xdrawglyph(Glyph, int, int); 101 static void xclear(int, int, int, int); 102 static int xgeommasktogravity(int); 103 static int ximopen(Display *); 104 static void ximinstantiate(Display *, XPointer, XPointer); 105 static void ximdestroy(XIM, XPointer, XPointer); 106 static int xicdestroy(XIC, XPointer, XPointer); 107 static void xinit(int, int); 108 static void cresize(int, int); 109 static void xresize(int, int); 110 static void xhints(void); 111 static int xloadcolor(int, const char *, Color *); 112 static int xloadfont(Font *, FcPattern *); 113 static void xloadfonts(const char *, double); 114 static void xunloadfont(Font *); 115 static void xunloadfonts(void); 116 static void xsetenv(void); 117 static void xseturgency(int); 118 static int evcol(XEvent *); 119 static int evrow(XEvent *); 120 121 static void expose(XEvent *); 122 static void visibility(XEvent *); 123 static void unmap(XEvent *); 124 static void kpress(XEvent *); 125 static void cmessage(XEvent *); 126 static void resize(XEvent *); 127 static void focus(XEvent *); 128 static uint buttonmask(uint); 129 static void brelease(XEvent *); 130 static void bpress(XEvent *); 131 static void bmotion(XEvent *); 132 static void propnotify(XEvent *); 133 static void selnotify(XEvent *); 134 static void selclear_(XEvent *); 135 static void selrequest(XEvent *); 136 static void setsel(char *, Time); 137 #if XRESOURCES_PATCH && XRESOURCES_RELOAD_PATCH || BACKGROUND_IMAGE_PATCH && BACKGROUND_IMAGE_RELOAD_PATCH 138 static void sigusr1_reload(int sig); 139 #endif // XRESOURCES_RELOAD_PATCH | BACKGROUND_IMAGE_RELOAD_PATCH 140 static int mouseaction(XEvent *, uint); 141 static void mousesel(XEvent *, int); 142 static void mousereport(XEvent *); 143 static char *kmap(KeySym, uint); 144 static int match(uint, uint); 145 146 static void run(void); 147 static void usage(void); 148 149 static void (*handler[LASTEvent])(XEvent *) = { 150 [KeyPress] = kpress, 151 [ClientMessage] = cmessage, 152 [ConfigureNotify] = resize, 153 [VisibilityNotify] = visibility, 154 [UnmapNotify] = unmap, 155 [Expose] = expose, 156 [FocusIn] = focus, 157 [FocusOut] = focus, 158 [MotionNotify] = bmotion, 159 [ButtonPress] = bpress, 160 [ButtonRelease] = brelease, 161 /* 162 * Uncomment if you want the selection to disappear when you select something 163 * different in another window. 164 */ 165 /* [SelectionClear] = selclear_, */ 166 [SelectionNotify] = selnotify, 167 /* 168 * PropertyNotify is only turned on when there is some INCR transfer happening 169 * for the selection retrieval. 170 */ 171 [PropertyNotify] = propnotify, 172 [SelectionRequest] = selrequest, 173 #if ST_EMBEDDER_PATCH 174 [CreateNotify] = createnotify, 175 [DestroyNotify] = destroynotify, 176 #endif // ST_EMBEDDER_PATCH 177 }; 178 179 /* Globals */ 180 Term term; 181 DC dc; 182 XWindow xw; 183 XSelection xsel; 184 TermWindow win; 185 186 #if CSI_22_23_PATCH 187 static int tstki; /* title stack index */ 188 static char *titlestack[TITLESTACKSIZE]; /* title stack */ 189 #endif // CSI_22_23_PATCH 190 191 /* Font Ring Cache */ 192 enum { 193 FRC_NORMAL, 194 FRC_ITALIC, 195 FRC_BOLD, 196 FRC_ITALICBOLD 197 }; 198 199 typedef struct { 200 XftFont *font; 201 int flags; 202 Rune unicodep; 203 } Fontcache; 204 205 /* Fontcache is an array now. A new font will be appended to the array. */ 206 static Fontcache *frc = NULL; 207 static int frclen = 0; 208 static int frccap = 0; 209 static char *usedfont = NULL; 210 static double usedfontsize = 0; 211 static double defaultfontsize = 0; 212 213 #if ALPHA_PATCH 214 static char *opt_alpha = NULL; 215 #endif // ALPHA_PATCH 216 static char *opt_class = NULL; 217 static char **opt_cmd = NULL; 218 static char *opt_embed = NULL; 219 static char *opt_font = NULL; 220 static char *opt_io = NULL; 221 static char *opt_line = NULL; 222 static char *opt_name = NULL; 223 static char *opt_title = NULL; 224 #if WORKINGDIR_PATCH 225 static char *opt_dir = NULL; 226 #endif // WORKINGDIR_PATCH 227 228 #if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH 229 static int focused = 0; 230 #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH 231 232 static uint buttons; /* bit field of pressed buttons */ 233 #if BLINKING_CURSOR_PATCH 234 static int cursorblinks = 0; 235 #endif // BLINKING_CURSOR_PATCH 236 #if VISUALBELL_1_PATCH 237 static int bellon = 0; /* visual bell status */ 238 #endif // VISUALBELL_1_PATCH 239 #if RELATIVEBORDER_PATCH 240 int borderpx; 241 #endif // RELATIVEBORDER_PATCH 242 #if SWAPMOUSE_PATCH 243 static Cursor cursor; 244 static XColor xmousefg, xmousebg; 245 #endif // SWAPMOUSE_PATCH 246 247 #include "patch/x_include.c" 248 249 void 250 clipcopy(const Arg *dummy) 251 { 252 Atom clipboard; 253 254 free(xsel.clipboard); 255 xsel.clipboard = NULL; 256 257 if (xsel.primary != NULL) { 258 xsel.clipboard = xstrdup(xsel.primary); 259 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); 260 XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); 261 } 262 } 263 264 void 265 clippaste(const Arg *dummy) 266 { 267 Atom clipboard; 268 269 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 270 if (IS_SET(MODE_KBDSELECT) && !kbds_issearchmode()) 271 return; 272 #endif // KEYBOARDSELECT_PATCH 273 274 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); 275 XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, 276 xw.win, CurrentTime); 277 } 278 279 void 280 numlock(const Arg *dummy) 281 { 282 win.mode ^= MODE_NUMLOCK; 283 } 284 285 void 286 selpaste(const Arg *dummy) 287 { 288 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 289 if (IS_SET(MODE_KBDSELECT) && !kbds_issearchmode()) 290 return; 291 #endif // KEYBOARDSELECT_PATCH 292 293 XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, 294 xw.win, CurrentTime); 295 } 296 297 void 298 ttysend(const Arg *arg) 299 { 300 ttywrite(arg->s, strlen(arg->s), 1); 301 } 302 303 void 304 zoom(const Arg *arg) 305 { 306 Arg larg; 307 308 larg.f = usedfontsize + arg->f; 309 #if SIXEL_PATCH 310 if (larg.f >= 1.0) 311 zoomabs(&larg); 312 #else 313 zoomabs(&larg); 314 #endif // SIXEL_PATCH 315 } 316 317 void 318 zoomabs(const Arg *arg) 319 { 320 #if SIXEL_PATCH 321 int i; 322 ImageList *im; 323 #endif // SIXEL_PATCH 324 325 xunloadfonts(); 326 xloadfonts(usedfont, arg->f); 327 #if FONT2_PATCH 328 xloadsparefonts(); 329 #endif // FONT2_PATCH 330 331 #if SIXEL_PATCH 332 /* delete old pixmaps so that xfinishdraw() can create new scaled ones */ 333 for (im = term.images, i = 0; i < 2; i++, im = term.images_alt) { 334 for (; im; im = im->next) { 335 if (im->pixmap) 336 XFreePixmap(xw.dpy, (Drawable)im->pixmap); 337 if (im->clipmask) 338 XFreePixmap(xw.dpy, (Drawable)im->clipmask); 339 im->pixmap = NULL; 340 im->clipmask = NULL; 341 } 342 } 343 #endif // SIXEL_PATCH 344 345 cresize(0, 0); 346 redraw(); 347 xhints(); 348 } 349 350 void 351 zoomreset(const Arg *arg) 352 { 353 Arg larg; 354 355 if (defaultfontsize > 0) { 356 larg.f = defaultfontsize; 357 zoomabs(&larg); 358 } 359 } 360 361 int 362 evcol(XEvent *e) 363 { 364 #if ANYSIZE_PATCH 365 int x = e->xbutton.x - win.hborderpx; 366 #else 367 int x = e->xbutton.x - borderpx; 368 #endif // ANYSIZE_PATCH 369 LIMIT(x, 0, win.tw - 1); 370 return x / win.cw; 371 } 372 373 int 374 evrow(XEvent *e) 375 { 376 #if ANYSIZE_PATCH 377 int y = e->xbutton.y - win.vborderpx; 378 #else 379 int y = e->xbutton.y - borderpx; 380 #endif // ANYSIZE_PATCH 381 LIMIT(y, 0, win.th - 1); 382 return y / win.ch; 383 } 384 385 uint 386 buttonmask(uint button) 387 { 388 return button == Button1 ? Button1Mask 389 : button == Button2 ? Button2Mask 390 : button == Button3 ? Button3Mask 391 : button == Button4 ? Button4Mask 392 : button == Button5 ? Button5Mask 393 : 0; 394 } 395 396 int 397 mouseaction(XEvent *e, uint release) 398 { 399 MouseShortcut *ms; 400 int screen = tisaltscr() ? S_ALT : S_PRI; 401 402 /* ignore Button<N>mask for Button<N> - it's set on release */ 403 uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); 404 405 for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { 406 if (ms->release == release && 407 ms->button == e->xbutton.button && 408 (!ms->screen || (ms->screen == screen)) && 409 (match(ms->mod, state) || /* exact or forced */ 410 match(ms->mod, state & ~forcemousemod))) { 411 ms->func(&(ms->arg)); 412 return 1; 413 } 414 } 415 416 return 0; 417 } 418 419 void 420 mousesel(XEvent *e, int done) 421 { 422 int type, seltype = SEL_REGULAR; 423 uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); 424 425 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 426 if (kbds_isselectmode()) 427 return; 428 #endif // KEYBOARDSELECT_PATCH 429 430 for (type = 1; type < LEN(selmasks); ++type) { 431 if (match(selmasks[type], state)) { 432 seltype = type; 433 break; 434 } 435 } 436 selextend(evcol(e), evrow(e), seltype, done); 437 if (done) 438 setsel(getsel(), e->xbutton.time); 439 } 440 441 void 442 mousereport(XEvent *e) 443 { 444 int len, btn, code; 445 int x = evcol(e), y = evrow(e); 446 int state = e->xbutton.state; 447 char buf[40]; 448 static int ox, oy; 449 450 if (e->type == MotionNotify) { 451 if (x == ox && y == oy) 452 return; 453 if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) 454 return; 455 /* MODE_MOUSEMOTION: no reporting if no button is pressed */ 456 if (IS_SET(MODE_MOUSEMOTION) && buttons == 0) 457 return; 458 459 /* Set btn to lowest-numbered pressed button, or 12 if no 460 * buttons are pressed. */ 461 for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++) 462 ; 463 code = 32; 464 } else { 465 btn = e->xbutton.button; 466 /* Only buttons 1 through 11 can be encoded */ 467 if (btn < 1 || btn > 11) 468 return; 469 if (e->type == ButtonRelease) { 470 /* MODE_MOUSEX10: no button release reporting */ 471 if (IS_SET(MODE_MOUSEX10)) 472 return; 473 /* Don't send release events for the scroll wheel */ 474 if (btn == 4 || btn == 5) 475 return; 476 } 477 code = 0; 478 } 479 480 ox = x; 481 oy = y; 482 483 /* Encode btn into code. If no button is pressed for a motion event in 484 * MODE_MOUSEMANY, then encode it as a release. */ 485 if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12) 486 code += 3; 487 else if (btn >= 8) 488 code += 128 + btn - 8; 489 else if (btn >= 4) 490 code += 64 + btn - 4; 491 else 492 code += btn - 1; 493 494 if (!IS_SET(MODE_MOUSEX10)) { 495 code += ((state & ShiftMask ) ? 4 : 0) 496 + ((state & Mod1Mask ) ? 8 : 0) /* meta key: alt */ 497 + ((state & ControlMask) ? 16 : 0); 498 } 499 500 if (IS_SET(MODE_MOUSESGR)) { 501 len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", 502 code, x+1, y+1, 503 e->type == ButtonRelease ? 'm' : 'M'); 504 } else if (x < 223 && y < 223) { 505 len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", 506 32+code, 32+x+1, 32+y+1); 507 } else { 508 return; 509 } 510 511 ttywrite(buf, len, 0); 512 } 513 514 void 515 bpress(XEvent *e) 516 { 517 int btn = e->xbutton.button; 518 struct timespec now; 519 int snap; 520 521 if (1 <= btn && btn <= 11) 522 buttons |= 1 << (btn-1); 523 524 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { 525 mousereport(e); 526 return; 527 } 528 529 if (mouseaction(e, 0)) 530 return; 531 532 if (btn == Button1) { 533 /* 534 * If the user clicks below predefined timeouts specific 535 * snapping behaviour is exposed. 536 */ 537 clock_gettime(CLOCK_MONOTONIC, &now); 538 if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { 539 snap = SNAP_LINE; 540 } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { 541 snap = SNAP_WORD; 542 } else { 543 snap = 0; 544 } 545 xsel.tclick2 = xsel.tclick1; 546 xsel.tclick1 = now; 547 548 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 549 if (kbds_isselectmode()) 550 return; 551 #endif // KEYBOARDSELECT_PATCH 552 553 selstart(evcol(e), evrow(e), snap); 554 555 #if OPENURLONCLICK_PATCH 556 clearurl(); 557 url_click = 1; 558 #endif // OPENURLONCLICK_PATCH 559 } 560 } 561 562 void 563 propnotify(XEvent *e) 564 { 565 XPropertyEvent *xpev; 566 Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); 567 568 xpev = &e->xproperty; 569 if (xpev->state == PropertyNewValue && 570 (xpev->atom == XA_PRIMARY || 571 xpev->atom == clipboard)) { 572 selnotify(e); 573 } 574 575 #if BACKGROUND_IMAGE_PATCH 576 if (pseudotransparency && 577 !strncmp(XGetAtomName(xw.dpy, e->xproperty.atom), "_NET_WM_STATE", 13)) { 578 updatexy(); 579 redraw(); 580 } 581 #endif // BACKGROUND_IMAGE_PATCH 582 } 583 584 void 585 selnotify(XEvent *e) 586 { 587 ulong nitems, ofs, rem; 588 int format; 589 uchar *data, *last, *repl; 590 Atom type, incratom, property = None; 591 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 592 int append = 0; 593 #endif // KEYBOARDSELECT_PATCH 594 595 incratom = XInternAtom(xw.dpy, "INCR", 0); 596 597 ofs = 0; 598 if (e->type == SelectionNotify) 599 property = e->xselection.property; 600 else if (e->type == PropertyNotify) 601 property = e->xproperty.atom; 602 603 if (property == None) 604 return; 605 606 do { 607 if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, 608 BUFSIZ/4, False, AnyPropertyType, 609 &type, &format, &nitems, &rem, 610 &data)) { 611 fprintf(stderr, "Clipboard allocation failed\n"); 612 return; 613 } 614 615 #if BACKGROUND_IMAGE_PATCH 616 if (e->type == PropertyNotify && nitems == 0 && rem == 0 && !pseudotransparency) 617 #else 618 if (e->type == PropertyNotify && nitems == 0 && rem == 0) 619 #endif // BACKGROUND_IMAGE_PATCH 620 { 621 /* 622 * If there is some PropertyNotify with no data, then 623 * this is the signal of the selection owner that all 624 * data has been transferred. We won't need to receive 625 * PropertyNotify events anymore. 626 */ 627 MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); 628 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, 629 &xw.attrs); 630 } 631 632 if (type == incratom) { 633 /* 634 * Activate the PropertyNotify events so we receive 635 * when the selection owner does send us the next 636 * chunk of data. 637 */ 638 #if BACKGROUND_IMAGE_PATCH 639 if (!pseudotransparency) { 640 #endif // BACKGROUND_IMAGE_PATCH 641 MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); 642 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, 643 &xw.attrs); 644 #if BACKGROUND_IMAGE_PATCH 645 } 646 #endif // BACKGROUND_IMAGE_PATCH 647 648 /* 649 * Deleting the property is the transfer start signal. 650 */ 651 XDeleteProperty(xw.dpy, xw.win, (int)property); 652 continue; 653 } 654 655 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 656 if (IS_SET(MODE_KBDSELECT) && kbds_issearchmode()) { 657 kbds_pasteintosearch(data, nitems * format / 8, append++); 658 } else { 659 /* 660 * As seen in getsel: 661 * Line endings are inconsistent in the terminal and GUI world 662 * copy and pasting. When receiving some selection data, 663 * replace all '\n' with '\r'. 664 * FIXME: Fix the computer world. 665 */ 666 repl = data; 667 last = data + nitems * format / 8; 668 while ((repl = memchr(repl, '\n', last - repl))) { 669 *repl++ = '\r'; 670 } 671 672 if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) 673 ttywrite("\033[200~", 6, 0); 674 ttywrite((char *)data, nitems * format / 8, 1); 675 if (IS_SET(MODE_BRCKTPASTE) && rem == 0) 676 ttywrite("\033[201~", 6, 0); 677 } 678 #else 679 /* 680 * As seen in getsel: 681 * Line endings are inconsistent in the terminal and GUI world 682 * copy and pasting. When receiving some selection data, 683 * replace all '\n' with '\r'. 684 * FIXME: Fix the computer world. 685 */ 686 repl = data; 687 last = data + nitems * format / 8; 688 while ((repl = memchr(repl, '\n', last - repl))) { 689 *repl++ = '\r'; 690 } 691 692 if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) 693 ttywrite("\033[200~", 6, 0); 694 ttywrite((char *)data, nitems * format / 8, 1); 695 if (IS_SET(MODE_BRCKTPASTE) && rem == 0) 696 ttywrite("\033[201~", 6, 0); 697 #endif // KEYBOARDSELECT_PATCH 698 XFree(data); 699 /* number of 32-bit chunks returned */ 700 ofs += nitems * format / 32; 701 } while (rem > 0); 702 703 /* 704 * Deleting the property again tells the selection owner to send the 705 * next data chunk in the property. 706 */ 707 XDeleteProperty(xw.dpy, xw.win, (int)property); 708 } 709 710 void 711 xclipcopy(void) 712 { 713 clipcopy(NULL); 714 } 715 716 void 717 selclear_(XEvent *e) 718 { 719 selclear(); 720 } 721 722 void 723 selrequest(XEvent *e) 724 { 725 XSelectionRequestEvent *xsre; 726 XSelectionEvent xev; 727 Atom xa_targets, string, clipboard; 728 char *seltext; 729 730 xsre = (XSelectionRequestEvent *) e; 731 xev.type = SelectionNotify; 732 xev.requestor = xsre->requestor; 733 xev.selection = xsre->selection; 734 xev.target = xsre->target; 735 xev.time = xsre->time; 736 if (xsre->property == None) 737 xsre->property = xsre->target; 738 739 /* reject */ 740 xev.property = None; 741 742 xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); 743 if (xsre->target == xa_targets) { 744 /* respond with the supported type */ 745 string = xsel.xtarget; 746 XChangeProperty(xsre->display, xsre->requestor, xsre->property, 747 XA_ATOM, 32, PropModeReplace, 748 (uchar *) &string, 1); 749 xev.property = xsre->property; 750 } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { 751 /* 752 * xith XA_STRING non ascii characters may be incorrect in the 753 * requestor. It is not our problem, use utf8. 754 */ 755 clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); 756 if (xsre->selection == XA_PRIMARY) { 757 seltext = xsel.primary; 758 } else if (xsre->selection == clipboard) { 759 seltext = xsel.clipboard; 760 } else { 761 fprintf(stderr, 762 "Unhandled clipboard selection 0x%lx\n", 763 xsre->selection); 764 return; 765 } 766 if (seltext != NULL) { 767 XChangeProperty(xsre->display, xsre->requestor, 768 xsre->property, xsre->target, 769 8, PropModeReplace, 770 (uchar *)seltext, strlen(seltext)); 771 xev.property = xsre->property; 772 } 773 } 774 775 /* all done, send a notification to the listener */ 776 if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) 777 fprintf(stderr, "Error sending SelectionNotify event\n"); 778 } 779 780 void 781 setsel(char *str, Time t) 782 { 783 if (!str) 784 return; 785 786 free(xsel.primary); 787 xsel.primary = str; 788 789 XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); 790 if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) 791 selclear(); 792 793 #if CLIPBOARD_PATCH 794 clipcopy(NULL); 795 #endif // CLIPBOARD_PATCH 796 } 797 798 #if XRESOURCES_PATCH && XRESOURCES_RELOAD_PATCH || BACKGROUND_IMAGE_PATCH && BACKGROUND_IMAGE_RELOAD_PATCH 799 void 800 sigusr1_reload(int sig) 801 { 802 #if XRESOURCES_PATCH && XRESOURCES_RELOAD_PATCH 803 reload_config(sig); 804 #endif // XRESOURCES_RELOAD_PATCH 805 #if BACKGROUND_IMAGE_PATCH && BACKGROUND_IMAGE_RELOAD_PATCH 806 reload_image(); 807 #endif // BACKGROUND_IMAGE_RELOAD_PATCH 808 signal(SIGUSR1, sigusr1_reload); 809 } 810 #endif // XRESOURCES_RELOAD_PATCH | BACKGROUND_IMAGE_RELOAD_PATCH 811 812 void 813 xsetsel(char *str) 814 { 815 setsel(str, CurrentTime); 816 } 817 818 void 819 brelease(XEvent *e) 820 { 821 int btn = e->xbutton.button; 822 823 if (1 <= btn && btn <= 11) 824 buttons &= ~(1 << (btn-1)); 825 826 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { 827 mousereport(e); 828 return; 829 } 830 831 if (mouseaction(e, 1)) 832 return; 833 834 if (btn == Button1) { 835 mousesel(e, 1); 836 #if OPENURLONCLICK_PATCH 837 if (url_click && e->xkey.state & url_opener_modkey) 838 openUrlOnClick(evcol(e), evrow(e), url_opener); 839 #endif // OPENURLONCLICK_PATCH 840 } 841 842 #if RIGHTCLICKTOPLUMB_PATCH 843 else if (btn == Button3) 844 plumb(xsel.primary); 845 #endif // RIGHTCLICKTOPLUMB_PATCH 846 } 847 848 void 849 bmotion(XEvent *e) 850 { 851 #if HIDECURSOR_PATCH 852 if (!xw.pointerisvisible) { 853 #if SWAPMOUSE_PATCH 854 if (win.mode & MODE_MOUSE) 855 XUndefineCursor(xw.dpy, xw.win); 856 else 857 XDefineCursor(xw.dpy, xw.win, xw.vpointer); 858 #else 859 XDefineCursor(xw.dpy, xw.win, xw.vpointer); 860 #endif // SWAPMOUSE_PATCH 861 xw.pointerisvisible = 1; 862 if (!IS_SET(MODE_MOUSEMANY)) 863 xsetpointermotion(0); 864 } 865 #endif // HIDECURSOR_PATCH 866 #if OPENURLONCLICK_PATCH 867 if (!IS_SET(MODE_MOUSE)) { 868 if (!(e->xbutton.state & Button1Mask) && detecturl(evcol(e), evrow(e), 1)) 869 XDefineCursor(xw.dpy, xw.win, xw.upointer); 870 else 871 XDefineCursor(xw.dpy, xw.win, xw.vpointer); 872 } 873 url_click = 0; 874 #endif // OPENURLONCLICK_PATCH 875 876 if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { 877 mousereport(e); 878 return; 879 } 880 881 mousesel(e, 0); 882 } 883 884 void 885 cresize(int width, int height) 886 { 887 int col, row; 888 889 if (width != 0) 890 win.w = width; 891 if (height != 0) 892 win.h = height; 893 894 col = (win.w - 2 * borderpx) / win.cw; 895 row = (win.h - 2 * borderpx) / win.ch; 896 col = MAX(2, col); 897 row = MAX(1, row); 898 899 #if ANYSIZE_PATCH 900 win.hborderpx = (win.w - col * win.cw) / 2; 901 win.vborderpx = (win.h - row * win.ch) / 2; 902 #endif // ANYSIZE_PATCH 903 904 tresize(col, row); 905 xresize(col, row); 906 ttyresize(win.tw, win.th); 907 } 908 909 void 910 xresize(int col, int row) 911 { 912 win.tw = col * win.cw; 913 win.th = row * win.ch; 914 915 #if !SINGLE_DRAWABLE_BUFFER_PATCH 916 XFreePixmap(xw.dpy, xw.buf); 917 xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, 918 #if ALPHA_PATCH 919 xw.depth 920 #else 921 DefaultDepth(xw.dpy, xw.scr) 922 #endif // ALPHA_PATCH 923 ); 924 XftDrawChange(xw.draw, xw.buf); 925 #endif // SINGLE_DRAWABLE_BUFFER_PATCH 926 xclear(0, 0, win.w, win.h); 927 928 /* resize to new width */ 929 #if LIGATURES_PATCH 930 xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); 931 xw.specseq = xrealloc(xw.specseq, col * sizeof(GlyphFontSeq)); 932 #else 933 xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); 934 #endif // LIGATURES_PATCH 935 } 936 937 ushort 938 sixd_to_16bit(int x) 939 { 940 return x == 0 ? 0 : 0x3737 + 0x2828 * x; 941 } 942 943 int 944 xloadcolor(int i, const char *name, Color *ncolor) 945 { 946 XRenderColor color = { .alpha = 0xffff }; 947 948 if (!name) { 949 if (BETWEEN(i, 16, 255)) { /* 256 color */ 950 if (i < 6*6*6+16) { /* same colors as xterm */ 951 color.red = sixd_to_16bit( ((i-16)/36)%6 ); 952 color.green = sixd_to_16bit( ((i-16)/6) %6 ); 953 color.blue = sixd_to_16bit( ((i-16)/1) %6 ); 954 } else { /* greyscale */ 955 color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); 956 color.green = color.blue = color.red; 957 } 958 return XftColorAllocValue(xw.dpy, xw.vis, 959 xw.cmap, &color, ncolor); 960 } else 961 name = colorname[i]; 962 } 963 964 return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); 965 } 966 967 #if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH 968 void 969 xloadalpha(void) 970 { 971 float const usedAlpha = focused ? alpha : alphaUnfocused; 972 if (opt_alpha) alpha = strtof(opt_alpha, NULL); 973 dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * usedAlpha); 974 dc.col[defaultbg].pixel &= 0x00FFFFFF; 975 dc.col[defaultbg].pixel |= (unsigned char)(0xff * usedAlpha) << 24; 976 } 977 #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH 978 979 #if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH 980 void 981 xloadcols(void) 982 { 983 static int loaded; 984 Color *cp; 985 986 if (!loaded) { 987 dc.collen = 1 + (defaultbg = MAX(LEN(colorname), 256)); 988 dc.col = xmalloc((dc.collen) * sizeof(Color)); 989 } 990 991 for (int i = 0; i+1 < dc.collen; ++i) 992 if (!xloadcolor(i, NULL, &dc.col[i])) { 993 if (colorname[i]) 994 die("could not allocate color '%s'\n", colorname[i]); 995 else 996 die("could not allocate color %d\n", i); 997 } 998 if (dc.collen) // cannot die, as the color is already loaded. 999 xloadcolor(focused ? bg : bgUnfocused, NULL, &dc.col[defaultbg]); 1000 1001 xloadalpha(); 1002 loaded = 1; 1003 } 1004 #else 1005 void 1006 xloadcols(void) 1007 { 1008 int i; 1009 static int loaded; 1010 Color *cp; 1011 1012 if (loaded) { 1013 for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) 1014 XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); 1015 } else { 1016 dc.collen = MAX(LEN(colorname), 256); 1017 dc.col = xmalloc(dc.collen * sizeof(Color)); 1018 } 1019 1020 for (i = 0; i < dc.collen; i++) 1021 if (!xloadcolor(i, NULL, &dc.col[i])) { 1022 if (colorname[i]) 1023 die("could not allocate color '%s'\n", colorname[i]); 1024 else 1025 die("could not allocate color %d\n", i); 1026 } 1027 #if ALPHA_PATCH 1028 /* set alpha value of bg color */ 1029 if (opt_alpha) 1030 alpha = strtof(opt_alpha, NULL); 1031 dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); 1032 dc.col[defaultbg].pixel &= 0x00FFFFFF; 1033 dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; 1034 dc.col[defaultbg].color.red *= alpha; 1035 dc.col[defaultbg].color.green *= alpha; 1036 dc.col[defaultbg].color.blue *= alpha; 1037 #endif // ALPHA_PATCH 1038 loaded = 1; 1039 } 1040 #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH 1041 1042 int 1043 xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) 1044 { 1045 if (!BETWEEN(x, 0, dc.collen - 1)) 1046 return 1; 1047 1048 *r = dc.col[x].color.red >> 8; 1049 *g = dc.col[x].color.green >> 8; 1050 *b = dc.col[x].color.blue >> 8; 1051 1052 return 0; 1053 } 1054 1055 int 1056 xsetcolorname(int x, const char *name) 1057 { 1058 Color ncolor; 1059 1060 if (!BETWEEN(x, 0, dc.collen - 1)) 1061 return 1; 1062 1063 if (!xloadcolor(x, name, &ncolor)) 1064 return 1; 1065 1066 XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); 1067 dc.col[x] = ncolor; 1068 1069 #if ALPHA_PATCH 1070 /* set alpha value of bg color */ 1071 if (x == defaultbg) { 1072 if (opt_alpha) 1073 alpha = strtof(opt_alpha, NULL); 1074 dc.col[defaultbg].color.alpha = (unsigned short)(0xffff * alpha); 1075 dc.col[defaultbg].pixel &= 0x00FFFFFF; 1076 dc.col[defaultbg].pixel |= (unsigned char)(0xff * alpha) << 24; 1077 dc.col[defaultbg].color.red *= alpha; 1078 dc.col[defaultbg].color.green *= alpha; 1079 dc.col[defaultbg].color.blue *= alpha; 1080 } 1081 #endif // ALPHA_PATCH 1082 return 0; 1083 } 1084 1085 /* 1086 * Absolute coordinates. 1087 */ 1088 void 1089 xclear(int x1, int y1, int x2, int y2) 1090 { 1091 #if BACKGROUND_IMAGE_PATCH 1092 if (pseudotransparency) 1093 XSetTSOrigin(xw.dpy, xw.bggc, -win.x, -win.y); 1094 XFillRectangle(xw.dpy, xw.buf, xw.bggc, x1, y1, x2-x1, y2-y1); 1095 #elif INVERT_PATCH 1096 Color c; 1097 c = dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg]; 1098 if (invertcolors) { 1099 c = invertedcolor(&c); 1100 } 1101 XftDrawRect(xw.draw, &c, x1, y1, x2-x1, y2-y1); 1102 #else 1103 XftDrawRect(xw.draw, 1104 &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], 1105 x1, y1, x2-x1, y2-y1); 1106 #endif // INVERT_PATCH 1107 } 1108 1109 void 1110 xclearwin(void) 1111 { 1112 xclear(0, 0, win.w, win.h); 1113 } 1114 1115 void 1116 xhints(void) 1117 { 1118 #if XRESOURCES_PATCH 1119 XClassHint class = {opt_name ? opt_name : "st", 1120 opt_class ? opt_class : "St"}; 1121 #else 1122 XClassHint class = {opt_name ? opt_name : termname, 1123 opt_class ? opt_class : termname}; 1124 #endif // XRESOURCES_PATCH 1125 XWMHints wm = {.flags = InputHint, .input = 1}; 1126 XSizeHints *sizeh; 1127 1128 sizeh = XAllocSizeHints(); 1129 1130 sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; 1131 sizeh->height = win.h; 1132 sizeh->width = win.w; 1133 #if ANYSIZE_PATCH || ANYSIZE_SIMPLE_PATCH 1134 sizeh->height_inc = 1; 1135 sizeh->width_inc = 1; 1136 #else 1137 sizeh->height_inc = win.ch; 1138 sizeh->width_inc = win.cw; 1139 #endif // ANYSIZE_PATCH 1140 sizeh->base_height = 2 * borderpx; 1141 sizeh->base_width = 2 * borderpx; 1142 sizeh->min_height = win.ch + 2 * borderpx; 1143 sizeh->min_width = win.cw + 2 * borderpx; 1144 if (xw.isfixed) { 1145 sizeh->flags |= PMaxSize; 1146 sizeh->min_width = sizeh->max_width = win.w; 1147 sizeh->min_height = sizeh->max_height = win.h; 1148 } 1149 if (xw.gm & (XValue|YValue)) { 1150 sizeh->flags |= USPosition | PWinGravity; 1151 sizeh->x = xw.l; 1152 sizeh->y = xw.t; 1153 sizeh->win_gravity = xgeommasktogravity(xw.gm); 1154 } 1155 1156 XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, 1157 &class); 1158 XFree(sizeh); 1159 } 1160 1161 int 1162 xgeommasktogravity(int mask) 1163 { 1164 switch (mask & (XNegative|YNegative)) { 1165 case 0: 1166 return NorthWestGravity; 1167 case XNegative: 1168 return NorthEastGravity; 1169 case YNegative: 1170 return SouthWestGravity; 1171 } 1172 1173 return SouthEastGravity; 1174 } 1175 1176 int 1177 ximopen(Display *dpy) 1178 { 1179 XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; 1180 XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; 1181 1182 xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); 1183 if (xw.ime.xim == NULL) 1184 return 0; 1185 1186 if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) 1187 fprintf(stderr, "XSetIMValues: " 1188 "Could not set XNDestroyCallback.\n"); 1189 1190 xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, 1191 NULL); 1192 1193 if (xw.ime.xic == NULL) { 1194 xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, 1195 XIMPreeditNothing | XIMStatusNothing, 1196 XNClientWindow, xw.win, 1197 XNDestroyCallback, &icdestroy, 1198 NULL); 1199 } 1200 if (xw.ime.xic == NULL) 1201 fprintf(stderr, "XCreateIC: Could not create input context.\n"); 1202 1203 return 1; 1204 } 1205 1206 void 1207 ximinstantiate(Display *dpy, XPointer client, XPointer call) 1208 { 1209 if (ximopen(dpy)) 1210 XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, 1211 ximinstantiate, NULL); 1212 } 1213 1214 void 1215 ximdestroy(XIM xim, XPointer client, XPointer call) 1216 { 1217 xw.ime.xim = NULL; 1218 XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, 1219 ximinstantiate, NULL); 1220 XFree(xw.ime.spotlist); 1221 } 1222 1223 int 1224 xicdestroy(XIC xim, XPointer client, XPointer call) 1225 { 1226 xw.ime.xic = NULL; 1227 return 1; 1228 } 1229 1230 int 1231 xloadfont(Font *f, FcPattern *pattern) 1232 { 1233 FcPattern *configured; 1234 FcPattern *match; 1235 FcResult result; 1236 XGlyphInfo extents; 1237 int wantattr, haveattr; 1238 1239 /* 1240 * Manually configure instead of calling XftMatchFont 1241 * so that we can use the configured pattern for 1242 * "missing glyph" lookups. 1243 */ 1244 configured = FcPatternDuplicate(pattern); 1245 if (!configured) 1246 return 1; 1247 1248 FcConfigSubstitute(NULL, configured, FcMatchPattern); 1249 XftDefaultSubstitute(xw.dpy, xw.scr, configured); 1250 1251 #if USE_XFTFONTMATCH_PATCH 1252 match = XftFontMatch(xw.dpy, xw.scr, pattern, &result); 1253 #else 1254 match = FcFontMatch(NULL, configured, &result); 1255 #endif // USE_XFTFONTMATCH_PATCH 1256 if (!match) { 1257 FcPatternDestroy(configured); 1258 return 1; 1259 } 1260 1261 if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { 1262 FcPatternDestroy(configured); 1263 FcPatternDestroy(match); 1264 return 1; 1265 } 1266 1267 if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == 1268 XftResultMatch)) { 1269 /* 1270 * Check if xft was unable to find a font with the appropriate 1271 * slant but gave us one anyway. Try to mitigate. 1272 */ 1273 if ((XftPatternGetInteger(f->match->pattern, "slant", 0, 1274 &haveattr) != XftResultMatch) || haveattr < wantattr) { 1275 f->badslant = 1; 1276 fputs("font slant does not match\n", stderr); 1277 } 1278 } 1279 1280 if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == 1281 XftResultMatch)) { 1282 if ((XftPatternGetInteger(f->match->pattern, "weight", 0, 1283 &haveattr) != XftResultMatch) || haveattr != wantattr) { 1284 f->badweight = 1; 1285 fputs("font weight does not match\n", stderr); 1286 } 1287 } 1288 1289 XftTextExtentsUtf8(xw.dpy, f->match, 1290 (const FcChar8 *) ascii_printable, 1291 strlen(ascii_printable), &extents); 1292 1293 f->set = NULL; 1294 f->pattern = configured; 1295 1296 f->ascent = f->match->ascent; 1297 f->descent = f->match->descent; 1298 f->lbearing = 0; 1299 f->rbearing = f->match->max_advance_width; 1300 1301 f->height = f->ascent + f->descent; 1302 #if WIDE_GLYPH_SPACING_PATCH 1303 f->width = DIVCEIL(extents.xOff > 18 ? extents.xOff / 3 : extents.xOff, strlen(ascii_printable)); 1304 #else 1305 f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); 1306 #endif // WIDE_GLYPH_SPACING_PATCH 1307 1308 return 0; 1309 } 1310 1311 void 1312 xloadfonts(const char *fontstr, double fontsize) 1313 { 1314 FcPattern *pattern; 1315 double fontval; 1316 1317 if (fontstr[0] == '-') 1318 pattern = XftXlfdParse(fontstr, False, False); 1319 else 1320 pattern = FcNameParse((const FcChar8 *)fontstr); 1321 1322 if (!pattern) 1323 die("can't open font %s\n", fontstr); 1324 1325 if (fontsize > 1) { 1326 FcPatternDel(pattern, FC_PIXEL_SIZE); 1327 FcPatternDel(pattern, FC_SIZE); 1328 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); 1329 usedfontsize = fontsize; 1330 } else { 1331 if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == 1332 FcResultMatch) { 1333 usedfontsize = fontval; 1334 } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == 1335 FcResultMatch) { 1336 usedfontsize = -1; 1337 } else { 1338 /* 1339 * Default font size is 12, if none given. This is to 1340 * have a known usedfontsize value. 1341 */ 1342 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); 1343 usedfontsize = 12; 1344 } 1345 defaultfontsize = usedfontsize; 1346 } 1347 1348 if (xloadfont(&dc.font, pattern)) 1349 die("can't open font %s\n", fontstr); 1350 1351 if (usedfontsize < 0) { 1352 FcPatternGetDouble(dc.font.match->pattern, 1353 FC_PIXEL_SIZE, 0, &fontval); 1354 usedfontsize = fontval; 1355 if (fontsize == 0) 1356 defaultfontsize = fontval; 1357 } 1358 1359 /* Setting character width and height. */ 1360 win.cw = ceilf(dc.font.width * cwscale); 1361 win.ch = ceilf(dc.font.height * chscale); 1362 #if VERTCENTER_PATCH 1363 win.cyo = ceilf(dc.font.height * (chscale - 1) / 2); 1364 #endif // VERTCENTER_PATCH 1365 1366 #if RELATIVEBORDER_PATCH 1367 borderpx = (int) ceilf(((float)borderperc / 100) * win.cw); 1368 #endif // RELATIVEBORDER_PATCH 1369 FcPatternDel(pattern, FC_SLANT); 1370 #if !DISABLE_ITALIC_FONTS_PATCH 1371 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); 1372 #endif // DISABLE_ITALIC_FONTS_PATCH 1373 if (xloadfont(&dc.ifont, pattern)) 1374 die("can't open font %s\n", fontstr); 1375 1376 FcPatternDel(pattern, FC_WEIGHT); 1377 #if !DISABLE_BOLD_FONTS_PATCH 1378 FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); 1379 #endif // DISABLE_BOLD_FONTS_PATCH 1380 if (xloadfont(&dc.ibfont, pattern)) 1381 die("can't open font %s\n", fontstr); 1382 1383 FcPatternDel(pattern, FC_SLANT); 1384 #if !DISABLE_ROMAN_FONTS_PATCH 1385 FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); 1386 #endif // DISABLE_ROMAN_FONTS_PATCH 1387 if (xloadfont(&dc.bfont, pattern)) 1388 die("can't open font %s\n", fontstr); 1389 1390 FcPatternDestroy(pattern); 1391 } 1392 1393 void 1394 xunloadfont(Font *f) 1395 { 1396 XftFontClose(xw.dpy, f->match); 1397 FcPatternDestroy(f->pattern); 1398 if (f->set) 1399 FcFontSetDestroy(f->set); 1400 } 1401 1402 void 1403 xunloadfonts(void) 1404 { 1405 #if LIGATURES_PATCH 1406 /* Clear Harfbuzz font cache. */ 1407 hbunloadfonts(); 1408 #endif // LIGATURES_PATCH 1409 1410 /* Free the loaded fonts in the font cache. */ 1411 while (frclen > 0) 1412 XftFontClose(xw.dpy, frc[--frclen].font); 1413 1414 xunloadfont(&dc.font); 1415 xunloadfont(&dc.bfont); 1416 xunloadfont(&dc.ifont); 1417 xunloadfont(&dc.ibfont); 1418 } 1419 1420 void 1421 xinit(int cols, int rows) 1422 { 1423 XGCValues gcvalues; 1424 #if HIDECURSOR_PATCH 1425 Pixmap blankpm; 1426 #elif !SWAPMOUSE_PATCH 1427 Cursor cursor; 1428 #endif // HIDECURSOR_PATCH 1429 Window parent, root; 1430 pid_t thispid = getpid(); 1431 #if !SWAPMOUSE_PATCH 1432 XColor xmousefg, xmousebg; 1433 #endif // SWAPMOUSE_PATCH 1434 #if ALPHA_PATCH 1435 XWindowAttributes attr; 1436 XVisualInfo vis; 1437 #endif // ALPHA_PATCH 1438 1439 #if !XRESOURCES_PATCH 1440 if (!(xw.dpy = XOpenDisplay(NULL))) 1441 die("can't open display\n"); 1442 #endif // XRESOURCES_PATCH 1443 xw.scr = XDefaultScreen(xw.dpy); 1444 1445 #if ALPHA_PATCH 1446 if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) { 1447 parent = XRootWindow(xw.dpy, xw.scr); 1448 xw.depth = 32; 1449 } else { 1450 XGetWindowAttributes(xw.dpy, parent, &attr); 1451 xw.depth = attr.depth; 1452 } 1453 1454 XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); 1455 xw.vis = vis.visual; 1456 #else 1457 xw.vis = XDefaultVisual(xw.dpy, xw.scr); 1458 #endif // ALPHA_PATCH 1459 1460 /* font */ 1461 if (!FcInit()) 1462 die("could not init fontconfig.\n"); 1463 1464 usedfont = (opt_font == NULL)? font : opt_font; 1465 xloadfonts(usedfont, 0); 1466 1467 #if FONT2_PATCH 1468 /* spare fonts */ 1469 xloadsparefonts(); 1470 #endif // FONT2_PATCH 1471 1472 /* colors */ 1473 #if ALPHA_PATCH 1474 xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); 1475 #else 1476 xw.cmap = XDefaultColormap(xw.dpy, xw.scr); 1477 #endif // ALPHA_PATCH 1478 xloadcols(); 1479 1480 /* adjust fixed window geometry */ 1481 #if ANYGEOMETRY_PATCH 1482 switch (geometry) { 1483 case CellGeometry: 1484 #if ANYSIZE_PATCH 1485 win.w = 2 * win.hborderpx + cols * win.cw; 1486 win.h = 2 * win.vborderpx + rows * win.ch; 1487 #else 1488 win.w = 2 * borderpx + cols * win.cw; 1489 win.h = 2 * borderpx + rows * win.ch; 1490 #endif // ANYGEOMETRY_PATCH | ANYSIZE_PATCH 1491 break; 1492 case PixelGeometry: 1493 win.w = cols; 1494 win.h = rows; 1495 cols = (win.w - 2 * borderpx) / win.cw; 1496 rows = (win.h - 2 * borderpx) / win.ch; 1497 break; 1498 } 1499 #elif ANYSIZE_PATCH 1500 win.w = 2 * win.hborderpx + cols * win.cw; 1501 win.h = 2 * win.vborderpx + rows * win.ch; 1502 #else 1503 win.w = 2 * borderpx + cols * win.cw; 1504 win.h = 2 * borderpx + rows * win.ch; 1505 #endif // ANYGEOMETRY_PATCH | ANYSIZE_PATCH 1506 if (xw.gm & XNegative) 1507 xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; 1508 if (xw.gm & YNegative) 1509 xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; 1510 1511 /* Events */ 1512 xw.attrs.background_pixel = dc.col[defaultbg].pixel; 1513 xw.attrs.border_pixel = dc.col[defaultbg].pixel; 1514 xw.attrs.bit_gravity = NorthWestGravity; 1515 xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask 1516 | ExposureMask | VisibilityChangeMask | StructureNotifyMask 1517 | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask 1518 #if ST_EMBEDDER_PATCH 1519 | SubstructureNotifyMask | SubstructureRedirectMask 1520 #endif // ST_EMBEDDER_PATCH 1521 ; 1522 xw.attrs.colormap = xw.cmap; 1523 #if OPENURLONCLICK_PATCH 1524 xw.attrs.event_mask |= PointerMotionMask; 1525 #endif // OPENURLONCLICK_PATCH 1526 1527 root = XRootWindow(xw.dpy, xw.scr); 1528 #if !ALPHA_PATCH 1529 if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) 1530 parent = root; 1531 #endif // ALPHA_PATCH 1532 xw.win = XCreateWindow(xw.dpy, root, xw.l, xw.t, 1533 #if ALPHA_PATCH 1534 win.w, win.h, 0, xw.depth, InputOutput, 1535 #else 1536 win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, 1537 #endif // ALPHA_PATCH 1538 xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity 1539 | CWEventMask | CWColormap, &xw.attrs); 1540 if (parent != root) 1541 XReparentWindow(xw.dpy, xw.win, parent, xw.l, xw.t); 1542 1543 memset(&gcvalues, 0, sizeof(gcvalues)); 1544 gcvalues.graphics_exposures = False; 1545 1546 #if ALPHA_PATCH 1547 #if SINGLE_DRAWABLE_BUFFER_PATCH 1548 xw.buf = xw.win; 1549 #else 1550 xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); 1551 #endif // SINGLE_DRAWABLE_BUFFER_PATCH 1552 dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); 1553 #else 1554 dc.gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, 1555 &gcvalues); 1556 #if SINGLE_DRAWABLE_BUFFER_PATCH 1557 xw.buf = xw.win; 1558 #else 1559 xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, 1560 DefaultDepth(xw.dpy, xw.scr)); 1561 #endif // SINGLE_DRAWABLE_BUFFER_PATCH 1562 #endif // ALPHA_PATCH 1563 XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); 1564 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); 1565 1566 /* font spec buffer */ 1567 #if LIGATURES_PATCH 1568 xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); 1569 xw.specseq = xmalloc(cols * sizeof(GlyphFontSeq)); 1570 #else 1571 xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); 1572 #endif // LIGATURES_PATCH 1573 1574 /* Xft rendering context */ 1575 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); 1576 1577 /* input methods */ 1578 if (!ximopen(xw.dpy)) { 1579 XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, 1580 ximinstantiate, NULL); 1581 } 1582 1583 /* white cursor, black outline */ 1584 #if HIDECURSOR_PATCH 1585 xw.pointerisvisible = 1; 1586 #if THEMED_CURSOR_PATCH 1587 xw.vpointer = XcursorLibraryLoadCursor(xw.dpy, mouseshape); 1588 #else 1589 xw.vpointer = XCreateFontCursor(xw.dpy, mouseshape); 1590 #endif // THEMED_CURSOR_PATCH 1591 XDefineCursor(xw.dpy, xw.win, xw.vpointer); 1592 #elif THEMED_CURSOR_PATCH 1593 cursor = XcursorLibraryLoadCursor(xw.dpy, mouseshape); 1594 XDefineCursor(xw.dpy, xw.win, cursor); 1595 #else 1596 cursor = XCreateFontCursor(xw.dpy, mouseshape); 1597 XDefineCursor(xw.dpy, xw.win, cursor); 1598 #endif // HIDECURSOR_PATCH 1599 1600 #if !THEMED_CURSOR_PATCH 1601 if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { 1602 xmousefg.red = 0xffff; 1603 xmousefg.green = 0xffff; 1604 xmousefg.blue = 0xffff; 1605 } 1606 1607 if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { 1608 xmousebg.red = 0x0000; 1609 xmousebg.green = 0x0000; 1610 xmousebg.blue = 0x0000; 1611 } 1612 #endif // THEMED_CURSOR_PATCH 1613 1614 #if HIDECURSOR_PATCH 1615 #if !THEMED_CURSOR_PATCH 1616 XRecolorCursor(xw.dpy, xw.vpointer, &xmousefg, &xmousebg); 1617 #endif // THEMED_CURSOR_PATCH 1618 blankpm = XCreateBitmapFromData(xw.dpy, xw.win, &(char){0}, 1, 1); 1619 xw.bpointer = XCreatePixmapCursor(xw.dpy, blankpm, blankpm, 1620 &xmousefg, &xmousebg, 0, 0); 1621 #elif !THEMED_CURSOR_PATCH 1622 XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); 1623 #endif // HIDECURSOR_PATCH 1624 1625 #if OPENURLONCLICK_PATCH 1626 xw.upointer = XCreateFontCursor(xw.dpy, XC_hand2); 1627 #if !HIDECURSOR_PATCH 1628 xw.vpointer = cursor; 1629 xw.pointerisvisible = 1; 1630 #endif // HIDECURSOR_PATCH 1631 #endif // OPENURLONCLICK_PATCH 1632 1633 xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); 1634 xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); 1635 xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); 1636 xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); 1637 XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); 1638 1639 #if NETWMICON_PATCH || NETWMICON_FF_PATCH || NETWMICON_LEGACY_PATCH 1640 setnetwmicon(); 1641 #endif // NETWMICON_PATCH 1642 1643 #if NO_WINDOW_DECORATIONS_PATCH 1644 Atom motifwmhints = XInternAtom(xw.dpy, "_MOTIF_WM_HINTS", False); 1645 unsigned int data[] = { 0x2, 0x0, 0x0, 0x0, 0x0 }; 1646 XChangeProperty(xw.dpy, xw.win, motifwmhints, motifwmhints, 16, 1647 PropModeReplace, (unsigned char *)data, 5); 1648 #endif // NO_WINDOW_DECORATIONS_PATCH 1649 1650 xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); 1651 XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, 1652 PropModeReplace, (uchar *)&thispid, 1); 1653 1654 #if FULLSCREEN_PATCH 1655 xw.netwmstate = XInternAtom(xw.dpy, "_NET_WM_STATE", False); 1656 xw.netwmfullscreen = XInternAtom(xw.dpy, "_NET_WM_STATE_FULLSCREEN", False); 1657 #endif // FULLSCREEN_PATCH 1658 1659 win.mode = MODE_NUMLOCK; 1660 resettitle(); 1661 xhints(); 1662 XMapWindow(xw.dpy, xw.win); 1663 XSync(xw.dpy, False); 1664 1665 clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); 1666 clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); 1667 xsel.primary = NULL; 1668 xsel.clipboard = NULL; 1669 xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); 1670 if (xsel.xtarget == None) 1671 xsel.xtarget = XA_STRING; 1672 1673 #if BOXDRAW_PATCH 1674 boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); 1675 #endif // BOXDRAW_PATCH 1676 } 1677 1678 #if LIGATURES_PATCH 1679 void 1680 xresetfontsettings(uint32_t mode, Font **font, int *frcflags) 1681 { 1682 *font = &dc.font; 1683 if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { 1684 *font = &dc.ibfont; 1685 *frcflags = FRC_ITALICBOLD; 1686 } else if (mode & ATTR_ITALIC) { 1687 *font = &dc.ifont; 1688 *frcflags = FRC_ITALIC; 1689 } else if (mode & ATTR_BOLD) { 1690 *font = &dc.bfont; 1691 *frcflags = FRC_BOLD; 1692 } 1693 } 1694 #endif // LIGATURES_PATCH 1695 1696 int 1697 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) 1698 { 1699 #if ANYSIZE_PATCH 1700 float winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, xp, yp; 1701 #else 1702 float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; 1703 #endif // ANYSIZE_PATCH 1704 ushort mode, prevmode = USHRT_MAX; 1705 Font *font = &dc.font; 1706 int frcflags = FRC_NORMAL; 1707 float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0f); 1708 Rune rune; 1709 FT_UInt glyphidx; 1710 FcResult fcres; 1711 FcPattern *fcpattern, *fontpattern; 1712 FcFontSet *fcsets[] = { NULL }; 1713 FcCharSet *fccharset; 1714 int i, f, numspecs = 0; 1715 #if LIGATURES_PATCH 1716 float cluster_xp, cluster_yp; 1717 HbTransformData shaped; 1718 1719 /* Initial values. */ 1720 xresetfontsettings(glyphs[0].mode, &font, &frcflags); 1721 #if VERTCENTER_PATCH 1722 xp = winx, yp = winy + font->ascent + win.cyo; 1723 #else 1724 xp = winx, yp = winy + font->ascent; 1725 #endif // VERTCENTER_PATCH 1726 cluster_xp = xp; cluster_yp = yp; 1727 /* Shape the segment. */ 1728 hbtransform(&shaped, font->match, glyphs, 0, len); 1729 #endif // LIGATURES_PATCH 1730 1731 #if LIGATURES_PATCH 1732 for (int code_idx = 0; code_idx < shaped.count; code_idx++) 1733 #elif VERTCENTER_PATCH 1734 for (i = 0, xp = winx, yp = winy + font->ascent + win.cyo; i < len; ++i) 1735 #else 1736 for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) 1737 #endif // LIGATURES_PATCH | VERTCENTER_PATCH 1738 { 1739 /* Fetch rune and mode for current glyph. */ 1740 #if LIGATURES_PATCH 1741 int idx = shaped.glyphs[code_idx].cluster; 1742 #else 1743 rune = glyphs[i].u; 1744 mode = glyphs[i].mode; 1745 #endif // LIGATURES_PATCH 1746 1747 /* Skip dummy wide-character spacing. */ 1748 #if LIGATURES_PATCH 1749 if (glyphs[idx].mode & ATTR_WDUMMY) 1750 continue; 1751 1752 /* Advance the drawing cursor if we've moved to a new cluster */ 1753 if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) { 1754 xp += runewidth; 1755 cluster_xp = xp; 1756 cluster_yp = yp; 1757 } 1758 1759 #if BOXDRAW_PATCH 1760 if (glyphs[idx].mode & ATTR_BOXDRAW) { 1761 /* minor shoehorning: boxdraw uses only this ushort */ 1762 specs[numspecs].font = font->match; 1763 specs[numspecs].glyph = boxdrawindex(&glyphs[idx]); 1764 specs[numspecs].x = xp; 1765 specs[numspecs].y = yp; 1766 numspecs++; 1767 } else if (shaped.glyphs[code_idx].codepoint != 0) { 1768 #else 1769 if (shaped.glyphs[code_idx].codepoint != 0) { 1770 #endif // BOXDRAW_PATCH 1771 /* If symbol is found, put it into the specs. */ 1772 specs[numspecs].font = font->match; 1773 specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint; 1774 specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.); 1775 specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.); 1776 cluster_xp += shaped.positions[code_idx].x_advance / 64.; 1777 cluster_yp += shaped.positions[code_idx].y_advance / 64.; 1778 numspecs++; 1779 } else { 1780 /* If it's not found, try to fetch it through the font cache. */ 1781 rune = glyphs[idx].u; 1782 for (f = 0; f < frclen; f++) { 1783 glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 1784 /* Everything correct. */ 1785 if (glyphidx && frc[f].flags == frcflags) 1786 break; 1787 /* We got a default font for a not found glyph. */ 1788 if (!glyphidx && frc[f].flags == frcflags 1789 && frc[f].unicodep == rune) { 1790 break; 1791 } 1792 } 1793 1794 /* Nothing was found. Use fontconfig to find matching font. */ 1795 if (f >= frclen) { 1796 if (!font->set) 1797 font->set = FcFontSort(0, font->pattern, 1, 0, &fcres); 1798 fcsets[0] = font->set; 1799 1800 /* 1801 * Nothing was found in the cache. Now use 1802 * some dozen of Fontconfig calls to get the 1803 * font for one single character. 1804 * 1805 * Xft and fontconfig are design failures. 1806 */ 1807 fcpattern = FcPatternDuplicate(font->pattern); 1808 fccharset = FcCharSetCreate(); 1809 1810 FcCharSetAddChar(fccharset, rune); 1811 FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 1812 FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 1813 1814 FcConfigSubstitute(0, fcpattern, FcMatchPattern); 1815 FcDefaultSubstitute(fcpattern); 1816 1817 fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); 1818 1819 /* Allocate memory for the new cache entry. */ 1820 if (frclen >= frccap) { 1821 frccap += 16; 1822 frc = xrealloc(frc, frccap * sizeof(Fontcache)); 1823 } 1824 1825 frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); 1826 if (!frc[frclen].font) 1827 die("XftFontOpenPattern failed seeking fallback font: %s\n", 1828 strerror(errno)); 1829 frc[frclen].flags = frcflags; 1830 frc[frclen].unicodep = rune; 1831 1832 glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 1833 1834 f = frclen; 1835 frclen++; 1836 1837 FcPatternDestroy(fcpattern); 1838 FcCharSetDestroy(fccharset); 1839 } 1840 1841 specs[numspecs].font = frc[f].font; 1842 specs[numspecs].glyph = glyphidx; 1843 specs[numspecs].x = (short)xp; 1844 specs[numspecs].y = (short)yp; 1845 numspecs++; 1846 } 1847 #else // !LIGATURES_PATCH 1848 if (mode == ATTR_WDUMMY) 1849 continue; 1850 1851 /* Determine font for glyph if different from previous glyph. */ 1852 if (prevmode != mode) { 1853 prevmode = mode; 1854 font = &dc.font; 1855 frcflags = FRC_NORMAL; 1856 runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); 1857 if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { 1858 font = &dc.ibfont; 1859 frcflags = FRC_ITALICBOLD; 1860 } else if (mode & ATTR_ITALIC) { 1861 font = &dc.ifont; 1862 frcflags = FRC_ITALIC; 1863 } else if (mode & ATTR_BOLD) { 1864 font = &dc.bfont; 1865 frcflags = FRC_BOLD; 1866 } 1867 #if VERTCENTER_PATCH 1868 yp = winy + font->ascent + win.cyo; 1869 #else 1870 yp = winy + font->ascent; 1871 #endif // VERTCENTER_PATCH 1872 } 1873 1874 #if BOXDRAW_PATCH 1875 if (mode & ATTR_BOXDRAW) { 1876 /* minor shoehorning: boxdraw uses only this ushort */ 1877 glyphidx = boxdrawindex(&glyphs[i]); 1878 } else { 1879 /* Lookup character index with default font. */ 1880 glyphidx = XftCharIndex(xw.dpy, font->match, rune); 1881 } 1882 #else 1883 /* Lookup character index with default font. */ 1884 glyphidx = XftCharIndex(xw.dpy, font->match, rune); 1885 #endif // BOXDRAW_PATCH 1886 if (glyphidx) { 1887 specs[numspecs].font = font->match; 1888 specs[numspecs].glyph = glyphidx; 1889 specs[numspecs].x = (short)xp; 1890 specs[numspecs].y = (short)yp; 1891 xp += runewidth; 1892 numspecs++; 1893 continue; 1894 } 1895 1896 /* Fallback on font cache, search the font cache for match. */ 1897 for (f = 0; f < frclen; f++) { 1898 glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 1899 /* Everything correct. */ 1900 if (glyphidx && frc[f].flags == frcflags) 1901 break; 1902 /* We got a default font for a not found glyph. */ 1903 if (!glyphidx && frc[f].flags == frcflags 1904 && frc[f].unicodep == rune) { 1905 break; 1906 } 1907 } 1908 1909 /* Nothing was found. Use fontconfig to find matching font. */ 1910 if (f >= frclen) { 1911 if (!font->set) 1912 font->set = FcFontSort(0, font->pattern, 1, 0, &fcres); 1913 fcsets[0] = font->set; 1914 1915 /* 1916 * Nothing was found in the cache. Now use 1917 * some dozen of Fontconfig calls to get the 1918 * font for one single character. 1919 * 1920 * Xft and fontconfig are design failures. 1921 */ 1922 fcpattern = FcPatternDuplicate(font->pattern); 1923 fccharset = FcCharSetCreate(); 1924 1925 FcCharSetAddChar(fccharset, rune); 1926 FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 1927 FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 1928 1929 #if !USE_XFTFONTMATCH_PATCH 1930 FcConfigSubstitute(0, fcpattern, FcMatchPattern); 1931 FcDefaultSubstitute(fcpattern); 1932 #endif // USE_XFTFONTMATCH_PATCH 1933 1934 fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); 1935 1936 /* Allocate memory for the new cache entry. */ 1937 if (frclen >= frccap) { 1938 frccap += 16; 1939 frc = xrealloc(frc, frccap * sizeof(Fontcache)); 1940 } 1941 1942 frc[frclen].font = XftFontOpenPattern(xw.dpy, fontpattern); 1943 if (!frc[frclen].font) 1944 die("XftFontOpenPattern failed seeking fallback font: %s\n", 1945 strerror(errno)); 1946 frc[frclen].flags = frcflags; 1947 frc[frclen].unicodep = rune; 1948 1949 glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 1950 1951 f = frclen; 1952 frclen++; 1953 1954 FcPatternDestroy(fcpattern); 1955 FcCharSetDestroy(fccharset); 1956 } 1957 1958 specs[numspecs].font = frc[f].font; 1959 specs[numspecs].glyph = glyphidx; 1960 specs[numspecs].x = (short)xp; 1961 specs[numspecs].y = (short)yp; 1962 xp += runewidth; 1963 numspecs++; 1964 #endif // LIGATURES_PATCH 1965 } 1966 1967 return numspecs; 1968 } 1969 1970 #if UNDERCURL_PATCH 1971 static int isSlopeRising (int x, int iPoint, int waveWidth) 1972 { 1973 // . . . . 1974 // / \ / \ / \ / \ 1975 // / \ / \ / \ / \ 1976 // . . . . . 1977 1978 // Find absolute `x` of point 1979 x += iPoint * (waveWidth/2); 1980 1981 // Find index of absolute wave 1982 int absSlope = x / ((float)waveWidth/2); 1983 1984 return (absSlope % 2); 1985 } 1986 1987 static int getSlope (int x, int iPoint, int waveWidth) 1988 { 1989 // Sizes: Caps are half width of slopes 1990 // 1_2 1_2 1_2 1_2 1991 // / \ / \ / \ / \ 1992 // / \ / \ / \ / \ 1993 // 0 3_0 3_0 3_0 3_ 1994 // <2-> <1> <---6----> 1995 1996 // Find type of first point 1997 int firstType; 1998 x -= (x / waveWidth) * waveWidth; 1999 if (x < (waveWidth * (2.f/6.f))) 2000 firstType = UNDERCURL_SLOPE_ASCENDING; 2001 else if (x < (waveWidth * (3.f/6.f))) 2002 firstType = UNDERCURL_SLOPE_TOP_CAP; 2003 else if (x < (waveWidth * (5.f/6.f))) 2004 firstType = UNDERCURL_SLOPE_DESCENDING; 2005 else 2006 firstType = UNDERCURL_SLOPE_BOTTOM_CAP; 2007 2008 // Find type of given point 2009 int pointType = (iPoint % 4); 2010 pointType += firstType; 2011 pointType %= 4; 2012 2013 return pointType; 2014 } 2015 #endif // UNDERCURL_PATCH 2016 2017 void 2018 xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y 2019 #if WIDE_GLYPHS_PATCH 2020 ,int dmode 2021 #endif // WIDE_GLYPHS_PATCH 2022 #if LIGATURES_PATCH 2023 , int charlen 2024 #endif // LIGATURES_PATCH 2025 ) { 2026 #if LIGATURES_PATCH 2027 int width = charlen * win.cw; 2028 #else 2029 int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); 2030 int width = charlen * win.cw; 2031 #endif // WIDE_GLYPHS_PATCH 2032 #if ANYSIZE_PATCH 2033 int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch; 2034 #else 2035 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch; 2036 #endif // ANYSIZE_PATCH 2037 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; 2038 XRenderColor colfg, colbg; 2039 XRectangle r; 2040 2041 /* Fallback on color display for attributes not supported by the font */ 2042 if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { 2043 if (dc.ibfont.badslant || dc.ibfont.badweight) 2044 base.fg = defaultattr; 2045 } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || 2046 (base.mode & ATTR_BOLD && dc.bfont.badweight)) { 2047 base.fg = defaultattr; 2048 } 2049 2050 if (IS_TRUECOL(base.fg)) { 2051 colfg.alpha = 0xffff; 2052 colfg.red = TRUERED(base.fg); 2053 colfg.green = TRUEGREEN(base.fg); 2054 colfg.blue = TRUEBLUE(base.fg); 2055 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); 2056 fg = &truefg; 2057 } else { 2058 fg = &dc.col[base.fg]; 2059 } 2060 2061 if (IS_TRUECOL(base.bg)) { 2062 colbg.alpha = 0xffff; 2063 colbg.green = TRUEGREEN(base.bg); 2064 colbg.red = TRUERED(base.bg); 2065 colbg.blue = TRUEBLUE(base.bg); 2066 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); 2067 bg = &truebg; 2068 } else { 2069 bg = &dc.col[base.bg]; 2070 } 2071 2072 #if !BOLD_IS_NOT_BRIGHT_PATCH 2073 /* Change basic system colors [0-7] to bright system colors [8-15] */ 2074 if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) 2075 fg = &dc.col[base.fg + 8]; 2076 #endif // BOLD_IS_NOT_BRIGHT_PATCH 2077 2078 if (IS_SET(MODE_REVERSE)) { 2079 if (fg == &dc.col[defaultfg]) { 2080 fg = &dc.col[defaultbg]; 2081 } else { 2082 colfg.red = ~fg->color.red; 2083 colfg.green = ~fg->color.green; 2084 colfg.blue = ~fg->color.blue; 2085 colfg.alpha = fg->color.alpha; 2086 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, 2087 &revfg); 2088 fg = &revfg; 2089 } 2090 2091 if (bg == &dc.col[defaultbg]) { 2092 bg = &dc.col[defaultfg]; 2093 } else { 2094 colbg.red = ~bg->color.red; 2095 colbg.green = ~bg->color.green; 2096 colbg.blue = ~bg->color.blue; 2097 colbg.alpha = bg->color.alpha; 2098 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, 2099 &revbg); 2100 bg = &revbg; 2101 } 2102 } 2103 2104 if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { 2105 colfg.red = fg->color.red / 2; 2106 colfg.green = fg->color.green / 2; 2107 colfg.blue = fg->color.blue / 2; 2108 colfg.alpha = fg->color.alpha; 2109 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); 2110 fg = &revfg; 2111 } 2112 2113 if (base.mode & ATTR_REVERSE) { 2114 #if SPOILER_PATCH 2115 if (bg == fg) { 2116 bg = &dc.col[defaultfg]; 2117 fg = &dc.col[defaultbg]; 2118 } else { 2119 temp = fg; 2120 fg = bg; 2121 bg = temp; 2122 } 2123 #else 2124 temp = fg; 2125 fg = bg; 2126 bg = temp; 2127 #endif // SPOILER_PATCH 2128 } 2129 2130 #if SELECTION_COLORS_PATCH 2131 if (base.mode & ATTR_SELECTED) { 2132 bg = &dc.col[selectionbg]; 2133 if (!ignoreselfg) 2134 fg = &dc.col[selectionfg]; 2135 } 2136 #endif // SELECTION_COLORS_PATCH 2137 2138 if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) 2139 fg = bg; 2140 2141 if (base.mode & ATTR_INVISIBLE) 2142 fg = bg; 2143 2144 #if INVERT_PATCH 2145 if (invertcolors) { 2146 revfg = invertedcolor(fg); 2147 revbg = invertedcolor(bg); 2148 fg = &revfg; 2149 bg = &revbg; 2150 } 2151 #endif // INVERT_PATCH 2152 2153 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 2154 if (base.mode & ATTR_HIGHLIGHT) { 2155 fg = &dc.col[(base.mode & ATTR_REVERSE) ? highlightbg : highlightfg]; 2156 bg = &dc.col[(base.mode & ATTR_REVERSE) ? highlightfg : highlightbg]; 2157 } 2158 #endif // KEYBOARDSELECT_PATCH 2159 2160 #if ALPHA_PATCH && ALPHA_GRADIENT_PATCH 2161 // gradient 2162 bg->color.alpha = grad_alpha * 0xffff * (win.h - y*win.ch) / win.h + stat_alpha * 0xffff; 2163 // uncomment to invert the gradient 2164 // bg->color.alpha = grad_alpha * 0xffff * (y*win.ch) / win.h + stat_alpha * 0xffff; 2165 #endif // ALPHA_PATCH | ALPHA_GRADIENT_PATCH 2166 2167 #if WIDE_GLYPHS_PATCH 2168 if (dmode & DRAW_BG) { 2169 #endif // WIDE_GLYPHS_PATCH 2170 /* Intelligent cleaning up of the borders. */ 2171 #if ANYSIZE_PATCH 2172 if (x == 0) { 2173 xclear(0, (y == 0)? 0 : winy, win.hborderpx, 2174 winy + win.ch + 2175 ((winy + win.ch >= win.vborderpx + win.th)? win.h : 0)); 2176 } 2177 if (winx + width >= win.hborderpx + win.tw) { 2178 xclear(winx + width, (y == 0)? 0 : winy, win.w, 2179 ((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch))); 2180 } 2181 if (y == 0) 2182 xclear(winx, 0, winx + width, win.vborderpx); 2183 if (winy + win.ch >= win.vborderpx + win.th) 2184 xclear(winx, winy + win.ch, winx + width, win.h); 2185 #else 2186 if (x == 0) { 2187 xclear(0, (y == 0)? 0 : winy, borderpx, 2188 winy + win.ch + 2189 ((winy + win.ch >= borderpx + win.th)? win.h : 0)); 2190 } 2191 if (winx + width >= borderpx + win.tw) { 2192 xclear(winx + width, (y == 0)? 0 : winy, win.w, 2193 ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); 2194 } 2195 if (y == 0) 2196 xclear(winx, 0, winx + width, borderpx); 2197 if (winy + win.ch >= borderpx + win.th) 2198 xclear(winx, winy + win.ch, winx + width, win.h); 2199 #endif // ANYSIZE_PATCH 2200 2201 /* Clean up the region we want to draw to. */ 2202 #if BACKGROUND_IMAGE_PATCH 2203 if (bg == &dc.col[defaultbg]) 2204 xclear(winx, winy, winx + width, winy + win.ch); 2205 else 2206 #endif // BACKGROUND_IMAGE_PATCH 2207 2208 #if !WIDE_GLYPHS_PATCH 2209 XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); 2210 #endif // WIDE_GLYPHS_PATCH 2211 2212 /* Set the clip region because Xft is sometimes dirty. */ 2213 r.x = 0; 2214 r.y = 0; 2215 r.height = win.ch; 2216 r.width = width; 2217 XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); 2218 2219 #if WIDE_GLYPHS_PATCH 2220 /* Fill the background */ 2221 XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); 2222 } 2223 #endif // WIDE_GLYPHS_PATCH 2224 2225 #if WIDE_GLYPHS_PATCH 2226 if (dmode & DRAW_FG) { 2227 #endif // WIDE_GLYPHS_PATCH 2228 #if BOXDRAW_PATCH 2229 if (base.mode & ATTR_BOXDRAW) { 2230 drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); 2231 } else { 2232 /* Render the glyphs. */ 2233 XftDrawGlyphFontSpec(xw.draw, fg, specs, len); 2234 } 2235 #else 2236 /* Render the glyphs. */ 2237 XftDrawGlyphFontSpec(xw.draw, fg, specs, len); 2238 #endif // BOXDRAW_PATCH 2239 2240 /* Render underline and strikethrough. */ 2241 if (base.mode & ATTR_UNDERLINE) { 2242 #if UNDERCURL_PATCH 2243 // Underline Color 2244 const int widthThreshold = 28; // +1 width every widthThreshold px of font 2245 int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width 2246 int linecolor; 2247 if ((base.ucolor[0] >= 0) && 2248 !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) && 2249 !(base.mode & ATTR_INVISIBLE) 2250 ) { 2251 // Special color for underline 2252 // Index 2253 if (base.ucolor[1] < 0) { 2254 linecolor = dc.col[base.ucolor[0]].pixel; 2255 } 2256 // RGB 2257 else { 2258 XColor lcolor; 2259 lcolor.red = base.ucolor[0] * 257; 2260 lcolor.green = base.ucolor[1] * 257; 2261 lcolor.blue = base.ucolor[2] * 257; 2262 lcolor.flags = DoRed | DoGreen | DoBlue; 2263 XAllocColor(xw.dpy, xw.cmap, &lcolor); 2264 linecolor = lcolor.pixel; 2265 } 2266 } else { 2267 // Foreground color for underline 2268 linecolor = fg->pixel; 2269 } 2270 2271 XGCValues ugcv = { 2272 .foreground = linecolor, 2273 .line_width = wlw, 2274 .line_style = LineSolid, 2275 .cap_style = CapNotLast 2276 }; 2277 2278 GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), 2279 GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, 2280 &ugcv); 2281 2282 // Underline Style 2283 if (base.ustyle != 3) { 2284 XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx, 2285 winy + dc.font.ascent * chscale + 1, width, wlw); 2286 } else if (base.ustyle == 3) { 2287 int ww = win.cw;//width; 2288 int wh = dc.font.descent - wlw/2 - 1;//r.height/7; 2289 int wx = winx; 2290 int wy = winy + win.ch - dc.font.descent; 2291 #if VERTCENTER_PATCH 2292 wy -= win.cyo; 2293 #endif // VERTCENTER_PATCH 2294 2295 #if UNDERCURL_STYLE == UNDERCURL_CURLY 2296 // Draw waves 2297 int narcs = charlen * 2 + 1; 2298 XArc *arcs = xmalloc(sizeof(XArc) * narcs); 2299 2300 int i = 0; 2301 for (i = 0; i < charlen-1; i++) { 2302 arcs[i*2] = (XArc) { 2303 .x = wx + win.cw * i + ww / 4, 2304 .y = wy, 2305 .width = win.cw / 2, 2306 .height = wh, 2307 .angle1 = 0, 2308 .angle2 = 180 * 64 2309 }; 2310 arcs[i*2+1] = (XArc) { 2311 .x = wx + win.cw * i + ww * 0.75, 2312 .y = wy, 2313 .width = win.cw/2, 2314 .height = wh, 2315 .angle1 = 180 * 64, 2316 .angle2 = 180 * 64 2317 }; 2318 } 2319 // Last wave 2320 arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh, 2321 0, 180 * 64 }; 2322 // Last wave tail 2323 arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.), 2324 wh, 180 * 64, 90 * 64}; 2325 // First wave tail 2326 i++; 2327 arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64, 2328 90 * 64 }; 2329 2330 XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs); 2331 2332 free(arcs); 2333 #elif UNDERCURL_STYLE == UNDERCURL_SPIKY 2334 // Make the underline corridor larger 2335 /* 2336 wy -= wh; 2337 */ 2338 wh *= 2; 2339 2340 // Set the angle of the slope to 45° 2341 ww = wh; 2342 2343 // Position of wave is independent of word, it's absolute 2344 wx = (wx / (ww/2)) * (ww/2); 2345 2346 int marginStart = winx - wx; 2347 2348 // Calculate number of points with floating precision 2349 float n = width; // Width of word in pixels 2350 n = (n / ww) * 2; // Number of slopes (/ or \) 2351 n += 2; // Add two last points 2352 int npoints = n; // Convert to int 2353 2354 // Total length of underline 2355 float waveLength = 0; 2356 2357 if (npoints >= 3) { 2358 // We add an aditional slot in case we use a bonus point 2359 XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); 2360 2361 // First point (Starts with the word bounds) 2362 points[0] = (XPoint) { 2363 .x = wx + marginStart, 2364 .y = (isSlopeRising(wx, 0, ww)) 2365 ? (wy - marginStart + ww/2.f) 2366 : (wy + marginStart) 2367 }; 2368 2369 // Second point (Goes back to the absolute point coordinates) 2370 points[1] = (XPoint) { 2371 .x = (ww/2.f) - marginStart, 2372 .y = (isSlopeRising(wx, 1, ww)) 2373 ? (ww/2.f - marginStart) 2374 : (-ww/2.f + marginStart) 2375 }; 2376 waveLength += (ww/2.f) - marginStart; 2377 2378 // The rest of the points 2379 for (int i = 2; i < npoints-1; i++) { 2380 points[i] = (XPoint) { 2381 .x = ww/2, 2382 .y = (isSlopeRising(wx, i, ww)) 2383 ? wh/2 2384 : -wh/2 2385 }; 2386 waveLength += ww/2; 2387 } 2388 2389 // Last point 2390 points[npoints-1] = (XPoint) { 2391 .x = ww/2, 2392 .y = (isSlopeRising(wx, npoints-1, ww)) 2393 ? wh/2 2394 : -wh/2 2395 }; 2396 waveLength += ww/2; 2397 2398 // End 2399 if (waveLength < width) { // Add a bonus point? 2400 int marginEnd = width - waveLength; 2401 points[npoints] = (XPoint) { 2402 .x = marginEnd, 2403 .y = (isSlopeRising(wx, npoints, ww)) 2404 ? (marginEnd) 2405 : (-marginEnd) 2406 }; 2407 2408 npoints++; 2409 } else if (waveLength > width) { // Is last point too far? 2410 int marginEnd = waveLength - width; 2411 points[npoints-1].x -= marginEnd; 2412 if (isSlopeRising(wx, npoints-1, ww)) 2413 points[npoints-1].y -= (marginEnd); 2414 else 2415 points[npoints-1].y += (marginEnd); 2416 } 2417 2418 // Draw the lines 2419 XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, 2420 CoordModePrevious); 2421 2422 // Draw a second underline with an offset of 1 pixel 2423 if ( ((win.ch / (widthThreshold/2)) % 2)) { 2424 points[0].x++; 2425 2426 XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, 2427 npoints, CoordModePrevious); 2428 } 2429 2430 // Free resources 2431 free(points); 2432 } 2433 #else // UNDERCURL_CAPPED 2434 // Cap is half of wave width 2435 float capRatio = 0.5f; 2436 2437 // Make the underline corridor larger 2438 wh *= 2; 2439 2440 // Set the angle of the slope to 45° 2441 ww = wh; 2442 ww *= 1 + capRatio; // Add a bit of width for the cap 2443 2444 // Position of wave is independent of word, it's absolute 2445 wx = (wx / ww) * ww; 2446 2447 float marginStart; 2448 switch(getSlope(winx, 0, ww)) { 2449 case UNDERCURL_SLOPE_ASCENDING: 2450 marginStart = winx - wx; 2451 break; 2452 case UNDERCURL_SLOPE_TOP_CAP: 2453 marginStart = winx - (wx + (ww * (2.f/6.f))); 2454 break; 2455 case UNDERCURL_SLOPE_DESCENDING: 2456 marginStart = winx - (wx + (ww * (3.f/6.f))); 2457 break; 2458 case UNDERCURL_SLOPE_BOTTOM_CAP: 2459 marginStart = winx - (wx + (ww * (5.f/6.f))); 2460 break; 2461 } 2462 2463 // Calculate number of points with floating precision 2464 float n = width; // Width of word in pixels 2465 // ._. 2466 n = (n / ww) * 4; // Number of points (./ \.) 2467 n += 2; // Add two last points 2468 int npoints = n; // Convert to int 2469 2470 // Position of the pen to draw the lines 2471 float penX = 0; 2472 float penY = 0; 2473 2474 if (npoints >= 3) { 2475 XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); 2476 2477 // First point (Starts with the word bounds) 2478 penX = winx; 2479 switch (getSlope(winx, 0, ww)) { 2480 case UNDERCURL_SLOPE_ASCENDING: 2481 penY = wy + wh/2.f - marginStart; 2482 break; 2483 case UNDERCURL_SLOPE_TOP_CAP: 2484 penY = wy; 2485 break; 2486 case UNDERCURL_SLOPE_DESCENDING: 2487 penY = wy + marginStart; 2488 break; 2489 case UNDERCURL_SLOPE_BOTTOM_CAP: 2490 penY = wy + wh/2.f; 2491 break; 2492 } 2493 points[0].x = penX; 2494 points[0].y = penY; 2495 2496 // Second point (Goes back to the absolute point coordinates) 2497 switch (getSlope(winx, 1, ww)) { 2498 case UNDERCURL_SLOPE_ASCENDING: 2499 penX += ww * (1.f/6.f) - marginStart; 2500 penY += 0; 2501 break; 2502 case UNDERCURL_SLOPE_TOP_CAP: 2503 penX += ww * (2.f/6.f) - marginStart; 2504 penY += -wh/2.f + marginStart; 2505 break; 2506 case UNDERCURL_SLOPE_DESCENDING: 2507 penX += ww * (1.f/6.f) - marginStart; 2508 penY += 0; 2509 break; 2510 case UNDERCURL_SLOPE_BOTTOM_CAP: 2511 penX += ww * (2.f/6.f) - marginStart; 2512 penY += -marginStart + wh/2.f; 2513 break; 2514 } 2515 points[1].x = penX; 2516 points[1].y = penY; 2517 2518 // The rest of the points 2519 for (int i = 2; i < npoints; i++) { 2520 switch (getSlope(winx, i, ww)) { 2521 case UNDERCURL_SLOPE_ASCENDING: 2522 case UNDERCURL_SLOPE_DESCENDING: 2523 penX += ww * (1.f/6.f); 2524 penY += 0; 2525 break; 2526 case UNDERCURL_SLOPE_TOP_CAP: 2527 penX += ww * (2.f/6.f); 2528 penY += -wh / 2.f; 2529 break; 2530 case UNDERCURL_SLOPE_BOTTOM_CAP: 2531 penX += ww * (2.f/6.f); 2532 penY += wh / 2.f; 2533 break; 2534 } 2535 points[i].x = penX; 2536 points[i].y = penY; 2537 } 2538 2539 // End 2540 float waveLength = penX - winx; 2541 if (waveLength < width) { // Add a bonus point? 2542 int marginEnd = width - waveLength; 2543 penX += marginEnd; 2544 switch(getSlope(winx, npoints, ww)) { 2545 case UNDERCURL_SLOPE_ASCENDING: 2546 case UNDERCURL_SLOPE_DESCENDING: 2547 //penY += 0; 2548 break; 2549 case UNDERCURL_SLOPE_TOP_CAP: 2550 penY += -marginEnd; 2551 break; 2552 case UNDERCURL_SLOPE_BOTTOM_CAP: 2553 penY += marginEnd; 2554 break; 2555 } 2556 2557 points[npoints].x = penX; 2558 points[npoints].y = penY; 2559 2560 npoints++; 2561 } else if (waveLength > width) { // Is last point too far? 2562 int marginEnd = waveLength - width; 2563 points[npoints-1].x -= marginEnd; 2564 switch(getSlope(winx, npoints-1, ww)) { 2565 case UNDERCURL_SLOPE_TOP_CAP: 2566 points[npoints-1].y += marginEnd; 2567 break; 2568 case UNDERCURL_SLOPE_BOTTOM_CAP: 2569 points[npoints-1].y -= marginEnd; 2570 break; 2571 default: 2572 break; 2573 } 2574 } 2575 2576 // Draw the lines 2577 XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, 2578 CoordModeOrigin); 2579 2580 // Draw a second underline with an offset of 1 pixel 2581 if ( ((win.ch / (widthThreshold/2)) % 2)) { 2582 for (int i = 0; i < npoints; i++) 2583 points[i].x++; 2584 2585 XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, 2586 npoints, CoordModeOrigin); 2587 } 2588 2589 // Free resources 2590 free(points); 2591 } 2592 #endif 2593 } 2594 2595 XFreeGC(xw.dpy, ugc); 2596 #elif VERTCENTER_PATCH 2597 XftDrawRect(xw.draw, fg, winx, winy + win.cyo + dc.font.ascent * chscale + 1, 2598 width, 1); 2599 #else 2600 XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, 2601 width, 1); 2602 #endif // UNDERCURL_PATCH | VERTCENTER_PATCH 2603 } 2604 2605 if (base.mode & ATTR_STRUCK) { 2606 #if VERTCENTER_PATCH 2607 XftDrawRect(xw.draw, fg, winx, winy + win.cyo + 2 * dc.font.ascent * chscale / 3, 2608 width, 1); 2609 #else 2610 XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, 2611 width, 1); 2612 #endif // VERTCENTER_PATCH 2613 } 2614 #if WIDE_GLYPHS_PATCH 2615 } 2616 #endif // WIDE_GLYPHS_PATCH 2617 2618 #if OPENURLONCLICK_PATCH 2619 /* underline url (openurlonclick patch) */ 2620 if (url_draw && y >= url_y1 && y <= url_y2) { 2621 int x1 = (y == url_y1) ? url_x1 : 0; 2622 int x2 = (y == url_y2) ? MIN(url_x2, term.col-1) : url_maxcol; 2623 if (x + charlen > x1 && x <= x2) { 2624 int xu = MAX(x, x1); 2625 int wu = (x2 - xu + 1) * win.cw; 2626 #if ANYSIZE_PATCH 2627 xu = win.hborderpx + xu * win.cw; 2628 #else 2629 xu = borderpx + xu * win.cw; 2630 #endif // ANYSIZE_PATCH 2631 #if VERTCENTER_PATCH 2632 XftDrawRect(xw.draw, fg, xu, winy + win.cyo + dc.font.ascent * chscale + 2, wu, 1); 2633 #else 2634 XftDrawRect(xw.draw, fg, xu, winy + dc.font.ascent * chscale + 2, wu, 1); 2635 #endif // VERTCENTER_PATCH 2636 url_draw = (y != url_y2 || x + charlen <= x2); 2637 } 2638 } 2639 #endif // OPENURLONCLICK_PATCH 2640 2641 /* Reset clip to none. */ 2642 XftDrawSetClip(xw.draw, 0); 2643 } 2644 2645 void 2646 xdrawglyph(Glyph g, int x, int y) 2647 { 2648 int numspecs; 2649 XftGlyphFontSpec *specs = xw.specbuf; 2650 2651 numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); 2652 xdrawglyphfontspecs(specs, g, numspecs, x, y 2653 #if WIDE_GLYPHS_PATCH 2654 ,DRAW_BG | DRAW_FG 2655 #endif // WIDE_GLYPHS_PATCH 2656 #if LIGATURES_PATCH 2657 ,(g.mode & ATTR_WIDE) ? 2 : 1 2658 #endif // LIGATURES_PATCH 2659 ); 2660 } 2661 2662 void 2663 #if LIGATURES_PATCH 2664 xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) 2665 #else 2666 xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) 2667 #endif // LIGATURES_PATCH 2668 { 2669 Color drawcol; 2670 #if DYNAMIC_CURSOR_COLOR_PATCH 2671 XRenderColor colbg; 2672 #endif // DYNAMIC_CURSOR_COLOR_PATCH 2673 2674 #if !DYNAMIC_CURSOR_COLOR_PATCH 2675 /* remove the old cursor */ 2676 if (selected(ox, oy)) 2677 #if SELECTION_COLORS_PATCH 2678 og.mode |= ATTR_SELECTED; 2679 #else 2680 og.mode ^= ATTR_REVERSE; 2681 #endif // SELECTION_COLORS_PATCH 2682 #endif // DYNAMIC_CURSOR_COLOR_PATCH 2683 2684 #if LIGATURES_PATCH 2685 /* Redraw the line where cursor was previously. 2686 * It will restore the ligatures broken by the cursor. */ 2687 xdrawline(line, 0, oy, len); 2688 #else 2689 xdrawglyph(og, ox, oy); 2690 #endif // LIGATURES_PATCH 2691 2692 #if HIDE_TERMINAL_CURSOR_PATCH 2693 if (IS_SET(MODE_HIDE) || !IS_SET(MODE_FOCUSED)) 2694 return; 2695 #else 2696 if (IS_SET(MODE_HIDE)) 2697 return; 2698 #endif // HIDE_TERMINAL_CURSOR_PATCH 2699 2700 /* 2701 * Select the right color for the right mode. 2702 */ 2703 g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE 2704 #if BOXDRAW_PATCH 2705 |ATTR_BOXDRAW 2706 #endif // BOXDRAW_PATCH 2707 #if DYNAMIC_CURSOR_COLOR_PATCH 2708 |ATTR_REVERSE 2709 #endif // DYNAMIC_CURSOR_COLOR_PATCH 2710 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 2711 |ATTR_HIGHLIGHT 2712 #endif // KEYBOARDSELECT_PATCH 2713 ; 2714 2715 if (IS_SET(MODE_REVERSE)) { 2716 g.mode |= ATTR_REVERSE; 2717 g.bg = defaultfg; 2718 #if SELECTION_COLORS_PATCH 2719 g.fg = defaultcs; 2720 drawcol = dc.col[defaultrcs]; 2721 #else 2722 if (selected(cx, cy)) { 2723 drawcol = dc.col[defaultcs]; 2724 g.fg = defaultrcs; 2725 } else { 2726 drawcol = dc.col[defaultrcs]; 2727 g.fg = defaultcs; 2728 } 2729 #endif // SELECTION_COLORS_PATCH 2730 } else { 2731 #if SELECTION_COLORS_PATCH 2732 g.fg = defaultbg; 2733 g.bg = defaultcs; 2734 drawcol = dc.col[defaultcs]; 2735 #else 2736 if (selected(cx, cy)) { 2737 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 2738 g.mode &= ~(ATTR_REVERSE | ATTR_HIGHLIGHT); 2739 #elif DYNAMIC_CURSOR_COLOR_PATCH 2740 g.mode &= ~ATTR_REVERSE; 2741 #endif // DYNAMIC_CURSOR_COLOR_PATCH 2742 g.fg = defaultfg; 2743 g.bg = defaultrcs; 2744 } else { 2745 #if DYNAMIC_CURSOR_COLOR_PATCH 2746 unsigned int tmpcol = g.bg; 2747 g.bg = g.fg; 2748 g.fg = tmpcol; 2749 #else 2750 g.fg = defaultbg; 2751 g.bg = defaultcs; 2752 #endif // DYNAMIC_CURSOR_COLOR_PATCH 2753 } 2754 2755 #if DYNAMIC_CURSOR_COLOR_PATCH 2756 if (IS_TRUECOL(g.bg)) { 2757 colbg.alpha = 0xffff; 2758 colbg.red = TRUERED(g.bg); 2759 colbg.green = TRUEGREEN(g.bg); 2760 colbg.blue = TRUEBLUE(g.bg); 2761 XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &drawcol); 2762 } else 2763 drawcol = dc.col[g.bg]; 2764 #else 2765 drawcol = dc.col[g.bg]; 2766 #endif // DYNAMIC_CURSOR_COLOR_PATCH 2767 #endif // SELECTION_COLORS_PATCH 2768 } 2769 2770 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 2771 if (g.mode & ATTR_HIGHLIGHT) 2772 g.mode ^= ATTR_REVERSE; 2773 #endif // KEYBOARDSELECT_PATCH 2774 2775 /* draw the new one */ 2776 if (IS_SET(MODE_FOCUSED)) { 2777 switch (win.cursor) { 2778 #if !BLINKING_CURSOR_PATCH 2779 case 7: /* st extension */ 2780 g.u = 0x2603; /* snowman (U+2603) */ 2781 /* FALLTHROUGH */ 2782 #endif // BLINKING_CURSOR_PATCH 2783 case 0: /* Blinking block */ 2784 case 1: /* Blinking block (default) */ 2785 #if BLINKING_CURSOR_PATCH 2786 if (IS_SET(MODE_BLINK)) 2787 break; 2788 /* FALLTHROUGH */ 2789 #endif // BLINKING_CURSOR_PATCH 2790 case 2: /* Steady block */ 2791 xdrawglyph(g, cx, cy); 2792 break; 2793 case 3: /* Blinking underline */ 2794 #if BLINKING_CURSOR_PATCH 2795 if (IS_SET(MODE_BLINK)) 2796 break; 2797 /* FALLTHROUGH */ 2798 #endif // BLINKING_CURSOR_PATCH 2799 case 4: /* Steady underline */ 2800 #if ANYSIZE_PATCH 2801 XftDrawRect(xw.draw, &drawcol, 2802 win.hborderpx + cx * win.cw, 2803 win.vborderpx + (cy + 1) * win.ch - \ 2804 cursorthickness, 2805 win.cw, cursorthickness); 2806 #else 2807 XftDrawRect(xw.draw, &drawcol, 2808 borderpx + cx * win.cw, 2809 borderpx + (cy + 1) * win.ch - \ 2810 cursorthickness, 2811 win.cw, cursorthickness); 2812 #endif // ANYSIZE_PATCH 2813 break; 2814 case 5: /* Blinking bar */ 2815 #if BLINKING_CURSOR_PATCH 2816 if (IS_SET(MODE_BLINK)) 2817 break; 2818 /* FALLTHROUGH */ 2819 #endif // BLINKING_CURSOR_PATCH 2820 case 6: /* Steady bar */ 2821 XftDrawRect(xw.draw, &drawcol, 2822 #if ANYSIZE_PATCH 2823 win.hborderpx + cx * win.cw, 2824 win.vborderpx + cy * win.ch, 2825 #else 2826 borderpx + cx * win.cw, 2827 borderpx + cy * win.ch, 2828 #endif // ANYSIZE_PATCH 2829 cursorthickness, win.ch); 2830 break; 2831 #if BLINKING_CURSOR_PATCH 2832 case 7: /* Blinking st cursor */ 2833 if (IS_SET(MODE_BLINK)) 2834 break; 2835 /* FALLTHROUGH */ 2836 case 8: /* Steady st cursor */ 2837 g.u = stcursor; 2838 xdrawglyph(g, cx, cy); 2839 break; 2840 #endif // BLINKING_CURSOR_PATCH 2841 } 2842 } else { 2843 XftDrawRect(xw.draw, &drawcol, 2844 #if ANYSIZE_PATCH 2845 win.hborderpx + cx * win.cw, 2846 win.vborderpx + cy * win.ch, 2847 #else 2848 borderpx + cx * win.cw, 2849 borderpx + cy * win.ch, 2850 #endif // ANYSIZE_PATCH 2851 win.cw - 1, 1); 2852 XftDrawRect(xw.draw, &drawcol, 2853 #if ANYSIZE_PATCH 2854 win.hborderpx + cx * win.cw, 2855 win.vborderpx + cy * win.ch, 2856 #else 2857 borderpx + cx * win.cw, 2858 borderpx + cy * win.ch, 2859 #endif // ANYSIZE_PATCH 2860 1, win.ch - 1); 2861 XftDrawRect(xw.draw, &drawcol, 2862 #if ANYSIZE_PATCH 2863 win.hborderpx + (cx + 1) * win.cw - 1, 2864 win.vborderpx + cy * win.ch, 2865 #else 2866 borderpx + (cx + 1) * win.cw - 1, 2867 borderpx + cy * win.ch, 2868 #endif // ANYSIZE_PATCH 2869 1, win.ch - 1); 2870 XftDrawRect(xw.draw, &drawcol, 2871 #if ANYSIZE_PATCH 2872 win.hborderpx + cx * win.cw, 2873 win.vborderpx + (cy + 1) * win.ch - 1, 2874 #else 2875 borderpx + cx * win.cw, 2876 borderpx + (cy + 1) * win.ch - 1, 2877 #endif // ANYSIZE_PATCH 2878 win.cw, 1); 2879 } 2880 } 2881 2882 void 2883 xsetenv(void) 2884 { 2885 char buf[sizeof(long) * 8 + 1]; 2886 2887 snprintf(buf, sizeof(buf), "%lu", xw.win); 2888 setenv("WINDOWID", buf, 1); 2889 } 2890 2891 void 2892 xseticontitle(char *p) 2893 { 2894 XTextProperty prop; 2895 DEFAULT(p, opt_title); 2896 2897 if (p[0] == '\0') 2898 p = opt_title; 2899 2900 if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, 2901 &prop) != Success) 2902 return; 2903 XSetWMIconName(xw.dpy, xw.win, &prop); 2904 XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); 2905 XFree(prop.value); 2906 } 2907 2908 #if CSI_22_23_PATCH 2909 void 2910 xsettitle(char *p, int pop) 2911 { 2912 XTextProperty prop; 2913 2914 free(titlestack[tstki]); 2915 if (pop) { 2916 titlestack[tstki] = NULL; 2917 tstki = (tstki - 1 + TITLESTACKSIZE) % TITLESTACKSIZE; 2918 p = titlestack[tstki] ? titlestack[tstki] : opt_title; 2919 } else if (p && p[0] != '\0') { 2920 titlestack[tstki] = xstrdup(p); 2921 } else { 2922 titlestack[tstki] = NULL; 2923 p = opt_title; 2924 } 2925 2926 if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, 2927 &prop) != Success) 2928 return; 2929 XSetWMName(xw.dpy, xw.win, &prop); 2930 XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); 2931 XFree(prop.value); 2932 } 2933 2934 void 2935 xpushtitle(void) 2936 { 2937 int tstkin = (tstki + 1) % TITLESTACKSIZE; 2938 2939 free(titlestack[tstkin]); 2940 titlestack[tstkin] = titlestack[tstki] ? xstrdup(titlestack[tstki]) : NULL; 2941 tstki = tstkin; 2942 } 2943 2944 void 2945 xfreetitlestack(void) 2946 { 2947 for (int i = 0; i < LEN(titlestack); i++) { 2948 free(titlestack[i]); 2949 titlestack[i] = NULL; 2950 } 2951 } 2952 #else 2953 void 2954 xsettitle(char *p) 2955 { 2956 XTextProperty prop; 2957 DEFAULT(p, opt_title); 2958 2959 if (p[0] == '\0') 2960 p = opt_title; 2961 2962 if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, 2963 &prop) != Success) 2964 return; 2965 XSetWMName(xw.dpy, xw.win, &prop); 2966 XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); 2967 XFree(prop.value); 2968 } 2969 #endif // CSI_22_23_PATCH 2970 2971 int 2972 xstartdraw(void) 2973 { 2974 #if W3M_PATCH 2975 if (IS_SET(MODE_VISIBLE)) 2976 XCopyArea(xw.dpy, xw.win, xw.buf, dc.gc, 0, 0, win.w, win.h, 0, 0); 2977 #endif // W3M_PATCH 2978 return IS_SET(MODE_VISIBLE); 2979 } 2980 2981 #if LIGATURES_PATCH && WIDE_GLYPHS_PATCH 2982 void 2983 xdrawline(Line line, int x1, int y1, int x2) 2984 { 2985 int i, j, x, ox, numspecs; 2986 Glyph new; 2987 GlyphFontSeq *seq = xw.specseq; 2988 XftGlyphFontSpec *specs = xw.specbuf; 2989 2990 /* Draw line in 2 passes: background and foreground. This way wide glyphs 2991 won't get truncated (#223) */ 2992 2993 /* background */ 2994 i = j = ox = 0; 2995 for (x = x1; x < x2; x++) { 2996 new = line[x]; 2997 if (new.mode == ATTR_WDUMMY) 2998 continue; 2999 if (selected(x, y1)) 3000 #if SELECTION_COLORS_PATCH 3001 new.mode |= ATTR_SELECTED; 3002 #else 3003 new.mode ^= ATTR_REVERSE; 3004 #endif // SELECTION_COLORS_PATCH 3005 if ((i > 0) && ATTRCMP(seq[j].base, new)) { 3006 numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1); 3007 xdrawglyphfontspecs(specs, seq[j].base, numspecs, ox, y1, DRAW_BG, x - ox); 3008 seq[j].charlen = x - ox; 3009 seq[j++].numspecs = numspecs; 3010 specs += numspecs; 3011 i = 0; 3012 } 3013 if (i == 0) { 3014 ox = x; 3015 seq[j].ox= ox; 3016 seq[j].base = new; 3017 } 3018 i++; 3019 } 3020 if (i > 0) { 3021 numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1); 3022 xdrawglyphfontspecs(specs, seq[j].base, numspecs, ox, y1, DRAW_BG, x2 - ox); 3023 seq[j].charlen = x2 - ox; 3024 seq[j++].numspecs = numspecs; 3025 } 3026 3027 /* foreground */ 3028 specs = xw.specbuf; 3029 for (i = 0; i < j; i++) { 3030 xdrawglyphfontspecs(specs, seq[i].base, seq[i].numspecs, seq[i].ox, y1, DRAW_FG, seq[i].charlen); 3031 specs += seq[i].numspecs; 3032 } 3033 3034 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 3035 kbds_drawstatusbar(y1); 3036 #endif // KEYBOARDSELECT_PATCH 3037 } 3038 #elif LIGATURES_PATCH 3039 void 3040 xdrawline(Line line, int x1, int y1, int x2) 3041 { 3042 int i, x, ox, numspecs; 3043 Glyph base, new; 3044 3045 XftGlyphFontSpec *specs = xw.specbuf; 3046 3047 i = ox = 0; 3048 for (x = x1; x < x2; x++) { 3049 new = line[x]; 3050 if (new.mode == ATTR_WDUMMY) 3051 continue; 3052 if (selected(x, y1)) 3053 #if SELECTION_COLORS_PATCH 3054 new.mode |= ATTR_SELECTED; 3055 #else 3056 new.mode ^= ATTR_REVERSE; 3057 #endif // SELECTION_COLORS_PATCH 3058 if ((i > 0) && ATTRCMP(base, new)) { 3059 numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1); 3060 xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox); 3061 i = 0; 3062 } 3063 if (i == 0) { 3064 ox = x; 3065 base = new; 3066 } 3067 i++; 3068 } 3069 if (i > 0) { 3070 numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1); 3071 xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); 3072 } 3073 3074 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 3075 kbds_drawstatusbar(y1); 3076 #endif // KEYBOARDSELECT_PATCH 3077 } 3078 #elif WIDE_GLYPHS_PATCH 3079 void 3080 xdrawline(Line line, int x1, int y1, int x2) 3081 { 3082 int i, x, ox, numspecs, numspecs_cached; 3083 Glyph base, new; 3084 XftGlyphFontSpec *specs; 3085 3086 numspecs_cached = xmakeglyphfontspecs(xw.specbuf, &line[x1], x2 - x1, x1, y1); 3087 3088 /* Draw line in 2 passes: background and foreground. This way wide glyphs 3089 won't get truncated (#223) */ 3090 for (int dmode = DRAW_BG; dmode <= DRAW_FG; dmode <<= 1) { 3091 specs = xw.specbuf; 3092 numspecs = numspecs_cached; 3093 i = ox = 0; 3094 for (x = x1; x < x2 && i < numspecs; x++) { 3095 new = line[x]; 3096 if (new.mode == ATTR_WDUMMY) 3097 continue; 3098 if (selected(x, y1)) 3099 #if SELECTION_COLORS_PATCH 3100 new.mode |= ATTR_SELECTED; 3101 #else 3102 new.mode ^= ATTR_REVERSE; 3103 #endif // SELECTION_COLORS_PATCH 3104 if (i > 0 && ATTRCMP(base, new)) { 3105 xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); 3106 specs += i; 3107 numspecs -= i; 3108 i = 0; 3109 } 3110 if (i == 0) { 3111 ox = x; 3112 base = new; 3113 } 3114 i++; 3115 } 3116 if (i > 0) 3117 xdrawglyphfontspecs(specs, base, i, ox, y1, dmode); 3118 } 3119 3120 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 3121 kbds_drawstatusbar(y1); 3122 #endif // KEYBOARDSELECT_PATCH 3123 } 3124 #else // !WIDE_GLYPHS_PATCH and !LIGATURES_PATCH 3125 void 3126 xdrawline(Line line, int x1, int y1, int x2) 3127 { 3128 int i, x, ox, numspecs; 3129 Glyph base, new; 3130 3131 XftGlyphFontSpec *specs = xw.specbuf; 3132 3133 numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); 3134 i = ox = 0; 3135 for (x = x1; x < x2 && i < numspecs; x++) { 3136 new = line[x]; 3137 if (new.mode == ATTR_WDUMMY) 3138 continue; 3139 if (selected(x, y1)) 3140 #if SELECTION_COLORS_PATCH 3141 new.mode |= ATTR_SELECTED; 3142 #else 3143 new.mode ^= ATTR_REVERSE; 3144 #endif // SELECTION_COLORS_PATCH 3145 if (i > 0 && ATTRCMP(base, new)) { 3146 xdrawglyphfontspecs(specs, base, i, ox, y1); 3147 specs += i; 3148 numspecs -= i; 3149 i = 0; 3150 } 3151 if (i == 0) { 3152 ox = x; 3153 base = new; 3154 } 3155 i++; 3156 } 3157 if (i > 0) 3158 xdrawglyphfontspecs(specs, base, i, ox, y1); 3159 3160 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 3161 kbds_drawstatusbar(y1); 3162 #endif // KEYBOARDSELECT_PATCH 3163 } 3164 #endif // WIDE_GLYPHS_PATCH | LIGATURES_PATCH 3165 3166 void 3167 xfinishdraw(void) 3168 { 3169 #if SIXEL_PATCH 3170 ImageList *im, *next; 3171 Imlib_Image origin, scaled; 3172 XGCValues gcvalues; 3173 GC gc = NULL; 3174 int width, height; 3175 int del, desty, mode, x1, x2, xend; 3176 #if ANYSIZE_PATCH 3177 int bw = win.hborderpx, bh = win.vborderpx; 3178 #else 3179 int bw = borderpx, bh = borderpx; 3180 #endif // ANYSIZE_PATCH 3181 Line line; 3182 #endif // SIXEL_PATCH 3183 3184 #if SIXEL_PATCH 3185 for (im = term.images; im; im = next) { 3186 next = im->next; 3187 3188 /* do not draw or process the image, if it is not visible */ 3189 if (im->x >= term.col || im->y >= term.row || im->y < 0) 3190 continue; 3191 3192 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 3193 /* do not draw the image on the search bar */ 3194 if (im->y == term.row-1 && IS_SET(MODE_KBDSELECT) && kbds_issearchmode()) 3195 continue; 3196 #endif // KEYBOARDSELECT_PATCH 3197 3198 /* scale the image */ 3199 width = MAX(im->width * win.cw / im->cw, 1); 3200 height = MAX(im->height * win.ch / im->ch, 1); 3201 if (!im->pixmap) { 3202 im->pixmap = (void *)XCreatePixmap(xw.dpy, xw.win, width, height, 3203 #if ALPHA_PATCH 3204 xw.depth 3205 #else 3206 DefaultDepth(xw.dpy, xw.scr) 3207 #endif // ALPHA_PATCH 3208 ); 3209 if (!im->pixmap) 3210 continue; 3211 if (win.cw == im->cw && win.ch == im->ch) { 3212 XImage ximage = { 3213 .format = ZPixmap, 3214 .data = (char *)im->pixels, 3215 .width = im->width, 3216 .height = im->height, 3217 .xoffset = 0, 3218 .byte_order = sixelbyteorder, 3219 .bitmap_bit_order = MSBFirst, 3220 .bits_per_pixel = 32, 3221 .bytes_per_line = im->width * 4, 3222 .bitmap_unit = 32, 3223 .bitmap_pad = 32, 3224 #if ALPHA_PATCH 3225 .depth = xw.depth 3226 #else 3227 .depth = 24 3228 #endif // ALPHA_PATCH 3229 }; 3230 XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, width, height); 3231 if (im->transparent) 3232 im->clipmask = (void *)sixel_create_clipmask((char *)im->pixels, width, height); 3233 } else { 3234 origin = imlib_create_image_using_data(im->width, im->height, (DATA32 *)im->pixels); 3235 if (!origin) 3236 continue; 3237 imlib_context_set_image(origin); 3238 imlib_image_set_has_alpha(1); 3239 imlib_context_set_anti_alias(im->transparent ? 0 : 1); /* anti-aliasing messes up the clip mask */ 3240 scaled = imlib_create_cropped_scaled_image(0, 0, im->width, im->height, width, height); 3241 imlib_free_image_and_decache(); 3242 if (!scaled) 3243 continue; 3244 imlib_context_set_image(scaled); 3245 imlib_image_set_has_alpha(1); 3246 XImage ximage = { 3247 .format = ZPixmap, 3248 .data = (char *)imlib_image_get_data_for_reading_only(), 3249 .width = width, 3250 .height = height, 3251 .xoffset = 0, 3252 .byte_order = sixelbyteorder, 3253 .bitmap_bit_order = MSBFirst, 3254 .bits_per_pixel = 32, 3255 .bytes_per_line = width * 4, 3256 .bitmap_unit = 32, 3257 .bitmap_pad = 32, 3258 #if ALPHA_PATCH 3259 .depth = xw.depth 3260 #else 3261 .depth = 24 3262 #endif // ALPHA_PATCH 3263 }; 3264 XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, width, height); 3265 if (im->transparent) 3266 im->clipmask = (void *)sixel_create_clipmask((char *)imlib_image_get_data_for_reading_only(), width, height); 3267 imlib_free_image_and_decache(); 3268 } 3269 } 3270 3271 /* create GC */ 3272 if (!gc) { 3273 memset(&gcvalues, 0, sizeof(gcvalues)); 3274 gcvalues.graphics_exposures = False; 3275 gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, &gcvalues); 3276 } 3277 3278 /* set the clip mask */ 3279 desty = bh + im->y * win.ch; 3280 if (im->clipmask) { 3281 XSetClipMask(xw.dpy, gc, (Drawable)im->clipmask); 3282 XSetClipOrigin(xw.dpy, gc, bw + im->x * win.cw, desty); 3283 } 3284 3285 /* draw only the parts of the image that are not erased */ 3286 #if SCROLLBACK_PATCH || REFLOW_PATCH 3287 line = TLINE(im->y) + im->x; 3288 #else 3289 line = term.line[im->y] + im->x; 3290 #endif // SCROLLBACK_PATCH || REFLOW_PATCH 3291 xend = MIN(im->x + im->cols, term.col); 3292 for (del = 1, x1 = im->x; x1 < xend; x1 = x2) { 3293 mode = line->mode & ATTR_SIXEL; 3294 for (x2 = x1 + 1; x2 < xend; x2++) { 3295 if (((++line)->mode & ATTR_SIXEL) != mode) 3296 break; 3297 } 3298 if (mode) { 3299 XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 3300 (x1 - im->x) * win.cw, 0, 3301 MIN((x2 - x1) * win.cw, width - (x1 - im->x) * win.cw), height, 3302 bw + x1 * win.cw, desty); 3303 del = 0; 3304 } 3305 } 3306 if (im->clipmask) 3307 XSetClipMask(xw.dpy, gc, None); 3308 3309 /* if all the parts are erased, we can delete the entire image */ 3310 if (del && im->x + im->cols <= term.col) 3311 delete_image(im); 3312 } 3313 if (gc) 3314 XFreeGC(xw.dpy, gc); 3315 #endif // SIXEL_PATCH 3316 3317 #if !SINGLE_DRAWABLE_BUFFER_PATCH 3318 XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0); 3319 #endif // SINGLE_DRAWABLE_BUFFER_PATCH 3320 XSetForeground(xw.dpy, dc.gc, dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg].pixel); 3321 } 3322 3323 void 3324 xximspot(int x, int y) 3325 { 3326 if (xw.ime.xic == NULL) 3327 return; 3328 3329 xw.ime.spot.x = borderpx + x * win.cw; 3330 xw.ime.spot.y = borderpx + (y + 1) * win.ch; 3331 3332 XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); 3333 } 3334 3335 void 3336 expose(XEvent *ev) 3337 { 3338 redraw(); 3339 } 3340 3341 void 3342 visibility(XEvent *ev) 3343 { 3344 XVisibilityEvent *e = &ev->xvisibility; 3345 3346 MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); 3347 } 3348 3349 void 3350 unmap(XEvent *ev) 3351 { 3352 #if ST_EMBEDDER_PATCH 3353 if (embed == ev->xunmap.window) { 3354 embed = 0; 3355 XRaiseWindow(xw.dpy, xw.win); 3356 XSetInputFocus(xw.dpy, xw.win, RevertToParent, CurrentTime); 3357 } 3358 #endif // ST_EMBEDDER_PATCH 3359 win.mode &= ~MODE_VISIBLE; 3360 } 3361 3362 void 3363 xsetpointermotion(int set) 3364 { 3365 #if HIDECURSOR_PATCH 3366 if (!set && !xw.pointerisvisible) 3367 return; 3368 #endif // HIDECURSOR_PATCH 3369 #if OPENURLONCLICK_PATCH 3370 set = 1; /* keep MotionNotify event enabled */ 3371 #endif // OPENURLONCLICK_PATCH 3372 MODBIT(xw.attrs.event_mask, set, PointerMotionMask); 3373 XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); 3374 } 3375 3376 void 3377 xsetmode(int set, unsigned int flags) 3378 { 3379 int mode = win.mode; 3380 MODBIT(win.mode, set, flags); 3381 #if SWAPMOUSE_PATCH 3382 if ((flags & MODE_MOUSE) 3383 #if HIDECURSOR_PATCH 3384 && xw.pointerisvisible 3385 #endif // HIDECURSOR_PATCH 3386 ) { 3387 if (win.mode & MODE_MOUSE) 3388 XUndefineCursor(xw.dpy, xw.win); 3389 else 3390 #if HIDECURSOR_PATCH 3391 XDefineCursor(xw.dpy, xw.win, xw.vpointer); 3392 #else 3393 XDefineCursor(xw.dpy, xw.win, cursor); 3394 #endif // HIDECURSOR_PATCH 3395 } 3396 #elif OPENURLONCLICK_PATCH 3397 if (win.mode & MODE_MOUSE && xw.pointerisvisible) 3398 XDefineCursor(xw.dpy, xw.win, xw.vpointer); 3399 #endif // SWAPMOUSE_PATCH 3400 if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) 3401 redraw(); 3402 } 3403 3404 int 3405 xsetcursor(int cursor) 3406 { 3407 #if BLINKING_CURSOR_PATCH 3408 if (!BETWEEN(cursor, 0, 8)) /* 7-8: st extensions */ 3409 #else 3410 if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ 3411 #endif // BLINKING_CURSOR_PATCH 3412 return 1; 3413 #if DEFAULT_CURSOR_PATCH 3414 #if BLINKING_CURSOR_PATCH 3415 win.cursor = (cursor ? cursor : cursorstyle); 3416 #else 3417 win.cursor = (cursor ? cursor : cursorshape); 3418 #endif // BLINKING_CURSOR_PATCH 3419 #else 3420 win.cursor = cursor; 3421 #endif // DEFAULT_CURSOR_PATCH 3422 #if BLINKING_CURSOR_PATCH 3423 cursorblinks = win.cursor == 0 || win.cursor == 1 || 3424 win.cursor == 3 || win.cursor == 5 || 3425 win.cursor == 7; 3426 #endif // BLINKING_CURSOR_PATCH 3427 return 0; 3428 } 3429 3430 void 3431 xseturgency(int add) 3432 { 3433 XWMHints *h = XGetWMHints(xw.dpy, xw.win); 3434 3435 MODBIT(h->flags, add, XUrgencyHint); 3436 XSetWMHints(xw.dpy, xw.win, h); 3437 XFree(h); 3438 } 3439 3440 void 3441 xbell(void) 3442 { 3443 if (!(IS_SET(MODE_FOCUSED))) 3444 xseturgency(1); 3445 if (bellvolume) 3446 XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); 3447 #if VISUALBELL_1_PATCH 3448 if (!bellon) /* turn visual bell on */ 3449 bellon = 1; 3450 #endif // VISUALBELL_1_PATCH 3451 } 3452 3453 void 3454 focus(XEvent *ev) 3455 { 3456 XFocusChangeEvent *e = &ev->xfocus; 3457 3458 #if ST_EMBEDDER_PATCH 3459 if (embed && ev->type == FocusIn) { 3460 XRaiseWindow(xw.dpy, embed); 3461 XSetInputFocus(xw.dpy, embed, RevertToParent, CurrentTime); 3462 sendxembed(XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); 3463 sendxembed(XEMBED_WINDOW_ACTIVATE, 0, 0, 0); 3464 } 3465 #endif // ST_EMBEDDER_PATCH 3466 3467 if (e->mode == NotifyGrab) 3468 return; 3469 3470 if (ev->type == FocusIn) { 3471 if (xw.ime.xic) 3472 XSetICFocus(xw.ime.xic); 3473 win.mode |= MODE_FOCUSED; 3474 xseturgency(0); 3475 if (IS_SET(MODE_FOCUS)) 3476 ttywrite("\033[I", 3, 0); 3477 #if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH 3478 if (!focused) { 3479 focused = 1; 3480 xloadcols(); 3481 tfulldirt(); 3482 } 3483 #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH 3484 } else { 3485 if (xw.ime.xic) 3486 XUnsetICFocus(xw.ime.xic); 3487 win.mode &= ~MODE_FOCUSED; 3488 if (IS_SET(MODE_FOCUS)) 3489 ttywrite("\033[O", 3, 0); 3490 #if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH 3491 if (focused) { 3492 focused = 0; 3493 xloadcols(); 3494 tfulldirt(); 3495 } 3496 #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH 3497 } 3498 } 3499 3500 int 3501 match(uint mask, uint state) 3502 { 3503 return mask == XK_ANY_MOD || mask == (state & ~ignoremod); 3504 } 3505 3506 char* 3507 kmap(KeySym k, uint state) 3508 { 3509 Key *kp; 3510 int i; 3511 3512 /* Check for mapped keys out of X11 function keys. */ 3513 for (i = 0; i < LEN(mappedkeys); i++) { 3514 if (mappedkeys[i] == k) 3515 break; 3516 } 3517 if (i == LEN(mappedkeys)) { 3518 if ((k & 0xFFFF) < 0xFD00) 3519 return NULL; 3520 } 3521 3522 for (kp = key; kp < key + LEN(key); kp++) { 3523 if (kp->k != k) 3524 continue; 3525 3526 if (!match(kp->mask, state)) 3527 continue; 3528 3529 if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) 3530 continue; 3531 if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) 3532 continue; 3533 3534 if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) 3535 continue; 3536 3537 return kp->s; 3538 } 3539 3540 return NULL; 3541 } 3542 3543 void 3544 kpress(XEvent *ev) 3545 { 3546 XKeyEvent *e = &ev->xkey; 3547 KeySym ksym = NoSymbol; 3548 char buf[64], *customkey; 3549 int len, screen; 3550 Rune c; 3551 Status status; 3552 Shortcut *bp; 3553 3554 #if HIDECURSOR_PATCH 3555 if (xw.pointerisvisible && hidecursor) { 3556 #if OPENURLONCLICK_PATCH 3557 #if ANYSIZE_PATCH 3558 int x = e->x - win.hborderpx; 3559 int y = e->y - win.vborderpx; 3560 #else 3561 int x = e->x - borderpx; 3562 int y = e->y - borderpx; 3563 #endif // ANYSIZE_PATCH 3564 LIMIT(x, 0, win.tw - 1); 3565 LIMIT(y, 0, win.th - 1); 3566 if (!detecturl(x / win.cw, y / win.ch, 0)) { 3567 XDefineCursor(xw.dpy, xw.win, xw.bpointer); 3568 xsetpointermotion(1); 3569 xw.pointerisvisible = 0; 3570 } 3571 #else 3572 XDefineCursor(xw.dpy, xw.win, xw.bpointer); 3573 xsetpointermotion(1); 3574 xw.pointerisvisible = 0; 3575 #endif // OPENURLONCLICK_PATCH 3576 } 3577 #endif // HIDECURSOR_PATCH 3578 3579 if (IS_SET(MODE_KBDLOCK)) 3580 return; 3581 3582 if (xw.ime.xic) { 3583 len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); 3584 if (status == XBufferOverflow) 3585 return; 3586 } else { 3587 len = XLookupString(e, buf, sizeof buf, &ksym, NULL); 3588 } 3589 3590 #if KEYBOARDSELECT_PATCH && REFLOW_PATCH 3591 if (IS_SET(MODE_KBDSELECT) ) { 3592 if (kbds_issearchmode()) { 3593 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { 3594 if (ksym == bp->keysym && match(bp->mod, e->state) && 3595 (!bp->screen || bp->screen == screen) && 3596 (bp->func == clippaste || bp->func == selpaste)) { 3597 bp->func(&(bp->arg)); 3598 return; 3599 } 3600 } 3601 } 3602 if (match(XK_NO_MOD, e->state) || 3603 (XK_Shift_L | XK_Shift_R) & e->state ) 3604 win.mode ^= kbds_keyboardhandler(ksym, buf, len, 0); 3605 return; 3606 } 3607 #elif KEYBOARDSELECT_PATCH 3608 if ( IS_SET(MODE_KBDSELECT) ) { 3609 if ( match(XK_NO_MOD, e->state) || 3610 (XK_Shift_L | XK_Shift_R) & e->state ) 3611 win.mode ^= trt_kbdselect(ksym, buf, len); 3612 return; 3613 } 3614 #endif // KEYBOARDSELECT_PATCH 3615 3616 screen = tisaltscr() ? S_ALT : S_PRI; 3617 3618 /* 1. shortcuts */ 3619 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { 3620 if (ksym == bp->keysym && match(bp->mod, e->state) && 3621 (!bp->screen || bp->screen == screen)) { 3622 bp->func(&(bp->arg)); 3623 return; 3624 } 3625 } 3626 3627 /* 2. custom keys from config.h */ 3628 if ((customkey = kmap(ksym, e->state))) { 3629 ttywrite(customkey, strlen(customkey), 1); 3630 return; 3631 } 3632 3633 /* 3. composed string from input method */ 3634 if (len == 0) 3635 return; 3636 if (len == 1 && e->state & Mod1Mask) { 3637 if (IS_SET(MODE_8BIT)) { 3638 if (*buf < 0177) { 3639 c = *buf | 0x80; 3640 len = utf8encode(c, buf); 3641 } 3642 } else { 3643 buf[1] = buf[0]; 3644 buf[0] = '\033'; 3645 len = 2; 3646 } 3647 } 3648 ttywrite(buf, len, 1); 3649 } 3650 3651 3652 void 3653 cmessage(XEvent *e) 3654 { 3655 /* 3656 * See xembed specs 3657 * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html 3658 */ 3659 if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { 3660 if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { 3661 win.mode |= MODE_FOCUSED; 3662 xseturgency(0); 3663 } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { 3664 win.mode &= ~MODE_FOCUSED; 3665 } 3666 } else if (e->xclient.data.l[0] == xw.wmdeletewin) { 3667 ttyhangup(); 3668 exit(0); 3669 } 3670 } 3671 3672 void 3673 resize(XEvent *e) 3674 { 3675 #if ST_EMBEDDER_PATCH 3676 XWindowChanges wc; 3677 #endif // ST_EMBEDDER_PATCH 3678 3679 #if BACKGROUND_IMAGE_PATCH 3680 if (pseudotransparency) { 3681 if (e->xconfigure.width == win.w && 3682 e->xconfigure.height == win.h && 3683 e->xconfigure.x == win.x && e->xconfigure.y == win.y) 3684 return; 3685 updatexy(); 3686 } else 3687 #endif // BACKGROUND_IMAGE_PATCH 3688 if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) 3689 return; 3690 3691 #if ST_EMBEDDER_PATCH 3692 if (embed) { 3693 wc.width = e->xconfigure.width; 3694 wc.height = e->xconfigure.height; 3695 XConfigureWindow(xw.dpy, embed, CWWidth | CWHeight, &wc); 3696 } 3697 #endif // ST_EMBEDDER_PATCH 3698 3699 cresize(e->xconfigure.width, e->xconfigure.height); 3700 } 3701 3702 void 3703 run(void) 3704 { 3705 XEvent ev; 3706 int w = win.w, h = win.h; 3707 fd_set rfd; 3708 int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; 3709 struct timespec seltv, *tv, now, lastblink, trigger; 3710 double timeout; 3711 3712 /* Waiting for window mapping */ 3713 do { 3714 XNextEvent(xw.dpy, &ev); 3715 /* 3716 * This XFilterEvent call is required because of XOpenIM. It 3717 * does filter out the key event and some client message for 3718 * the input method too. 3719 */ 3720 if (XFilterEvent(&ev, None)) 3721 continue; 3722 if (ev.type == ConfigureNotify) { 3723 w = ev.xconfigure.width; 3724 h = ev.xconfigure.height; 3725 } 3726 } while (ev.type != MapNotify); 3727 3728 ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); 3729 cresize(w, h); 3730 3731 for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { 3732 FD_ZERO(&rfd); 3733 FD_SET(ttyfd, &rfd); 3734 FD_SET(xfd, &rfd); 3735 3736 #if SYNC_PATCH 3737 if (XPending(xw.dpy) || ttyread_pending()) 3738 #else 3739 if (XPending(xw.dpy)) 3740 #endif // SYNC_PATCH 3741 timeout = 0; /* existing events might not set xfd */ 3742 3743 seltv.tv_sec = timeout / 1E3; 3744 seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); 3745 tv = timeout >= 0 ? &seltv : NULL; 3746 3747 if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { 3748 if (errno == EINTR) 3749 continue; 3750 die("select failed: %s\n", strerror(errno)); 3751 } 3752 clock_gettime(CLOCK_MONOTONIC, &now); 3753 3754 #if SYNC_PATCH 3755 int ttyin = FD_ISSET(ttyfd, &rfd) || ttyread_pending(); 3756 if (ttyin) 3757 ttyread(); 3758 #else 3759 if (FD_ISSET(ttyfd, &rfd)) 3760 ttyread(); 3761 #endif // SYNC_PATCH 3762 3763 xev = 0; 3764 while (XPending(xw.dpy)) { 3765 xev = 1; 3766 XNextEvent(xw.dpy, &ev); 3767 if (XFilterEvent(&ev, None)) 3768 continue; 3769 if (handler[ev.type]) 3770 (handler[ev.type])(&ev); 3771 } 3772 3773 /* 3774 * To reduce flicker and tearing, when new content or event 3775 * triggers drawing, we first wait a bit to ensure we got 3776 * everything, and if nothing new arrives - we draw. 3777 * We start with trying to wait minlatency ms. If more content 3778 * arrives sooner, we retry with shorter and shorter periods, 3779 * and eventually draw even without idle after maxlatency ms. 3780 * Typically this results in low latency while interacting, 3781 * maximum latency intervals during `cat huge.txt`, and perfect 3782 * sync with periodic updates from animations/key-repeats/etc. 3783 */ 3784 #if SYNC_PATCH 3785 if (ttyin || xev) 3786 #else 3787 if (FD_ISSET(ttyfd, &rfd) || xev) 3788 #endif // SYNC_PATCH 3789 { 3790 if (!drawing) { 3791 trigger = now; 3792 #if BLINKING_CURSOR_PATCH 3793 if (IS_SET(MODE_BLINK)) { 3794 win.mode ^= MODE_BLINK; 3795 } 3796 lastblink = now; 3797 #endif // BLINKING_CURSOR_PATCH 3798 drawing = 1; 3799 } 3800 timeout = (maxlatency - TIMEDIFF(now, trigger)) \ 3801 / maxlatency * minlatency; 3802 if (timeout > 0) 3803 continue; /* we have time, try to find idle */ 3804 } 3805 3806 #if SYNC_PATCH 3807 if (tinsync(su_timeout)) { 3808 /* 3809 * on synchronized-update draw-suspension: don't reset 3810 * drawing so that we draw ASAP once we can (just after 3811 * ESU). it won't be too soon because we already can 3812 * draw now but we skip. we set timeout > 0 to draw on 3813 * SU-timeout even without new content. 3814 */ 3815 timeout = minlatency; 3816 continue; 3817 } 3818 #endif // SYNC_PATCH 3819 3820 /* idle detected or maxlatency exhausted -> draw */ 3821 timeout = -1; 3822 #if BLINKING_CURSOR_PATCH 3823 if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK))) 3824 #else 3825 if (blinktimeout && tattrset(ATTR_BLINK)) 3826 #endif // BLINKING_CURSOR_PATCH 3827 { 3828 timeout = blinktimeout - TIMEDIFF(now, lastblink); 3829 if (timeout <= 0) { 3830 if (-timeout > blinktimeout) /* start visible */ 3831 win.mode |= MODE_BLINK; 3832 win.mode ^= MODE_BLINK; 3833 tsetdirtattr(ATTR_BLINK); 3834 lastblink = now; 3835 timeout = blinktimeout; 3836 } 3837 } 3838 3839 #if VISUALBELL_1_PATCH 3840 if (bellon) { 3841 bellon++; 3842 bellon %= 3; 3843 MODBIT(win.mode, !IS_SET(MODE_REVERSE), MODE_REVERSE); 3844 redraw(); 3845 } 3846 else 3847 draw(); 3848 #else 3849 draw(); 3850 #endif // VISUALBELL_1_PATCH 3851 XFlush(xw.dpy); 3852 drawing = 0; 3853 } 3854 } 3855 3856 void 3857 usage(void) 3858 { 3859 die("usage: %s [-aiv] [-c class]" 3860 #if WORKINGDIR_PATCH 3861 " [-d path]" 3862 #endif // WORKINGDIR_PATCH 3863 " [-f font] [-g geometry]" 3864 " [-n name] [-o file]\n" 3865 " [-T title] [-t title] [-w windowid]" 3866 " [[-e] command [args ...]]\n" 3867 " %s [-aiv] [-c class]" 3868 #if WORKINGDIR_PATCH 3869 " [-d path]" 3870 #endif // WORKINGDIR_PATCH 3871 " [-f font] [-g geometry]" 3872 " [-n name] [-o file]\n" 3873 " [-T title] [-t title] [-w windowid] -l line" 3874 " [stty_args ...]\n", argv0, argv0); 3875 } 3876 3877 int 3878 main(int argc, char *argv[]) 3879 { 3880 xw.l = xw.t = 0; 3881 xw.isfixed = False; 3882 #if BLINKING_CURSOR_PATCH 3883 xsetcursor(cursorstyle); 3884 #else 3885 xsetcursor(cursorshape); 3886 #endif // BLINKING_CURSOR_PATCH 3887 3888 ARGBEGIN { 3889 case 'a': 3890 allowaltscreen = 0; 3891 break; 3892 #if ALPHA_PATCH 3893 case 'A': 3894 opt_alpha = EARGF(usage()); 3895 break; 3896 #endif // ALPHA_PATCH 3897 case 'c': 3898 opt_class = EARGF(usage()); 3899 break; 3900 #if WORKINGDIR_PATCH 3901 case 'd': 3902 opt_dir = EARGF(usage()); 3903 break; 3904 #endif // WORKINGDIR_PATCH 3905 case 'e': 3906 if (argc > 0) 3907 --argc, ++argv; 3908 goto run; 3909 case 'f': 3910 opt_font = EARGF(usage()); 3911 break; 3912 case 'g': 3913 xw.gm = XParseGeometry(EARGF(usage()), 3914 &xw.l, &xw.t, &cols, &rows); 3915 #if ANYGEOMETRY_PATCH 3916 geometry = CellGeometry; 3917 #endif // ANYGEOMETRY_PATCH 3918 break; 3919 #if ANYGEOMETRY_PATCH 3920 case 'G': 3921 xw.gm = XParseGeometry(EARGF(usage()), 3922 &xw.l, &xw.t, &width, &height); 3923 geometry = PixelGeometry; 3924 break; 3925 #endif // ANYGEOMETRY_PATCH 3926 case 'i': 3927 xw.isfixed = 1; 3928 break; 3929 case 'o': 3930 opt_io = EARGF(usage()); 3931 break; 3932 case 'l': 3933 opt_line = EARGF(usage()); 3934 break; 3935 case 'n': 3936 opt_name = EARGF(usage()); 3937 break; 3938 case 't': 3939 case 'T': 3940 opt_title = EARGF(usage()); 3941 break; 3942 case 'w': 3943 opt_embed = EARGF(usage()); 3944 break; 3945 case 'v': 3946 die("%s " VERSION "\n", argv0); 3947 break; 3948 default: 3949 usage(); 3950 } ARGEND; 3951 3952 run: 3953 if (argc > 0) /* eat all remaining arguments */ 3954 opt_cmd = argv; 3955 3956 if (!opt_title) 3957 opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; 3958 3959 setlocale(LC_CTYPE, ""); 3960 XSetLocaleModifiers(""); 3961 #if XRESOURCES_PATCH && XRESOURCES_RELOAD_PATCH || BACKGROUND_IMAGE_PATCH && BACKGROUND_IMAGE_RELOAD_PATCH 3962 signal(SIGUSR1, sigusr1_reload); 3963 #endif // XRESOURCES_RELOAD_PATCH | BACKGROUND_IMAGE_RELOAD_PATCH 3964 #if XRESOURCES_PATCH 3965 if (!(xw.dpy = XOpenDisplay(NULL))) 3966 die("Can't open display\n"); 3967 3968 config_init(xw.dpy); 3969 #endif // XRESOURCES_PATCH 3970 #if LIGATURES_PATCH 3971 hbcreatebuffer(); 3972 #endif // LIGATURES_PATCH 3973 3974 #if ANYGEOMETRY_PATCH 3975 switch (geometry) { 3976 case CellGeometry: 3977 xinit(cols, rows); 3978 break; 3979 case PixelGeometry: 3980 xinit(width, height); 3981 cols = (win.w - 2 * borderpx) / win.cw; 3982 rows = (win.h - 2 * borderpx) / win.ch; 3983 break; 3984 } 3985 #endif // ANYGEOMETRY_PATCH 3986 3987 cols = MAX(cols, 1); 3988 rows = MAX(rows, 1); 3989 #if ALPHA_PATCH && ALPHA_FOCUS_HIGHLIGHT_PATCH 3990 defaultbg = MAX(LEN(colorname), 256); 3991 #endif // ALPHA_FOCUS_HIGHLIGHT_PATCH 3992 tnew(cols, rows); 3993 #if !ANYGEOMETRY_PATCH 3994 xinit(cols, rows); 3995 #endif // ANYGEOMETRY_PATCH 3996 #if BACKGROUND_IMAGE_PATCH 3997 bginit(); 3998 #endif // BACKGROUND_IMAGE_PATCH 3999 xsetenv(); 4000 selinit(); 4001 #if WORKINGDIR_PATCH 4002 if (opt_dir && chdir(opt_dir)) 4003 die("Can't change to working directory %s\n", opt_dir); 4004 #endif // WORKINGDIR_PATCH 4005 run(); 4006 #if LIGATURES_PATCH 4007 hbdestroybuffer(); 4008 #endif // LIGATURES_PATCH 4009 4010 return 0; 4011 }