X-Git-Url: http://git.subgeniuskitty.com/xmenu/.git/blobdiff_plain/02511d092bbf97f19beac22fa5731ff29b818d73..6abae763c6cce70c9b753778b5e3742a7719715a:/xmenu.c diff --git a/xmenu.c b/xmenu.c index c29c31d..88d834f 100644 --- a/xmenu.c +++ b/xmenu.c @@ -17,78 +17,14 @@ #include #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; @@ -97,6 +33,7 @@ static Atom netatom[NetLast]; /* flags */ 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 */ @@ -104,93 +41,12 @@ 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, "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 [-irw] [-p position] [title]\n"); + exit(1); } /* parse position string from -p, @@ -233,7 +89,86 @@ error: 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) { @@ -329,55 +264,6 @@ initmonitor(void) } } -/* read xrdb for configuration options */ -static void -initresources(void) -{ - long n; - char *type; - char *xrm; - 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) @@ -729,29 +615,26 @@ static void 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). * @@ -760,9 +643,10 @@ setupitems(struct Menu *menu) * 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); } } @@ -775,12 +659,12 @@ 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 (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; @@ -885,6 +769,14 @@ grabkeyboard(void) errx(1, "could not grab keyboard"); } +/* ungrab pointer and keyboard */ +static void +ungrab(void) +{ + XUngrabPointer(dpy, CurrentTime); + XUngrabKeyboard(dpy, CurrentTime); +} + /* load and scale icon */ static Imlib_Image loadicon(const char *file) @@ -957,12 +849,12 @@ loadicon(const char *file) 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)); @@ -977,21 +869,30 @@ drawitems(struct Menu *menu) 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); @@ -1202,6 +1103,17 @@ itemcycle(struct Menu *currmenu, int direction) 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) @@ -1209,6 +1121,7 @@ run(struct Menu *currmenu) struct Menu *menu; struct Item *item; struct Item *previtem = NULL; + struct Item *lastitem; KeySym ksym; XEvent ev; @@ -1237,6 +1150,8 @@ run(struct Menu *currmenu) 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) @@ -1282,11 +1197,10 @@ selectitem: item = itemcycle(currmenu, ITEMNEXT); } else if (ksym >= XK_1 && ksym <= XK_9){ item = itemcycle(currmenu, ITEMFIRST); - for(int i = ksym-XK_1; i > 0; i-=1){ + lastitem = itemcycle(currmenu, ITEMLAST); + for (int i = ksym - XK_1; i > 0 && item != lastitem; i--) { currmenu->selected = item; item = itemcycle(currmenu, ITEMNEXT); - if (item == itemcycle(currmenu, ITEMLAST)) - break; } } else if ((ksym == XK_Return || ksym == XK_Right || ksym == KSYMRIGHT) && currmenu->selected != NULL) { @@ -1356,33 +1270,82 @@ cleanmenu(struct Menu *menu) 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 [-iw] [-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; }