X-Git-Url: https://git.subgeniuskitty.com/xmenu/.git/blobdiff_plain/ef667106e9638b8be411e4665dfe24dc2bf2705a..50da9089cffefa09022c4199caa0f9eb36bcb434:/xmenu.c diff --git a/xmenu.c b/xmenu.c index 3eab38d..88d834f 100644 --- a/xmenu.c +++ b/xmenu.c @@ -23,6 +23,8 @@ 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; @@ -31,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 */ @@ -42,7 +45,7 @@ static int wflag = 0; /* whether to let the window manager control XMenu */ static void usage(void) { - (void)fprintf(stderr, "usage: xmenu [-iw] [-p position] [title]\n"); + (void)fprintf(stderr, "usage: xmenu [-irw] [-p position] [title]\n"); exit(1); } @@ -86,6 +89,85 @@ error: errx(1, "improper position: %s", optarg); } +/* 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) @@ -182,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) @@ -582,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). * @@ -613,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); } } @@ -738,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) @@ -810,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)); @@ -830,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); @@ -1055,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) @@ -1091,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) @@ -1209,27 +1270,21 @@ 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); } /* xmenu: generate menu from stdin and print selected entry to stdout */ @@ -1238,30 +1293,6 @@ main(int argc, char *argv[]) { 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) @@ -1270,6 +1301,14 @@ main(int argc, char *argv[]) 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) { @@ -1282,18 +1321,10 @@ main(int argc, char *argv[]) /* 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) @@ -1309,9 +1340,12 @@ main(int argc, char *argv[]) /* run event loop */ run(rootmenu); - /* freeing stuff */ + /* clean stuff */ + ungrab(); cleanmenu(rootmenu); - cleanup(); + cleandc(); + XrmDestroyDatabase(xdb); + XCloseDisplay(dpy); return 0; }