aboutsummaryrefslogtreecommitdiffstats
path: root/src/drw/drw.c
diff options
context:
space:
mode:
authorphilw <dscr@duck.com>2025-04-15 01:41:05 +0200
committerphilw <dscr@duck.com>2025-04-15 01:41:05 +0200
commit8146021a2ea4130cfb0d2bf846b451ec538b1bb6 (patch)
tree3a946ef5ee4769bf4eae89b4e77858cc67b8060b /src/drw/drw.c
parent515b7f3b4f048b29325e3e38f0f4a2ef898e8daa (diff)
downloaddwm-8146021a2ea4130cfb0d2bf846b451ec538b1bb6.tar.gz
dwm-8146021a2ea4130cfb0d2bf846b451ec538b1bb6.zip
Refactor the project
Refactored core logic to improve readability and maintainability. Documented the 'transient.c' file to provide clear explanations of its purpose and functions. Updated the 'util.c' file with necessary comments and improvements to existing code. Documented the 'util.h' header file to clarify function prototypes and usage. This update should improve the overall code quality and make it easier for future development. Signed-off-by: philw <dscr@duck.com>
Diffstat (limited to 'src/drw/drw.c')
-rw-r--r--src/drw/drw.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/src/drw/drw.c b/src/drw/drw.c
new file mode 100644
index 0000000..8f53beb
--- /dev/null
+++ b/src/drw/drw.c
@@ -0,0 +1,425 @@
1/* See LICENSE file for copyright and license details. */
2#include <X11/Xft/Xft.h>
3#include <X11/Xlib.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "drw.h"
9#include "util.h"
10
11#define UTF_INVALID 0xFFFD
12#define UTF_SIZ 4
13
14static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
15static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0,
16 0xF8};
17static const long utfmin[UTF_SIZ + 1] = {0, 0, 0x80, 0x800, 0x10000};
18static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF,
19 0x10FFFF};
20
21static long utf8decodebyte(const char c, size_t *i) {
22 for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
23 if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
24 return (unsigned char)c & ~utfmask[*i];
25 return 0;
26}
27
28static size_t utf8validate(long *u, size_t i) {
29 if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
30 *u = UTF_INVALID;
31 for (i = 1; *u > utfmax[i]; ++i)
32 ;
33 return i;
34}
35
36static size_t utf8decode(const char *c, long *u, size_t clen) {
37 size_t i, j, len, type;
38 long udecoded;
39
40 *u = UTF_INVALID;
41 if (!clen)
42 return 0;
43 udecoded = utf8decodebyte(c[0], &len);
44 if (!BETWEEN(len, 1, UTF_SIZ))
45 return 1;
46 for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
47 udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
48 if (type)
49 return j;
50 }
51 if (j < len)
52 return 0;
53 *u = udecoded;
54 utf8validate(u, len);
55
56 return len;
57}
58
59Drw *drw_create(Display *dpy, int screen, Window root, unsigned int w,
60 unsigned int h) {
61 Drw *drw = ecalloc(1, sizeof(Drw));
62
63 drw->dpy = dpy;
64 drw->screen = screen;
65 drw->root = root;
66 drw->w = w;
67 drw->h = h;
68 drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
69 drw->gc = XCreateGC(dpy, root, 0, NULL);
70 XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
71
72 return drw;
73}
74
75void drw_resize(Drw *drw, unsigned int w, unsigned int h) {
76 if (!drw)
77 return;
78
79 drw->w = w;
80 drw->h = h;
81 if (drw->drawable)
82 XFreePixmap(drw->dpy, drw->drawable);
83 drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h,
84 DefaultDepth(drw->dpy, drw->screen));
85}
86
87void drw_free(Drw *drw) {
88 XFreePixmap(drw->dpy, drw->drawable);
89 XFreeGC(drw->dpy, drw->gc);
90 drw_fontset_free(drw->fonts);
91 free(drw);
92}
93
94/* This function is an implementation detail. Library users should use
95 * drw_fontset_create instead.
96 */
97static Fnt *xfont_create(Drw *drw, const char *fontname,
98 FcPattern *fontpattern) {
99 Fnt *font;
100 XftFont *xfont = NULL;
101 FcPattern *pattern = NULL;
102
103 if (fontname) {
104 /* Using the pattern found at font->xfont->pattern does not yield the
105 * same substitution results as using the pattern returned by
106 * FcNameParse; using the latter results in the desired fallback
107 * behaviour whereas the former just results in missing-character
108 * rectangles being drawn, at least with some fonts. */
109 if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
110 fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
111 return NULL;
112 }
113 if (!(pattern = FcNameParse((FcChar8 *)fontname))) {
114 fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n",
115 fontname);
116 XftFontClose(drw->dpy, xfont);
117 return NULL;
118 }
119 } else if (fontpattern) {
120 if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
121 fprintf(stderr, "error, cannot load font from pattern.\n");
122 return NULL;
123 }
124 } else {
125 die("no font specified.");
126 }
127
128 font = ecalloc(1, sizeof(Fnt));
129 font->xfont = xfont;
130 font->pattern = pattern;
131 font->h = xfont->ascent + xfont->descent;
132 font->dpy = drw->dpy;
133
134 return font;
135}
136
137static void xfont_free(Fnt *font) {
138 if (!font)
139 return;
140 if (font->pattern)
141 FcPatternDestroy(font->pattern);
142 XftFontClose(font->dpy, font->xfont);
143 free(font);
144}
145
146Fnt *drw_fontset_create(Drw *drw, const char *fonts[], size_t fontcount) {
147 Fnt *cur, *ret = NULL;
148 size_t i;
149
150 if (!drw || !fonts)
151 return NULL;
152
153 for (i = 1; i <= fontcount; i++) {
154 if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
155 cur->next = ret;
156 ret = cur;
157 }
158 }
159 return (drw->fonts = ret);
160}
161
162void drw_fontset_free(Fnt *font) {
163 if (font) {
164 drw_fontset_free(font->next);
165 xfont_free(font);
166 }
167}
168
169void drw_clr_create(Drw *drw, Clr *dest, const char *clrname) {
170 if (!drw || !dest || !clrname)
171 return;
172
173 if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
174 DefaultColormap(drw->dpy, drw->screen), clrname, dest))
175 die("error, cannot allocate color '%s'", clrname);
176}
177
178/* Wrapper to create color schemes. The caller has to call free(3) on the
179 * returned color scheme when done using it. */
180Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) {
181 size_t i;
182 Clr *ret;
183
184 /* need at least two colors for a scheme */
185 if (!drw || !clrnames || clrcount < 2 ||
186 !(ret = ecalloc(clrcount, sizeof(XftColor))))
187 return NULL;
188
189 for (i = 0; i < clrcount; i++)
190 drw_clr_create(drw, &ret[i], clrnames[i]);
191 return ret;
192}
193
194void drw_setfontset(Drw *drw, Fnt *set) {
195 if (drw)
196 drw->fonts = set;
197}
198
199void drw_setscheme(Drw *drw, Clr *scm) {
200 if (drw)
201 drw->scheme = scm;
202}
203
204void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h,
205 int filled, int invert) {
206 if (!drw || !drw->scheme)
207 return;
208 XSetForeground(drw->dpy, drw->gc,
209 invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
210 if (filled)
211 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
212 else
213 XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
214}
215
216int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h,
217 unsigned int lpad, const char *text, int invert) {
218 int i, ty, ellipsis_x = 0;
219 unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len;
220 XftDraw *d = NULL;
221 Fnt *usedfont, *curfont, *nextfont;
222 int utf8strlen, utf8charlen, render = x || y || w || h;
223 long utf8codepoint = 0;
224 const char *utf8str;
225 FcCharSet *fccharset;
226 FcPattern *fcpattern;
227 FcPattern *match;
228 XftResult result;
229 int charexists = 0, overflow = 0;
230 /* keep track of a couple codepoints for which we have no match. */
231 enum { nomatches_len = 64 };
232 static struct {
233 long codepoint[nomatches_len];
234 unsigned int idx;
235 } nomatches;
236 static unsigned int ellipsis_width = 0;
237
238 if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
239 return 0;
240
241 if (!render) {
242 w = invert ? invert : ~invert;
243 } else {
244 XSetForeground(drw->dpy, drw->gc,
245 drw->scheme[invert ? ColFg : ColBg].pixel);
246 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
247 d = XftDrawCreate(drw->dpy, drw->drawable,
248 DefaultVisual(drw->dpy, drw->screen),
249 DefaultColormap(drw->dpy, drw->screen));
250 x += lpad;
251 w -= lpad;
252 }
253
254 usedfont = drw->fonts;
255 if (!ellipsis_width && render)
256 ellipsis_width = drw_fontset_getwidth(drw, "...");
257 while (1) {
258 ew = ellipsis_len = utf8strlen = 0;
259 utf8str = text;
260 nextfont = NULL;
261 while (*text) {
262 utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
263 for (curfont = drw->fonts; curfont; curfont = curfont->next) {
264 charexists = charexists ||
265 XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
266 if (charexists) {
267 drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
268 if (ew + ellipsis_width <= w) {
269 /* keep track where the ellipsis still fits */
270 ellipsis_x = x + ew;
271 ellipsis_w = w - ew;
272 ellipsis_len = utf8strlen;
273 }
274
275 if (ew + tmpw > w) {
276 overflow = 1;
277 /* called from drw_fontset_getwidth_clamp():
278 * it wants the width AFTER the overflow
279 */
280 if (!render)
281 x += tmpw;
282 else
283 utf8strlen = ellipsis_len;
284 } else if (curfont == usedfont) {
285 utf8strlen += utf8charlen;
286 text += utf8charlen;
287 ew += tmpw;
288 } else {
289 nextfont = curfont;
290 }
291 break;
292 }
293 }
294
295 if (overflow || !charexists || nextfont)
296 break;
297 else
298 charexists = 0;
299 }
300
301 if (utf8strlen) {
302 if (render) {
303 ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
304 XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
305 usedfont->xfont, x, ty, (XftChar8 *)utf8str,
306 utf8strlen);
307 }
308 x += ew;
309 w -= ew;
310 }
311 if (render && overflow)
312 drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
313
314 if (!*text || overflow) {
315 break;
316 } else if (nextfont) {
317 charexists = 0;
318 usedfont = nextfont;
319 } else {
320 /* Regardless of whether or not a fallback font is found, the
321 * character must be drawn. */
322 charexists = 1;
323
324 for (i = 0; i < nomatches_len; ++i) {
325 /* avoid calling XftFontMatch if we know we won't find a match */
326 if (utf8codepoint == nomatches.codepoint[i])
327 goto no_match;
328 }
329
330 fccharset = FcCharSetCreate();
331 FcCharSetAddChar(fccharset, utf8codepoint);
332
333 if (!drw->fonts->pattern) {
334 /* Refer to the comment in xfont_create for more information. */
335 die("the first font in the cache must be loaded from a font string.");
336 }
337
338 fcpattern = FcPatternDuplicate(drw->fonts->pattern);
339 FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
340 FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
341
342 FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
343 FcDefaultSubstitute(fcpattern);
344 match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
345
346 FcCharSetDestroy(fccharset);
347 FcPatternDestroy(fcpattern);
348
349 if (match) {
350 usedfont = xfont_create(drw, NULL, match);
351 if (usedfont &&
352 XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
353 for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
354 ; /* NOP */
355 curfont->next = usedfont;
356 } else {
357 xfont_free(usedfont);
358 nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint;
359 no_match:
360 usedfont = drw->fonts;
361 }
362 }
363 }
364 }
365 if (d)
366 XftDrawDestroy(d);
367
368 return x + (render ? w : 0);
369}
370
371void drw_map(Drw *drw, Window win, int x, int y, unsigned int w,
372 unsigned int h) {
373 if (!drw)
374 return;
375
376 XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
377 XSync(drw->dpy, False);
378}
379
380unsigned int drw_fontset_getwidth(Drw *drw, const char *text) {
381 if (!drw || !drw->fonts || !text)
382 return 0;
383 return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
384}
385
386unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text,
387 unsigned int n) {
388 unsigned int tmp = 0;
389 if (drw && drw->fonts && text && n)
390 tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
391 return MIN(n, tmp);
392}
393
394void drw_font_getexts(Fnt *font, const char *text, unsigned int len,
395 unsigned int *w, unsigned int *h) {
396 XGlyphInfo ext;
397
398 if (!font || !text)
399 return;
400
401 XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
402 if (w)
403 *w = ext.xOff;
404 if (h)
405 *h = font->h;
406}
407
408Cur *drw_cur_create(Drw *drw, int shape) {
409 Cur *cur;
410
411 if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
412 return NULL;
413
414 cur->cursor = XCreateFontCursor(drw->dpy, shape);
415
416 return cur;
417}
418
419void drw_cur_free(Drw *drw, Cur *cursor) {
420 if (!cursor)
421 return;
422
423 XFreeCursor(drw->dpy, cursor->cursor);
424 free(cursor);
425}