Readding the -w option
authorphillbush <phillbush@cock.li>
Mon, 1 Jun 2020 01:26:33 +0000 (22:26 -0300)
committerphillbush <phillbush@cock.li>
Mon, 1 Jun 2020 01:26:33 +0000 (22:26 -0300)
xmenu.1
xmenu.c

diff --git a/xmenu.1 b/xmenu.1
index 5201032..1aa2434 100644 (file)
--- a/xmenu.1
+++ b/xmenu.1
@@ -3,6 +3,8 @@
 xmenu \- menu utility for X
 .SH SYNOPSIS
 .B xmenu
 xmenu \- menu utility for X
 .SH SYNOPSIS
 .B xmenu
+.RB [ \-w ]
+.RI [ title ]
 .SH DESCRIPTION
 .B xmenu
 is a menu for X,
 .SH DESCRIPTION
 .B xmenu
 is a menu for X,
@@ -10,6 +12,12 @@ it reads a list of newline-separated items from stdin,
 shows a menu for the user to select one of the items,
 and outputs the item selected to stdout.
 .PP
 shows a menu for the user to select one of the items,
 and outputs the item selected to stdout.
 .PP
+The options are as follows:
+.TP
+.B -w
+Asks the window manager to draw a border around the menus.
+This option may be buggy in some window managers, specially tiled ones.
+.PP
 Each item read from stdin has the following format:
 .IP
 .EX
 Each item read from stdin has the following format:
 .IP
 .EX
@@ -36,6 +44,10 @@ The output is the string that will be output after selecting the item.
 If an item does not have an output, its label is used as its output.
 .IP
 The newline terminates the item specification.
 If an item does not have an output, its label is used as its output.
 .IP
 The newline terminates the item specification.
+.PP
+If the argument
+.I title
+is given, the title of the menu window is set to it.
 .SH USAGE
 .B xmenu
 is controlled by the mouse,
 .SH USAGE
 .B xmenu
 is controlled by the mouse,
