Include Xinerama header dep in README
[xmenu] / xmenu.c
diff --git a/xmenu.c b/xmenu.c
index 51412b3..88d834f 100644 (file)
--- a/xmenu.c
+++ b/xmenu.c
@@ -23,6 +23,8 @@ static int screen;
 static Visual *visual;
 static Window rootwin;
 static Colormap colormap;
 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 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 */
 
 /* 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 */
 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)
 {
 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);
 }
 
        exit(1);
 }
 
@@ -86,6 +89,85 @@ error:
        errx(1, "improper position: %s", optarg);
 }
 
        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)
 /* 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)
 /* init draw context */
 static void
 initdc(void)
@@ -582,29 +615,26 @@ static void
 setupitems(struct Menu *menu)
 {
        struct Item *item;
 setupitems(struct Menu *menu)
 {
        struct Item *item;
+       int itemwidth;
 
        menu->w = config.width_pixels;
 
        menu->w = config.width_pixels;
+       menu->maxtextw = 0;
        for (item = menu->list; item != NULL; item = item->next) {
        for (item = menu->list; item != NULL; item = item->next) {
-               int itemwidth;
-               int textwidth;
-
                item->y = menu->h;
                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 == NULL)   /* height for separator item */
                        item->h = config.separator_pixels;
                else
                        item->h = config.height_pixels;
                menu->h += item->h;
-
                if (item->label)
                if (item->label)
-                       textwidth = drawtext(NULL, NULL, 0, 0, 0, item->label);
+                       item->textw = drawtext(NULL, NULL, 0, 0, 0, item->label);
                else
                else
-                       textwidth = 0;
+                       item->textw = 0;
 
                /*
                 * set menu width
                 *
 
                /*
                 * 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).
                 *
                 * 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.
                 */
                 * 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);
                itemwidth += (iflag || !menu->hasicon) ? 0 : config.iconsize + config.horzpadding;
                menu->w = MAX(menu->w, itemwidth);
+               menu->maxtextw = MAX(menu->maxtextw, item->textw);
        }
 }
 
        }
 }
 
@@ -628,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 */
        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;
 
                        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;
                        menu->y = config.posy;
                else if (mon.y + mon.h > height)
                        menu->y = mon.y + mon.h - height;
@@ -738,6 +769,14 @@ grabkeyboard(void)
        errx(1, "could not 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 */
 static Imlib_Image
 loadicon(const char *file)
 /* 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)
 {
 static void
 drawitems(struct Menu *menu)
 {
+       XftDraw *dsel, *dunsel;
        struct Item *item;
        struct Item *item;
+       int textx;
+       int x, y;
 
        for (item = menu->list; item != NULL; item = item->next) {
 
        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->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 = 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 */
                        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);
                        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);
                        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);
 
                        XftDrawDestroy(dsel);
                        XftDrawDestroy(dunsel);
 
@@ -1055,6 +1103,17 @@ itemcycle(struct Menu *currmenu, int direction)
        return item;
 }
 
        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)
 /* run event loop */
 static void
 run(struct Menu *currmenu)
@@ -1091,6 +1150,8 @@ run(struct Menu *currmenu)
                        drawmenus(currmenu);
                        break;
                case ButtonRelease:
                        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)
                        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);
 }
 
        free(menu);
 }
 
-/* cleanup X and exit */
+/* cleanup draw context */
 static void
 static void
-cleanup(void)
+cleandc(void)
 {
        size_t i;
 
 {
        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);
        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]);
        for (i = 0; i < dc.nfonts; i++)
                XftFontClose(dpy, dc.fonts[i]);
-
        XFreeGC(dpy, dc.gc);
        XFreeGC(dpy, dc.gc);
-       XCloseDisplay(dpy);
 }
 
 /* xmenu: generate menu from stdin and print selected entry to stdout */
 }
 
 /* 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;
 {
        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)
 
        /* 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);
        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) {
 
        /* imlib2 stuff */
        if (!iflag) {
@@ -1282,18 +1321,10 @@ main(int argc, char *argv[])
 
        /* initializers */
        initmonitor();
 
        /* initializers */
        initmonitor();
-       initresources();
        initdc();
        initiconsize();
        initatoms();
 
        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)
        /* 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);
 
        /* run event loop */
        run(rootmenu);
 
-       /* freeing stuff */
+       /* clean stuff */
+       ungrab();
        cleanmenu(rootmenu);
        cleanmenu(rootmenu);
-       cleanup();
+       cleandc();
+       XrmDestroyDatabase(xdb);
+       XCloseDisplay(dpy);
 
        return 0;
 }
 
        return 0;
 }