all repos — dwm @ 14343e69cc596b847f71f1e825d3019ab1a29aa8

fork of suckless dynamic window manager

drw.c (view 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#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, 0xF8};
 16static const long utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
 17static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
 18
 19static long
 20utf8decodebyte(const char c, size_t *i) {
 21	for(*i = 0; *i < (UTF_SIZ + 1); ++(*i))
 22		if(((unsigned char)c & utfmask[*i]) == utfbyte[*i])
 23			return (unsigned char)c & ~utfmask[*i];
 24	return 0;
 25}
 26
 27static size_t
 28utf8validate(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
 37utf8decode(const char *c, long *u, size_t clen) {
 38	size_t i, j, len, type;
 39	long udecoded;
 40
 41	*u = UTF_INVALID;
 42	if(!clen)
 43		return 0;
 44	udecoded = utf8decodebyte(c[0], &len);
 45	if(!BETWEEN(len, 1, UTF_SIZ))
 46		return 1;
 47	for(i = 1, j = 1; i < clen && j < len; ++i, ++j) {
 48		udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
 49		if(type != 0)
 50			return j;
 51	}
 52	if(j < len)
 53		return 0;
 54	*u = udecoded;
 55	utf8validate(u, len);
 56	return len;
 57}
 58
 59Drw *
 60drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) {
 61	Drw *drw = (Drw *)calloc(1, sizeof(Drw));
 62	if(!drw)
 63		return NULL;
 64	drw->dpy = dpy;
 65	drw->screen = screen;
 66	drw->root = root;
 67	drw->w = w;
 68	drw->h = h;
 69	drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
 70	drw->gc = XCreateGC(dpy, root, 0, NULL);
 71	drw->fontcount = 0;
 72	XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
 73	return drw;
 74}
 75
 76void
 77drw_resize(Drw *drw, unsigned int w, unsigned int h) {
 78	if(!drw)
 79		return;
 80	drw->w = w;
 81	drw->h = h;
 82	if(drw->drawable != 0)
 83		XFreePixmap(drw->dpy, drw->drawable);
 84	drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
 85}
 86
 87void
 88drw_free(Drw *drw) {
 89	size_t i;
 90	for (i = 0; i < drw->fontcount; i++) {
 91		drw_font_free(drw->fonts[i]);
 92	}
 93	XFreePixmap(drw->dpy, drw->drawable);
 94	XFreeGC(drw->dpy, drw->gc);
 95	free(drw);
 96}
 97
 98/* This function is an implementation detail. Library users should use
 99 * drw_font_create instead.
100 */
101static Fnt *
102drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) {
103	Fnt *font;
104
105	if (!(fontname || fontpattern))
106		die("No font specified.\n");
107
108	if (!(font = (Fnt *)calloc(1, sizeof(Fnt))))
109		return NULL;
110
111	if (fontname) {
112		/* Using the pattern found at font->xfont->pattern does not yield same
113		 * the same substitution results as using the pattern returned by
114		 * FcNameParse; using the latter results in the desired fallback
115		 * behaviour whereas the former just results in
116		 * missing-character-rectangles being drawn, at least with some fonts.
117		 */
118		if (!(font->xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)) ||
119		    !(font->pattern = FcNameParse((FcChar8 *) fontname))) {
120			if (font->xfont) {
121				XftFontClose(drw->dpy, font->xfont);
122				font->xfont = NULL;
123			}
124			fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
125		}
126	} else if (fontpattern) {
127		if (!(font->xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
128			fprintf(stderr, "error, cannot load font pattern.\n");
129		} else {
130			font->pattern = NULL;
131		}
132	}
133
134	if (!font->xfont) {
135		free(font);
136		return NULL;
137	}
138
139	font->ascent = font->xfont->ascent;
140	font->descent = font->xfont->descent;
141	font->h = font->ascent + font->descent;
142	font->dpy = drw->dpy;
143	return font;
144}
145
146Fnt*
147drw_font_create(Drw *drw, const char *fontname) {
148	return drw_font_xcreate(drw, fontname, NULL);
149}
150
151void
152drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount) {
153	size_t i;
154	Fnt *font;
155	for (i = 0; i < fontcount; i++) {
156		if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
157			die("Font cache exhausted.\n");
158		} else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) {
159			drw->fonts[drw->fontcount++] = font;
160		}
161	}
162}
163
164void
165drw_font_free(Fnt *font) {
166	if(!font)
167		return;
168	if(font->pattern)
169		FcPatternDestroy(font->pattern);
170	XftFontClose(font->dpy, font->xfont);
171	free(font);
172}
173
174Clr *
175drw_clr_create(Drw *drw, const char *clrname) {
176	Clr *clr;
177	Colormap cmap;
178	Visual *vis;
179
180	if(!drw)
181		return NULL;
182	clr = (Clr *)calloc(1, sizeof(Clr));
183	if(!clr)
184		return NULL;
185	cmap = DefaultColormap(drw->dpy, drw->screen);
186	vis = DefaultVisual(drw->dpy, drw->screen);
187	if(!XftColorAllocName(drw->dpy, vis, cmap, clrname, &clr->rgb))
188		die("error, cannot allocate color '%s'\n", clrname);
189	clr->pix = clr->rgb.pixel;
190	return clr;
191}
192
193void
194drw_clr_free(Clr *clr) {
195	if(clr)
196		free(clr);
197}
198
199void
200drw_setscheme(Drw *drw, ClrScheme *scheme) {
201	if(drw && scheme)
202		drw->scheme = scheme;
203}
204
205void
206drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert) {
207	int dx;
208
209	if(!drw || !drw->fontcount || !drw->scheme)
210		return;
211	XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix);
212	dx = (drw->fonts[0]->ascent + drw->fonts[0]->descent + 2) / 4;
213	if(filled)
214		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x+1, y+1, dx+1, dx+1);
215	else if(empty)
216		XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x+1, y+1, dx, dx);
217}
218
219int
220drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) {
221	char buf[1024];
222	int tx, ty, th;
223	Extnts tex;
224	Colormap cmap;
225	Visual *vis;
226	XftDraw *d;
227	Fnt *curfont, *nextfont;
228	size_t i, len;
229	int utf8strlen, utf8charlen, render;
230	long utf8codepoint = 0;
231	const char *utf8str;
232	FcCharSet *fccharset;
233	FcPattern *fcpattern;
234	FcPattern *match;
235	XftResult result;
236	int charexists = 0;
237
238	if (!(render = x || y || w || h)) {
239		w = ~w;
240	}
241
242	if (!drw || !drw->scheme) {
243		return 0;
244	} else if (render) {
245		XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->fg->pix : drw->scheme->bg->pix);
246		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
247	}
248
249	if (!text || !drw->fontcount) {
250		return 0;
251	} else if (render) {
252		cmap = DefaultColormap(drw->dpy, drw->screen);
253		vis = DefaultVisual(drw->dpy, drw->screen);
254		d = XftDrawCreate(drw->dpy, drw->drawable, vis, cmap);
255	}
256
257	curfont = drw->fonts[0];
258	while (1) {
259		utf8strlen = 0;
260		utf8str = text;
261		nextfont = NULL;
262		while (*text) {
263			utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
264			for (i = 0; i < drw->fontcount; i++) {
265				charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint);
266				if (charexists) {
267					if (drw->fonts[i] == curfont) {
268						utf8strlen += utf8charlen;
269						text += utf8charlen;
270					} else {
271						nextfont = drw->fonts[i];
272					}
273					break;
274				}
275			}
276
277			if (!charexists || (nextfont && nextfont != curfont)) {
278				break;
279			} else {
280				charexists = 0;
281			}
282		}
283
284		if (utf8strlen) {
285			drw_font_getexts(curfont, utf8str, utf8strlen, &tex);
286			/* shorten text if necessary */
287			for(len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--)
288				drw_font_getexts(curfont, utf8str, len, &tex);
289
290			if (len) {
291				memcpy(buf, utf8str, len);
292				buf[len] = '\0';
293				if(len < utf8strlen)
294					for(i = len; i && i > len - 3; buf[--i] = '.');
295
296				if (render) {
297					th = curfont->ascent + curfont->descent;
298					ty = y + (h / 2) - (th / 2) + curfont->ascent;
299					tx = x + (h / 2);
300					XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len);
301				}
302
303				x += tex.w;
304				w -= tex.w;
305			}
306		}
307
308		if (!*text) {
309			break;
310		} else if (nextfont) {
311			charexists = 0;
312			curfont = nextfont;
313		} else {
314			/* Regardless of whether or not a fallback font is found, the
315			 * character must be drawn.
316			 */
317			charexists = 1;
318
319			if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
320				continue;
321			}
322
323			fccharset = FcCharSetCreate();
324			FcCharSetAddChar(fccharset, utf8codepoint);
325
326			if (!drw->fonts[0]->pattern) {
327				/* Refer to the comment in drw_font_xcreate for more
328				 * information.
329				 */
330				die("The first font in the cache must be loaded from a font string.\n");
331			}
332
333			fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern);
334			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
335			FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
336
337			FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
338			FcDefaultSubstitute(fcpattern);
339			match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
340
341			FcCharSetDestroy(fccharset);
342			FcPatternDestroy(fcpattern);
343
344			if (match) {
345				curfont = drw_font_xcreate(drw, NULL, match);
346				if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) {
347					drw->fonts[drw->fontcount++] = curfont;
348				} else {
349					if (curfont) {
350						drw_font_free(curfont);
351					}
352					curfont = drw->fonts[0];
353				}
354			}
355		}
356	}
357
358	if (render) {
359		XftDrawDestroy(d);
360	}
361
362	return x;
363}
364
365void
366drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) {
367	if(!drw)
368		return;
369	XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
370	XSync(drw->dpy, False);
371}
372
373
374void
375drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex) {
376	XGlyphInfo ext;
377
378	if(!font || !text)
379		return;
380	XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
381	tex->h = font->h;
382	tex->w = ext.xOff;
383}
384
385unsigned int
386drw_font_getexts_width(Fnt *font, const char *text, unsigned int len) {
387	Extnts tex;
388
389	if(!font)
390		return -1;
391	drw_font_getexts(font, text, len, &tex);
392	return tex.w;
393}
394
395Cur *
396drw_cur_create(Drw *drw, int shape) {
397	Cur *cur = (Cur *)calloc(1, sizeof(Cur));
398
399	if(!drw || !cur)
400		return NULL;
401	cur->cursor = XCreateFontCursor(drw->dpy, shape);
402	return cur;
403}
404
405void
406drw_cur_free(Drw *drw, Cur *cursor) {
407	if(!drw || !cursor)
408		return;
409	XFreeCursor(drw->dpy, cursor->cursor);
410	free(cursor);
411}