diff --git a/xmenu.c b/xmenu.c
index 6f75c2c..2096e70 100644 (file)
--- a/xmenu.c
+++ b/xmenu.c
@@ -47,14 +47,14 @@ struct Geometry {
 struct Item {
        char *label;            /* string to be drawed on menu */
        char *output;           /* string to be outputed when item is clicked */
 struct Item {
        char *label;            /* string to be drawed on menu */
        char *output;           /* string to be outputed when item is clicked */
-       char *file;             /* filename of the image */
+       char *file;             /* filename of the icon */
        int y;                  /* item y position relative to menu */
        int h;                  /* item height */
        size_t labellen;        /* strlen(label) */
        struct Item *prev;      /* previous item */
        struct Item *next;      /* next item */
        struct Menu *submenu;   /* submenu spawned by clicking on item */
        int y;                  /* item y position relative to menu */
        int h;                  /* item height */
        size_t labellen;        /* strlen(label) */
        struct Item *prev;      /* previous item */
        struct Item *next;      /* next item */
        struct Menu *submenu;   /* submenu spawned by clicking on item */
-       Imlib_Image image;
+       Imlib_Image icon;
 };
 
 /* menu structure */
 };
 
 /* menu structure */
@@ -81,7 +81,7 @@ static struct Menu *buildmenutree(unsigned level, const char *label, const char
 static struct Menu *parsestdin(void);
 static void setupmenusize(struct Geometry *geom, struct Menu *menu);
 static void setupmenupos(struct Geometry *geom, struct Menu *menu);
 static struct Menu *parsestdin(void);
 static void setupmenusize(struct Geometry *geom, struct Menu *menu);
 static void setupmenupos(struct Geometry *geom, struct Menu *menu);
-static void setupmenu(struct Geometry *geom, struct Menu *menu);
+static void setupmenu(struct Geometry *geom, struct Menu *menu, XClassHint *classh);
 static void grabpointer(void);
 static void grabkeyboard(void);
 static struct Menu *getmenu(struct Menu *currmenu, Window win);
 static void grabpointer(void);
 static void grabkeyboard(void);
 static struct Menu *getmenu(struct Menu *currmenu, Window win);
@@ -103,6 +103,10 @@ static Visual *visual;
 static Window rootwin;
 static Colormap colormap;
 static struct DC dc;
 static Window rootwin;
 static Colormap colormap;
 static struct DC dc;
+static Atom wmdelete;
+
+/* flags */
+static int wflag = 0;   /* whether to let the window manager control XMenu */
 
 #include "config.h"
 
 
 #include "config.h"
 
@@ -112,10 +116,14 @@ main(int argc, char *argv[])
 {
        struct Menu *rootmenu;
        struct Geometry geom;
 {
        struct Menu *rootmenu;
        struct Geometry geom;
+       XClassHint classh;
        int ch;
 
        int ch;
 
-       while ((ch = getopt(argc, argv, "")) != -1) {
+       while ((ch = getopt(argc, argv, "w")) != -1) {
                switch (ch) {
                switch (ch) {
+               case 'w':
+                       wflag = 1;
+                       break;
                default:
                        usage();
                        break;
                default:
                        usage();
                        break;
@@ -124,7 +132,7 @@ main(int argc, char *argv[])
        argc -= optind;
        argv += optind;
 
        argc -= optind;
        argv += optind;
 
-       if (argc != 0)
+       if (argc > 1)
                usage();
 
        /* open connection to server and set X variables */
                usage();
 
        /* open connection to server and set X variables */
@@ -134,6 +142,7 @@ 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);
+       wmdelete=XInternAtom(dpy, "WM_DELETE_WINDOW", True);
 
        /* imlib2 stuff */
        imlib_set_cache_size(2048 * 1024);
 
        /* imlib2 stuff */
        imlib_set_cache_size(2048 * 1024);
@@ -147,15 +156,24 @@ main(int argc, char *argv[])
        setupdc();
        calcgeom(&geom);
 
        setupdc();
        calcgeom(&geom);
 
+       /* 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");
        /* generate menus and set them up */
        rootmenu = parsestdin();
        if (rootmenu == NULL)
                errx(1, "no menu generated");
-       setupmenu(&geom, rootmenu);
+       setupmenu(&geom, rootmenu, &classh);
 
        /* grab mouse and keyboard */
 
        /* grab mouse and keyboard */
-       grabpointer();
-       grabkeyboard();
+       if (!wflag) {
+               grabpointer();
+               grabkeyboard();
+       }
 
        /* run event loop */
        run(rootmenu);
 
        /* run event loop */
        run(rootmenu);
@@ -293,7 +311,7 @@ allocitem(const char *label, const char *output, char *file)
                item->labellen = strlen(item->label);
        item->next = NULL;
        item->submenu = NULL;
                item->labellen = strlen(item->label);
        item->next = NULL;
        item->submenu = NULL;
-       item->image = NULL;
+       item->icon = NULL;
 
        return item;
 }
 
        return item;
 }
@@ -317,18 +335,22 @@ allocmenu(struct Menu *parent, struct Item *list, unsigned level)
        menu->y = 0;    /* calculated by setupmenu() */
        menu->level = level;
 
        menu->y = 0;    /* calculated by setupmenu() */
        menu->level = level;
 
-       swa.override_redirect = True;
+       swa.override_redirect = (wflag) ? False : True;
        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;
        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;
+       if (wflag)
+               swa.event_mask |= StructureNotifyMask;
        menu->win = XCreateWindow(dpy, rootwin, 0, 0, 1, 1, 0,
                                  CopyFromParent, CopyFromParent, CopyFromParent,
                                  CWOverrideRedirect | CWBackPixel |
                                  CWBorderPixel | CWEventMask | CWSaveUnder,
                                  &swa);
 
        menu->win = XCreateWindow(dpy, rootwin, 0, 0, 1, 1, 0,
                                  CopyFromParent, CopyFromParent, CopyFromParent,
                                  CWOverrideRedirect | CWBackPixel |
                                  CWBorderPixel | CWEventMask | CWSaveUnder,
                                  &swa);
 
+       XSetWMProtocols(dpy, menu->win, &wmdelete, 1);
+
        return menu;
 }
 
        return menu;
 }
 
@@ -432,28 +454,28 @@ parsestdin(void)
        return rootmenu;
 }
 
        return rootmenu;
 }
 
-/* load and scale image */
+/* load and scale icon */
 static Imlib_Image
 static Imlib_Image
-loadimage(const char *file, int size)
+loadicon(const char *file, int size)
 {
 {
-       Imlib_Image image;
+       Imlib_Image icon;
        int width;
        int height;
        int imgsize;
 
        int width;
        int height;
        int imgsize;
 
-       image = imlib_load_image(file);
-       if (image == NULL)
-               errx(1, "cannot load image %s", file);
+       icon = imlib_load_image(file);
+       if (icon == NULL)
+               errx(1, "cannot load icon %s", file);
 
 
-       imlib_context_set_image(image);
+       imlib_context_set_image(icon);
 
        width = imlib_image_get_width();
        height = imlib_image_get_height();
        imgsize = MIN(width, height);
 
 
        width = imlib_image_get_width();
        height = imlib_image_get_height();
        imgsize = MIN(width, height);
 
-       image = imlib_create_cropped_scaled_image(0, 0, imgsize, imgsize, size, size);
+       icon = imlib_create_cropped_scaled_image(0, 0, imgsize, imgsize, size, size);
 
 
-       return image;
+       return icon;
 }
 
 /* setup the size of a menu and the position of its items */
 }
 
 /* setup the size of a menu and the position of its items */
@@ -480,18 +502,26 @@ setupmenusize(struct Geometry *geom, struct Menu *menu)
                labelwidth = ext.xOff + dc.font->height * 2 + IMGPADDING * 2;
                menu->w = MAX(menu->w, labelwidth);
 
                labelwidth = ext.xOff + dc.font->height * 2 + IMGPADDING * 2;
                menu->w = MAX(menu->w, labelwidth);
 
-               /* create image */
+               /* create icon */
                if (item->file != NULL)
                if (item->file != NULL)
-                       item->image = loadimage(item->file, dc.font->height);
+                       item->icon = loadicon(item->file, dc.font->height);
        }
 }
 
 /* setup the position of a menu */
 static void
        }
 }
 
 /* setup the position of a menu */
 static void
