X-Git-Url: https://git.subgeniuskitty.com/xmenu/.git/blobdiff_plain/8902c43b920e55bb96fc7006e6a10f6a6abf332e..675a2008a6c9ad34d199aebdac6447d33a9cb53e:/xmenu.c diff --git a/xmenu.c b/xmenu.c index 310b17d..56a3dd4 100644 --- a/xmenu.c +++ b/xmenu.c @@ -1,7 +1,9 @@ #include +#include #include #include #include +#include #include #include #include @@ -17,11 +19,14 @@ * Function declarations */ +/* argument parser */ +static void parseposition(const char *optarg); + /* initializers, and their helper routines */ static void ealloccolor(const char *s, XftColor *color); static void initresources(void); static void initdc(void); -static void initscreengeom(void); +static void initconfig(void); static void initatoms(void); /* structure builders, and their helper routines */ @@ -31,10 +36,10 @@ static struct Menu *buildmenutree(unsigned level, const char *label, const char static struct Menu *parsestdin(void); /* image loader */ -static Imlib_Image loadicon(const char *file, int size); +static Imlib_Image loadicon(const char *file); /* structure setters, and their helper routines */ -static void setupmenusize(struct Menu *menu); +static void setupitems(struct Menu *menu); static void setupmenupos(struct Menu *menu); static void setupmenu(struct Menu *menu, XClassHint *classh); @@ -78,6 +83,8 @@ static Atom wmdelete; static Atom netatom[NetLast]; /* flags */ +static int iflag = 0; /* whether to disable icons */ +static int pflag = 0; /* whether the user specified a position */ static int wflag = 0; /* whether to let the window manager control XMenu */ /* include config variable */ @@ -96,8 +103,15 @@ main(int argc, char *argv[]) XClassHint classh; int ch; - while ((ch = getopt(argc, argv, "w")) != -1) { + 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; @@ -121,16 +135,18 @@ main(int argc, char *argv[]) colormap = DefaultColormap(dpy, screen); /* imlib2 stuff */ - 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); + 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 */ initresources(); initdc(); - initscreengeom(); + initconfig(); initatoms(); /* set window class */ @@ -162,6 +178,30 @@ main(int argc, char *argv[]) return 0; } +/* parse position string from -p, put results on config.x and config.y */ +static void +parseposition(const char *optarg) +{ + long n; + const char *s = optarg; + char *endp; + + n = strtol(s, &endp, 10); + if (errno == ERANGE || n > INT_MAX || n < 0 || endp == s || *endp != 'x') + goto error; + config.posx = n; + s = endp+1; + n = strtol(s, &endp, 10); + if (errno == ERANGE || n > INT_MAX || n < 0 || endp == s || *endp != '\0') + goto error; + config.posy = n; + + return; + +error: + errx(1, "improper position: %s", optarg); +} + /* get color from color string */ static void ealloccolor(const char *s, XftColor *color) @@ -198,6 +238,9 @@ initresources(void) 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) @@ -236,17 +279,19 @@ initdc(void) dc.gc = XCreateGC(dpy, rootwin, 0, NULL); } -/* calculate screen geometry */ +/* calculate configuration values that are not set manually */ static void -initscreengeom(void) +initconfig(void) { Window dw; /* dummy variable */ int di; /* dummy variable */ unsigned du; /* dummy variable */ - XQueryPointer(dpy, rootwin, &dw, &dw, &config.cursx, &config.cursy, &di, &di, &du); + if (!pflag) /* if the user haven't specified a position, use cursor position*/ + XQueryPointer(dpy, rootwin, &dw, &dw, &config.posx, &config.posy, &di, &di, &du); config.screenw = DisplayWidth(dpy, screen); config.screenh = DisplayHeight(dpy, screen); + config.iconsize = config.height_pixels - config.iconpadding * 2; } /* intern atoms */ @@ -300,7 +345,7 @@ allocitem(const char *label, const char *output, char *file) return item; } -/* allocate a menu */ +/* allocate a menu and create its window */ static struct Menu * allocmenu(struct Menu *parent, struct Item *list, unsigned level) { @@ -333,8 +378,6 @@ allocmenu(struct Menu *parent, struct Item *list, unsigned level) CWBorderPixel | CWEventMask | CWSaveUnder, &swa); - XSetWMProtocols(dpy, menu->win, &wmdelete, 1); - return menu; } @@ -440,7 +483,7 @@ parsestdin(void) /* load and scale icon */ static Imlib_Image -loadicon(const char *file, int size) +loadicon(const char *file) { Imlib_Image icon; int width; @@ -457,18 +500,20 @@ loadicon(const char *file, int size) height = imlib_image_get_height(); imgsize = MIN(width, height); - icon = imlib_create_cropped_scaled_image(0, 0, imgsize, imgsize, size, size); + icon = imlib_create_cropped_scaled_image(0, 0, imgsize, imgsize, + config.iconsize, + config.iconsize); return icon; } -/* setup the size of a menu and the position of its items */ +/* setup the height, width and icon of the items of a menu */ static void -setupmenusize(struct Menu *menu) +setupitems(struct Menu *menu) { XGlyphInfo ext; struct Item *item; - int labelwidth; + int itemwidth; menu->w = config.width_pixels; for (item = menu->list; item != NULL; item = item->next) { @@ -484,13 +529,46 @@ setupmenusize(struct Menu *menu) XftTextExtentsUtf8(dpy, dc.font, (XftChar8 *)item->label, item->labellen, &ext); - /* set menu width */ - labelwidth = ext.xOff + item->h * 2; - menu->w = MAX(menu->w, labelwidth); + /* + * set menu width + * + * the item width depends on the size of its label (ext.xOff), + * and it is only used to calculate the width of the menu (which + * is equal to the width of the largest item). + * + * the horizontal padding appears 4 times through the width of a + * item: before and after its icon, and before and after its triangle + * if the iflag is set (icons are disabled) then the horizontal + * padding appears before the label and around the triangle. + */ + itemwidth = ext.xOff + config.triangle_width + config.horzpadding * 3; + itemwidth += (iflag) ? 0 : config.iconsize + config.horzpadding; + menu->w = MAX(menu->w, itemwidth); /* create icon */ - if (item->file != NULL) - item->icon = loadicon(item->file, item->h - config.iconpadding * 2); + if (item->file != NULL && !iflag) { + item->icon = loadicon(item->file); + + item->sel = XCreatePixmap(dpy, menu->win, + config.iconsize, config.iconsize, + DefaultDepth(dpy, screen)); + XSetForeground(dpy, dc.gc, dc.selected[ColorBG].pixel); + XFillRectangle(dpy, item->sel, dc.gc, 0, 0, + config.iconsize, config.iconsize); + imlib_context_set_drawable(item->sel); + imlib_context_set_image(item->icon); + imlib_render_image_on_drawable(0, 0); + + item->unsel = XCreatePixmap(dpy, menu->win, + config.iconsize, config.iconsize, + DefaultDepth(dpy, screen)); + XSetForeground(dpy, dc.gc, dc.normal[ColorBG].pixel); + XFillRectangle(dpy, item->unsel, dc.gc, 0, 0, + config.iconsize, config.iconsize); + imlib_context_set_drawable(item->unsel); + imlib_context_set_image(item->icon); + imlib_render_image_on_drawable(0, 0); + } } } @@ -503,13 +581,13 @@ setupmenupos(struct Menu *menu) 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 (config.screenw - config.cursx >= menu->w) - menu->x = config.cursx; - else if (config.cursx > width) - menu->x = config.cursx - width; + if (pflag || config.screenw - config.posx >= menu->w) + menu->x = config.posx; + else if (config.posx > width) + menu->x = config.posx - width; - if (config.screenh - config.cursy >= height) - menu->y = config.cursy; + if (pflag || config.screenh - config.posy >= height) + menu->y = config.posy; else if (config.screenh > height) menu->y = config.screenh - height; } else { /* else, calculate in respect to parent menu */ @@ -538,7 +616,7 @@ setupmenu(struct Menu *menu, XClassHint *classh) XTextProperty wintitle; /* setup size and position of menus */ - setupmenusize(menu); + setupitems(menu); setupmenupos(menu); /* update menu geometry */ @@ -568,10 +646,10 @@ setupmenu(struct Menu *menu, XClassHint *classh) DefaultDepth(dpy, screen)); menu->draw = XftDrawCreate(dpy, menu->pixmap, visual, colormap); - /* set ewmh window properties */ + /* set WM protocols and ewmh window properties */ + XSetWMProtocols(dpy, menu->win, &wmdelete, 1); XChangeProperty(dpy, menu->win, netatom[NetWMName], utf8string, 8, - PropModeReplace, - (unsigned char *)title, strlen(title)); + PropModeReplace, (unsigned char *)title, strlen(title)); XChangeProperty(dpy, menu->win, netatom[NetWMWindowType], XA_ATOM, 32, PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypePopupMenu], 1); @@ -684,7 +762,8 @@ drawseparator(struct Menu *menu, struct Item *item) y = item->y + item->h/2; XSetForeground(dpy, dc.gc, dc.separator.pixel); - XDrawLine(dpy, menu->pixmap, dc.gc, 0, y, menu->w, y); + XDrawLine(dpy, menu->pixmap, dc.gc, config.horzpadding, y, + menu->w - config.horzpadding, y); } /* draw regular item */ @@ -693,15 +772,16 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color) { int x, y; - x = item->h; + x = config.horzpadding; + x += (iflag) ? 0 : config.horzpadding + config.iconsize; y = item->y + (item->h + dc.font->ascent) / 2; XSetForeground(dpy, dc.gc, color[ColorFG].pixel); XftDrawStringUtf8(menu->draw, &color[ColorFG], dc.font, - x, y, (XftChar8 *)item->label, item->labellen); + x, y, (XftChar8 *)item->label, item->labellen); /* draw triangle, if item contains a submenu */ if (item->submenu != NULL) { - x = menu->w - (item->h + config.triangle_width + 1) / 2; + x = menu->w - config.triangle_width - config.horzpadding; y = item->y + (item->h - config.triangle_height + 1) / 2; XPoint triangle[] = { @@ -716,12 +796,15 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color) } /* draw icon */ - if (item->file != NULL) { - x = config.iconpadding; + if (item->icon != NULL) { + x = config.horzpadding; y = item->y + config.iconpadding; - imlib_context_set_drawable(menu->pixmap); - imlib_context_set_image(item->icon); - imlib_render_image_on_drawable(x, y); + if (color == dc.selected) + XCopyArea(dpy, item->sel, menu->pixmap, dc.gc, 0, 0, + menu->w, menu->h, x, y); + else + XCopyArea(dpy, item->unsel, menu->pixmap, dc.gc, 0, 0, + menu->w, menu->h, x, y); } } @@ -996,6 +1079,6 @@ cleanup(void) static void usage(void) { - (void)fprintf(stderr, "usage: xmenu [-w] [title]\n"); + (void)fprintf(stderr, "usage: xmenu [-iw] [-p position] [title]\n"); exit(1); }