nixos-dotfiles

nixos-dotfiles

https://git.tonybtw.com/nixos-dotfiles.git git://git.tonybtw.com/nixos-dotfiles.git
11,286 bytes raw
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)
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, screen));
60
	drw->gc = XCreateGC(dpy, root, 0, NULL);
61
	XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
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, DefaultDepth(drw->dpy, drw->screen));
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 not yield the
100
		 * same substitution results as using the pattern returned by
101
		 * FcNameParse; using the latter results in the desired fallback
102
		 * behaviour whereas the former just results in missing-character
103
		 * rectangles being drawn, at least with some fonts. */
104
		if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
105
			fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
106
			return NULL;
107
		}
108
		if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
109
			fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
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 pattern.\n");
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->screen),
176
	                       DefaultColormap(drw->dpy, drw->screen),
177
	                       clrname, dest))
178
		die("error, cannot allocate color '%s'", clrname);
179
}
180
181
/* Wrapper to create color schemes. The caller has to call free(3) on the
182
 * returned color scheme when done using it. */
183
Clr *
184
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
185
{
186
	size_t i;
187
	Clr *ret;
188
189
	/* need at least two colors for a scheme */
190
	if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
191
		return NULL;
192
193
	for (i = 0; i < clrcount; i++)
194
		drw_clr_create(drw, &ret[i], clrnames[i]);
195
	return ret;
196
}
197
198
void
199
drw_setfontset(Drw *drw, Fnt *set)
200
{
201
	if (drw)
202
		drw->fonts = set;
203
}
204
205
void
206
drw_setscheme(Drw *drw, Clr *scm)
207
{
208
	if (drw)
209
		drw->scheme = scm;
210
}
211
212
void
213
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
214
{
215
	if (!drw || !drw->scheme)
216
		return;
217
	XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
218
	if (filled)
219
		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
220
	else
221
		XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
222
}
223
224
int
225
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
226
{
227
	int ty, ellipsis_x = 0;
228
	unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
229
	XftDraw *d = NULL;
230
	Fnt *usedfont, *curfont, *nextfont;
231
	int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
232
	long utf8codepoint = 0;
233
	const char *utf8str;
234
	FcCharSet *fccharset;
235
	FcPattern *fcpattern;
236
	FcPattern *match;
237
	XftResult result;
238
	int charexists = 0, overflow = 0;
239
	/* keep track of a couple codepoints for which we have no match. */
240
	static unsigned int nomatches[128], ellipsis_width, invalid_width;
241
	static const char invalid[] = "�";
242
243
	if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
244
		return 0;
245
246
	if (!render) {
247
		w = invert ? invert : ~invert;
248
	} else {
249
		XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
250
		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
251
		if (w < lpad)
252
			return x + w;
253
		d = XftDrawCreate(drw->dpy, drw->drawable,
254
		                  DefaultVisual(drw->dpy, drw->screen),
255
		                  DefaultColormap(drw->dpy, drw->screen));
256
		x += lpad;
257
		w -= lpad;
258
	}
259
260
	usedfont = drw->fonts;
261
	if (!ellipsis_width && render)
262
		ellipsis_width = drw_fontset_getwidth(drw, "...");
263
	if (!invalid_width && render)
264
		invalid_width = drw_fontset_getwidth(drw, invalid);
265
	while (1) {
266
		ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
267
		utf8str = text;
268
		nextfont = NULL;
269
		while (*text) {
270
			utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
271
			for (curfont = drw->fonts; curfont; curfont = curfont->next) {
272
				charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
273
				if (charexists) {
274
					drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
275
					if (ew + ellipsis_width <= w) {
276
						/* keep track where the ellipsis still fits */
277
						ellipsis_x = x + ew;
278
						ellipsis_w = w - ew;
279
						ellipsis_len = utf8strlen;
280
					}
281
282
					if (ew + tmpw > w) {
283
						overflow = 1;
284
						/* called from drw_fontset_getwidth_clamp():
285
						 * it wants the width AFTER the overflow
286
						 */
287
						if (!render)
288
							x += tmpw;
289
						else
290
							utf8strlen = ellipsis_len;
291
					} else if (curfont == usedfont) {
292
						text += utf8charlen;
293
						utf8strlen += utf8err ? 0 : utf8charlen;
294
						ew += utf8err ? 0 : tmpw;
295
					} else {
296
						nextfont = curfont;
297
					}
298
					break;
299
				}
300
			}
301
302
			if (overflow || !charexists || nextfont || utf8err)
303
				break;
304
			else
305
				charexists = 0;
306
		}
307
308
		if (utf8strlen) {
309
			if (render) {
310
				ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
311
				XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
312
				                  usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen);
313
			}
314
			x += ew;
315
			w -= ew;
316
		}
317
		if (utf8err && (!render || invalid_width < w)) {
318
			if (render)
319
				drw_text(drw, x, y, w, h, 0, invalid, invert);
320
			x += invalid_width;
321
			w -= invalid_width;
322
		}
323
		if (render && overflow)
324
			drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
325
326
		if (!*text || overflow) {
327
			break;
328
		} else if (nextfont) {
329
			charexists = 0;
330
			usedfont = nextfont;
331
		} else {
332
			/* Regardless of whether or not a fallback font is found, the
333
			 * character must be drawn. */
334
			charexists = 1;
335
336
			hash = (unsigned int)utf8codepoint;
337
			hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
338
			hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
339
			h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
340
			h1 = (hash >> 17) % LENGTH(nomatches);
341
			/* avoid expensive XftFontMatch call when we know we won't find a match */
342
			if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
343
				goto no_match;
344
345
			fccharset = FcCharSetCreate();
346
			FcCharSetAddChar(fccharset, utf8codepoint);
347
348
			if (!drw->fonts->pattern) {
349
				/* Refer to the comment in xfont_create for more information. */
350
				die("the first font in the cache must be loaded from a font string.");
351
			}
352
353
			fcpattern = FcPatternDuplicate(drw->fonts->pattern);
354
			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
355
			FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
356
357
			FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
358
			FcDefaultSubstitute(fcpattern);
359
			match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
360
361
			FcCharSetDestroy(fccharset);
362
			FcPatternDestroy(fcpattern);
363
364
			if (match) {
365
				usedfont = xfont_create(drw, NULL, match);
366
				if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
367
					for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
368
						; /* NOP */
369
					curfont->next = usedfont;
370
				} else {
371
					xfont_free(usedfont);
372
					nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
373
no_match:
374
					usedfont = drw->fonts;
375
				}
376
			}
377
		}
