Refactored code, changed resources and config.h
authorphillbush <phillbush@cock.li>
Tue, 26 May 2020 21:43:45 +0000 (18:43 -0300)
committerphillbush <phillbush@cock.li>
Tue, 26 May 2020 21:43:45 +0000 (18:43 -0300)
config.h
xmenu.1
xmenu.c

index 7bd94bd..a3e4f95 100644 (file)
--- a/config.h
+++ b/config.h
@@ -1,11 +1,20 @@
-static const char *font = "monospace:size=9";
-static const char *background = "#FFFFFF";
-static const char *foreground = "#2E3436";
-static const char *selbackground = "#3584E4";
-static const char *selforeground = "#FFFFFF";
-static const char *separator = "#CDC7C2";
-static const char *border = "#E6E6E6";
-static int width = 130;
-static int itemborder = 4;
-static int menuborder = 1;
-static int separatorsize = 1;
+/* font */
+static const char *font = "monospace:size=9";    /* for regular items */
+
+/* colors */
+static const char *background_color = "#FFFFFF";
+static const char *foreground_color = "#2E3436";
+static const char *selbackground_color = "#3584E4";
+static const char *selforeground_color = "#FFFFFF";
+static const char *separator_color = "#CDC7C2";
+static const char *border_color = "#E6E6E6";
+
+/* sizes in pixels */
+static int width_pixels = 130;      /* minimum width of a menu */
+static int padding_pixels = 4;      /* padding around label in a item */
+static int border_pixels = 1;       /* menu border */
+static int separator_pixels = 3;    /* space around separator */
+
+/* geometry of the right-pointing isoceles triangle for submenus */
+static const int triangle_width = 3;
+static const int triangle_height = 7;
diff --git a/xmenu.1 b/xmenu.1
index 7764afd..eac4699 100644 (file)
--- a/xmenu.1
+++ b/xmenu.1
@@ -94,13 +94,13 @@ The color of the separator between itens in the menu.
 .B xmenu.width
 The minimum width, in pixels, of the items in the menu.
 .TP
 .B xmenu.width
 The minimum width, in pixels, of the items in the menu.
 .TP
-.B xmenu.itemborder
-The size in pixels of the border around the label text in items in the menu.
+.B xmenu.padding
+The size in pixels of the padding around the label text in items in the menu.
 .TP
 .TP
-.B xmenu.menuborder
+.B xmenu.borderWidth
 The size in pixels of the border around the menu.
 .TP
 The size in pixels of the border around the menu.
 .TP
-.B xmenu.separatorsize
+.B xmenu.separatorWidth
 The size in pixels of the item separator.
 
 .SH EXAMPLES
 The size in pixels of the item separator.
 
 .SH EXAMPLES
