My build of dmenu from suckless.org.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

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