/* text drawer, and its helper routine */
static FcChar32 getnextutf8char(const char *s, const char **end_ret);
+static XftFont *getfontucode(FcChar32 ucode);
static int drawtext(XftDraw *draw, XftColor *color, int x, int y, unsigned h, const char *text);
/* structure setters, and their helper routines */
static Atom netatom[NetLast];
/* flags */
+static int fflag = 0; /* whether glyphs should align based on the first font */
static int iflag = 0; /* whether to disable icons */
static int mflag = 0; /* whether the user specified a monitor with -p */
static int pflag = 0; /* whether the user specified a position with -p */
XClassHint classh;
int ch;
- while ((ch = getopt(argc, argv, "ip:w")) != -1) {
+ while ((ch = getopt(argc, argv, "fip:w")) != -1) {
switch (ch) {
+ case 'f':
+ fflag = 1;
+ break;
case 'i':
iflag = 1;
break;
i = 0;
while (isspace(*p))
p++;
- while (*p != '\0' && *p != ',') {
+ while (i < sizeof buf && *p != '\0' && *p != ',') {
buf[i++] = *p++;
}
+ if (i >= sizeof buf)
+ errx(1, "font name too long");
if (*p == ',')
p++;
buf[i] = '\0';
+ if (nfont == 0)
+ if ((dc.pattern = FcNameParse((FcChar8 *)buf)) == NULL)
+ errx(1, "the first font in the cache must be loaded from a font string");
if ((dc.fonts[nfont++] = XftFontOpenName(dpy, screen, buf)) == NULL)
errx(1, "cannot load font");
}
return ucode;
}
-/* draw text into XftDraw */
+/* get which font contains a given code point */
+static XftFont *
+getfontucode(FcChar32 ucode)
+{
+ FcCharSet *fccharset;
+ FcPattern *fcpattern;
+ FcPattern *match;
+ XftResult result;
+ XftFont *retfont;
+ size_t i;
+
+ for (i = 0; i < dc.nfonts; i++)
+ if (XftCharExists(dpy, dc.fonts[i], ucode) == FcTrue)
+ return dc.fonts[i];
+
+ /* create a charset containing our code point */
+ fccharset = FcCharSetCreate();
+ FcCharSetAddChar(fccharset, ucode);
+
+ /* create a pattern akin to the dc.pattern but containing our code point */
+ fcpattern = FcPatternDuplicate(dc.pattern);
+ FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
+
+ /* find pattern matching fcpattern */
+ FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
+ FcDefaultSubstitute(fcpattern);
+ match = XftFontMatch(dpy, screen, fcpattern, &result);
+
+ /* if found a pattern, open its font */
+ if (match) {
+ retfont = XftFontOpenPattern(dpy, match);
+ if (retfont && XftCharExists(dpy, retfont, ucode) == FcTrue) {
+ if ((dc.fonts = realloc(dc.fonts, dc.nfonts+1)) == NULL)
+ err(1, "realloc");
+ dc.fonts[dc.nfonts++] = retfont;
+ return retfont;
+ } else {
+ XftFontClose(dpy, retfont);
+ }
+ }
+
+ /* in case no fount was found, return the first one */
+ return dc.fonts[0];
+}
+
+/* draw text into XftDraw, return width of text glyphs */
static int
drawtext(XftDraw *draw, XftColor *color, int x, int y, unsigned h, const char *text)
{
- const char *s, *nexts;
- FcChar32 ucode;
- XftFont *currfont;
- int textlen = 0;
+ int textwidth = 0;
+ int texty;
- s = text;
- while (*s) {
+ texty = y + (h - (dc.fonts[0]->ascent + dc.fonts[0]->descent))/2 + dc.fonts[0]->ascent;
+
+ while (*text) {
+ XftFont *currfont;
XGlyphInfo ext;
- int charexists;
+ FcChar32 ucode;
+ const char *next;
size_t len;
- size_t i;
-
- charexists = 0;
- ucode = getnextutf8char(s, &nexts);
- for (i = 0; i < dc.nfonts; i++) {
- charexists = XftCharExists(dpy, dc.fonts[i], ucode);
- if (charexists)
- break;
- }
- if (charexists)
- currfont = dc.fonts[i];
- len = nexts - s;
+ ucode = getnextutf8char(text, &next);
+ currfont = getfontucode(ucode);
- XftTextExtentsUtf8(dpy, currfont, (XftChar8 *)s,
- len, &ext);
- textlen += ext.xOff;
+ len = next - text;
+ XftTextExtentsUtf8(dpy, currfont, (XftChar8 *)text, len, &ext);
+ textwidth += ext.xOff;
if (draw) {
- int texty;
-
- texty = y + (h - (currfont->ascent + currfont->descent))/2 + currfont->ascent;
- XftDrawStringUtf8(draw, color, currfont, x, texty,
- (XftChar8 *)s, len);
+ if (!fflag)
+ texty = y + (h - (currfont->ascent + currfont->descent))/2 + currfont->ascent;
+ XftDrawStringUtf8(draw, color, currfont, x, texty, (XftChar8 *)text, len);
x += ext.xOff;
}
- s = nexts;
+ text = next;
}
- return textlen;
+ return textwidth;
}
/* setup the height, width and icon of the items of a menu */
static void
cleanup(void)
{
+ size_t i;
+
XUngrabPointer(dpy, CurrentTime);
XUngrabKeyboard(dpy, CurrentTime);
XftColorFree(dpy, visual, colormap, &dc.separator);
XftColorFree(dpy, visual, colormap, &dc.border);
+ for (i = 0; i < dc.nfonts; i++)
+ XftFontClose(dpy, dc.fonts[i]);
+
XFreeGC(dpy, dc.gc);
XCloseDisplay(dpy);
}
static void
usage(void)
{
- (void)fprintf(stderr, "usage: xmenu [-iw] [-p position] [title]\n");
+ (void)fprintf(stderr, "usage: xmenu [-fiw] [-p position] [title]\n");
exit(1);
}