~ubuntu-branches/ubuntu/dapper/ratmenu/dapper

« back to all changes in this revision

Viewing changes to ratmenu.c

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Walther
  • Date: 2001-10-11 12:15:45 UTC
  • Revision ID: james.westby@ubuntu.com-20011011121545-etni0lx6s4l0sxtg
Tags: 1.6
Changed default resource to "ratmenu" from "9menu"

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ratmenu.c
 
3
 *
 
4
 * This program puts up a window that is just a menu, and executes
 
5
 * commands that correspond to the items selected.
 
6
 *
 
7
 * Initial idea: Arnold Robbins
 
8
 * Version using libXg: Matty Farrow (some ideas borrowed)
 
9
 * This code by: David Hogan and Arnold Robbins
 
10
 *
 
11
 * Copyright (c), Arnold Robbins and David Hogan
 
12
 *
 
13
 * Arnold Robbins
 
14
 * arnold@skeeve.atl.ga.us
 
15
 * October, 1994
 
16
 *
 
17
 * Code added to cause pop-up (unIconify) to move menu to mouse.
 
18
 * Christopher Platt
 
19
 * platt@coos.dartmouth.edu
 
20
 * May, 1995
 
21
 *
 
22
 * Said code moved to -teleport option, and -warp option added.
 
23
 * Arnold Robbins
 
24
 * June, 1995
 
25
 *
 
26
 * Code added to allow -fg and -bg colors.
 
27
 * John M. O'Donnell
 
28
 * odonnell@stpaul.lampf.lanl.gov
 
29
 * April, 1997
 
30
 *
 
31
 * Ratpoison windowmanager specific hacking; removed a lot of junk
 
32
 * and added keyboard functionality
 
33
 * Jonathan Walther
 
34
 * djw@reactor-core.org
 
35
 * September, 2001
 
36
 */
 
37
 
 
38
#include <stdio.h>
 
39
#include <fcntl.h>
 
40
#include <signal.h>
 
41
#include <stdlib.h>
 
42
#include <string.h>
 
43
#include <unistd.h>
 
44
#include <sys/types.h>
 
45
#include <sys/wait.h>
 
46
#include <X11/keysym.h>
 
47
#include <X11/X.h>
 
48
#include <X11/Xlib.h>
 
49
#include <X11/Xutil.h>
 
50
#include <X11/Xatom.h>
 
51
 
 
52
char version[] = "@(#) ratmenu version 1.4";
 
53
 
 
54
#define FONT "9x15bold"
 
55
#define MenuMask (ExposureMask|StructureNotifyMask|KeyPressMask)
 
56
 
 
57
Display *dpy;           /* lovely X stuff */
 
58
int screen;
 
59
Window root;
 
60
Window menuwin;
 
61
GC gc;
 
62
unsigned long fg;
 
63
unsigned long bg;
 
64
char *fgcname = NULL;
 
65
char *bgcname = NULL;
 
66
Colormap dcmap;
 
67
XColor color;
 
68
XFontStruct *font;
 
69
Atom wm_protocols;
 
70
Atom wm_delete_window;
 
71
int g_argc;                     /* for XSetWMProperties to use */
 
72
char **g_argv;
 
73
int savex, savey;
 
74
Window savewindow;
 
75
 
 
76
 
 
77
char *progname;         /* my name */
 
78
char *displayname;      /* X display */
 
79
char *fontname = FONT;  /* font */
 
80
char *labelname;        /* window and icon name */
 
81
 
 
82
char **labels;          /* list of labels and commands */
 
83
char **commands;
 
84
int numitems;
 
85
enum {left, center, right} align;
 
86
 
 
87
char *shell = "/bin/sh";        /* default shell */
 
88
 
 
89
void ask_wm_for_delete(void);
 
90
void reap(int);
 
91
void redraw_snazzy(int, int, int);
 
92
void redraw_dreary(int, int, int);
 