diff --git a/xmenu.c b/xmenu.c
index 03c184e..dbfe76d 100644 (file)
--- a/xmenu.c
+++ b/xmenu.c
@@ -25,7 +25,8 @@ enum {ColorFG, ColorBG, ColorLast};
 struct DC {
        XftColor normal[ColorLast];
        XftColor selected[ColorLast];
 struct DC {
        XftColor normal[ColorLast];
        XftColor selected[ColorLast];
-       XftColor decoration[ColorLast];
+       XftColor border;
+       XftColor separator;
 
        Drawable d;
        GC gc;
 
        Drawable d;
        GC gc;
@@ -33,7 +34,7 @@ struct DC {
 };
 
 /* menu geometry structure */
 };
 
 /* menu geometry structure */
-struct Geometry {
+struct MenuGeometry {
        int itemb;      /* item border */
        int itemw;      /* item width */
        int itemh;      /* item height */
        int itemb;      /* item border */
        int itemw;      /* item width */
        int itemh;      /* item height */
@@ -80,6 +81,8 @@ static void setupgeom(void);
 static struct Item *allocitem(const char *label, const char *output);
 static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level);
 static void getmenuitem(Window win, int y, struct Menu **menu_ret, struct Item **item_ret);
 static struct Item *allocitem(const char *label, const char *output);
 static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level);
 static void getmenuitem(Window win, int y, struct Menu **menu_ret, struct Item **item_ret);
+static void drawseparator(struct Menu *menu, struct Item *item);
+static void drawitem(struct Menu *menu, struct Item *item, XftColor *color);
 static void drawmenu(void);
 static void calcscreengeom(void);
 static void calcmenu(struct Menu *menu);
 static void drawmenu(void);
 static void calcscreengeom(void);
 static void calcmenu(struct Menu *menu);
@@ -99,16 +102,14 @@ static Visual *visual;
 static Window rootwin;
 static int screen;
 static struct DC dc;
 static Window rootwin;
 static int screen;
 static struct DC dc;
+static struct ScreenGeometry screengeom;
 
 /* menu variables */
 static struct Menu *rootmenu = NULL;
 static struct Menu *currmenu = NULL;
 static char **menutitle;
 static int menutitlecount;
 
 /* menu variables */
 static struct Menu *rootmenu = NULL;
 static struct Menu *currmenu = NULL;
 static char **menutitle;
 static int menutitlecount;
-
-/* geometry variables */
-static struct Geometry geom;
-static struct ScreenGeometry screengeom;
+static struct MenuGeometry geom;
 
 #include "config.h"
 
 
 #include "config.h"
 
@@ -154,10 +155,6 @@ main(int argc, char *argv[])
        grabpointer();
        grabkeyboard();
 
        grabpointer();
        grabkeyboard();
 
-       /* map root menu */
-       setcurrmenu(rootmenu);
-       XMapWindow(dpy, rootmenu->win);
-
        /* run event loop */
        run();
 
        /* run event loop */
        run();
 
@@ -171,44 +168,44 @@ getresources(void)
 {
        char *xrm;
        long n;
 {
        char *xrm;
        long n;
+       char *type;
+       XrmDatabase xdb;
+       XrmValue xval;
 
        XrmInitialize();
 
        XrmInitialize();
-       if ((xrm = XResourceManagerString(dpy))) {
-               char *type;
-               XrmDatabase xdb;
-               XrmValue xval;
-
-               xdb = XrmGetStringDatabase(xrm);
-
-               if (XrmGetResource(xdb, "xmenu.menuborder", "*", &type, &xval) == True)
-                       if ((n = strtol(xval.addr, NULL, 10)) > 0)
-                               menuborder = n;
-               if (XrmGetResource(xdb, "xmenu.separatorsize", "*", &type, &xval) == True)
-                       if ((n = strtol(xval.addr, NULL, 10)) > 0)
-                               separatorsize = n;
-               if (XrmGetResource(xdb, "xmenu.itemborder", "*", &type, &xval) == True)
-                       if ((n = strtol(xval.addr, NULL, 10)) > 0)
-                               itemborder = n;
-               if (XrmGetResource(xdb, "xmenu.width", "*", &type, &xval) == True)
-                       if ((n = strtol(xval.addr, NULL, 10)) > 0)
-                               width = n;
-               if (XrmGetResource(xdb, "xmenu.background", "*", &type, &xval) == True)
-                       background = strdup(xval.addr);
-               if (XrmGetResource(xdb, "xmenu.foreground", "*", &type, &xval) == True)
-                       foreground = strdup(xval.addr);
-               if (XrmGetResource(xdb, "xmenu.selbackground", "*", &type, &xval) == True)
-                       selbackground = strdup(xval.addr);
-               if (XrmGetResource(xdb, "xmenu.selforeground", "*", &type, &xval) == True)
-                       selforeground = strdup(xval.addr);
-               if (XrmGetResource(xdb, "xmenu.separator", "*", &type, &xval) == True)
-                       separator = strdup(xval.addr);
-               if (XrmGetResource(xdb, "xmenu.border", "*", &type, &xval) == True)
-                       border = strdup(xval.addr);
-               if (XrmGetResource(xdb, "xmenu.font", "*", &type, &xval) == True)
-                       font = strdup(xval.addr);
-
-               XrmDestroyDatabase(xdb);
-       }
+       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)
+                       border_pixels = n;
+       if (XrmGetResource(xdb, "xmenu.separatorWidth", "*", &type, &xval) == True)
+               if ((n = strtol(xval.addr, NULL, 10)) > 0)
+                       separator_pixels = n;
+       if (XrmGetResource(xdb, "xmenu.padding", "*", &type, &xval) == True)
+               if ((n = strtol(xval.addr, NULL, 10)) > 0)
+                       padding_pixels = n;
+       if (XrmGetResource(xdb, "xmenu.width", "*", &type, &xval) == True)
+               if ((n = strtol(xval.addr, NULL, 10)) > 0)
+                       width_pixels = n;
+       if (XrmGetResource(xdb, "xmenu.background", "*", &type, &xval) == True)
+               background_color = strdup(xval.addr);
+       if (XrmGetResource(xdb, "xmenu.foreground", "*", &type, &xval) == True)
+               foreground_color = strdup(xval.addr);
+       if (XrmGetResource(xdb, "xmenu.selbackground", "*", &type, &xval) == True)
+               selbackground_color = strdup(xval.addr);
+       if (XrmGetResource(xdb, "xmenu.selforeground", "*", &type, &xval) == True)
+               selforeground_color = strdup(xval.addr);
+       if (XrmGetResource(xdb, "xmenu.separator", "*", &type, &xval) == True)
+               separator_color = strdup(xval.addr);
+       if (XrmGetResource(xdb, "xmenu.border", "*", &type, &xval) == True)
+               border_color = strdup(xval.addr);
+       if (XrmGetResource(xdb, "xmenu.font", "*", &type, &xval) == True)
+               font = strdup(xval.addr);
+
+       XrmDestroyDatabase(xdb);
 }
 
 /* get color from color string */
 }
 
 /* get color from color string */
