Introduction
Introduction Statistics Contact Development Disclaimer Help
drw.c - dmenu - dynamic menu
git clone git://git.suckless.org/dmenu
Log
Files
Refs
README
LICENSE
---
drw.c (11585B)
---
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,…
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, …
25 static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10…
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, unsign…
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->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy,…
60 drw->gc = XCreateGC(dpy, root, 0, NULL);
61 XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMite…
62
63 return drw;
64 }
65
66 void
67 drw_resize(Drw *drw, unsigned int w, unsigned int h)
68 {
69 if (!drw)
70 return;
71
72 drw->w = w;
73 drw->h = h;
74 if (drw->drawable)
75 XFreePixmap(drw->dpy, drw->drawable);
76 drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, Default…
77 }
78
79 void
80 drw_free(Drw *drw)
81 {
82 XFreePixmap(drw->dpy, drw->drawable);
83 XFreeGC(drw->dpy, drw->gc);
84 drw_fontset_free(drw->fonts);
85 free(drw);
86 }
87
88 /* This function is an implementation detail. Library users should use
89 * drw_fontset_create instead.
90 */
91 static Fnt *
92 xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
93 {
94 Fnt *font;
95 XftFont *xfont = NULL;
96 FcPattern *pattern = NULL;
97
98 if (fontname) {
99 /* Using the pattern found at font->xfont->pattern does …
100 * same substitution results as using the pattern return…
101 * FcNameParse; using the latter results in the desired …
102 * behaviour whereas the former just results in missing-…
103 * rectangles being drawn, at least with some fonts. */
104 if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fon…
105 fprintf(stderr, "error, cannot load font from na…
106 return NULL;
107 }
108 if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
109 fprintf(stderr, "error, cannot parse font name t…
110 XftFontClose(drw->dpy, xfont);
111 return NULL;
112 }
113 } else if (fontpattern) {
114 if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))…
115 fprintf(stderr, "error, cannot load font from pa…
116 return NULL;
117 }
118 } else {
119 die("no font specified.");
120 }
121
122 font = ecalloc(1, sizeof(Fnt));
123 font->xfont = xfont;
124 font->pattern = pattern;
125 font->h = xfont->ascent + xfont->descent;
126 font->dpy = drw->dpy;
127
128 return font;
129 }
130
131 static void
132 xfont_free(Fnt *font)
133 {
134 if (!font)
135 return;
136 if (font->pattern)
137 FcPatternDestroy(font->pattern);
138 XftFontClose(font->dpy, font->xfont);
139 free(font);
140 }
141
142 Fnt*
143 drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
144 {
145 Fnt *cur, *ret = NULL;
146 size_t i;
147
148 if (!drw || !fonts)
149 return NULL;
150
151 for (i = 1; i <= fontcount; i++) {
152 if ((cur = xfont_create(drw, fonts[fontcount - i], NULL)…
153 cur->next = ret;
154 ret = cur;
155 }
156 }
157 return (drw->fonts = ret);
158 }
159
160 void
161 drw_fontset_free(Fnt *font)
162 {
163 if (font) {
164 drw_fontset_free(font->next);
165 xfont_free(font);
166 }
167 }
168
169 void
170 drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
171 {
172 if (!drw || !dest || !clrname)
173 return;
174
175 if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->sc…
176 DefaultColormap(drw->dpy, drw->screen),
177 clrname, dest))
178 die("error, cannot allocate color '%s'", clrname);
179 }
180
181 /* Create color schemes. */
182 Clr *
183 drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
184 {
185 size_t i;
186 Clr *ret;
187
188 /* need at least two colors for a scheme */
189 if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcoun…
190 return NULL;
191
192 for (i = 0; i < clrcount; i++)
193 drw_clr_create(drw, &ret[i], clrnames[i]);
194 return ret;
195 }
196
197 void
198 drw_clr_free(Drw *drw, Clr *c)
199 {
200 if (!drw || !c)
201 return;
202
203 /* c is typedef XftColor Clr */
204 XftColorFree(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
205 DefaultColormap(drw->dpy, drw->screen), c);
206 }
207
208 void
209 drw_scm_free(Drw *drw, Clr *scm, size_t clrcount)
210 {
211 size_t i;
212
213 if (!drw || !scm)
214 return;
215
216 for (i = 0; i < clrcount; i++)
217 drw_clr_free(drw, &scm[i]);
218 free(scm);
219 }
220
221 void
222 drw_setfontset(Drw *drw, Fnt *set)
223 {
224 if (drw)
225 drw->fonts = set;
226 }
227
228 void
229 drw_setscheme(Drw *drw, Clr *scm)
230 {
231 if (drw)
232 drw->scheme = scm;
233 }
234
235 void
236 drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int fil…
237 {
238 if (!drw || !drw->scheme)
239 return;
240 XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pi…
241 if (filled)
242 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w…
243 else
244 XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w…
245 }
246
247 int
248 drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigne…
249 {
250 int ty, ellipsis_x = 0;
251 unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h…
252 XftDraw *d = NULL;
253 Fnt *usedfont, *curfont, *nextfont;
254 int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
255 long utf8codepoint = 0;
256 const char *utf8str;
257 FcCharSet *fccharset;
258 FcPattern *fcpattern;
259 FcPattern *match;
260 XftResult result;
261 int charexists = 0, overflow = 0;
262 /* keep track of a couple codepoints for which we have no match.…
263 static unsigned int nomatches[128], ellipsis_width, invalid_widt…
264 static const char invalid[] = "�";
265
266 if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->f…
267 return 0;
268
269 if (!render) {
270 w = invert ? invert : ~invert;
271 } else {
272 XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? C…
273 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w…
274 if (w < lpad)
275 return x + w;
276 d = XftDrawCreate(drw->dpy, drw->drawable,
277 DefaultVisual(drw->dpy, drw->screen),
278 DefaultColormap(drw->dpy, drw->screen)…
279 x += lpad;
280 w -= lpad;
281 }
282
283 usedfont = drw->fonts;
284 if (!ellipsis_width && render)
285 ellipsis_width = drw_fontset_getwidth(drw, "...");
286 if (!invalid_width && render)
287 invalid_width = drw_fontset_getwidth(drw, invalid);
288 while (1) {
289 ew = ellipsis_len = utf8err = utf8charlen = utf8strlen =…
290 utf8str = text;
291 nextfont = NULL;
292 while (*text) {
293 utf8charlen = utf8decode(text, &utf8codepoint, &…
294 for (curfont = drw->fonts; curfont; curfont = cu…
295 charexists = charexists || XftCharExists…
296 if (charexists) {
297 drw_font_getexts(curfont, text, …
298 if (ew + ellipsis_width <= w) {
299 /* keep track where the …
300 ellipsis_x = x + ew;
301 ellipsis_w = w - ew;
302 ellipsis_len = utf8strle…
303 }
304
305 if (ew + tmpw > w) {
306 overflow = 1;
307 /* called from drw_fonts…
308 * it wants the width AF…
309 */
310 if (!render)
311 x += tmpw;
312 else
313 utf8strlen = ell…
314 } else if (curfont == usedfont) {
315 text += utf8charlen;
316 utf8strlen += utf8err ? …
317 ew += utf8err ? 0 : tmpw;
318 } else {
319 nextfont = curfont;
320 }
321 break;
322 }
323 }
324
325 if (overflow || !charexists || nextfont || utf8e…
326 break;
327 else
328 charexists = 0;
329 }
330
331 if (utf8strlen) {
332 if (render) {
333 ty = y + (h - usedfont->h) / 2 + usedfon…
334 XftDrawStringUtf8(d, &drw->scheme[invert…
335 usedfont->xfont, x, ty…
336 }
337 x += ew;
338 w -= ew;
339 }
340 if (utf8err && (!render || invalid_width < w)) {
341 if (render)
342 drw_text(drw, x, y, w, h, 0, invalid, in…
343 x += invalid_width;
344 w -= invalid_width;
345 }
346 if (render && overflow)
347 drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "…
348
349 if (!*text || overflow) {
350 break;
351 } else if (nextfont) {
352 charexists = 0;
353 usedfont = nextfont;
354 } else {
355 /* Regardless of whether or not a fallback font …
356 * character must be drawn. */
357 charexists = 1;
358
359 hash = (unsigned int)utf8codepoint;
360 hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
361 hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
362 h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
363 h1 = (hash >> 17) % LENGTH(nomatches);
364 /* avoid expensive XftFontMatch call when we kno…
365 if (nomatches[h0] == utf8codepoint || nomatches[…
366 goto no_match;
367
368 fccharset = FcCharSetCreate();
369 FcCharSetAddChar(fccharset, utf8codepoint);
370
371 if (!drw->fonts->pattern) {
372 /* Refer to the comment in xfont_create …
373 die("the first font in the cache must be…
374 }
375
376 fcpattern = FcPatternDuplicate(drw->fonts->patte…
377 FcPatternAddCharSet(fcpattern, FC_CHARSET, fccha…
378 FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
379
380 FcConfigSubstitute(NULL, fcpattern, FcMatchPatte…
381 FcDefaultSubstitute(fcpattern);
382 match = XftFontMatch(drw->dpy, drw->screen, fcpa…
383
384 FcCharSetDestroy(fccharset);
385 FcPatternDestroy(fcpattern);
386
387 if (match) {
388 usedfont = xfont_create(drw, NULL, match…
389 if (usedfont && XftCharExists(drw->dpy, …
390 for (curfont = drw->fonts; curfo…
391 ; /* NOP */
392 curfont->next = usedfont;
393 } else {
394 xfont_free(usedfont);
395 nomatches[nomatches[h0] ? h1 : h…
396 no_match:
397 usedfont = drw->fonts;
398 }
399 }
400 }
401 }
402 if (d)
403 XftDrawDestroy(d);
404
405 return x + (render ? w : 0);
406 }
407
408 void
409 drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int…
410 {
411 if (!drw)
412 return;
413
414 XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, …
415 XSync(drw->dpy, False);
416 }
417
418 unsigned int
419 drw_fontset_getwidth(Drw *drw, const char *text)
420 {
421 if (!drw || !drw->fonts || !text)
422 return 0;
423 return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
424 }
425
426 unsigned int
427 drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
428 {
429 unsigned int tmp = 0;
430 if (drw && drw->fonts && text && n)
431 tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
432 return MIN(n, tmp);
433 }
434
435 void
436 drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned…
437 {
438 XGlyphInfo ext;
439
440 if (!font || !text)
441 return;
442
443 XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len…
444 if (w)
445 *w = ext.xOff;
446 if (h)
447 *h = font->h;
448 }
449
450 Cur *
451 drw_cur_create(Drw *drw, int shape)
452 {
453 Cur *cur;
454
455 if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
456 return NULL;
457
458 cur->cursor = XCreateFontCursor(drw->dpy, shape);
459
460 return cur;
461 }
462
463 void
464 drw_cur_free(Drw *drw, Cur *cursor)
465 {
466 if (!cursor)
467 return;
468
469 XFreeCursor(drw->dpy, cursor->cursor);
470 free(cursor);
471 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.