93
void (*redraw) (int, int, int) = redraw_snazzy;
 
94
void run_menu(void);
 
95
void set_wm_hints(int, int);
 
96
void spawn(char*);
 
97
void usage(void);
 
98
 
 
99
/* main --- crack arguments, set up X stuff, run the main menu loop */
 
100
 
 
101
int
 
102
main(int argc, char **argv)
 
103
{
 
104
        int i, j;
 
105
        char *cp;
 
106
        XGCValues gv;
 
107
        unsigned long mask;
 
108
 
 
109
        g_argc = argc;
 
110
        g_argv = argv;
 
111
 
 
112
        /* set default label name */
 
113
        if ((cp = strrchr(argv[0], '/')) == NULL)
 
114
                labelname = argv[0];
 
115
        else
 
116
                labelname = ++cp;
 
117
 
 
118
        /* and program name for diagnostics */
 
119
        progname = labelname;
 
120
 
 
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) {
 
129
                        shell = argv[++i];
 
130
                } else if (strcmp(argv[i], "-fg") == 0)
 
131
                        fgcname = argv[++i];
 
132
                else if (strcmp(argv[i], "-bg") == 0)
 
133
                        bgcname = argv[++i];
 
134
                else if (strcmp(argv[i], "-align") == 0) {
 
135
                        if (strcmp(argv[i+1], "left") == 0)
 
136
                                align = left;
 
137
                        else if (strcmp(argv[i+1], "center") == 0)
 
138
                                align = center;
 
139
                        else if (strcmp(argv[i+1], "right") == 0)
 
140
                                align = right;
 
141
                        else
 
142
                                usage();
 
143
                        i++;
 
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;
 
149
                        else
 
150
                                usage();
 
151
                        i++;
 
152
                } else if (strcmp(argv[i], "-version") == 0) {
 
153
                        printf("%s\n", version);
 
154
                        exit(0);
 
155
                } else if (argv[i][0] == '-')
 
156
                        usage();
 
157
                else
 
158
                        break;
 
159
        }
 
160
 
 
161
        if ((argc-i) % 2 == 1) /* every menu item needs a label AND command */
 
162
                usage();
 
163
 
 
164
        if ((argc-i) * 2 <= 0)
 
165
                usage();
 
166
 
 
167
        numitems = (argc-i) / 2;
 
168
 
 
169
        labels   = (char **) malloc(numitems * sizeof(char *));
 
170
        commands = (char **) malloc(numitems * sizeof(char *));
 
171
 
 
172
        if (commands == NULL || labels == NULL) {
 
173
                fprintf(stderr, "%s: no memory!\n", progname);
 
174
                exit(1);
 
175
        }
 
176
 
 
177
        for (j=0; i < argc; j++) {
 
178
                labels[j]   = argv[i++];
 
179
                commands[j] = argv[i++];
 
180
        }
 
181
 
 
182
        dpy = XOpenDisplay(displayname);
 
183
        if (dpy == NULL) {
 
184
                fprintf(stderr, "%s: cannot open display", progname);
 
185
                if (displayname != NULL)
 
186
                        fprintf(stderr, " %s", displayname);
 
187
                fprintf(stderr, "\n");
 
188
                exit(1);
 
189
        }
 
190
        screen = DefaultScreen(dpy);
 
191
        root = RootWindow(dpy, screen);
 
192
        
 
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;
 
197
 
 
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;
 
201
 
 
202
        if ((font = XLoadQueryFont(dpy, fontname)) == NULL) {
 
203
                fprintf(stderr, "%s: fatal: cannot load font %s\n", progname, fontname);
 
204
                exit(1);
 
205
        }
 
206
 
 
207
        gv.foreground = fg^bg;
 
208
        gv.background = bg;
 
209
        gv.font = font->fid;
 
210
        gv.function = GXxor;
 
211
        gv.line_width = 0;
 