@@ -224,18 +221,18 @@ static void
 setupdc(void)
 {
        /* get color pixels */
 setupdc(void)
 {
        /* get color pixels */
-       getcolor(background,    &dc.normal[ColorBG]);
-       getcolor(foreground,    &dc.normal[ColorFG]);
-       getcolor(selbackground, &dc.selected[ColorBG]);
-       getcolor(selforeground, &dc.selected[ColorFG]);
-       getcolor(separator,     &dc.decoration[ColorBG]);
-       getcolor(border,        &dc.decoration[ColorFG]);
+       getcolor(background_color,    &dc.normal[ColorBG]);
+       getcolor(foreground_color,    &dc.normal[ColorFG]);
+       getcolor(selbackground_color, &dc.selected[ColorBG]);
+       getcolor(selforeground_color, &dc.selected[ColorFG]);
+       getcolor(separator_color,     &dc.separator);
+       getcolor(border_color,        &dc.border);
 
        /* try to get font */
        if ((dc.font = XftFontOpenName(dpy, screen, font)) == NULL)
                errx(1, "cannot load font");
 
 
        /* try to get font */
        if ((dc.font = XftFontOpenName(dpy, screen, font)) == NULL)
                errx(1, "cannot load font");
 
-       /* create GC */
+       /* create common GC */
        dc.gc = XCreateGC(dpy, rootwin, 0, NULL);
 }
 
        dc.gc = XCreateGC(dpy, rootwin, 0, NULL);
 }
 
@@ -243,11 +240,11 @@ setupdc(void)
 static void
 setupgeom(void)
 {
 static void
 setupgeom(void)
 {
-       geom.itemb = itemborder;
-       geom.itemh = dc.font->height + itemborder * 2;
-       geom.itemw = width;
-       geom.border = menuborder;
-       geom.separator = separatorsize;
+       geom.itemb = padding_pixels;
+       geom.itemh = dc.font->height + padding_pixels * 2;
+       geom.itemw = width_pixels;
+       geom.border = border_pixels;
+       geom.separator = separator_pixels;
 }
 
 /* allocate an item */
 }
 
 /* allocate an item */
@@ -299,13 +296,15 @@ allocmenu(struct Menu *parent, struct Item *list, unsigned level)
        menu->level = level;
 
        swa.override_redirect = True;
        menu->level = level;
 
        swa.override_redirect = True;
-       swa.background_pixel = dc.decoration[ColorBG].pixel;
-       swa.border_pixel = dc.decoration[ColorFG].pixel;
+       swa.background_pixel = dc.normal[ColorBG].pixel;
+       swa.border_pixel = dc.border.pixel;
+       swa.save_under = True;  /* pop-up windows should save_under*/
        swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask
                       | PointerMotionMask | LeaveWindowMask;
        menu->win = XCreateWindow(dpy, rootwin, 0, 0, geom.itemw, geom.itemh, geom.border,
                                  CopyFromParent, CopyFromParent, CopyFromParent,
        swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask
                       | PointerMotionMask | LeaveWindowMask;
        menu->win = XCreateWindow(dpy, rootwin, 0, 0, geom.itemw, geom.itemh, geom.border,
                                  CopyFromParent, CopyFromParent, CopyFromParent,
-                                 CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask,
+                                 CWOverrideRedirect | CWBackPixel |
+                                 CWBorderPixel | CWEventMask | CWSaveUnder,
                                  &swa);
 
        return menu;
                                  &swa);
 
        return menu;
