at4rka

dwm-drw.c

Oct 11th, 2025
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.80 KB | None | 0 0
  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 "drw.h"
  9. #include "util.h"
  10.  
  11. #define UTF_INVALID 0xFFFD
  12.  
  13. static int
  14. utf8decode(const char *s_in, long *u, int *err)
  15. {
  16.     static const unsigned char lens[] = {
  17.         /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  18.         /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0,  /* invalid */
  19.         /* 110XX */ 2, 2, 2, 2,
  20.         /* 1110X */ 3, 3,
  21.         /* 11110 */ 4,
  22.         /* 11111 */ 0,  /* invalid */
  23.     };
  24.     static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
  25.     static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
  26.  
  27.     const unsigned char *s = (const unsigned char *)s_in;
  28.     int len = lens[*s >> 3];
  29.     *u = UTF_INVALID;
  30.     *err = 1;
  31.     if (len == 0)
  32.         return 1;
  33.  
  34.     long cp = s[0] & leading_mask[len - 1];
  35.     for (int i = 1; i < len; ++i) {
  36.         if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
  37.             return i;
  38.         cp = (cp << 6) | (s[i] & 0x3F);
  39.     }
  40.     /* out of range, surrogate, overlong encoding */
  41.     if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
  42.         return len;
  43.  
  44.     *err = 0;
  45.     *u = cp;
  46.     return len;
  47. }
  48.  
  49. Drw *
  50. drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
  51. {
  52.     Drw *drw = ecalloc(1, sizeof(Drw));
  53.  
  54.     drw->dpy = dpy;
  55.     drw->screen = screen;
  56.     drw->root = root;
  57.     drw->w = w;
  58.     drw->h = h;
  59.     drw->visual = visual;
  60.     drw->depth = depth;
  61.     drw->cmap = cmap;
  62.     drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
  63.     drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
  64.     XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
  65.  
  66.     return drw;
  67. }
  68.  
  69. void
  70. drw_resize(Drw *drw, unsigned int w, unsigned int h)
  71. {
  72.     if (!drw)
  73.         return;
  74.  
  75.     drw->w = w;
  76.     drw->h = h;
  77.     if (drw->drawable)
  78.         XFreePixmap(drw->dpy, drw->drawable);
  79.     drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
  80. }
  81.  
  82. void
  83. drw_free(Drw *drw)
  84. {
  85.     XFreePixmap(drw->dpy, drw->drawable);
  86.     XFreeGC(drw->dpy, drw->gc);
  87.     drw_fontset_free(drw->fonts);
  88.     free(drw);
  89. }
  90.  
  91. /* This function is an implementation detail. Library users should use
  92.  * drw_fontset_create instead.
  93.  */
  94. static Fnt *
  95. xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
  96. {
  97.     Fnt *font;
  98.     XftFont *xfont = NULL;
  99.     FcPattern *pattern = NULL;
  100.  
  101.     if (fontname) {
  102.         /* Using the pattern found at font->xfont->pattern does not yield the
  103.          * same substitution results as using the pattern returned by
  104.          * FcNameParse; using the latter results in the desired fallback
  105.          * behaviour whereas the former just results in missing-character
  106.          * rectangles being drawn, at least with some fonts. */
  107.         if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
  108.             fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
  109.             return NULL;
  110.         }
  111.         if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
  112.             fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
  113.             XftFontClose(drw->dpy, xfont);
  114.             return NULL;
  115.         }
  116.     } else if (fontpattern) {
  117.         if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
  118.             fprintf(stderr, "error, cannot load font from pattern.\n");
  119.             return NULL;
  120.         }
  121.     } else {
  122.         die("no font specified.");
  123.     }
  124.  
  125.     font = ecalloc(1, sizeof(Fnt));
  126.     font->xfont = xfont;
  127.     font->pattern = pattern;
  128.     font->h = xfont->ascent + xfont->descent;
  129.     font->dpy = drw->dpy;
  130.  
  131.     return font;
  132. }
  133.  
  134. static void
  135. xfont_free(Fnt *font)
  136. {
  137.     if (!font)
  138.         return;
  139.     if (font->pattern)
  140.         FcPatternDestroy(font->pattern);
  141.     XftFontClose(font->dpy, font->xfont);
  142.     free(font);
  143. }
  144.  
  145. Fnt*
  146. drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
  147. {
  148.     Fnt *cur, *ret = NULL;
  149.     size_t i;
  150.  
  151.     if (!drw || !fonts)
  152.         return NULL;
  153.  
  154.     for (i = 1; i <= fontcount; i++) {
  155.         if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
  156.             cur->next = ret;
  157.             ret = cur;
  158.         }
  159.     }
  160.     return (drw->fonts = ret);
  161. }
  162.  
  163. void
  164. drw_fontset_free(Fnt *font)
  165. {
  166.     if (font) {
  167.         drw_fontset_free(font->next);
  168.         xfont_free(font);
  169.     }
  170. }
  171.  
  172. void
  173. drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
  174. {
  175.     if (!drw || !dest || !clrname)
  176.         return;
  177.  
  178.     if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
  179.                            clrname, dest))
  180.         die("error, cannot allocate color '%s'", clrname);
  181.     dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
  182. }
  183.  
  184. /* Create color schemes. */
  185. Clr *
  186. drw_scm_create(Drw *drw, char *clrnames[], const unsigned int alphas[], size_t clrcount)
  187. {
  188.     size_t i;
  189.     Clr *ret;
  190.  
  191.     /* need at least two colors for a scheme */
  192.     if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(Clr))))
  193.         return NULL;
  194.  
  195.     for (i = 0; i < clrcount; i++)
  196.         drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
  197.     return ret;
  198. }
  199.  
  200. void
  201. drw_clr_free(Drw *drw, Clr *c)
  202. {
  203.     if (!drw || !c)
  204.         return;
  205.  
  206.     /* c is typedef XftColor Clr */
  207.     XftColorFree(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
  208.                  DefaultColormap(drw->dpy, drw->screen), c);
  209. }
  210.  
  211. void
  212. drw_scm_free(Drw *drw, Clr *scm, size_t clrcount)
  213. {
  214.     size_t i;
  215.  
  216.     if (!drw || !scm)
  217.         return;
  218.  
  219.     for (i = 0; i < clrcount; i++)
  220.         drw_clr_free(drw, &scm[i]);
  221.     free(scm);
  222. }
  223.  
  224. void
  225. drw_setfontset(Drw *drw, Fnt *set)
  226. {
  227.     if (drw)
  228.         drw->fonts = set;
  229. }
  230.  
  231. void
  232. drw_setscheme(Drw *drw, Clr *scm)
  233. {
  234.     if (drw)
  235.         drw->scheme = scm;
  236. }
  237.  
  238. void
  239. drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
  240. {
  241.     if (!drw || !drw->scheme)
  242.         return;
  243.     XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
  244.     if (filled)
  245.         XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
  246.     else
  247.         XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
  248. }
  249.  
  250. int
  251. drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
  252. {
  253.     int ty, ellipsis_x = 0;
  254.     unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
  255.     XftDraw *d = NULL;
  256.     Fnt *usedfont, *curfont, *nextfont;
  257.     int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
  258.     long utf8codepoint = 0;
  259.     const char *utf8str;
  260.     FcCharSet *fccharset;
  261.     FcPattern *fcpattern;
  262.     FcPattern *match;
  263.     XftResult result;
  264.     int charexists = 0, overflow = 0;
  265.     /* keep track of a couple codepoints for which we have no match. */
  266.     static unsigned int nomatches[128], ellipsis_width, invalid_width;
  267.     static const char invalid[] = "�";
  268.  
  269.     if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
  270.         return 0;
  271.  
  272.     if (!render) {
  273.         w = invert ? invert : ~invert;
  274.     } else {
  275.         XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
  276.         XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
  277.         if (w < lpad)
  278.             return x + w;
  279.         d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
  280.         x += lpad;
  281.         w -= lpad;
  282.     }
  283.  
  284.     usedfont = drw->fonts;
  285.     if (!ellipsis_width && render)
  286.         ellipsis_width = drw_fontset_getwidth(drw, "...");
  287.     if (!invalid_width && render)
  288.         invalid_width = drw_fontset_getwidth(drw, invalid);
  289.     while (1) {
  290.         ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
  291.         utf8str = text;
  292.         nextfont = NULL;
  293.         while (*text) {
  294.             utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
  295.             for (curfont = drw->fonts; curfont; curfont = curfont->next) {
  296.                 charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
  297.                 if (charexists) {
  298.                     drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
  299.                     if (ew + ellipsis_width <= w) {
  300.                         /* keep track where the ellipsis still fits */
  301.                         ellipsis_x = x + ew;
  302.                         ellipsis_w = w - ew;
  303.                         ellipsis_len = utf8strlen;
  304.                     }
  305.  
  306.                     if (ew + tmpw > w) {
  307.                         overflow = 1;
  308.                         /* called from drw_fontset_getwidth_clamp():
  309.                          * it wants the width AFTER the overflow
  310.                          */
  311.                         if (!render)
  312.                             x += tmpw;
  313.                         else
  314.                             utf8strlen = ellipsis_len;
  315.                     } else if (curfont == usedfont) {
  316.                         text += utf8charlen;
  317.                         utf8strlen += utf8err ? 0 : utf8charlen;
  318.                         ew += utf8err ? 0 : tmpw;
  319.                     } else {
  320.                         nextfont = curfont;
  321.                     }
  322.                     break;
  323.                 }
  324.             }
  325.  
  326.             if (overflow || !charexists || nextfont || utf8err)
  327.                 break;
  328.             else
  329.                 charexists = 0;
  330.         }
  331.  
  332.         if (utf8strlen) {
  333.             if (render) {
  334.                 ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
  335.                 XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
  336.                                   usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen);
  337.             }
  338.             x += ew;
  339.             w -= ew;
  340.         }
  341.         if (utf8err && (!render || invalid_width < w)) {
  342.             if (render)
  343.                 drw_text(drw, x, y, w, h, 0, invalid, invert);
  344.             x += invalid_width;
  345.             w -= invalid_width;
  346.         }
  347.         if (render && overflow)
  348.             drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
  349.  
  350.         if (!*text || overflow) {
  351.             break;
  352.         } else if (nextfont) {
  353.             charexists = 0;
  354.             usedfont = nextfont;
  355.         } else {
  356.             /* Regardless of whether or not a fallback font is found, the
  357.              * character must be drawn. */
  358.             charexists = 1;
  359.  
  360.             hash = (unsigned int)utf8codepoint;
  361.             hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
  362.             hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
  363.             h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
  364.             h1 = (hash >> 17) % LENGTH(nomatches);
  365.             /* avoid expensive XftFontMatch call when we know we won't find a match */
  366.             if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
  367.                 goto no_match;
  368.  
  369.             fccharset = FcCharSetCreate();
  370.             FcCharSetAddChar(fccharset, utf8codepoint);
  371.  
  372.             if (!drw->fonts->pattern) {
  373.                 /* Refer to the comment in xfont_create for more information. */
  374.                 die("the first font in the cache must be loaded from a font string.");
  375.             }
  376.  
  377.             fcpattern = FcPatternDuplicate(drw->fonts->pattern);
  378.             FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
  379.             FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
  380.  
  381.             FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
  382.             FcDefaultSubstitute(fcpattern);
  383.             match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
  384.  
  385.             FcCharSetDestroy(fccharset);
  386.             FcPatternDestroy(fcpattern);
  387.  
  388.             if (match) {
  389.                 usedfont = xfont_create(drw, NULL, match);
  390.                 if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
  391.                     for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
  392.                         ; /* NOP */
  393.                     curfont->next = usedfont;
  394.                 } else {
  395.                     xfont_free(usedfont);
  396.                     nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
  397. no_match:
  398.                     usedfont = drw->fonts;
  399.                 }
  400.             }
  401.         }
  402.     }
  403.     if (d)
  404.         XftDrawDestroy(d);
  405.  
  406.     return x + (render ? w : 0);
  407. }
  408.  
  409. void
  410. drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
  411. {
  412.     if (!drw)
  413.         return;
  414.  
  415.     XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
  416.     XSync(drw->dpy, False);
  417. }
  418.  
  419. unsigned int
  420. drw_fontset_getwidth(Drw *drw, const char *text)
  421. {
  422.     if (!drw || !drw->fonts || !text)
  423.         return 0;
  424.     return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
  425. }
  426.  
  427. unsigned int
  428. drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
  429. {
  430.     unsigned int tmp = 0;
  431.     if (drw && drw->fonts && text && n)
  432.         tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
  433.     return MIN(n, tmp);
  434. }
  435.  
  436. void
  437. drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
  438. {
  439.     XGlyphInfo ext;
  440.  
  441.     if (!font || !text)
  442.         return;
  443.  
  444.     XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
  445.     if (w)
  446.         *w = ext.xOff;
  447.     if (h)
  448.         *h = font->h;
  449. }
  450.  
  451. Cur *
  452. drw_cur_create(Drw *drw, int shape)
  453. {
  454.     Cur *cur;
  455.  
  456.     if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
  457.         return NULL;
  458.  
  459.     cur->cursor = XCreateFontCursor(drw->dpy, shape);
  460.  
  461.     return cur;
  462. }
  463.  
  464. void
  465. drw_cur_free(Drw *drw, Cur *cursor)
  466. {
  467.     if (!cursor)
  468.         return;
  469.  
  470.     XFreeCursor(drw->dpy, cursor->cursor);
  471.     free(cursor);
  472. }
  473.  
Advertisement
Add Comment
Please, Sign In to add comment