212
        mask = GCForeground | GCBackground | GCFunction | GCFont | GCLineWidth;
 
213
        gc = XCreateGC(dpy, root, mask, &gv);
 
214
 
 
215
        signal(SIGCHLD, reap);
 
216
 
 
217
        run_menu();
 
218
 
 
219
        XCloseDisplay(dpy);
 
220
        exit(0);
 
221
}
 
222
 
 
223
/* spawn --- run a command */
 
224
 
 
225
void
 
226
spawn(char *com)
 
227
{
 
228
        int pid;
 
229
        static char *sh_base = NULL;
 
230
 
 
231
        if (sh_base == NULL) {
 
232
                sh_base = strrchr(shell, '/');
 
233
                if (sh_base != NULL)
 
234
                        sh_base++;
 
235
                else
 
236
                        sh_base = shell;
 
237
        }
 
238
 
 
239
        pid = fork();
 
240
        if (pid < 0) {
 
241
                fprintf(stderr, "%s: can't fork\n", progname);
 
242
                return;
 
243
        } else if (pid > 0)
 
244
                return;
 
245
 
 
246
        close(ConnectionNumber(dpy));
 
247
        execl(shell, sh_base, "-c", com, NULL);
 
248
        _exit(1);
 
249
}
 
250
 
 
251
/* reap --- collect dead children */
 
252
 
 
253
void
 
254
reap(int s)
 
255
{
 
256
        (void) wait((int *) NULL);
 
257
        signal(s, reap);
 
258
}
 
259
 
 
260
/* usage --- print a usage message and die */
 
261
 
 
262
void
 
263
usage(void)
 
264
{
 
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);
 
269
        exit(0);
 
270
}
 
271
 
 
272
/* run_menu --- put up the window, execute selected commands */
 
273
 
 
274
void
 
275
run_menu(void)
 
276
{
 
277
        KeySym key;
 
278
        XEvent ev;
 
279
        XClientMessageEvent *cmsg;
 
280
        int i, cur, wide, high, dx, dy;
 
281
 
 
282
        dx = 0;
 
283
        for (i = 0; i < numitems; i++) {
 
284
                wide = XTextWidth(font, labels[i], strlen(labels[i])) + 4;
 
285
                if (wide > dx)
 
286
                        dx = wide;
 
287
        }
 
288
        wide = dx;
 
289
 
 
290
 
 
291
        high = font->ascent + font->descent + 1;
 
292
        dy = numitems * high;
 
293
 
 
294
        set_wm_hints(wide, dy);
 
295
 
 
296
        ask_wm_for_delete();
 
297
 
 
298
 
 
299
        XSelectInput(dpy, menuwin, MenuMask);
 
300
 
 
301
        XMapWindow(dpy, menuwin);
 
302
 
 
303
        cur = 0; /* Currently selected menu item */
 
304
 
 
305
        for (;;) {
 
306
                XNextEvent(dpy, &ev);
 
307
                switch (ev.type) {
 
308
                case KeyPress:
 
309
                        XLookupString(&ev.xkey, NULL, 0, &key, NULL);
 
310
                        switch (key) {
 
311
                        case XK_Escape: case XK_q:
 
312
                                return;
 
313
                                break;
 
314
                        case XK_Right: case XK_Return: case XK_l:
 
315
                                spawn(commands[cur]);
 
316
                                return;
 
317
                                break;
 
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);
 
321
                                break;
 
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);
 
325
                                break;
 
326
                        }
 
327
                        break;
 
328
                case UnmapNotify: XClearWindow(dpy, menuwin); break;
 
329
                case MapNotify: case Expose: redraw(cur, high, wide); break;
 
330
                case ClientMessage:
 
331
                        cmsg = &ev.xclient;
 
332
                        if (cmsg->message_type == wm_protocols
 
333
                                && cmsg->data.l[0] == wm_delete_window)
 
334
                                return;
 
335
                }
 
