drw.c (18857B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <X11/Xlib.h> 6 #include <X11/Xft/Xft.h> 7 8 #include "patches.h" 9 #include "drw.h" 10 #include "util.h" 11 12 #if BIDI_PATCH && !PANGO_PATCH 13 #include <fribidi.h> 14 #endif //BIDI_PATCH 15 16 #if !PANGO_PATCH || HIGHLIGHT_PATCH 17 #define UTF_INVALID 0xFFFD 18 19 static int 20 utf8decode(const char *s_in, long *u, int *err) 21 { 22 static const unsigned char lens[] = { 23 /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 24 /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */ 25 /* 110XX */ 2, 2, 2, 2, 26 /* 1110X */ 3, 3, 27 /* 11110 */ 4, 28 /* 11111 */ 0, /* invalid */ 29 }; 30 static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 }; 31 static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 }; 32 33 const unsigned char *s = (const unsigned char *)s_in; 34 int len = lens[*s >> 3]; 35 *u = UTF_INVALID; 36 *err = 1; 37 if (len == 0) 38 return 1; 39 40 long cp = s[0] & leading_mask[len - 1]; 41 for (int i = 1; i < len; ++i) { 42 if (s[i] == '\0' || (s[i] & 0xC0) != 0x80) 43 return i; 44 cp = (cp << 6) | (s[i] & 0x3F); 45 } 46 /* out of range, surrogate, overlong encoding */ 47 if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1]) 48 return len; 49 50 *err = 0; 51 *u = cp; 52 return len; 53 } 54 55 #if HIGHLIGHT_PATCH 56 int 57 utf8len(const char *c) 58 { 59 long utf8codepoint = 0; 60 int utf8err = 0; 61 return utf8decode(c, &utf8codepoint, &utf8err); 62 } 63 #endif // HIGHLIGHT_PATCH 64 #endif // PANGO_PATCH 65 66 #if BIDI_PATCH && !PANGO_PATCH 67 static char fribidi_text[BUFSIZ] = ""; 68 69 static void 70 apply_fribidi(const char *str) 71 { 72 FriBidiStrIndex len = strlen(str); 73 FriBidiChar logical[BUFSIZ]; 74 FriBidiChar visual[BUFSIZ]; 75 FriBidiParType base = FRIBIDI_PAR_ON; 76 FriBidiCharSet charset; 77 78 charset = fribidi_parse_charset("UTF-8"); 79 len = fribidi_charset_to_unicode(charset, str, len, logical); 80 fribidi_log2vis(logical, len, &base, visual, NULL, NULL, NULL); 81 fribidi_unicode_to_charset(charset, visual, len, fribidi_text); 82 } 83 #endif //BIDI_PATCH 84 85 Drw * 86 #if ALPHA_PATCH 87 drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) 88 #else 89 drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 90 #endif // ALPHA_PATCH 91 { 92 Drw *drw = ecalloc(1, sizeof(Drw)); 93 94 drw->dpy = dpy; 95 drw->screen = screen; 96 drw->root = root; 97 drw->w = w; 98 drw->h = h; 99 #if ALPHA_PATCH 100 drw->visual = visual; 101 drw->depth = depth; 102 drw->cmap = cmap; 103 drw->drawable = XCreatePixmap(dpy, root, w, h, depth); 104 drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL); 105 #else 106 drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); 107 drw->gc = XCreateGC(dpy, root, 0, NULL); 108 #endif // ALPHA_PATCH 109 XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); 110 111 return drw; 112 } 113 114 void 115 drw_resize(Drw *drw, unsigned int w, unsigned int h) 116 { 117 if (!drw) 118 return; 119 120 drw->w = w; 121 drw->h = h; 122 if (drw->drawable) 123 XFreePixmap(drw->dpy, drw->drawable); 124 #if ALPHA_PATCH 125 drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); 126 #else 127 drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); 128 #endif // ALPHA_PATCH 129 } 130 131 void 132 drw_free(Drw *drw) 133 { 134 XFreePixmap(drw->dpy, drw->drawable); 135 XFreeGC(drw->dpy, drw->gc); 136 #if PANGO_PATCH 137 drw_font_free(drw->font); 138 #else 139 drw_fontset_free(drw->fonts); 140 #endif // PANGO_PATCH 141 free(drw); 142 } 143 144 #if PANGO_PATCH 145 /* This function is an implementation detail. Library users should use 146 * drw_font_create instead. 147 */ 148 static Fnt * 149 xfont_create(Drw *drw, const char *fontname) 150 { 151 Fnt *font; 152 PangoFontMap *fontmap; 153 PangoContext *context; 154 PangoFontDescription *desc; 155 PangoFontMetrics *metrics; 156 157 if (!fontname) { 158 die("no font specified."); 159 } 160 161 font = ecalloc(1, sizeof(Fnt)); 162 font->dpy = drw->dpy; 163 164 fontmap = pango_xft_get_font_map(drw->dpy, drw->screen); 165 context = pango_font_map_create_context(fontmap); 166 desc = pango_font_description_from_string(fontname); 167 font->layout = pango_layout_new(context); 168 pango_layout_set_font_description(font->layout, desc); 169 170 metrics = pango_context_get_metrics(context, desc, pango_language_from_string ("en-us")); 171 font->h = pango_font_metrics_get_height(metrics) / PANGO_SCALE; 172 173 pango_font_metrics_unref(metrics); 174 g_object_unref(context); 175 176 return font; 177 } 178 #else 179 /* This function is an implementation detail. Library users should use 180 * drw_fontset_create instead. 181 */ 182 static Fnt * 183 xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) 184 { 185 Fnt *font; 186 XftFont *xfont = NULL; 187 FcPattern *pattern = NULL; 188 189 if (fontname) { 190 /* Using the pattern found at font->xfont->pattern does not yield the 191 * same substitution results as using the pattern returned by 192 * FcNameParse; using the latter results in the desired fallback 193 * behaviour whereas the former just results in missing-character 194 * rectangles being drawn, at least with some fonts. */ 195 if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 196 fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); 197 return NULL; 198 } 199 if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 200 fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); 201 XftFontClose(drw->dpy, xfont); 202 return NULL; 203 } 204 } else if (fontpattern) { 205 if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { 206 fprintf(stderr, "error, cannot load font from pattern.\n"); 207 return NULL; 208 } 209 } else { 210 die("no font specified."); 211 } 212 213 #if NO_COLOR_EMOJI_PATCH 214 /* Do not allow using color fonts. This is a workaround for a BadLength 215 * error from Xft with color glyphs. Modelled on the Xterm workaround. See 216 * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 217 * https://lists.suckless.org/dev/1701/30932.html 218 * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 219 * and lots more all over the internet. 220 */ 221 FcBool iscol; 222 if (FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { 223 XftFontClose(drw->dpy, xfont); 224 return NULL; 225 } 226 #endif // NO_COLOR_EMOJI_PATCH 227 228 font = ecalloc(1, sizeof(Fnt)); 229 font->xfont = xfont; 230 font->pattern = pattern; 231 font->h = xfont->ascent + xfont->descent; 232 font->dpy = drw->dpy; 233 234 return font; 235 } 236 #endif // PANGO_PATCH 237 238 static void 239 xfont_free(Fnt *font) 240 { 241 if (!font) 242 return; 243 #if PANGO_PATCH 244 if (font->layout) 245 g_object_unref(font->layout); 246 #else 247 if (font->pattern) 248 FcPatternDestroy(font->pattern); 249 XftFontClose(font->dpy, font->xfont); 250 #endif // PANGO_PATCH 251 free(font); 252 } 253 254 #if PANGO_PATCH 255 Fnt* 256 drw_font_create(Drw* drw, const char *font) 257 { 258 Fnt *fnt = NULL; 259 260 if (!drw || !font) 261 return NULL; 262 263 fnt = xfont_create(drw, font); 264 265 return (drw->font = fnt); 266 } 267 #else 268 Fnt* 269 drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) 270 { 271 Fnt *cur, *ret = NULL; 272 size_t i; 273 274 if (!drw || !fonts) 275 return NULL; 276 277 for (i = 1; i <= fontcount; i++) { 278 if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { 279 cur->next = ret; 280 ret = cur; 281 } 282 } 283 return (drw->fonts = ret); 284 } 285 #endif // PANGO_PATCH 286 287 #if PANGO_PATCH 288 void 289 drw_font_free(Fnt *font) 290 { 291 if (font) 292 xfont_free(font); 293 } 294 #else 295 void 296 drw_fontset_free(Fnt *font) 297 { 298 if (font) { 299 drw_fontset_free(font->next); 300 xfont_free(font); 301 } 302 } 303 #endif // PANGO_PATCH 304 305 void 306 #if ALPHA_PATCH 307 drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha) 308 #else 309 drw_clr_create(Drw *drw, Clr *dest, const char *clrname) 310 #endif // ALPHA_PATCH 311 { 312 if (!drw || !dest || !clrname) 313 return; 314 315 #if ALPHA_PATCH 316 if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, 317 clrname, dest)) 318 die("error, cannot allocate color '%s'", clrname); 319 320 dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24); 321 #else 322 if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 323 DefaultColormap(drw->dpy, drw->screen), 324 clrname, dest)) 325 die("error, cannot allocate color '%s'", clrname); 326 #endif // ALPHA_PATCH 327 } 328 329 /* Create color schemes. */ 330 Clr * 331 #if ALPHA_PATCH 332 drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount) 333 #else 334 drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) 335 #endif // ALPHA_PATCH 336 { 337 size_t i; 338 Clr *ret; 339 340 /* need at least two colors for a scheme */ 341 if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(Clr)))) 342 return NULL; 343 344 for (i = 0; i < clrcount; i++) 345 #if ALPHA_PATCH 346 drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]); 347 #else 348 drw_clr_create(drw, &ret[i], clrnames[i]); 349 #endif // ALPHA_PATCH 350 return ret; 351 } 352 353 void 354 drw_clr_free(Drw *drw, Clr *c) 355 { 356 if (!drw || !c) 357 return; 358 359 /* c is typedef XftColor Clr */ 360 XftColorFree(drw->dpy, DefaultVisual(drw->dpy, drw->screen), 361 DefaultColormap(drw->dpy, drw->screen), c); 362 } 363 364 void 365 drw_scm_free(Drw *drw, Clr *scm, size_t clrcount) 366 { 367 size_t i; 368 369 if (!drw || !scm) 370 return; 371 372 for (i = 0; i < clrcount; i++) 373 drw_clr_free(drw, &scm[i]); 374 free(scm); 375 } 376 377 #if !PANGO_PATCH 378 void 379 drw_setfontset(Drw *drw, Fnt *set) 380 { 381 if (drw) 382 drw->fonts = set; 383 } 384 #endif // PANGO_PATCH 385 386 void 387 drw_setscheme(Drw *drw, Clr *scm) 388 { 389 if (drw) 390 drw->scheme = scm; 391 } 392 393 void 394 drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) 395 { 396 if (!drw || !drw->scheme) 397 return; 398 XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); 399 if (filled) 400 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 401 else 402 XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); 403 } 404 405 #if PANGO_PATCH 406 int 407 drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup) 408 { 409 char buf[1024]; 410 int i, ty, th; 411 unsigned int ew, eh; 412 XftDraw *d = NULL; 413 size_t len; 414 int render = x || y || w || h; 415 416 if (!drw || (render && !drw->scheme) || !text || !drw->font) 417 return 0; 418 419 if (!render) { 420 w = invert ? invert : ~invert; 421 } else { 422 XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 423 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 424 #if ALPHA_PATCH 425 d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); 426 #else 427 d = XftDrawCreate(drw->dpy, drw->drawable, 428 DefaultVisual(drw->dpy, drw->screen), 429 DefaultColormap(drw->dpy, drw->screen)); 430 #endif // ALPHA_PATCH 431 x += lpad; 432 w -= lpad; 433 } 434 435 len = strlen(text); 436 437 if (len) { 438 drw_font_getexts(drw->font, text, len, &ew, &eh, markup); 439 th = eh; 440 /* shorten text if necessary */ 441 for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--) { 442 drw_font_getexts(drw->font, text, len, &ew, &eh, markup); 443 if (eh > th) 444 th = eh; 445 } 446 447 if (len) { 448 memcpy(buf, text, len); 449 buf[len] = '\0'; 450 if (len < strlen(text)) 451 for (i = len; i && i > len - 3; buf[--i] = '.') 452 ; /* NOP */ 453 454 if (render) { 455 ty = y + (h - th) / 2; 456 if (markup) 457 pango_layout_set_markup(drw->font->layout, buf, len); 458 else 459 pango_layout_set_text(drw->font->layout, buf, len); 460 pango_xft_render_layout(d, &drw->scheme[invert ? ColBg : ColFg], 461 drw->font->layout, x * PANGO_SCALE, ty * PANGO_SCALE); 462 if (markup) /* clear markup attributes */ 463 pango_layout_set_attributes(drw->font->layout, NULL); 464 } 465 x += ew; 466 w -= ew; 467 } 468 } 469 470 if (d) 471 XftDrawDestroy(d); 472 473 return x + (render ? w : 0); 474 } 475 #else 476 int 477 drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) 478 { 479 int ty, ellipsis_x = 0; 480 unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1; 481 XftDraw *d = NULL; 482 Fnt *usedfont, *curfont, *nextfont; 483 int utf8strlen, utf8charlen, utf8err, render = x || y || w || h; 484 long utf8codepoint = 0; 485 const char *utf8str; 486 FcCharSet *fccharset; 487 FcPattern *fcpattern; 488 FcPattern *match; 489 XftResult result; 490 int charexists = 0, overflow = 0; 491 /* keep track of a couple codepoints for which we have no match. */ 492 static unsigned int nomatches[128], ellipsis_width, invalid_width; 493 static const char invalid[] = "�"; 494 const char *ellipsis = "..."; 495 496 if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) 497 return 0; 498 499 if (!render) { 500 w = invert ? invert : ~invert; 501 } else { 502 XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); 503 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); 504 #if ALPHA_PATCH 505 d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); 506 #else 507 d = XftDrawCreate(drw->dpy, drw->drawable, 508 DefaultVisual(drw->dpy, drw->screen), 509 DefaultColormap(drw->dpy, drw->screen)); 510 #endif // ALPHA_PATCH 511 x += lpad; 512 w -= lpad; 513 } 514 515 if (!ellipsis_width && render) 516 ellipsis_width = drw_fontset_getwidth(drw, ellipsis); 517 if (!invalid_width && render) 518 invalid_width = drw_fontset_getwidth(drw, invalid); 519 520 #if BIDI_PATCH 521 apply_fribidi(text); 522 text = fribidi_text; 523 #endif // BIDI_PATCH 524 525 usedfont = drw->fonts; 526 while (1) { 527 ew = ellipsis_len = utf8err = utf8strlen = 0; 528 utf8str = text; 529 nextfont = NULL; 530 while (*text) { 531 utf8charlen = utf8decode(text, &utf8codepoint, &utf8err); 532 for (curfont = drw->fonts; curfont; curfont = curfont->next) { 533 charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); 534 if (charexists) { 535 drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); 536 if (ew + ellipsis_width <= w) { 537 /* keep track where the ellipsis still fits */ 538 ellipsis_x = x + ew; 539 ellipsis_w = w - ew; 540 ellipsis_len = utf8strlen; 541 } 542 543 if (ew + tmpw > w) { 544 overflow = 1; 545 /* called from drw_fontset_getwidth_clamp(): 546 * it wants the width AFTER the overflow 547 */ 548 if (!render) 549 x += tmpw; 550 else 551 utf8strlen = ellipsis_len; 552 } else if (curfont == usedfont) { 553 text += utf8charlen; 554 utf8strlen += utf8err ? 0 : utf8charlen; 555 ew += utf8err ? 0 : tmpw; 556 } else { 557 nextfont = curfont; 558 } 559 break; 560 } 561 } 562 563 if (overflow || !charexists || nextfont || utf8err) 564 break; 565 else 566 charexists = 0; 567 } 568 569 if (utf8strlen) { 570 if (render) { 571 ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; 572 XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], 573 usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); 574 } 575 x += ew; 576 w -= ew; 577 } 578 if (utf8err && (!render || invalid_width < w)) { 579 if (render) 580 drw_text(drw, x, y, w, h, 0, invalid, invert); 581 x += invalid_width; 582 w -= invalid_width; 583 } 584 if (render && overflow && ellipsis_w) 585 drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, ellipsis, invert); 586 587 if (!*text || overflow) { 588 break; 589 } else if (nextfont) { 590 charexists = 0; 591 usedfont = nextfont; 592 } else { 593 /* Regardless of whether or not a fallback font is found, the 594 * character must be drawn. */ 595 charexists = 1; 596 597 hash = (unsigned int)utf8codepoint; 598 hash = ((hash >> 16) ^ hash) * 0x21F0AAAD; 599 hash = ((hash >> 15) ^ hash) * 0xD35A2D97; 600 h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches); 601 h1 = (hash >> 17) % LENGTH(nomatches); 602 /* avoid expensive XftFontMatch call when we know we won't find a match */ 603 if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint) 604 goto no_match; 605 606 fccharset = FcCharSetCreate(); 607 FcCharSetAddChar(fccharset, utf8codepoint); 608 609 if (!drw->fonts->pattern) { 610 /* Refer to the comment in xfont_create for more information. */ 611 die("the first font in the cache must be loaded from a font string."); 612 } 613 614 fcpattern = FcPatternDuplicate(drw->fonts->pattern); 615 FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); 616 FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 617 #if NO_COLOR_EMOJI_PATCH 618 FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); 619 #endif // NO_COLOR_EMOJI_PATCH 620 621 FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); 622 FcDefaultSubstitute(fcpattern); 623 match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); 624 625 FcCharSetDestroy(fccharset); 626 FcPatternDestroy(fcpattern); 627 628 if (match) { 629 usedfont = xfont_create(drw, NULL, match); 630 if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { 631 for (curfont = drw->fonts; curfont->next; curfont = curfont->next) 632 ; /* NOP */ 633 curfont->next = usedfont; 634 } else { 635 xfont_free(usedfont); 636 nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint; 637 no_match: 638 usedfont = drw->fonts; 639 } 640 } 641 } 642 } 643 if (d) 644 XftDrawDestroy(d); 645 646 return x + (render ? w : 0); 647 } 648 #endif // PANGO_PATCH 649 650 void 651 drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 652 { 653 if (!drw) 654 return; 655 656 XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); 657 XSync(drw->dpy, False); 658 } 659 660 #if PANGO_PATCH 661 unsigned int 662 drw_font_getwidth(Drw *drw, const char *text, Bool markup) 663 { 664 if (!drw || !drw->font || !text) 665 return 0; 666 return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup); 667 } 668 669 unsigned int 670 drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) 671 { 672 unsigned int tmp = 0; 673 if (drw && drw->font && text && n) 674 tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n, True); 675 return MIN(n, tmp); 676 } 677 #else 678 unsigned int 679 drw_fontset_getwidth(Drw *drw, const char *text) 680 { 681 if (!drw || !drw->fonts || !text) 682 return 0; 683 return drw_text(drw, 0, 0, 0, 0, 0, text, 0); 684 } 685 686 unsigned int 687 drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) 688 { 689 unsigned int tmp = 0; 690 if (drw && drw->fonts && text && n) 691 tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); 692 return MIN(n, tmp); 693 } 694 #endif // PANGO_PATCH 695 696 #if PANGO_PATCH 697 void 698 drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup) 699 { 700 if (!font || !text) 701 return; 702 703 PangoRectangle r; 704 if (markup) 705 pango_layout_set_markup(font->layout, text, len); 706 else 707 pango_layout_set_text(font->layout, text, len); 708 pango_layout_get_extents(font->layout, 0, &r); 709 if (markup) /* clear markup attributes */ 710 pango_layout_set_attributes(font->layout, NULL); 711 if (w) 712 *w = r.width / PANGO_SCALE; 713 if (h) 714 *h = r.height / PANGO_SCALE; 715 } 716 #else 717 void 718 drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) 719 { 720 XGlyphInfo ext; 721 722 if (!font || !text) 723 return; 724 725 XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); 726 if (w) 727 *w = ext.xOff; 728 if (h) 729 *h = font->h; 730 } 731 #endif // PANGO_PATCH 732 733 Cur * 734 drw_cur_create(Drw *drw, int shape) 735 { 736 Cur *cur; 737 738 if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) 739 return NULL; 740 741 cur->cursor = XCreateFontCursor(drw->dpy, shape); 742 743 return cur; 744 } 745 746 void 747 drw_cur_free(Drw *drw, Cur *cursor) 748 { 749 if (!cursor) 750 return; 751 752 XFreeCursor(drw->dpy, cursor->cursor); 753 free(cursor); 754 } 755 756 #if SCROLL_PATCH 757 #include "patch/scroll.c" 758 #endif