#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;
/* 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, "ip:w")) != -1) {
- switch (ch) {
- 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, "could not 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 [-iw] [-p position] [title]\n");
+ exit(1);
}
/* parse position string from -p,
errx(1, "improper position: %s", optarg);
}
-/* parse color string */
+/* parse font string */
static void
parsefonts(const char *s)
{
mon.y = info[selmon].y_org;
mon.w = info[selmon].width;
mon.h = info[selmon].height;
+
+ XFree(info);
}
if (!pflag) {
static void
initresources(void)
{
- char *xrm;
long n;
char *type;
+ char *xrm;
XrmDatabase xdb;
XrmValue xval;
file = label + 4;
label = strtok(NULL, "\t\n");
}
- if (file && *file == '\0')
- errx(1, "blank icon filename");
/* get the output */
output = strtok(NULL, "\n");
int imgsize;
icon = imlib_load_image_with_error_return(file, &errcode);
- if (icon == NULL) {
+ 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";
errstr = "unknown error";
break;
}
- errx(1, "could not load icon (%s): %s", errstr, file);
+ warnx("could not load icon (%s): %s", errstr, file);
+ return NULL;
}
imlib_context_set_image(icon);
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;
struct Menu *menu;
struct Item *item;
struct Item *previtem = NULL;
+ struct Item *lastitem;
KeySym ksym;
XEvent ev;
/* 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);
}
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 [-iw] [-p position] [title]\n");
- exit(1);
+ struct Menu *rootmenu;
+ XClassHint classh;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "ip:w")) != -1) {
+ switch (ch) {
+ 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, "could not 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;
}