336
        }
 
337
}
 
338
 
 
339
/* set_wm_hints --- set all the window manager hints */
 
340
 
 
341
void
 
342
set_wm_hints(int wide, int high)
 
343
{
 
344
        XWMHints *wmhints;
 
345
        XSizeHints *sizehints;
 
346
        XClassHint *classhints;
 
347
        XTextProperty wname;
 
348
 
 
349
        if ((sizehints = XAllocSizeHints()) == NULL) {
 
350
                fprintf(stderr, "%s: could not allocate size hints\n",
 
351
                        progname);
 
352
                exit(1);
 
353
        }
 
354
 
 
355
        if ((wmhints = XAllocWMHints()) == NULL) {
 
356
                fprintf(stderr, "%s: could not allocate window manager hints\n",
 
357
                        progname);
 
358
                exit(1);
 
359
        }
 
360
 
 
361
        if ((classhints = XAllocClassHint()) == NULL) {
 
362
                fprintf(stderr, "%s: could not allocate class hints\n",
 
363
                        progname);
 
364
                exit(1);
 
365
        }
 
366
 
 
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;
 
376
 
 
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;
 
380
 
 
381
        if (XStringListToTextProperty(& labelname, 1, & wname) == 0) {
 
382
                fprintf(stderr, "%s: could not allocate window name structure\n",
 
383
                        progname);
 
384
                exit(1);
 
385
        }
 
386
 
 
387
        menuwin = XCreateSimpleWindow(dpy, root, sizehints->x, sizehints->y,
 
388
                                sizehints->width, sizehints->height, 1, fg, bg);
 
389
 
 
390
        wmhints->input = True;
 
391
        wmhints->initial_state = NormalState;
 
392
        wmhints->flags = StateHint | InputHint;
 
393
 
 
394
        classhints->res_name = progname;
 
395
        classhints->res_class = "ratmenu";
 
396
 
 
397
        XSetWMProperties(dpy, menuwin, & wname, NULL,
 
398
                g_argv, g_argc, sizehints, wmhints, classhints);
 
399
}
 
400
 
 
401
/* ask_wm_for_delete --- jump through hoops to ask WM to delete us */
 
402
 
 
403
void
 
404
ask_wm_for_delete(void)
 
405
{
 
406
        int status;
 
407
 
 
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);
 
411
 
 
412
        if (status != True)
 
413
                fprintf(stderr, "%s: could not ask for clean delete\n",
 
414
                        progname);
 
415
}
 
416
 
 
417
/* redraw --- actually draw the menu */
 
418
 
 
419
void
 
420
redraw_snazzy (int cur, int high, int wide)
 
421
{
 
422
        int i, j, ty, tx;
 
423
 
 
424
        XClearWindow(dpy, menuwin);
 
425
        for (i = 0, j = cur; i < numitems; i++, j++) {
 
426
                j %= numitems;
 
427
                if (align == left) {
 
428
                        tx = 0;
 
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]));
 
433
                }
 
434
                ty = i*high + font->ascent + 1;
 
435
                XDrawString(dpy, menuwin, gc, tx, ty, labels[j], strlen(labels[j]));
 
436
        }
 
437
        XFillRectangle(dpy, menuwin, gc, 0, 0, wide, high); 
 
438
}
 
439
 
 
440
void
 
441
redraw_dreary (int cur, int high, int wide)
 
442
{
 
443
        int i, ty, tx;
 
444
 
 
445
        XClearWindow(dpy, menuwin);
 
446
        for (i = 0; i < numitems; i++) {
 
447
                if (align == left) {
 
448
                        tx = 0;
 
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]));
 
453
                }
 
454
                ty = i*high + font->ascent + 1;
 
455
                XDrawString(dpy, menuwin, gc, tx, ty, labels[i], strlen(labels[i]));
 
456
        }
 
457
        XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high); 
 
458
}