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