My dmenu build
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

427 lines
9.6 KiB

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