4
* This program puts up a window that is just a menu, and executes
5
* commands that correspond to the items selected.
7
* Initial idea: Arnold Robbins
8
* Version using libXg: Matty Farrow (some ideas borrowed)
9
* This code by: David Hogan and Arnold Robbins
11
* Copyright (c), Arnold Robbins and David Hogan
14
* arnold@skeeve.atl.ga.us
17
* Code added to cause pop-up (unIconify) to move menu to mouse.
19
* platt@coos.dartmouth.edu
22
* Said code moved to -teleport option, and -warp option added.
26
* Code added to allow -fg and -bg colors.
28
* odonnell@stpaul.lampf.lanl.gov
31
* Ratpoison windowmanager specific hacking; removed a lot of junk
32
* and added keyboard functionality
34
* djw@reactor-core.org
44
#include <sys/types.h>
46
#include <X11/keysym.h>
49
#include <X11/Xutil.h>
50
#include <X11/Xatom.h>
52
char version[] = "@(#) ratmenu version 1.4";
54
#define FONT "9x15bold"
55
#define MenuMask (ExposureMask|StructureNotifyMask|KeyPressMask)
57
Display *dpy; /* lovely X stuff */
70
Atom wm_delete_window;
71
int g_argc; /* for XSetWMProperties to use */
77
char *progname; /* my name */
78
char *displayname; /* X display */
79
char *fontname = FONT; /* font */
80
char *labelname; /* window and icon name */
82
char **labels; /* list of labels and commands */
85
enum {left, center, right} align;
87
char *shell = "/bin/sh"; /* default shell */
89
void ask_wm_for_delete(void);
91
void redraw_snazzy(int, int, int);
92
void redraw_dreary(int, int, int);
93
void (*redraw) (int, int, int) = redraw_snazzy;
95
void set_wm_hints(int, int);
99
/* main --- crack arguments, set up X stuff, run the main menu loop */
102
main(int argc, char **argv)
112
/* set default label name */
113
if ((cp = strrchr(argv[0], '/')) == NULL)
118
/* and program name for diagnostics */
119
progname = labelname;
121
for (i = 1; i < argc; i++) {
122
if (strcmp(argv[i], "-display") == 0) {
123
displayname = argv[++i];
124
} else if (strcmp(argv[i], "-font") == 0) {
125
fontname = argv[++i];
126
} else if (strcmp(argv[i], "-label") == 0) {
127
labelname = argv[++i];
128
} else if (strcmp(argv[i], "-shell") == 0) {
130
} else if (strcmp(argv[i], "-fg") == 0)
132
else if (strcmp(argv[i], "-bg") == 0)
134
else if (strcmp(argv[i], "-align") == 0) {
135
if (strcmp(argv[i+1], "left") == 0)
137
else if (strcmp(argv[i+1], "center") == 0)
139
else if (strcmp(argv[i+1], "right") == 0)
144
} else if (strcmp(argv[i], "-style") == 0) {
145
if (strcmp(argv[i+1], "snazzy") == 0)
146
redraw = redraw_snazzy;
147
else if (strcmp(argv[i+1], "dreary") == 0)
148
redraw = redraw_dreary;
152
} else if (strcmp(argv[i], "-version") == 0) {
153
printf("%s\n", version);
155
} else if (argv[i][0] == '-')
161
if ((argc-i) % 2 == 1) /* every menu item needs a label AND command */
164
if ((argc-i) * 2 <= 0)
167
numitems = (argc-i) / 2;
169
labels = (char **) malloc(numitems * sizeof(char *));
170
commands = (char **) malloc(numitems * sizeof(char *));
172
if (commands == NULL || labels == NULL) {
173
fprintf(stderr, "%s: no memory!\n", progname);
177
for (j=0; i < argc; j++) {
178
labels[j] = argv[i++];
179
commands[j] = argv[i++];
182
dpy = XOpenDisplay(displayname);
184
fprintf(stderr, "%s: cannot open display", progname);
185
if (displayname != NULL)
186
fprintf(stderr, " %s", displayname);
187
fprintf(stderr, "\n");
190
screen = DefaultScreen(dpy);
191
root = RootWindow(dpy, screen);
193
dcmap = DefaultColormap (dpy, screen);
194
if (fgcname == NULL || XParseColor(dpy, dcmap, fgcname, &color) == 0 || XAllocColor(dpy, dcmap, &color) == 0)
195
fg = BlackPixel(dpy, screen);
196
else fg = color.pixel;
198
if (bgcname == NULL || XParseColor(dpy, dcmap, bgcname, &color) == 0 || XAllocColor(dpy, dcmap, &color) == 0)
199
bg = WhitePixel(dpy, screen);
200
else bg = color.pixel;
202
if ((font = XLoadQueryFont(dpy, fontname)) == NULL) {
203
fprintf(stderr, "%s: fatal: cannot load font %s\n", progname, fontname);
207
gv.foreground = fg^bg;
212
mask = GCForeground | GCBackground | GCFunction | GCFont | GCLineWidth;
213
gc = XCreateGC(dpy, root, mask, &gv);
215
signal(SIGCHLD, reap);
223
/* spawn --- run a command */
229
static char *sh_base = NULL;
231
if (sh_base == NULL) {
232
sh_base = strrchr(shell, '/');
241
fprintf(stderr, "%s: can't fork\n", progname);
246
close(ConnectionNumber(dpy));
247
execl(shell, sh_base, "-c", com, NULL);
251
/* reap --- collect dead children */
256
(void) wait((int *) NULL);
260
/* usage --- print a usage message and die */
265
fprintf(stderr, "usage: %s [-display displayname] [-style {snazzy|dreary} "
266
"[-shell shell] [-label name] [-align {left|center|right}] "
267
"[-fg fgcolor] [-bg bgcolor] [-font fname] "
268
"[-version] [menuitem command] ...\n", progname);
272
/* run_menu --- put up the window, execute selected commands */
279
XClientMessageEvent *cmsg;
280
int i, cur, wide, high, dx, dy;
283
for (i = 0; i < numitems; i++) {
284
wide = XTextWidth(font, labels[i], strlen(labels[i])) + 4;
291
high = font->ascent + font->descent + 1;
292
dy = numitems * high;
294
set_wm_hints(wide, dy);
299
XSelectInput(dpy, menuwin, MenuMask);
301
XMapWindow(dpy, menuwin);
303
cur = 0; /* Currently selected menu item */
306
XNextEvent(dpy, &ev);
309
XLookupString(&ev.xkey, NULL, 0, &key, NULL);
311
case XK_Escape: case XK_q:
314
case XK_Right: case XK_Return: case XK_l:
315
spawn(commands[cur]);
318
case XK_Tab: case XK_space: case XK_Down: case XK_j: case XK_plus:
319
++cur == numitems ? cur = 0 : 0 ;
320
redraw(cur, high, wide);
322
case XK_BackSpace: case XK_Up: case XK_k: case XK_minus:
323
cur-- == 0 ? cur = numitems - 1 : 0 ;
324
redraw(cur, high, wide);
328
case UnmapNotify: XClearWindow(dpy, menuwin); break;
329
case MapNotify: case Expose: redraw(cur, high, wide); break;
332
if (cmsg->message_type == wm_protocols
333
&& cmsg->data.l[0] == wm_delete_window)
339
/* set_wm_hints --- set all the window manager hints */
342
set_wm_hints(int wide, int high)
345
XSizeHints *sizehints;
346
XClassHint *classhints;
349
if ((sizehints = XAllocSizeHints()) == NULL) {
350
fprintf(stderr, "%s: could not allocate size hints\n",
355
if ((wmhints = XAllocWMHints()) == NULL) {
356
fprintf(stderr, "%s: could not allocate window manager hints\n",
361
if ((classhints = XAllocClassHint()) == NULL) {
362
fprintf(stderr, "%s: could not allocate class hints\n",
367
/* fill in hints in order to parse geometry spec */
368
sizehints->width = sizehints->min_width = sizehints->max_width = wide;
369
sizehints->height = sizehints->min_height = sizehints->max_height = high;
370
sizehints->flags = USSize|PSize|PMinSize|PMaxSize;
371
if (XWMGeometry(dpy, screen, "", "", 1, sizehints,
372
&sizehints->x, &sizehints->y,
373
&sizehints->width, &sizehints->height,
374
&sizehints->win_gravity) & (XValue|YValue))
375
sizehints->flags |= USPosition;
377
/* override -geometry for size of window */
378
sizehints->width = sizehints->min_width = sizehints->max_width = wide;
379
sizehints->height = sizehints->min_height = sizehints->max_height = high;
381
if (XStringListToTextProperty(& labelname, 1, & wname) == 0) {
382
fprintf(stderr, "%s: could not allocate window name structure\n",
387
menuwin = XCreateSimpleWindow(dpy, root, sizehints->x, sizehints->y,
388
sizehints->width, sizehints->height, 1, fg, bg);
390
wmhints->input = True;
391
wmhints->initial_state = NormalState;
392
wmhints->flags = StateHint | InputHint;
394
classhints->res_name = progname;
395
classhints->res_class = "ratmenu";
397
XSetWMProperties(dpy, menuwin, & wname, NULL,
398
g_argv, g_argc, sizehints, wmhints, classhints);
401
/* ask_wm_for_delete --- jump through hoops to ask WM to delete us */
404
ask_wm_for_delete(void)
408
wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
409
wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
410
status = XSetWMProtocols(dpy, menuwin, & wm_delete_window, 1);
413
fprintf(stderr, "%s: could not ask for clean delete\n",
417
/* redraw --- actually draw the menu */
420
redraw_snazzy (int cur, int high, int wide)
424
XClearWindow(dpy, menuwin);
425
for (i = 0, j = cur; i < numitems; i++, j++) {
429
} else if (align == center) {
430
tx = (wide - XTextWidth(font, labels[j], strlen(labels[j]))) / 2;
431
} else {/* align == right */
432
tx = wide - XTextWidth(font, labels[j], strlen(labels[j]));
434
ty = i*high + font->ascent + 1;
435
XDrawString(dpy, menuwin, gc, tx, ty, labels[j], strlen(labels[j]));
437
XFillRectangle(dpy, menuwin, gc, 0, 0, wide, high);
441
redraw_dreary (int cur, int high, int wide)
445
XClearWindow(dpy, menuwin);
446
for (i = 0; i < numitems; i++) {
449
} else if (align == center) {
450
tx = (wide - XTextWidth(font, labels[i], strlen(labels[i]))) / 2;
451
} else {/* align == right */
452
tx = wide - XTextWidth(font, labels[i], strlen(labels[i]));
454
ty = i*high + font->ascent + 1;
455
XDrawString(dpy, menuwin, gc, tx, ty, labels[i], strlen(labels[i]));
457
XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high);