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