#include <Imlib2.h>
#include "xmenu.h"
-/*
- * Function declarations
- */
-
-/* argument parser */
-static void parseposition(char *optarg);
-
-/* initializers, and their helper routines */
-static void parsefonts(const char *s);
-static void ealloccolor(const char *s, XftColor *color);
-static void initmonitor(void);
-static void initresources(void);
-static void initdc(void);
-static void initiconsize(void);
-static void initatoms(void);
-
-/* structure builders, and their helper routines */
-static struct Item *allocitem(const char *label, const char *output, char *file);
-static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level);
-static struct Menu *buildmenutree(unsigned level, const char *label, const char *output, char *file);
-static struct Menu *parsestdin(void);
-
-/* 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 void setupitems(struct Menu *menu);
-static void setupmenupos(struct Menu *menu);
-static void setupmenu(struct Menu *menu, XClassHint *classh);
-
-/* grabbers */
-static void grabpointer(void);
-static void grabkeyboard(void);
-
-/* item drawer, and its helper routine */
-static Imlib_Image loadicon(const char *file);
-static void drawitems(struct Menu *menu);
-
-/* menu drawers and mappers */
-static void drawmenus(struct Menu *currmenu);
-static void mapmenu(struct Menu *currmenu);
-
-/* getters */
-static struct Menu *getmenu(struct Menu *currmenu, Window win);
-static struct Item *getitem(struct Menu *menu, int y);
-
-/* cycle through items */
-static struct Item *itemcycle(struct Menu *currmenu, int direction);
-
-/* main event loop */
-static void run(struct Menu *currmenu);
-
-/* cleaners */
-static void cleanmenu(struct Menu *menu);
-static void cleanup(void);
-
-/* show usage */
-static void usage(void);
-
-
-/*
- * Variable declarations
- */
-
/* X stuff */
static Display *dpy;
static int screen;
static Visual *visual;
static Window rootwin;
static Colormap colormap;
+static XrmDatabase xdb;
+static char *xrm;
static struct DC dc;
static struct Monitor mon;
static Atom utf8string;
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 rflag = 0; /* whether to disable right-click */
static int mflag = 0; /* whether the user specified a monitor with -p */
static int pflag = 0; /* whether the user specified a position with -p */
static int wflag = 0; /* whether to let the window manager control XMenu */
/* include config variable */
#include "config.h"
-
-/*
- * Function implementations
- */
-
-/* xmenu: generate menu from stdin and print selected entry to stdout */
-int
-main(int argc, char *argv[])
+/* show usage */
+static void
+usage(void)
{
- struct Menu *rootmenu;
- XClassHint classh;
- int ch;
-
- while ((ch = getopt(argc, argv, "fip:w")) != -1) {
- switch (ch) {
- case 'f':
- fflag = 1;
- break;
- case 'i':
- iflag = 1;
- break;
- case 'p':
- pflag = 1;
- parseposition(optarg);
- break;
- case 'w':
- wflag = 1;
- break;
- default:
- usage();
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc > 1)
- usage();
-
- /* open connection to server and set X variables */
- if ((dpy = XOpenDisplay(NULL)) == NULL)
- errx(1, "cannot open display");
- screen = DefaultScreen(dpy);
- visual = DefaultVisual(dpy, screen);
- rootwin = RootWindow(dpy, screen);
- colormap = DefaultColormap(dpy, screen);
-
- /* imlib2 stuff */
- if (!iflag) {
- imlib_set_cache_size(2048 * 1024);
- imlib_context_set_dither(1);
- imlib_context_set_display(dpy);
- imlib_context_set_visual(visual);
- imlib_context_set_colormap(colormap);
- }
-
- /* initializers */
- initmonitor();
- initresources();
- initdc();
- initiconsize();
- initatoms();
-
- /* set window class */
- classh.res_class = PROGNAME;
- if (argc == 1)
- classh.res_name = *argv;
- else
- classh.res_name = PROGNAME;
-
- /* generate menus and set them up */
- rootmenu = parsestdin();
- if (rootmenu == NULL)
- errx(1, "no menu generated");
- setupmenu(rootmenu, &classh);
-
- /* grab mouse and keyboard */
- if (!wflag) {
- grabpointer();
- grabkeyboard();
- }
-
- /* run event loop */
- run(rootmenu);
-
- /* freeing stuff */
- cleanmenu(rootmenu);
- cleanup();
-
- return 0;
+ (void)fprintf(stderr, "usage: xmenu [-irw] [-p position] [title]\n");
+ exit(1);
}
/* parse position string from -p,
errx(1, "improper position: %s", optarg);
}
-/* parse color string */
+/* get configuration from X resources */
+static void
+getresources(void)
+{
+ char *type;
+ XrmValue xval;
+
+ if (xrm == NULL || xdb == NULL)
+ return;
+
+ if (XrmGetResource(xdb, "xmenu.borderWidth", "*", &type, &xval) == True)
+ GETNUM(config.border_pixels, xval.addr)
+ if (XrmGetResource(xdb, "xmenu.separatorWidth", "*", &type, &xval) == True)
+ GETNUM(config.separator_pixels, xval.addr)
+ if (XrmGetResource(xdb, "xmenu.height", "*", &type, &xval) == True)
+ GETNUM(config.height_pixels, xval.addr)
+ if (XrmGetResource(xdb, "xmenu.width", "*", &type, &xval) == True)
+ GETNUM(config.width_pixels, xval.addr)
+ if (XrmGetResource(xdb, "xmenu.gap", "*", &type, &xval) == True)
+ GETNUM(config.gap_pixels, xval.addr)
+ if (XrmGetResource(xdb, "xmenu.background", "*", &type, &xval) == True)
+ config.background_color = xval.addr;
+ if (XrmGetResource(xdb, "xmenu.foreground", "*", &type, &xval) == True)
+ config.foreground_color = xval.addr;
+ if (XrmGetResource(xdb, "xmenu.selbackground", "*", &type, &xval) == True)
+ config.selbackground_color = xval.addr;
+ if (XrmGetResource(xdb, "xmenu.selforeground", "*", &type, &xval) == True)
+ config.selforeground_color = xval.addr;
+ if (XrmGetResource(xdb, "xmenu.separator", "*", &type, &xval) == True)
+ config.separator_color = xval.addr;
+ if (XrmGetResource(xdb, "xmenu.border", "*", &type, &xval) == True)
+ config.border_color = xval.addr;
+ if (XrmGetResource(xdb, "xmenu.font", "*", &type, &xval) == True)
+ config.font = xval.addr;
+ if (XrmGetResource(xdb, "xmenu.alignment", "*", &type, &xval) == True) {
+ if (strcasecmp(xval.addr, "center") == 0)
+ config.alignment = CenterAlignment;
+ else if (strcasecmp(xval.addr, "left") == 0)
+ config.alignment = LeftAlignment;
+ else if (strcasecmp(xval.addr, "right") == 0)
+ config.alignment = RightAlignment;
+ }
+}
+
+/* get configuration from command-line options */
+static char *
+getoptions(int argc, char *argv[])
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "ip:rw")) != -1) {
+ switch (ch) {
+ case 'i':
+ iflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ parseposition(optarg);
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'w':
+ wflag = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc > 1)
+ usage();
+ else if (argc == 1)
+ return *argv;
+ return PROGNAME;
+}
+
+/* parse font string */
static void
parsefonts(const char *s)
{
i = 0;
while (isspace(*p))
p++;
- while (i < sizeof buf && *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 == ',')
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 (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)) {
mon.y = info[selmon].y_org;
mon.w = info[selmon].width;
mon.h = info[selmon].height;
+
+ XFree(info);
}
if (!pflag) {
}
}
-/* read xrdb for configuration options */
-static void
-initresources(void)
-{
- char *xrm;
- long n;
- char *type;
- XrmDatabase xdb;
- XrmValue xval;
-
- XrmInitialize();
- if ((xrm = XResourceManagerString(dpy)) == NULL)
- return;
-
- xdb = XrmGetStringDatabase(xrm);
-
- if (XrmGetResource(xdb, "xmenu.borderWidth", "*", &type, &xval) == True)
- if ((n = strtol(xval.addr, NULL, 10)) > 0)
- config.border_pixels = n;
- if (XrmGetResource(xdb, "xmenu.separatorWidth", "*", &type, &xval) == True)
- if ((n = strtol(xval.addr, NULL, 10)) > 0)
- config.separator_pixels = n;
- if (XrmGetResource(xdb, "xmenu.height", "*", &type, &xval) == True)
- if ((n = strtol(xval.addr, NULL, 10)) > 0)
- config.height_pixels = n;
- if (XrmGetResource(xdb, "xmenu.width", "*", &type, &xval) == True)
- if ((n = strtol(xval.addr, NULL, 10)) > 0)
- config.width_pixels = n;
- if (XrmGetResource(xdb, "xmenu.gap", "*", &type, &xval) == True)
- if ((n = strtol(xval.addr, NULL, 10)) > 0)
- config.gap_pixels = n;
- if (XrmGetResource(xdb, "xmenu.background", "*", &type, &xval) == True)
- config.background_color = strdup(xval.addr);
- if (XrmGetResource(xdb, "xmenu.foreground", "*", &type, &xval) == True)
- config.foreground_color = strdup(xval.addr);
- if (XrmGetResource(xdb, "xmenu.selbackground", "*", &type, &xval) == True)
- config.selbackground_color = strdup(xval.addr);
- if (XrmGetResource(xdb, "xmenu.selforeground", "*", &type, &xval) == True)
- config.selforeground_color = strdup(xval.addr);
- if (XrmGetResource(xdb, "xmenu.separator", "*", &type, &xval) == True)
- config.separator_color = strdup(xval.addr);
- if (XrmGetResource(xdb, "xmenu.border", "*", &type, &xval) == True)
- config.border_color = strdup(xval.addr);
- if (XrmGetResource(xdb, "xmenu.font", "*", &type, &xval) == True)
- config.font = strdup(xval.addr);
-
- XrmDestroyDatabase(xdb);
-}
-
/* init draw context */
static void
initdc(void)
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)
static XftFont *
getfontucode(FcChar32 ucode)
{
- FcCharSet *fccharset;
- FcPattern *fcpattern;
- FcPattern *match;
+ FcCharSet *fccharset = NULL;
+ FcPattern *fcpattern = NULL;
+ FcPattern *match = NULL;
+ XftFont *retfont = NULL;
XftResult result;
- XftFont *retfont;
size_t i;
for (i = 0; i < dc.nfonts; i++)
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);
+ /* 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 */
- FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
- FcDefaultSubstitute(fcpattern);
- match = XftFontMatch(dpy, screen, fcpattern, &result);
+ if (fcpattern) {
+ FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
+ FcDefaultSubstitute(fcpattern);
+ match = XftFontMatch(dpy, screen, fcpattern, &result);
+ }
/* if found a pattern, open its font */
if (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;
+ dc.fonts[dc.nfonts] = retfont;
+ return dc.fonts[dc.nfonts++];
} else {
XftFontClose(dpy, retfont);
}
drawtext(XftDraw *draw, XftColor *color, int x, int y, unsigned h, const char *text)
{
int textwidth = 0;
- int texty;
-
- texty = y + (h - (dc.fonts[0]->ascent + dc.fonts[0]->descent))/2 + dc.fonts[0]->ascent;
while (*text) {
XftFont *currfont;
textwidth += ext.xOff;
if (draw) {
- if (!fflag)
- texty = y + (h - (currfont->ascent + currfont->descent))/2 + currfont->ascent;
+ int texty;
+
+ texty = y + (h - (currfont->ascent + currfont->descent))/2 + currfont->ascent;
XftDrawStringUtf8(draw, color, currfont, x, texty, (XftChar8 *)text, len);
x += ext.xOff;
}
setupitems(struct Menu *menu)
{
struct Item *item;
+ int itemwidth;
menu->w = config.width_pixels;
+ menu->maxtextw = 0;
for (item = menu->list; item != NULL; item = item->next) {
- int itemwidth;
- int textwidth;
-
item->y = menu->h;
-
if (item->label == NULL) /* height for separator item */
item->h = config.separator_pixels;
else
item->h = config.height_pixels;
menu->h += item->h;
-
if (item->label)
- textwidth = drawtext(NULL, NULL, 0, 0, 0, item->label);
+ item->textw = drawtext(NULL, NULL, 0, 0, 0, item->label);
else
- textwidth = 0;
+ item->textw = 0;
/*
* set menu width
*
- * the item width depends on the size of its label (textwidth),
+ * the item width depends on the size of its label (item->textw),
* and it is only used to calculate the width of the menu (which
* is equal to the width of the largest item).
*
* if the iflag is set (icons are disabled) then the horizontal
* padding appears 3 times: before the label and around the triangle.
*/
- itemwidth = textwidth + config.triangle_width + config.horzpadding * 3;
+ itemwidth = item->textw + config.triangle_width + config.horzpadding * 3;
itemwidth += (iflag || !menu->hasicon) ? 0 : config.iconsize + config.horzpadding;
menu->w = MAX(menu->w, itemwidth);
+ menu->maxtextw = MAX(menu->maxtextw, item->textw);
}
}
width = menu->w + config.border_pixels * 2;
height = menu->h + config.border_pixels * 2;
if (menu->parent == NULL) { /* if root menu, calculate in respect to cursor */
- if (pflag || (config.posx > mon.x && mon.x + mon.w - config.posx >= width))
+ if (pflag || (config.posx >= mon.x && mon.x + mon.w - config.posx >= width))
menu->x = config.posx;
else if (config.posx > width)
menu->x = config.posx - width;
- if (pflag || (config.posy > mon.y && mon.y + mon.h - config.posy >= height))
+ if (pflag || (config.posy >= mon.y && mon.y + mon.h - config.posy >= height))
menu->y = config.posy;
else if (mon.y + mon.h > height)
menu->y = mon.y + mon.h - height;
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");
+}
+
+/* ungrab pointer and keyboard */
+static void
+ungrab(void)
+{
+ XUngrabPointer(dpy, CurrentTime);
+ XUngrabKeyboard(dpy, CurrentTime);
}
/* 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);
static void
drawitems(struct Menu *menu)
{
+ XftDraw *dsel, *dunsel;
struct Item *item;
+ int textx;
+ int x, y;
for (item = menu->list; item != NULL; item = item->next) {
- XftDraw *dsel, *dunsel;
- int x, y;
-
item->unsel = XCreatePixmap(dpy, menu->win, menu->w, item->h,
DefaultDepth(dpy, screen));
item->sel = item->unsel;
} else {
-
item->sel = XCreatePixmap(dpy, menu->win, menu->w, item->h,
DefaultDepth(dpy, screen));
XSetForeground(dpy, dc.gc, dc.selected[ColorBG].pixel);
XFillRectangle(dpy, item->sel, dc.gc, 0, 0, menu->w, item->h);
/* draw text */
- x = config.horzpadding;
- x += (iflag || !menu->hasicon) ? 0 : config.horzpadding + config.iconsize;
+ textx = config.horzpadding;
+ textx += (iflag || !menu->hasicon) ? 0 : config.horzpadding + config.iconsize;
+ switch (config.alignment) {
+ case CenterAlignment:
+ textx += (menu->maxtextw - item->textw) / 2;
+ break;
+ case RightAlignment:
+ textx += menu->maxtextw - item->textw;
+ break;
+ default:
+ break;
+ }
dsel = XftDrawCreate(dpy, item->sel, visual, colormap);
dunsel = XftDrawCreate(dpy, item->unsel, visual, colormap);
XSetForeground(dpy, dc.gc, dc.selected[ColorFG].pixel);
- drawtext(dsel, &dc.selected[ColorFG], x, 0, item->h, item->label);
+ drawtext(dsel, &dc.selected[ColorFG], textx, 0, item->h, item->label);
XSetForeground(dpy, dc.gc, dc.normal[ColorFG].pixel);
- drawtext(dunsel, &dc.normal[ColorFG], x, 0, item->h, item->label);
+ drawtext(dunsel, &dc.normal[ColorFG], textx, 0, item->h, item->label);
XftDrawDestroy(dsel);
XftDrawDestroy(dunsel);
Convex, CoordModeOrigin);
}
- /* draw icon */
- if (item->file != NULL && !iflag) {
+ /* try to load icon */
+ if (item->file && !iflag) {
item->icon = loadicon(item->file);
+ free(item->file);
+ }
+ /* draw icon if properly loaded */
+ if (item->icon) {
imlib_context_set_image(item->icon);
imlib_context_set_drawable(item->sel);
imlib_render_image_on_drawable(config.horzpadding, config.iconpadding);
imlib_context_set_drawable(item->unsel);
imlib_render_image_on_drawable(config.horzpadding, config.iconpadding);
+ imlib_context_set_image(item->icon);
+ imlib_free_image();
}
}
}
static struct Item *
itemcycle(struct Menu *currmenu, int direction)
{
- struct Item *item;
+ struct Item *item = NULL;
struct Item *lastitem;
- item = NULL;
+ for (lastitem = currmenu->list; lastitem && lastitem->next; lastitem = lastitem->next)
+ ;
- if (direction == ITEMNEXT) {
+ /* select item (either separator or labeled item) in given direction */
+ switch (direction) {
+ case ITEMNEXT:
if (currmenu->selected == NULL)
item = currmenu->list;
else if (currmenu->selected->next != NULL)
item = currmenu->selected->next;
-
- while (item != NULL && item->label == NULL)
- item = item->next;
-
- if (item == NULL)
- item = currmenu->list;
- } else {
- for (lastitem = currmenu->list;
- lastitem != NULL && lastitem->next != NULL;
- lastitem = lastitem->next)
- ;
-
+ break;
+ case ITEMPREV:
if (currmenu->selected == NULL)
item = lastitem;
else if (currmenu->selected->prev != NULL)
item = currmenu->selected->prev;
+ break;
+ case ITEMFIRST:
+ item = currmenu->list;
+ break;
+ case ITEMLAST:
+ item = lastitem;
+ break;
+ }
+ /*
+ * the selected item can be a separator
+ * let's select the closest labeled item (ie., one that isn't a separator)
+ */
+ switch (direction) {
+ case ITEMNEXT:
+ case ITEMFIRST:
+ while (item != NULL && item->label == NULL)
+ item = item->next;
+ if (item == NULL)
+ item = currmenu->list;
+ break;
+ case ITEMPREV:
+ case ITEMLAST:
while (item != NULL && item->label == NULL)
item = item->prev;
-
if (item == NULL)
item = lastitem;
+ break;
}
return item;
}
+/* check if button is used to open a item on click */
+static int
+isclickbutton(unsigned int button)
+{
+ if (button == Button1)
+ return 1;
+ if (!rflag && button == Button3)
+ return 1;
+ return 0;
+}
+
/* run event loop */
static void
run(struct Menu *currmenu)
struct Menu *menu;
struct Item *item;
struct Item *previtem = NULL;
+ struct Item *lastitem;
KeySym ksym;
XEvent ev;
drawmenus(currmenu);
break;
case ButtonRelease:
+ if (!isclickbutton(ev.xbutton.button))
+ break;
menu = getmenu(currmenu, ev.xbutton.window);
item = getitem(menu, ev.xbutton.y);
if (menu == NULL || item == NULL)
/* cycle through menu */
item = NULL;
- if (ksym == XK_ISO_Left_Tab || ksym == XK_Up) {
+ if (ksym == XK_Home || ksym == KSYMFIRST) {
+ item = itemcycle(currmenu, ITEMFIRST);
+ } else if (ksym == XK_End || ksym == KSYMLAST) {
+ item = itemcycle(currmenu, ITEMLAST);
+ } else if (ksym == XK_ISO_Left_Tab || ksym == XK_Up || ksym == KSYMUP) {
item = itemcycle(currmenu, ITEMPREV);
- } else if (ksym == XK_Tab || ksym == XK_Down) {
+ } else if (ksym == XK_Tab || ksym == XK_Down || ksym == KSYMDOWN) {
item = itemcycle(currmenu, ITEMNEXT);
- } else if ((ksym == XK_Return || ksym == XK_Right) &&
- currmenu->selected != NULL) {
+ } else if (ksym >= XK_1 && ksym <= XK_9){
+ item = itemcycle(currmenu, ITEMFIRST);
+ lastitem = itemcycle(currmenu, ITEMLAST);
+ for (int i = ksym - XK_1; i > 0 && item != lastitem; i--) {
+ currmenu->selected = item;
+ item = itemcycle(currmenu, ITEMNEXT);
+ }
+ } else if ((ksym == XK_Return || ksym == XK_Right || ksym == KSYMRIGHT) &&
+ currmenu->selected != NULL) {
item = currmenu->selected;
goto selectitem;
- } else if ((ksym == XK_Escape || ksym == XK_Left) &&
+ } else if ((ksym == XK_Escape || ksym == XK_Left || ksym == KSYMLEFT) &&
currmenu->parent != NULL) {
item = currmenu->parent->selected;
currmenu = currmenu->parent;
if (tmp->label != tmp->output)
free(tmp->label);
free(tmp->output);
- if (tmp->file != NULL) {
- free(tmp->file);
- if (tmp->icon != NULL) {
- imlib_context_set_image(tmp->icon);
- imlib_free_image();
- }
- }
item = item->next;
free(tmp);
}
free(menu);
}
-/* cleanup X and exit */
+/* cleanup draw context */
static void
-cleanup(void)
+cleandc(void)
{
size_t i;
- XUngrabPointer(dpy, CurrentTime);
- XUngrabKeyboard(dpy, CurrentTime);
-
XftColorFree(dpy, visual, colormap, &dc.normal[ColorBG]);
XftColorFree(dpy, visual, colormap, &dc.normal[ColorFG]);
XftColorFree(dpy, visual, colormap, &dc.selected[ColorBG]);
XftColorFree(dpy, visual, colormap, &dc.selected[ColorFG]);
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);
}
-/* show usage */
-static void
-usage(void)
+/* xmenu: generate menu from stdin and print selected entry to stdout */
+int
+main(int argc, char *argv[])
{
- (void)fprintf(stderr, "usage: xmenu [-fiw] [-p position] [title]\n");
- exit(1);
+ struct Menu *rootmenu;
+ XClassHint classh;
+
+ /* open connection to server and set X variables */
+ if ((dpy = XOpenDisplay(NULL)) == NULL)
+ errx(1, "could not open display");
+ screen = DefaultScreen(dpy);
+ visual = DefaultVisual(dpy, screen);
+ rootwin = RootWindow(dpy, screen);
+ colormap = DefaultColormap(dpy, screen);
+ XrmInitialize();
+ if ((xrm = XResourceManagerString(dpy)) != NULL)
+ xdb = XrmGetStringDatabase(xrm);
+
+ /* get configuration */
+ getresources();
+ classh.res_class = PROGNAME;
+ classh.res_name = getoptions(argc, argv);
+
+ /* imlib2 stuff */
+ if (!iflag) {
+ imlib_set_cache_size(2048 * 1024);
+ imlib_context_set_dither(1);
+ imlib_context_set_display(dpy);
+ imlib_context_set_visual(visual);
+ imlib_context_set_colormap(colormap);
+ }
+
+ /* initializers */
+ initmonitor();
+ initdc();
+ initiconsize();
+ initatoms();
+
+ /* generate menus and set them up */
+ rootmenu = parsestdin();
+ if (rootmenu == NULL)
+ errx(1, "no menu generated");
+ setupmenu(rootmenu, &classh);
+
+ /* grab mouse and keyboard */
+ if (!wflag) {
+ grabpointer();
+ grabkeyboard();
+ }
+
+ /* run event loop */
+ run(rootmenu);
+
+ /* clean stuff */
+ ungrab();
+ cleanmenu(rootmenu);
+ cleandc();
+ XrmDestroyDatabase(xdb);
+ XCloseDisplay(dpy);
+
+ return 0;
}