-setupmenupos(struct Geometry *geom, struct Menu *menu)
+setupmenupos(struct Geometry *g, struct Menu *menu)
 {
 {
+       static struct Geometry *geom = NULL;
        int width, height;
 
        int width, height;
 
+       /*
+        * Save the geometry so functions can call setupmenupos() without
+        * having to know the geometry.
+        */
+       if (g != NULL)
+               geom = g;
+
        width = menu->w + geom->border * 2;
        height = menu->h + geom->border * 2;
        if (menu->parent == NULL) { /* if root menu, calculate in respect to cursor */
        width = menu->w + geom->border * 2;
        height = menu->h + geom->border * 2;
        if (menu->parent == NULL) { /* if root menu, calculate in respect to cursor */
@@ -521,12 +551,12 @@ setupmenupos(struct Geometry *geom, struct Menu *menu)
 
 /* recursivelly setup menu configuration and its pixmap */
 static void
 
 /* recursivelly setup menu configuration and its pixmap */
 static void
-setupmenu(struct Geometry *geom, struct Menu *menu)
+setupmenu(struct Geometry *geom, struct Menu *menu, XClassHint *classh)
 {
        struct Item *item;
 {
        struct Item *item;
-       static XClassHint classh = {PROGNAME, PROGNAME};
        XWindowChanges changes;
        XSizeHints sizeh;
        XWindowChanges changes;
        XSizeHints sizeh;
+       XTextProperty wintitle;
 
        /* setup size and position of menus */
        setupmenusize(geom, menu);
 
        /* setup size and position of menus */
        setupmenusize(geom, menu);
@@ -540,12 +570,18 @@ setupmenu(struct Geometry *geom, struct Menu *menu)
        changes.y = menu->y;
        XConfigureWindow(dpy, menu->win, CWBorderWidth | CWWidth | CWHeight | CWX | CWY, &changes);
 
        changes.y = menu->y;
        XConfigureWindow(dpy, menu->win, CWBorderWidth | CWWidth | CWHeight | CWX | CWY, &changes);
 
+       /* set window title (used if wflag is on) */
+       if (menu->parent == NULL) {
+               XStringListToTextProperty(&classh->res_name, 1, &wintitle);
+       } else {
+               XStringListToTextProperty(&menu->caller->output, 1, &wintitle);
+       }
+
        /* set window manager hints */
        sizeh.flags = PMaxSize | PMinSize;
        sizeh.min_width = sizeh.max_width = menu->w;
        sizeh.min_height = sizeh.max_height = menu->h;
        /* set window manager hints */
        sizeh.flags = PMaxSize | PMinSize;
        sizeh.min_width = sizeh.max_width = menu->w;
        sizeh.min_height = sizeh.max_height = menu->h;
-       XSetWMProperties(dpy, menu->win, NULL, NULL, NULL, 0, &sizeh,
-                        NULL, &classh);
+       XSetWMProperties(dpy, menu->win, &wintitle, NULL, NULL, 0, &sizeh, NULL, classh);
 
        /* create pixmap and XftDraw */
        menu->pixmap = XCreatePixmap(dpy, menu->win, menu->w, menu->h,
 
        /* create pixmap and XftDraw */
        menu->pixmap = XCreatePixmap(dpy, menu->win, menu->w, menu->h,
@@ -555,7 +591,7 @@ setupmenu(struct Geometry *geom, struct Menu *menu)
        /* calculate positions of submenus */
        for (item = menu->list; item != NULL; item = item->next) {
                if (item->submenu != NULL)
        /* calculate positions of submenus */
        for (item = menu->list; item != NULL; item = item->next) {
                if (item->submenu != NULL)
-                       setupmenu(geom, item->submenu);
+                       setupmenu(geom, item->submenu, classh);
        }
 }
 
        }
 }
 
@@ -668,6 +704,12 @@ mapmenu(struct Menu *currmenu)
 
        /* map menus from currmenu (inclusive) until lcamenu (exclusive) */
        for (menu = currmenu; menu != lcamenu; menu = menu->parent) {
 
        /* map menus from currmenu (inclusive) until lcamenu (exclusive) */
        for (menu = currmenu; menu != lcamenu; menu = menu->parent) {
+
+               if (wflag) {
+                       setupmenupos(NULL, menu);
+                       XMoveWindow(dpy, menu->win, menu->x, menu->y);
+               }
+
                XMapWindow(dpy, menu->win);
        }
 
                XMapWindow(dpy, menu->win);
        }
 
@@ -714,12 +756,12 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
                             Convex, CoordModeOrigin);
        }
 
                             Convex, CoordModeOrigin);
        }
 
-       /* draw image */
+       /* draw icon */
        if (item->file != NULL) {
                x = IMGPADDING / 2;
                y = item->y + (item->h - dc.font->height) / 2;
                imlib_context_set_drawable(menu->pixmap);
        if (item->file != NULL) {
                x = IMGPADDING / 2;
                y = item->y + (item->h - dc.font->height) / 2;
                imlib_context_set_drawable(menu->pixmap);
-               imlib_context_set_image(item->image);
+               imlib_context_set_image(item->icon);
                imlib_render_image_on_drawable(x, y);
        }
 }
                imlib_render_image_on_drawable(x, y);
        }
 }
