eb3b3c3a23097e80b9f02729ea9de959cd6cfc42
[dwm.git] / drw.c
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
14 static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
15 static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
16 static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
17 static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
18
19 static long
20 utf8decodebyte(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
27 static size_t
28 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
36 static size_t
37 utf8decode(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
59 Drw *
60 drw_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
76 void
77 drw_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
87 void
88 drw_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 */
101 static Fnt *
102 drw_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
146 Fnt*
147 drw_font_create(Drw *drw, const char *fontname) {
148 return drw_font_xcreate(drw, fontname, NULL);
149 }
150
151 void
152 drw_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
164 void
165 drw_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
174 Clr *
175 drw_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
193 void
194 drw_clr_free(Clr *clr) {
195 if(clr)
196 free(clr);
197 }
198
199 void
200 drw_setscheme(Drw *drw, ClrScheme *scheme) {
201 if(drw && scheme)
202 drw->scheme = scheme;
203 }
204
205 void
206 drw_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
219 int
220 drw_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
365 void
366 drw_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
374 void
375 drw_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
385 unsigned int
386 drw_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
395 Cur *
396 drw_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
405 void
406 drw_cur_free(Drw *drw, Cur *cursor) {
407 if(!drw || !cursor)
408 return;
409 XFreeCursor(drw->dpy, cursor->cursor);
410 free(cursor);
411 }