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