Readding the -w option

This commit is contained in:
phillbush 2020-05-31 22:26:33 -03:00
parent 7b867888e0
commit 3bec05ea77
2 changed files with 101 additions and 32 deletions

12
xmenu.1
View File

@ -3,6 +3,8 @@
xmenu \- menu utility for X xmenu \- menu utility for X
.SH SYNOPSIS .SH SYNOPSIS
.B xmenu .B xmenu
.RB [ \-w ]
.RI [ title ]
.SH DESCRIPTION .SH DESCRIPTION
.B xmenu .B xmenu
is a menu for X, 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, shows a menu for the user to select one of the items,
and outputs the item selected to stdout. and outputs the item selected to stdout.
.PP .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: Each item read from stdin has the following format:
.IP .IP
.EX .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. If an item does not have an output, its label is used as its output.
.IP .IP
The newline terminates the item specification. 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 .SH USAGE
.B xmenu .B xmenu
is controlled by the mouse, is controlled by the mouse,

117
xmenu.c
View File

@ -47,14 +47,14 @@ struct Geometry {
struct Item { struct Item {
char *label; /* string to be drawed on menu */ char *label; /* string to be drawed on menu */
char *output; /* string to be outputed when item is clicked */ 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 y; /* item y position relative to menu */
int h; /* item height */ int h; /* item height */
size_t labellen; /* strlen(label) */ size_t labellen; /* strlen(label) */
struct Item *prev; /* previous item */ struct Item *prev; /* previous item */
struct Item *next; /* next item */ struct Item *next; /* next item */
struct Menu *submenu; /* submenu spawned by clicking on 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 struct Menu *parsestdin(void);
static void setupmenusize(struct Geometry *geom, struct Menu *menu); static void setupmenusize(struct Geometry *geom, struct Menu *menu);
static void setupmenupos(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 grabpointer(void);
static void grabkeyboard(void); static void grabkeyboard(void);
static struct Menu *getmenu(struct Menu *currmenu, Window win); static struct Menu *getmenu(struct Menu *currmenu, Window win);
@ -103,6 +103,10 @@ static Visual *visual;
static Window rootwin; static Window rootwin;
static Colormap colormap; static Colormap colormap;
static struct DC dc; 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 Menu *rootmenu;
struct Geometry geom; 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: default:
usage(); usage();
break; break;
@ -124,7 +132,7 @@ main(int argc, char *argv[])
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (argc != 0) if (argc > 1)
usage(); usage();
/* open connection to server and set X variables */ /* open connection to server and set X variables */
@ -134,6 +142,7 @@ main(int argc, char *argv[])
visual = DefaultVisual(dpy, screen); visual = DefaultVisual(dpy, screen);
rootwin = RootWindow(dpy, screen); rootwin = RootWindow(dpy, screen);
colormap = DefaultColormap(dpy, screen); colormap = DefaultColormap(dpy, screen);
wmdelete=XInternAtom(dpy, "WM_DELETE_WINDOW", True);
/* imlib2 stuff */ /* imlib2 stuff */
imlib_set_cache_size(2048 * 1024); imlib_set_cache_size(2048 * 1024);
@ -147,15 +156,24 @@ main(int argc, char *argv[])
setupdc(); setupdc();
calcgeom(&geom); 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 */ /* generate menus and set them up */
rootmenu = parsestdin(); rootmenu = parsestdin();
if (rootmenu == NULL) if (rootmenu == NULL)
errx(1, "no menu generated"); errx(1, "no menu generated");
setupmenu(&geom, rootmenu); setupmenu(&geom, rootmenu, &classh);
/* grab mouse and keyboard */ /* grab mouse and keyboard */
if (!wflag) {
grabpointer(); grabpointer();
grabkeyboard(); grabkeyboard();
}
/* run event loop */ /* run event loop */
run(rootmenu); run(rootmenu);
@ -293,7 +311,7 @@ allocitem(const char *label, const char *output, char *file)
item->labellen = strlen(item->label); item->labellen = strlen(item->label);
item->next = NULL; item->next = NULL;
item->submenu = 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->y = 0; /* calculated by setupmenu() */
menu->level = level; menu->level = level;
swa.override_redirect = True; swa.override_redirect = (wflag) ? False : True;
swa.background_pixel = dc.normal[ColorBG].pixel; swa.background_pixel = dc.normal[ColorBG].pixel;
swa.border_pixel = dc.border.pixel; swa.border_pixel = dc.border.pixel;
swa.save_under = True; /* pop-up windows should save_under*/ swa.save_under = True; /* pop-up windows should save_under*/
swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask swa.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask
| PointerMotionMask | LeaveWindowMask; | PointerMotionMask | LeaveWindowMask;
if (wflag)
swa.event_mask |= StructureNotifyMask;
menu->win = XCreateWindow(dpy, rootwin, 0, 0, 1, 1, 0, menu->win = XCreateWindow(dpy, rootwin, 0, 0, 1, 1, 0,
CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWOverrideRedirect | CWBackPixel |
CWBorderPixel | CWEventMask | CWSaveUnder, CWBorderPixel | CWEventMask | CWSaveUnder,
&swa); &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 width;
int height; int height;
int imgsize; int imgsize;
image = imlib_load_image(file); icon = imlib_load_image(file);
if (image == NULL) if (icon == NULL)
errx(1, "cannot load image %s", file); errx(1, "cannot load icon %s", file);
imlib_context_set_image(image); imlib_context_set_image(icon);
width = imlib_image_get_width(); width = imlib_image_get_width();
height = imlib_image_get_height(); height = imlib_image_get_height();
imgsize = MIN(width, 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; labelwidth = ext.xOff + dc.font->height * 2 + IMGPADDING * 2;
menu->w = MAX(menu->w, labelwidth); 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 */ /* setup the position of a menu */
static void 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; width = menu->w + geom->border * 2;
height = menu->h + geom->border * 2; height = menu->h + geom->border * 2;
if (menu->parent == NULL) { /* if root menu, calculate in respect to cursor */ 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 */ /* recursivelly setup menu configuration and its pixmap */
static void 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; XWindowChanges changes;
XSizeHints sizeh; XSizeHints sizeh;
XTextProperty wintitle;
/* setup size and position of menus */ /* setup size and position of menus */
setupmenusize(geom, menu); setupmenusize(geom, menu);
@ -540,12 +570,18 @@ setupmenu(struct Geometry *geom, struct Menu *menu)
changes.y = menu->y; changes.y = menu->y;
XConfigureWindow(dpy, menu->win, CWBorderWidth | CWWidth | CWHeight | CWX | CWY, &changes); 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 */ /* set window manager hints */
sizeh.flags = PMaxSize | PMinSize; sizeh.flags = PMaxSize | PMinSize;
sizeh.min_width = sizeh.max_width = menu->w; sizeh.min_width = sizeh.max_width = menu->w;
sizeh.min_height = sizeh.max_height = menu->h; sizeh.min_height = sizeh.max_height = menu->h;
XSetWMProperties(dpy, menu->win, NULL, NULL, NULL, 0, &sizeh, XSetWMProperties(dpy, menu->win, &wintitle, NULL, NULL, 0, &sizeh, NULL, classh);
NULL, &classh);
/* create pixmap and XftDraw */ /* create pixmap and XftDraw */
menu->pixmap = XCreatePixmap(dpy, menu->win, menu->w, menu->h, 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 */ /* calculate positions of submenus */
for (item = menu->list; item != NULL; item = item->next) { for (item = menu->list; item != NULL; item = item->next) {
if (item->submenu != NULL) 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) */ /* map menus from currmenu (inclusive) until lcamenu (exclusive) */
for (menu = currmenu; menu != lcamenu; menu = menu->parent) { 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) { if (item->file != NULL) {
x = IMGPADDING / 2; x = IMGPADDING / 2;
y = item->y + (item->h - dc.font->height) / 2; y = item->y + (item->h - dc.font->height) / 2;
imlib_context_set_drawable(menu->pixmap); 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; currmenu->selected = NULL;
drawmenu(currmenu); drawmenu(currmenu);
break; 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); free(tmp->output);
if (tmp->file != NULL) { if (tmp->file != NULL) {
free(tmp->file); free(tmp->file);
if (tmp->image != NULL) { if (tmp->icon != NULL) {
imlib_context_set_image(tmp->image); imlib_context_set_image(tmp->icon);
imlib_free_image(); imlib_free_image();
} }
} }
@ -949,6 +1006,6 @@ cleanup(void)
static void static void
usage(void) usage(void)
{ {
(void)fprintf(stderr, "usage: xmenu\n"); (void)fprintf(stderr, "usage: xmenu [-w] [title]\n");
exit(1); exit(1);
} }