@@ -891,6 +933,21 @@ selectitem:
                        currmenu->selected = NULL;
                        drawmenu(currmenu);
                        break;
                        currmenu->selected = NULL;
                        drawmenu(currmenu);
                        break;
+               case ConfigureNotify:
+                       menu = getmenu(currmenu, ev.xconfigure.window);
+                       if (menu == NULL)
+                               break;
+                       menu->x = ev.xconfigure.x;
+                       menu->y = ev.xconfigure.y;
+                       break;
+               case ClientMessage:
+                       /* user closed window */
+                       menu = getmenu(currmenu, ev.xclient.window);
+                       if (menu->parent == NULL)
+                               return;     /* closing the root menu closes the program */
+                       currmenu = menu->parent;
+                       mapmenu(currmenu);
+                       break;
                }
        }
 }
                }
        }
 }
@@ -912,8 +969,8 @@ freemenu(struct Menu *menu)
                free(tmp->output);
                if (tmp->file != NULL) {
                        free(tmp->file);
                free(tmp->output);
                if (tmp->file != NULL) {
                        free(tmp->file);
-                       if (tmp->image != NULL) {
-                               imlib_context_set_image(tmp->image);
+                       if (tmp->icon != NULL) {
+                               imlib_context_set_image(tmp->icon);
                                imlib_free_image();
                        }
                }
                                imlib_free_image();
                        }
                }
@@ -949,6 +1006,6 @@ cleanup(void)
 static void
 usage(void)
 {
 static void
 usage(void)
 {
-       (void)fprintf(stderr, "usage: xmenu\n");
+       (void)fprintf(stderr, "usage: xmenu [-w] [title]\n");
        exit(1);
 }
        exit(1);
 }