static void initmonitor(void);
static void initresources(void);
static void initdc(void);
-static void initconfig(void);
+static void initiconsize(void);
static void initatoms(void);
/* structure builders, and their helper routines */
/* 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 */
/* open connection to server and set X variables */
if ((dpy = XOpenDisplay(NULL)) == NULL)
- errx(1, "cannot open display");
+ errx(1, "could not open display");
screen = DefaultScreen(dpy);
visual = DefaultVisual(dpy, screen);
rootwin = RootWindow(dpy, screen);
initmonitor();
initresources();
initdc();
- initconfig();
+ initiconsize();
initatoms();
/* set window class */
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");
+ errx(1, "could not load font");
}
}
ealloccolor(const char *s, XftColor *color)
{
if(!XftColorAllocName(dpy, visual, colormap, s, color))
- errx(1, "cannot allocate color: %s", s);
+ errx(1, "could not allocate color: %s", s);
}
/* query monitor information and cursor position */
if ((info = XineramaQueryScreens(dpy, &nmons)) != NULL) {
int selmon = 0;
- if (!mflag || (mflag && (config.monitor < 0 || config.monitor >= nmons))) {
+ if (!mflag || config.monitor < 0 || config.monitor >= nmons) {
for (i = 0; i < nmons; i++) {
- if (cursx >= info[i].x_org && cursx <= info[i].x_org + info[i].width &&
- cursy >= info[i].y_org && cursy <= info[i].y_org + info[i].height) {
+ if (BETWEEN(cursx, info[i].x_org, info[i].x_org + info[i].width) &&
+ BETWEEN(cursy, info[i].y_org, info[i].y_org + info[i].height)) {
selmon = i;
break;
}
dc.gc = XCreateGC(dpy, rootwin, 0, NULL);
}
-/* calculate configuration values that are not set manually */
+/* calculate icon size */
static void
-initconfig(void)
+initiconsize(void)
{
- config.screenw = DisplayWidth(dpy, screen);
- config.screenh = DisplayHeight(dpy, screen);
config.iconsize = config.height_pixels - config.iconpadding * 2;
}
}
item->y = 0;
item->h = 0;
- if (item->label == NULL)
- item->labellen = 0;
- else
- item->labellen = strlen(item->label);
item->next = NULL;
item->submenu = NULL;
item->icon = NULL;
menu->list = list;
menu->caller = NULL;
menu->selected = NULL;
- menu->w = 0; /* calculated by setupmenu() */
- menu->h = 0; /* calculated by setupmenu() */
- menu->x = 0; /* calculated by setupmenu() */
- menu->y = 0; /* calculated by setupmenu() */
+ menu->w = 0; /* recalculated by setupmenu() */
+ menu->h = 0; /* recalculated by setupmenu() */
+ menu->x = mon.x; /* recalculated by setupmenu() */
+ menu->y = mon.y; /* recalculated by setupmenu() */
menu->level = level;
menu->drawn = 0;
+ menu->hasicon = 0;
swa.override_redirect = (wflag) ? False : True;
swa.background_pixel = dc.normal[ColorBG].pixel;
menu = menu->parent, i++)
;
if (menu == NULL)
- errx(1, "reached NULL menu");
+ errx(1, "improper indentation detected");
/* find last item in the new menu */
for (item = menu->list; item->next != NULL; item = item->next)
curritem->prev = NULL;
}
+ if (curritem->file)
+ prevmenu->hasicon = 1;
+
return rootmenu;
}
return ucode;
}
-/* draw text into XftDraw */
+/* get which font contains a given code point */
+static XftFont *
+getfontucode(FcChar32 ucode)
+{
+ FcCharSet *fccharset = NULL;
+ FcPattern *fcpattern = NULL;
+ FcPattern *match = NULL;
+ XftFont *retfont = NULL;
+ XftResult result;
+ 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 charset */
+ if (fccharset) {
+ fcpattern = FcPatternDuplicate(dc.pattern);
+ FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
+ }
+
+ /* find pattern matching fcpattern */
+ if (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 dc.fonts[dc.nfonts++];
+ } 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;
- s = text;
- while (*s) {
+ 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];
+ ucode = getnextutf8char(text, &next);
+ currfont = getfontucode(ucode);
- len = nexts - s;
-
- 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);
+ 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 */
menu->h += item->h;
if (item->label)
- textwidth = drawtext(NULL, NULL, 0, 0, item->h, item->label);
+ textwidth = drawtext(NULL, NULL, 0, 0, 0, item->label);
else
textwidth = 0;
* padding appears 3 times: before the label and around the triangle.
*/
itemwidth = textwidth + config.triangle_width + config.horzpadding * 3;
- itemwidth += (iflag) ? 0 : config.iconsize + config.horzpadding;
+ itemwidth += (iflag || !menu->hasicon) ? 0 : config.iconsize + config.horzpadding;
menu->w = MAX(menu->w, itemwidth);
}
}
if (pflag || (config.posy > mon.y && mon.y + mon.h - config.posy >= height))
menu->y = config.posy;
- else if (config.screenh > height)
+ else if (mon.y + mon.h > height)
menu->y = mon.y + mon.h - height;
} else { /* else, calculate in respect to parent menu */
int parentwidth;
else if (menu->parent->x > menu->w + config.border_pixels + config.gap_pixels)
menu->x = menu->parent->x - menu->w - config.border_pixels - config.gap_pixels;
- if (mon.y + mon.h - (menu->caller->y + menu->parent->y) > height)
+ if (mon.y + mon.h - (menu->caller->y + menu->parent->y) >= height)
menu->y = menu->caller->y + menu->parent->y;
- else if (mon.y + mon.h - menu->parent->y > height)
- menu->y = menu->parent->y;
else if (mon.y + mon.h > height)
menu->y = mon.y + mon.h - height;
}
XStringListToTextProperty(&title, 1, &wintitle);
/* set window manager hints */
- sizeh.flags = PMaxSize | PMinSize;
+ sizeh.flags = USPosition | PMaxSize | PMinSize;
sizeh.min_width = sizeh.max_width = menu->w;
sizeh.min_height = sizeh.max_height = menu->h;
XSetWMProperties(dpy, menu->win, &wintitle, NULL, NULL, 0, &sizeh, NULL, classh);
return;
nanosleep(&ts, NULL);
}
- errx(1, "cannot grab keyboard");
+ errx(1, "could not grab pointer");
}
/* try to grab keyboard, we may have to wait for another process to ungrab */
return;
nanosleep(&ts, NULL);
}
- errx(1, "cannot grab keyboard");
+ errx(1, "could not grab keyboard");
}
/* load and scale icon */
loadicon(const char *file)
{
Imlib_Image icon;
+ Imlib_Load_Error errcode;
+ const char *errstr;
int width;
int height;
int imgsize;
- icon = imlib_load_image(file);
- if (icon == NULL)
- errx(1, "cannot load icon %s", file);
+ icon = imlib_load_image_with_error_return(file, &errcode);
+ if (*file == '\0') {
+ warnx("could not load icon (file name is blank)");
+ return NULL;
+ } else if (icon == NULL) {
+ switch (errcode) {
+ case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:
+ errstr = "file does not exist";
+ break;
+ case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY:
+ errstr = "file is directory";
+ break;
+ case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ:
+ case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE:
+ errstr = "permission denied";
+ break;
+ case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT:
+ errstr = "unknown file format";
+ break;
+ case IMLIB_LOAD_ERROR_PATH_TOO_LONG:
+ errstr = "path too long";
+ break;
+ case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT:
+ case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY:
+ case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE:
+ errstr = "improper path";
+ break;
+ case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS:
+ errstr = "too many symbolic links";
+ break;
+ case IMLIB_LOAD_ERROR_OUT_OF_MEMORY:
+ errstr = "out of memory";
+ break;
+ case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS:
+ errstr = "out of file descriptors";
+ break;
+ default:
+ errstr = "unknown error";
+ break;
+ }
+ warnx("could not load icon (%s): %s", errstr, file);
+ return NULL;
+ }
imlib_context_set_image(icon);
/* draw text */
x = config.horzpadding;
- x += (iflag) ? 0 : config.horzpadding + config.iconsize;
+ x += (iflag || !menu->hasicon) ? 0 : config.horzpadding + config.iconsize;
dsel = XftDrawCreate(dpy, item->sel, visual, colormap);
dunsel = XftDrawCreate(dpy, item->unsel, visual, colormap);
XSetForeground(dpy, dc.gc, dc.selected[ColorFG].pixel);
}
/* draw icon */
- if (item->file != NULL && !iflag) {
+ if (item->file && !iflag)
item->icon = loadicon(item->file);
+ if (item->icon) {
imlib_context_set_image(item->icon);
imlib_context_set_drawable(item->sel);
imlib_render_image_on_drawable(config.horzpadding, config.iconpadding);
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);
}