@@ -573,6 +572,7 @@ setcurrmenu(struct Menu *currmenu_new)
        /* if there was no currmenu, skip calculations */
        if (currmenu == NULL) {
                currmenu = currmenu_new;
        /* if there was no currmenu, skip calculations */
        if (currmenu == NULL) {
                currmenu = currmenu_new;
+               XMapWindow(dpy, currmenu->win);
                return;
        }
 
                return;
        }
 
@@ -607,9 +607,49 @@ setcurrmenu(struct Menu *currmenu_new)
        item = NULL;
        for (menu = currmenu; menu != lcamenu; menu = menu->parent) {
                XMapWindow(dpy, menu->win);
        item = NULL;
        for (menu = currmenu; menu != lcamenu; menu = menu->parent) {
                XMapWindow(dpy, menu->win);
-               if (item != NULL)
-                       menu->selected = item;
-               item = menu->caller;
+       }
+}
+
+/* draw separator item */
+static void
+drawseparator(struct Menu *menu, struct Item *item)
+{
+       int linex, liney, linew;
+
+       linex = dc.font->height;
+       liney = item->y + item->h/2;
+       linew = menu->w - dc.font->height;
+
+       XSetForeground(dpy, dc.gc, dc.separator.pixel);
+       XDrawLine(dpy, menu->pixmap, dc.gc, linex, liney, linew, liney);
+}
+
+/* draw regular item */
+static void
+drawitem(struct Menu *menu, struct Item *item, XftColor *color)
+{
+       int x, y;
+
+       x = 0 + dc.font->height;
+       y = item->y + dc.font->height + geom.itemb / 2;
+       XSetForeground(dpy, dc.gc, color[ColorFG].pixel);
+       XftDrawStringUtf8(menu->draw, &color[ColorFG], dc.font,
+                      x, y, item->label, item->labellen);
+
+       /* draw triangle, if item contains a submenu */
+       if (item->submenu != NULL) {
+               x = menu->w - dc.font->height + geom.itemb - 1;
+               y = item->y + geom.itemh/2 - triangle_height/2 - 1;
+
+               XPoint triangle[] = {
+                       {x, y},
+                       {x + triangle_width, y + triangle_height/2},
+                       {x, y + triangle_height},
+                       {x, y}
+               };
+
+               XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle),
+                            Convex, CoordModeOrigin);
        }
 }
 
        }
 }
 
@@ -623,52 +663,26 @@ drawmenu(void)
        for (menu = currmenu; menu != NULL; menu = menu->parent) {
                for (item = menu->list; item != NULL; item = item->next) {
                        XftColor *color;
        for (menu = currmenu; menu != NULL; menu = menu->parent) {
                for (item = menu->list; item != NULL; item = item->next) {
                        XftColor *color;
-                       int labelx, labely;
 
                        /* determine item color */
 
                        /* determine item color */
-                       if (item == menu->selected)
+                       if (item == menu->selected && item->label != NULL)
                                color = dc.selected;
                        else
                                color = dc.normal;
 
                                color = dc.selected;
                        else
                                color = dc.normal;
 
-                       /* continue if item is a separator */
-                       if (item->label == NULL)
-                               continue;
-
                        /* draw item box */
                        XSetForeground(dpy, dc.gc, color[ColorBG].pixel);
                        /* draw item box */
                        XSetForeground(dpy, dc.gc, color[ColorBG].pixel);
-                       XDrawRectangle(dpy, menu->pixmap, dc.gc, 0, item->y,
-                                      menu->w, item->h);
                        XFillRectangle(dpy, menu->pixmap, dc.gc, 0, item->y,
                                       menu->w, item->h);
 
                        XFillRectangle(dpy, menu->pixmap, dc.gc, 0, item->y,
                                       menu->w, item->h);
 
-                       /* draw item label */
-                       labelx = 0 + dc.font->height;
-                       labely = item->y + dc.font->height + geom.itemb / 2;
-                       XSetForeground(dpy, dc.gc, color[ColorFG].pixel);
-                       XftDrawStringUtf8(menu->draw, &color[ColorFG], dc.font,
-                                         labelx, labely, item->label,
-                                         item->labellen);
-
-                       /* draw triangle, if item contains a submenu */
-                       if (item->submenu != NULL) {
-                               int trianglex = menu->w - dc.font->height + geom.itemb - 1;
-                               int triangley = item->y + (3 * item->h)/8 -1;
-
-                               XPoint triangle[] = {
-                                       {trianglex, triangley},
-                                       {trianglex + item->h/8 + 1, item->y + item->h/2},
-                                       {trianglex, triangley + item->h/4 + 2},
-                                       {trianglex, triangley}
-                               };
-
-                               XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle),
-                                            Convex, CoordModeOrigin);
-                       }
-
-                       XCopyArea(dpy, menu->pixmap, menu->win, dc.gc, 0, item->y,
-                                 menu->w, item->h, 0, item->y);
+                       if (item->label == NULL)  /* item is a separator */
+                               drawseparator(menu, item);
+                       else                      /* item is a regular item */
+                               drawitem(menu, item, color);
                }
                }