378
	}
379
	if (d)
380
		XftDrawDestroy(d);
381
382
	return x + (render ? w : 0);
383
}
384
385
void
386
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
387
{
388
	if (!drw)
389
		return;
390
391
	XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
392
	XSync(drw->dpy, False);
393
}
394
395
unsigned int
396
drw_fontset_getwidth(Drw *drw, const char *text)
397
{
398
	if (!drw || !drw->fonts || !text)
399
		return 0;
400
	return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
401
}
402
403
unsigned int
404
drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
405
{
406
	unsigned int tmp = 0;
407
	if (drw && drw->fonts && text && n)
408
		tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
409
	return MIN(n, tmp);
410
}
411
412
void
413
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
414
{
415
	XGlyphInfo ext;
416
417
	if (!font || !text)
418
		return;
419
420
	XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
421
	if (w)
422
		*w = ext.xOff;
423
	if (h)
424
		*h = font->h;
425
}
426
427
Cur *
428
drw_cur_create(Drw *drw, int shape)
429
{
430
	Cur *cur;
431
432
	if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
433
		return NULL;
434
435
	cur->cursor = XCreateFontCursor(drw->dpy, shape);
436
437
	return cur;
438
}
439
440
void
441
drw_cur_free(Drw *drw, Cur *cursor)
442
{
443
	if (!cursor)
444
		return;
445
446
	XFreeCursor(drw->dpy, cursor->cursor);
447
	free(cursor);
448
}