mirror of
https://github.com/0intro/wmii
synced 2024-11-26 15:49:37 +03:00
532 lines
12 KiB
C
532 lines
12 KiB
C
/* Licence
|
|
* =======
|
|
*
|
|
* 9menu is free software, and is Copyright (c) 1994 by David Hogan and
|
|
* Arnold Robbins. Permission is granted to all sentient beings to use
|
|
* this software, to make copies of it, and to distribute those copies,
|
|
* provided that:
|
|
*
|
|
* (1) the copyright and licence notices are left intact
|
|
* (2) the recipients are aware that it is free software
|
|
* (3) any unapproved changes in functionality are either
|
|
* (i) only distributed as patches
|
|
* or (ii) distributed as a new program which is not called 9menu
|
|
* and whose documentation gives credit where it is due
|
|
* (4) the authors are not held responsible for any defects
|
|
* or shortcomings in the software, or damages caused by it.
|
|
*
|
|
* There is no warranty for this software. Have a nice day.
|
|
*
|
|
* --
|
|
* Arnold Robbins
|
|
* arnold@skeeve.com
|
|
*
|
|
* 9menu.c
|
|
*
|
|
* This program puts up a window that is just a menu, and executes
|
|
* commands that correspond to the items selected.
|
|
*
|
|
* Initial idea: Arnold Robbins
|
|
* Version using libXg: Matty Farrow (some ideas borrowed)
|
|
* This code by: David Hogan and Arnold Robbins
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <X11/X.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xproto.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/keysymdef.h>
|
|
#include <X11/keysym.h>
|
|
|
|
char version[] = "@(#) 9menu version 1.8";
|
|
|
|
Display *dpy; /* lovely X stuff */
|
|
int screen;
|
|
Window root;
|
|
Window menuwin;
|
|
GC gc;
|
|
unsigned long selbg;
|
|
unsigned long selfg;
|
|
unsigned long normbg;
|
|
unsigned long normfg;
|
|
unsigned long border;
|
|
char *sfgname = NULL;
|
|
char *sbgname = NULL;
|
|
char *nfgname = NULL;
|
|
char *nbgname = NULL;
|
|
char *brcname = NULL;
|
|
Colormap defcmap;
|
|
XColor color;
|
|
XFontStruct *font;
|
|
int g_argc; /* for XSetWMProperties to use */
|
|
char **g_argv;
|
|
int f_argc; /* for labels read from files */
|
|
char **f_argv;
|
|
char *initial = "";
|
|
int cur;
|
|
|
|
char *fontlist[] = { /* default font list if no -font */
|
|
"pelm.latin1.9",
|
|
"lucm.latin1.9",
|
|
"blit",
|
|
"9x15bold",
|
|
"9x15",
|
|
"lucidasanstypewriter-12",
|
|
"fixed",
|
|
NULL
|
|
};
|
|
|
|
char *progname; /* my name */
|
|
char *displayname; /* X display */
|
|
char *fontname; /* font */
|
|
char *filename; /* file to read options or labels from */
|
|
|
|
char **labels; /* list of labels and commands */
|
|
char **commands;
|
|
int numitems;
|
|
|
|
void usage(), run_menu();
|
|
void create_window();
|
|
void redraw(), warpmouse();
|
|
void memory();
|
|
int args();
|
|
|
|
/* memory --- print the out of memory message and die */
|
|
|
|
void
|
|
memory(s)
|
|
char *s;
|
|
{
|
|
fprintf(stderr, "%s: couldn't allocate memory for %s\n", progname, s);
|
|
exit(1);
|
|
}
|
|
|
|
/* args --- go through the argument list, set options */
|
|
|
|
int
|
|
args(int argc, char **argv)
|
|
{
|
|
int i;
|
|
if (argc == 0 || argv == NULL || argv[0] == '\0')
|
|
return -1;
|
|
for (i = 0; i < argc && argv[i] != NULL; i++) {
|
|
if (strcmp(argv[i], "-display") == 0)
|
|
displayname = argv[++i];
|
|
else if (strcmp(argv[i], "-file") == 0)
|
|
filename = argv[++i];
|
|
else if (strcmp(argv[i], "-font") == 0)
|
|
fontname = argv[++i];
|
|
else if (strncmp(argv[i], "-initial", 9) == 0)
|
|
initial = argv[++i];
|
|
else if (strncmp(argv[i], "-nb", 3) == 0)
|
|
nbgname = argv[i][3] ? &argv[i][3] : argv[++i];
|
|
else if (strncmp(argv[i], "-nf", 3) == 0)
|
|
nfgname = argv[i][3] ? &argv[i][3] : argv[++i];
|
|
else if (strncmp(argv[i], "-sb", 3) == 0)
|
|
sbgname = argv[i][3] ? &argv[i][3] : argv[++i];
|
|
else if (strncmp(argv[i], "-sf", 3) == 0)
|
|
sfgname = argv[i][3] ? &argv[i][3] : argv[++i];
|
|
else if (strncmp(argv[i], "-br", 3) == 0)
|
|
brcname = argv[i][3] ? &argv[i][3] : argv[++i];
|
|
else if (strcmp(argv[i], "-version") == 0) {
|
|
printf("%s\n", version);
|
|
exit(0);
|
|
} else if (argv[i][0] == '-')
|
|
usage();
|
|
else
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/* main --- crack arguments, set up X stuff, run the main menu loop */
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int i, j;
|
|
char *cp;
|
|
XGCValues gv;
|
|
unsigned long mask;
|
|
int nlabels = 0;
|
|
|
|
g_argc = argc;
|
|
g_argv = argv;
|
|
|
|
/* set default label name */
|
|
if ((cp = strrchr(argv[0], '/')) == NULL)
|
|
progname = argv[0];
|
|
else
|
|
progname = ++cp;
|
|
|
|
++argv;
|
|
--argc;
|
|
|
|
i = args(argc, argv);
|
|
|
|
numitems = argc - i;
|
|
|
|
if (numitems <= 0 && filename == NULL)
|
|
usage();
|
|
|
|
if (filename) {
|
|
/* Read options and labels from file */
|
|
char fbuf[1024];
|
|
FILE *fp;
|
|
|
|
fp = fopen(filename, "r");
|
|
if (fp == NULL) {
|
|
fprintf(stderr, "%s: couldn't open '%s'\n", progname,
|
|
filename);
|
|
exit(1);
|
|
}
|
|
while (fgets(fbuf, sizeof fbuf, fp)) {
|
|
char *s = fbuf;
|
|
strtok(s, "\n");
|
|
if (s[0] == '-') {
|
|
char *temp[3];
|
|
temp[0] = s;
|
|
temp[1] = strchr(s, ' ');
|
|
if (temp[1]) {
|
|
*(temp[1]++) = '\0';
|
|
s = malloc(strlen(temp[1]) + 1);
|
|
if (s == NULL)
|
|
memory("temporary argument");
|
|
strcpy(s, temp[1]);
|
|
temp[1] = s;
|
|
}
|
|
temp[2] = 0;
|
|
args(temp[1] ? 2 : 1, temp);
|
|
continue;
|
|
}
|
|
if (s[0] == '#')
|
|
continue;
|
|
/* allow - in menu items to be escaped */
|
|
if (s[0] == '\\')
|
|
++s;
|
|
/* allocate space */
|
|
if (f_argc < nlabels + 1) {
|
|
int k;
|
|
char **temp = malloc(sizeof(char *) * (f_argc + 5));
|
|
if (temp == 0)
|
|
memory("temporary item");
|
|
|
|
for (k = 0; k < nlabels; k++)
|
|
temp[k] = f_argv[k];
|
|
|
|
free(f_argv);
|
|
f_argv = temp;
|
|
f_argc += 5;
|
|
}
|
|
f_argv[nlabels] = malloc(strlen(s) + 1);
|
|
if (f_argv[nlabels] == NULL)
|
|
memory("temporary text");
|
|
strcpy(f_argv[nlabels], s);
|
|
++nlabels;
|
|
}
|
|
}
|
|
|
|
labels = (char **) malloc((numitems + nlabels) * sizeof(char *));
|
|
commands = (char **) malloc((numitems + nlabels) * sizeof(char *));
|
|
if (commands == NULL || labels == NULL)
|
|
memory("command and label arrays");
|
|
|
|
for (j = 0; j < numitems; j++) {
|
|
labels[j] = argv[i + j];
|
|
if ((cp = strchr(labels[j], ':')) != NULL) {
|
|
*cp++ = '\0';
|
|
commands[j] = cp;
|
|
} else
|
|
commands[j] = labels[j];
|
|
if(strcmp(labels[j], initial) == 0)
|
|
cur = j;
|
|
}
|
|
|
|
/*
|
|
* Now we no longer need i (our offset into argv) so we recycle it,
|
|
* while keeping the old value of j!
|
|
*/
|
|
for (i = 0; i < nlabels; i++) {
|
|
labels[j] = f_argv[i];
|
|
if ((cp = strchr(labels[j], ':')) != NULL) {
|
|
*cp++ = '\0';
|
|
commands[j] = cp;
|
|
} else
|
|
commands[j] = labels[j];
|
|
++j;
|
|
}
|
|
|
|
/* And now we merge the totals */
|
|
numitems += nlabels;
|
|
|
|
dpy = XOpenDisplay(displayname);
|
|
if (dpy == NULL) {
|
|
fprintf(stderr, "%s: cannot open display", progname);
|
|
if (displayname != NULL)
|
|
fprintf(stderr, " %s", displayname);
|
|
fprintf(stderr, "\n");
|
|
exit(1);
|
|
}
|
|
screen = DefaultScreen(dpy);
|
|
root = RootWindow(dpy, screen);
|
|
/*
|
|
* This used to be
|
|
* black = BlackPixel(dpy, screen);
|
|
* white = WhitePixel(dpy, screen);
|
|
*/
|
|
defcmap = DefaultColormap(dpy, screen);
|
|
if (sbgname == NULL
|
|
|| XParseColor(dpy, defcmap, sbgname, &color) == 0
|
|
|| XAllocColor(dpy, defcmap, &color) == 0)
|
|
selbg = BlackPixel(dpy, screen);
|
|
else
|
|
selbg = color.pixel;
|
|
|
|
if (sfgname == NULL
|
|
|| XParseColor(dpy, defcmap, sfgname, &color) == 0
|
|
|| XAllocColor(dpy, defcmap, &color) == 0)
|
|
selfg = WhitePixel(dpy, screen);
|
|
else
|
|
selfg = color.pixel;
|
|
|
|
if (nbgname == NULL
|
|
|| XParseColor(dpy, defcmap, nbgname, &color) == 0
|
|
|| XAllocColor(dpy, defcmap, &color) == 0)
|
|
normbg = selfg;
|
|
else
|
|
normbg = color.pixel;
|
|
|
|
if (nfgname == NULL
|
|
|| XParseColor(dpy, defcmap, nfgname, &color) == 0
|
|
|| XAllocColor(dpy, defcmap, &color) == 0)
|
|
normfg = selbg;
|
|
else
|
|
normfg = color.pixel;
|
|
|
|
if (brcname == NULL
|
|
|| XParseColor(dpy, defcmap, brcname, &color) == 0
|
|
|| XAllocColor(dpy, defcmap, &color) == 0)
|
|
border = selbg;
|
|
else
|
|
border = color.pixel;
|
|
|
|
/* try user's font first */
|
|
if (fontname != NULL) {
|
|
font = XLoadQueryFont(dpy, fontname);
|
|
if (font == NULL)
|
|
fprintf(stderr, "%s: warning: can't load font %s\n",
|
|
progname, fontname);
|
|
}
|
|
|
|
/* if no user font, try one of our default fonts */
|
|
if (font == NULL) {
|
|
for (i = 0; fontlist[i] != NULL; i++) {
|
|
font = XLoadQueryFont(dpy, fontlist[i]);
|
|
if (font != NULL)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (font == NULL) {
|
|
fprintf(stderr, "%s: fatal: cannot load a font\n", progname);
|
|
exit(1);
|
|
}
|
|
|
|
gv.foreground = normfg;
|
|
gv.background = normbg;
|
|
gv.font = font->fid;
|
|
gv.line_width = 0;
|
|
mask = GCForeground | GCBackground | GCFont | GCLineWidth;
|
|
gc = XCreateGC(dpy, root, mask, &gv);
|
|
|
|
run_menu();
|
|
|
|
XCloseDisplay(dpy);
|
|
exit(0);
|
|
}
|
|
|
|
/* usage --- print a usage message and die */
|
|
|
|
void
|
|
usage()
|
|
{
|
|
fprintf(stderr, "usage: %s [-display <displayname>] [-font <fontname>] ", progname);
|
|
fprintf(stderr, "[-file filename] ");
|
|
fprintf(stderr, "[-{n,s}{f,b} <color>] [-br <color>] ");
|
|
fprintf(stderr, "[-version] menitem[:command] ...\n");
|
|
exit(0);
|
|
}
|
|
|
|
/* run_menu --- put up the window, execute selected commands */
|
|
|
|
void
|
|
run_menu()
|
|
{
|
|
XEvent ev;
|
|
int i, old, wide, high, dx, dy;
|
|
|
|
dx = 0;
|
|
for (i = 0; i < numitems; i++) {
|
|
wide = XTextWidth(font, labels[i], strlen(labels[i])) + 4;
|
|
if (wide > dx)
|
|
dx = wide;
|
|
}
|
|
wide = dx;
|
|
|
|
high = font->ascent + font->descent + 1;
|
|
dy = numitems * high;
|
|
|
|
enum {
|
|
MouseMask =
|
|
ButtonPressMask
|
|
| ButtonReleaseMask
|
|
| ButtonMotionMask
|
|
| PointerMotionMask,
|
|
MenuMask =
|
|
MouseMask
|
|
| StructureNotifyMask
|
|
| ExposureMask
|
|
};
|
|
|
|
create_window(wide, high);
|
|
warpmouse(wide, high);
|
|
|
|
XSelectInput(dpy, menuwin, MenuMask);
|
|
|
|
XMapWindow(dpy, menuwin);
|
|
|
|
i = 0; /* save menu Item position */
|
|
|
|
for (;;) {
|
|
XNextEvent(dpy, &ev);
|
|
switch (ev.type) {
|
|
default:
|
|
fprintf(stderr, "%s: unknown ev.type %d\n",
|
|
progname, ev.type);
|
|
break;
|
|
case ButtonRelease:
|
|
i = ev.xbutton.y/high;
|
|
if (ev.xbutton.x < 0 || ev.xbutton.x > wide)
|
|
return;
|
|
else if (i < 0 || i >= numitems)
|
|
return;
|
|
|
|
printf("%s\n", commands[i]);
|
|
return;
|
|
case ButtonPress:
|
|
case MotionNotify:
|
|
old = cur;
|
|
cur = ev.xbutton.y/high;
|
|
if (ev.xbutton.x < 0 || ev.xbutton.x > wide)
|
|
cur = ~0;
|
|
if (cur == old)
|
|
break;
|
|
redraw(high, wide);
|
|
break;
|
|
case MapNotify:
|
|
redraw(high, wide);
|
|
if(XGrabPointer(dpy, menuwin, False, MouseMask,
|
|
GrabModeAsync, GrabModeAsync,
|
|
0, None, CurrentTime
|
|
) != GrabSuccess) {
|
|
fprintf(stderr, "Failed to grab the mouse\n");
|
|
}
|
|
break;
|
|
case Expose:
|
|
redraw(high, wide);
|
|
break;
|
|
case MappingNotify: /* why do we get this? */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* set_wm_hints --- set all the window manager hints */
|
|
|
|
void
|
|
create_window(int wide, int high)
|
|
{
|
|
XSetWindowAttributes wa = { 0 };
|
|
unsigned int h;
|
|
int x, y, dummy;
|
|
Window wdummy;
|
|
|
|
h = high * numitems;
|
|
|
|
XQueryPointer(dpy, root, &wdummy, &wdummy, &x, &y,
|
|
&dummy, &dummy, &dummy);
|
|
x -= wide / 2;
|
|
if (x < 0)
|
|
x = 0;
|
|
else if (x + wide > DisplayWidth(dpy, screen))
|
|
x = DisplayWidth(dpy, screen) - wide;
|
|
|
|
y -= cur * high + high / 2;
|
|
if (y < 0)
|
|
y = 0;
|
|
else if (y + h > DisplayHeight(dpy, screen))
|
|
y = DisplayHeight(dpy, screen) - h;
|
|
|
|
wa.override_redirect = True;
|
|
wa.border_pixel = border;
|
|
wa.background_pixel = normbg;
|
|
menuwin = XCreateWindow(dpy, root, x, y, wide, h,
|
|
1, DefaultDepth(dpy, screen), CopyFromParent,
|
|
DefaultVisual(dpy, screen),
|
|
CWOverrideRedirect
|
|
| CWBackPixel
|
|
| CWBorderPixel
|
|
| CWEventMask,
|
|
&wa);
|
|
|
|
XSetCommand(dpy, menuwin, g_argv, g_argc);
|
|
}
|
|
|
|
/* redraw --- actually redraw the menu */
|
|
|
|
void
|
|
redraw(int high, int wide)
|
|
{
|
|
int tx, ty, i;
|
|
|
|
for (i = 0; i < numitems; i++) {
|
|
tx = (wide - XTextWidth(font, labels[i], strlen(labels[i]))) / 2;
|
|
ty = i*high + font->ascent + 1;
|
|
if (cur == i)
|
|
XSetForeground(dpy, gc, selbg);
|
|
else
|
|
XSetForeground(dpy, gc, normbg);
|
|
XFillRectangle(dpy, menuwin, gc, 0, i*high, wide, high);
|
|
if (cur == i)
|
|
XSetForeground(dpy, gc, selfg);
|
|
else
|
|
XSetForeground(dpy, gc, normfg);
|
|
XDrawString(dpy, menuwin, gc, tx, ty, labels[i], strlen(labels[i]));
|
|
}
|
|
}
|
|
|
|
/* warpmouse --- bring the mouse to the menu */
|
|
|
|
void
|
|
warpmouse(int wide, int high)
|
|
{
|
|
int offset;
|
|
|
|
/* move tip of pointer into middle of menu item */
|
|
offset = (font->ascent + font->descent + 1) / 2;
|
|
offset += 6; /* fudge factor */
|
|
|
|
XWarpPointer(dpy, None, menuwin, 0, 0, 0, 0,
|
|
wide/2, cur*high-high/2+offset);
|
|
}
|