+
+               XCopyArea(dpy, menu->pixmap, menu->win, dc.gc, 0, 0,
+                             menu->w, menu->h, 0, 0);
        }
 }
 
        }
 }
 
@@ -723,6 +737,8 @@ run(void)
        KeySym ksym;
        XEvent ev;
 
        KeySym ksym;
        XEvent ev;
 
+       setcurrmenu(rootmenu);
+
        while (!XNextEvent(dpy, &ev)) {
                switch(ev.type) {
                case Expose:
        while (!XNextEvent(dpy, &ev)) {
                switch(ev.type) {
                case Expose:
@@ -731,38 +747,34 @@ run(void)
                        break;
                case MotionNotify:
                        getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
                        break;
                case MotionNotify:
                        getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
-                       if (menu != NULL && item != NULL) {
-                               if (previtem != item) {
-                                       if (item->submenu != NULL)
-                                               setcurrmenu(item->submenu);
-                                       else
-                                               setcurrmenu(menu);
-                                       previtem = item;
-                                       drawmenu();
-                               } else if (menu->selected != item) {
-                                       menu->selected = item;
-                                       drawmenu();
-                               }
+                       if (menu == NULL || item == NULL)
+                               break;
+                       if (previtem != item) {
+                               menu->selected = item;
+                               if (item->submenu != NULL)
+                                       setcurrmenu(item->submenu);
+                               else
+                                       setcurrmenu(menu);
+                               previtem = item;
+                               drawmenu();
                        }
                        break;
                case ButtonRelease:
                        getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
                        }
                        break;
                case ButtonRelease:
                        getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
-                       if (menu != NULL && item != NULL) {
-selectitem:
-                               if (item->label == NULL)
-                                       break;  /* ignore separators */
-                               if (item->submenu != NULL) {
-                                       setcurrmenu(item->submenu);
-                               } else {
-                                       printf("%s\n", item->output);
-                                       return;
-                               }
-                               currmenu->selected = currmenu->list;
-                               drawmenu();
+                       if (menu == NULL || item == NULL)
                                break;
                                break;
+selectitem:
+                       if (item->label == NULL)
+                               break;  /* ignore separators */
+                       if (item->submenu != NULL) {
+                               setcurrmenu(item->submenu);
                        } else {
                        } else {
+                               printf("%s\n", item->output);
                                return;
                        }
                                return;
                        }
+                       currmenu->selected = currmenu->list;
+                       drawmenu();
+                       break;
                case ButtonPress:
                        getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
                        if (menu == NULL || item == NULL)
                case ButtonPress:
                        getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
                        if (menu == NULL || item == NULL)
@@ -798,6 +810,7 @@ selectitem:
                        drawmenu();
                        break;
                case LeaveNotify:
                        drawmenu();
                        break;
                case LeaveNotify:
+                       previtem = NULL;
                        currmenu->selected = NULL;
                        drawmenu();
                        break;
                        currmenu->selected = NULL;
                        drawmenu();
                        break;
@@ -805,7 +818,7 @@ selectitem:
        }
 }
 
        }
 }
 
-/* recursivelly free a pixmap */
+/* recursivelly free pixmaps and destroy windows */
 static void
 freewindow(struct Menu *menu)
 {
 static void
 freewindow(struct Menu *menu)
 {
@@ -830,8 +843,8 @@ cleanup(void)
        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.normal[ColorFG]);
        XftColorFree(dpy, visual, colormap, &dc.selected[ColorBG]);
        XftColorFree(dpy, visual, colormap, &dc.selected[ColorFG]);
-       XftColorFree(dpy, visual, colormap, &dc.decoration[ColorBG]);
-       XftColorFree(dpy, visual, colormap, &dc.decoration[ColorFG]);
+       XftColorFree(dpy, visual, colormap, &dc.separator);
+       XftColorFree(dpy, visual, colormap, &dc.border);
 
        XFreeGC(dpy, dc.gc);
        XCloseDisplay(dpy);
 
        XFreeGC(dpy, dc.gc);
        XCloseDisplay(dpy);