kiss-mfavila-large/ports/snarf/tmp/9menu-1.10.shar

1142 lines
27 KiB
Bash
Executable File

#! /bin/sh
echo - 'README'
cat << 'EOF-README' > 'README'
Thu Mar 19 23:51:38 IST 2015
This is 9menu, a simple program that allows you to create X menus from the
shell, where each menu item will run a command. 9menu is intended for use
with 9wm, but can be used with any other window manager.
The idea of a command line menu generator is from xmenu, but xmenu was
exclusively a pop-up menu, not what I wanted.
The files are:
README --- this file
9menu.1 --- man page for 9menu
9menu.c --- source code
Imakefile --- a contributed Imakefile,
from Bengt Kleberg <bengt@softwell.se>
Makefile.noimake --- a simple makefile.
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
EOF-README
echo - '9menu.1'
cat << 'EOF-9menu.1' > '9menu.1'
.TH 9MENU 1 "Apr 16 2020"
.SH NAME
9menu \- create a menu to run commands
.SH SYNOPSIS
.B 9menu
[
.BI \-bg " background-color"
] [
.BI \-display " displayname"
] [
.BI \-file " name"
] [
.BI \-fg " foreground-color"
] [
.BI \-font " fname"
] [
.BI \-geometry " geom"
] [
.B \-iconic
] [
.BI \-label " name"
] [
.B \-path
] [
.B \-popdown
] [
.B \-popup
] [
.BI \-shell " prog"
] [
.B \-teleport
] [
.B \-version
] [
.B \-warp
]
.IR menuitem [: command ]
\&...
.SH DESCRIPTION
.I 9menu
is a simple program that accepts a list of menu item and command
pairs on the command line.
It creates a window that consists of nothing but a menu.
When a particular item is selected, the corresponding command is executed.
.PP
Either
.B Button1
or
.B Button3
may be used to select an item.
Alternatively, the UP-ARROW and DOWN-ARROW cursor keys may be used to
highlight different items, with ENTER used to select the
highlighted item.
.PP
Menu items and commands are separated by a colon. The colon and command
are optional. If they are missing, then the menu item is assumed to be
a command that can be executed directly.
.PP
A menu item consisting of the word
.B exit
causes
.I 9menu
to exit when it is selected.
Otherwise, to stop
.I 9menu ,
delete it using the window manager.
The
.B exit
menu item can be anywhere in the list, although by convention it is last.
If a command is supplied along with the
.B exit
item, that command is executed before
.I 9menu
exits.
.PP
If a menu item's command starts with the word
.BR exec ,
.I 9menu
ceases operating after launching it.
.PP
.I 9menu
accepts the following command line options,
listed alphabetically:
.TP
.BI \-bg " background-color"
Set the background color to
.IR background-color .
By default, the background color is white.
.TP
.BI \-display " displayname"
Use the X display
.IR displayname ,
instead of the default display.
.TP
.BI \-file " filename"
Read items to display from
.IR filename ,
in addition to any other command line arguments. This is intended for use
with
.B #!
in scripts.
A
.I filename
of
.B -
causes
.I 9menu
to read items from standard input.
.TP
.BI \-fg " foreground-color"
Set the foreground color to
.IR foreground-color .
By default, the foreground color is black.
.TP
.BI \-font " fname"
Use the font
.IR fname ,
instead of one of the default fonts built into
.IR 9wm .
.TP
.BI \-geometry " geom"
Use
.I geom
(a geometry in standard X format) as the geometry of the menu.
This is most useful for specifying the initial location of the menu.
Note that
.I 9menu
overrides the size part of the geometry specification. The window is
always just large enough to hold the menu.
.TP
.B \-iconic
Start up in the iconified state.
.TP
.BI \-label " name"
Change both the window and icon labels of the window to
.IR name .
The default label is the last component of the path used to run
.IR 9menu ,
typically,
.BR 9menu .
.TP
.B \-path
Append the current directory to the command search path.
.TP
.B \-popdown
Once an item is selected, the menu window automatically iconifies itself.
.TP
.B \-popup
Act like a pop-up menu. Once a menu item is selected,
.I 9menu
exits.
This option overrides
.BR \-popdown .
.TP
.BI \-shell " prog"
Use
.I prog
as the shell to run commands, instead of
.BR /bin/sh .
A popular alternative shell is
.IR rc (1).
If the shell cannot be executed,
.I 9menu
then
.I silently
falls back to using
.BR /bin/sh .
.TP
.B \-teleport
Move the menu to where the mouse is when the menu is uniconified.
This option is particularly useful when combined with
.BR \-popdown .
.TP
.B \-version
This option prints the version of
.I 9menu
on the standard output, and then exits with an exit value of zero.
.TP
.B \-warp
Warp the mouse to the menu when the menu is uniconified.
After the selection is made, restore the mouse to where it was.
This option is particularly useful when combined with
.BR \-popdown .
.SH EXAMPLES
.ft B
.nf
9menu \-label Remotes xterm 'acme:ssh acme xterm' 'herman:ssh herman 9term' &
.sp
9menu \-label 'X progs' gv xdvi xeyes xneko exit &
.ft
.fi
.SH SEE ALSO
.IR sam (1),
.IR 9term (1),
.IR 9wm (1),
.IR rc (1).
.PP
.IR "The Plan 9 Programmer's Manual" .
.SH VERSION
This man page documents
.I 9menu
version 1.10.
.PP
Source code is available from
.BR ftp://ftp.freefriends.org/arnold/Source/9menu.shar.gz .
.PP
The code with full history is also available via Git from
.BR http://github.com/arnoldrobbins/9menu .
.SH BUGS
This program has grown to have too many options.
.SH AUTHORS
The initial idea for this program was by Arnold Robbins, after having
worked with John Mackin's GWM Blit emulation.
Matty Farrow wrote a version using libXg, from which some ideas were borrowed.
This code was written by David Hogan and Arnold Robbins.
Rich Salz motivated the
.B \-shell
option.
Christopher Platt motivated the
.B \-teleport
option.
John O'Donnell supplied the basic code for the
.B \-fg
and
.B \-bg
options.
Peter Seebach provided the base code for the
.B \-file
and
.B \-path
options.
Matthias Bauer made it work with the keyboard.
EOF-9menu.1
echo - '9menu.c'
cat << 'EOF-9menu.c' > '9menu.c'
/*
* 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
*
* Copyright (c), Arnold Robbins and David Hogan
*
* Arnold Robbins
* arnold@skeeve.com
* October, 1994
*
* Code added to cause pop-up (unIconify) to move menu to mouse.
* Christopher Platt
* platt@coos.dartmouth.edu
* May, 1995
*
* Said code moved to -teleport option, and -warp option added.
* Arnold Robbins
* June, 1995
*
* Code added to allow -fg and -bg colors.
* John M. O'Donnell
* odonnell@stpaul.lampf.lanl.gov
* April, 1997
*
* Code added for -file and -path options.
* Peter Seebach
* seebs@plethora.net
* October, 2001
*
* Code added to allow up and down arrow keys to go up
* and down menu and RETURN to select an item.
* Matthias Bauer
* bauerm@immd1.informatik.uni-erlangen.de
* June, 2003
*
* spawn() changed to do exec directly if -popup, based on
* suggestion from
* Andrew Stribblehill
* a.d.stribblehill@durham.ac.uk
* June, 2004
*
* Allow "-" to mean read info from stdin.
* suggestion from
* Peter Bailey, by way of
* Andrew Stribblehill
* a.d.stribblehill@durham.ac.uk
* August, 2005
*
* Fix compile warnings (getcwd and getting a key sym).
* Arnold Robbins
* January, 2015.
*/
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.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>
#include <X11/XKBlib.h>
char version[] = "9menu version 1.10";
Display *dpy; /* lovely X stuff */
int screen;
Window root;
Window menuwin;
GC gc;
unsigned long black;
unsigned long white;
char *fgcname = NULL;
char *bgcname = NULL;
Colormap defcmap;
XColor color;
XFontStruct *font;
Atom wm_protocols;
Atom wm_delete_window;
int g_argc; /* for XSetWMProperties to use */
char **g_argv;
int f_argc; /* for labels read from files */
char **f_argv;
char *geometry = "";
int savex, savey;
Window savewindow;
char *fontlist[] = { /* default font list if no -font */
"pelm.latin1.9",
"lucm.latin1.9",
"blit",
"9x15bold",
"9x15",
"lucidasanstypewriter-12",
"fixed",
NULL
};
/* the 9menu icon, for garish window managers */
#define nine_menu_width 40
#define nine_menu_height 40
static unsigned char nine_menu_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00,
0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00,
0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04,
0x00, 0x80, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xff, 0xff,
0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00,
0xfc, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00,
0x80, 0xe0, 0x01, 0x04, 0x00, 0x80, 0xe0, 0x00, 0xfc, 0xff, 0xff, 0xe0,
0x01, 0xfc, 0xff, 0xff, 0x20, 0x03, 0x04, 0x00, 0x80, 0x00, 0x06, 0x04,
0x00, 0x80, 0x00, 0x0c, 0xfc, 0xff, 0xff, 0x00, 0x08, 0xfc, 0xff, 0xff,
0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00,
0xfc, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00,
0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00,
0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04,
0x00, 0x80, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xff, 0xff,
0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00,
0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/* Modify this to your liking */
#define CONFIG_MENU_UP_KEY XK_Up
#define CONFIG_MENU_DOWN_KEY XK_Down
#define CONFIG_MENU_SELECT_KEY XK_Return
char *progname; /* my name */
char *displayname; /* X display */
char *fontname; /* font */
char *labelname; /* window and icon name */
char *filename; /* file to read options or labels from */
bool popup = false; /* true if we're a popup window */
bool popdown = false; /* autohide after running a command */
bool iconic = false; /* start iconified */
bool teleport = false; /* teleport the menu */
bool warp = false; /* warp the mouse */
char **labels; /* list of labels and commands */
char **commands;
int numitems;
char *shell = "/bin/sh"; /* default shell */
extern void usage(), run_menu(), spawn(char *com), ask_wm_for_delete();
extern void reap(int sig), set_wm_hints(int wide, int high);
extern void redraw(int cur, int high, int wide);
extern void teleportmenu(int cur, int wide, int high);
extern void warpmouse(int cur, int wide, int high);
extern void restoremouse();
extern void memory(char *msg);
extern int args(int argc, char **argv);
/* memory --- print the out of memory message and die */
void
memory(char *s)
{
fprintf(stderr, "%s: couldn't allocate memory for %s: %s\n", progname, s, strerror(errno));
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] == '\0')
return -1;
for (i = 0; i < argc && argv[i] != NULL; i++) {
if (strcmp(argv[i], "-display") == 0) {
displayname = argv[i+1];
i++;
} else if (strcmp(argv[i], "-file") == 0) {
filename = argv[i+1];
i++;
} else if (strcmp(argv[i], "-font") == 0) {
fontname = argv[i+1];
i++;
} else if (strcmp(argv[i], "-geometry") == 0) {
geometry = argv[i+1];
i++;
} else if (strcmp(argv[i], "-label") == 0) {
labelname = argv[i+1];
i++;
} else if (strcmp(argv[i], "-shell") == 0) {
shell = argv[i+1];
i++;
} else if (strcmp(argv[i], "-popup") == 0)
popup = true;
else if (strcmp(argv[i], "-popdown") == 0)
popdown = true;
else if (strcmp(argv[i], "-fg") == 0)
fgcname = argv[++i];
else if (strcmp(argv[i], "-bg") == 0)
bgcname = argv[++i];
else if (strcmp(argv[i], "-iconic") == 0)
iconic = true;
else if (strcmp(argv[i], "-path") == 0) {
char pathbuf[MAXPATHLEN];
char *s, *t;
s = getenv("PATH");
if (s != NULL) {
/* append current dir to PATH */
char *cp = getcwd(pathbuf, MAXPATHLEN);
t = malloc(strlen(s) + strlen(pathbuf) + 7);
sprintf(t, "PATH=%s:%s", pathbuf, s);
putenv(t);
}
} else if (strcmp(argv[i], "-teleport") == 0)
teleport = true;
else if (strcmp(argv[i], "-warp") == 0)
warp = true;
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)
labelname = argv[0];
else
labelname = ++cp;
++argv;
--argc;
/* and program name for diagnostics */
progname = labelname;
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;
if (strcmp(filename, "-") == 0) {
fp = stdin;
} else {
fp = fopen(filename, "r");
}
if (fp == NULL) {
fprintf(stderr, "%s: couldn't open '%s': %s\n", progname,
filename, strerror(errno));
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];
}
/*
* 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 (fgcname == NULL
|| XParseColor(dpy, defcmap, fgcname, &color) == 0
|| XAllocColor(dpy, defcmap, &color) == 0)
black = BlackPixel(dpy, screen);
else
black = color.pixel;
if (bgcname == NULL
|| XParseColor(dpy, defcmap, bgcname, &color) == 0
|| XAllocColor(dpy, defcmap, &color) == 0)
white = WhitePixel(dpy, screen);
else
white = 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 = black^white;
gv.background = white;
gv.font = font->fid;
gv.function = GXxor;
gv.line_width = 0;
mask = GCForeground | GCBackground | GCFunction | GCFont | GCLineWidth;
gc = XCreateGC(dpy, root, mask, &gv);
signal(SIGCHLD, reap);
run_menu();
XCloseDisplay(dpy);
exit(0);
}
/* spawn --- run a command */
void
spawn(char *com)
{
int pid;
static char *sh_base = NULL;
if (sh_base == NULL) {
sh_base = strrchr(shell, '/');
if (sh_base != NULL)
sh_base++;
else
sh_base = shell;
}
/*
* Since -popup means run command and exit, just
* fall straight into exec code. Thus only fork
* if not popup.
*/
if (! popup) {
if (strncmp(com, "exec ", 5) != 0) {
pid = fork();
if (pid < 0) {
fprintf(stderr, "%s: can't fork: %s\n", progname, strerror(errno));
return;
} else if (pid > 0)
return;
} else {
com += 5;
}
}
close(ConnectionNumber(dpy));
execl(shell, sh_base, "-c", com, NULL);
execl("/bin/sh", "sh", "-c", com, NULL);
_exit(1);
}
/* reap --- collect dead children */
void
reap(int sig)
{
(void) wait((int *) NULL);
signal(sig, reap);
}
/* usage --- print a usage message and die */
void
usage()
{
fprintf(stderr, "usage: %s [-display displayname] [-font fname] ", progname);
fprintf(stderr, "[-file filename] [-path]");
fprintf(stderr, "[-geometry geom] [-shell shell] [-label name] ");
fprintf(stderr, "[-popup] [-popdown] [-iconic] [-teleport] ");
fprintf(stderr, "[-warp] [-version] menitem:command ...\n");
exit(0);
}
/* run_menu --- put up the window, execute selected commands */
void
run_menu()
{
XEvent ev;
XClientMessageEvent *cmsg;
KeySym key;
int i, cur, old, wide, high, ico, 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;
old = cur = -1;
high = font->ascent + font->descent + 1;
dy = numitems * high;
set_wm_hints(wide, dy);
ask_wm_for_delete();
#define MenuMask (ButtonPressMask|ButtonReleaseMask\
|LeaveWindowMask|PointerMotionMask|ButtonMotionMask\
|ExposureMask|StructureNotifyMask|KeyPressMask)
XSelectInput(dpy, menuwin, MenuMask);
XMapWindow(dpy, menuwin);
ico = 1; /* warp to first item */
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:
/* allow button 1 or button 3 */
if (ev.xbutton.button == Button2)
break;
i = ev.xbutton.y/high;
if (ev.xbutton.x < 0 || ev.xbutton.x > wide)
break;
else if (i < 0 || i >= numitems)
break;
if (warp)
restoremouse();
if (strcmp(labels[i], "exit") == 0) {
if (commands[i] != labels[i]) {
spawn(commands[i]);
}
return;
}
spawn(commands[i]);
if (popup)
return;
if (cur >= 0 && cur < numitems)
XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high);
if (popdown)
XIconifyWindow(dpy, menuwin, screen);
cur = -1;
break;
case ButtonPress:
case MotionNotify:
old = cur;
cur = ev.xbutton.y/high;
if (ev.xbutton.x < 0 || ev.xbutton.x > wide)
cur = -1;
else if (cur < 0 || cur >= numitems)
cur = -1;
if (cur == old)
break;
if (old >= 0 && old < numitems)
XFillRectangle(dpy, menuwin, gc, 0, old*high, wide, high);
if (cur >= 0 && cur < numitems)
XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high);
break;
case KeyPress:
/* http://stackoverflow.com/questions/9838385/replace-of-xkeycodetokeysym */
key = XkbKeycodeToKeysym(dpy, ev.xkey.keycode, 0,
ev.xkey.state & ShiftMask ? 1 : 0);
if (key != CONFIG_MENU_UP_KEY
&& key != CONFIG_MENU_DOWN_KEY
&& key != CONFIG_MENU_SELECT_KEY)
break;
/* adjust i so mapping will work */
if (key == CONFIG_MENU_UP_KEY) {
old = cur;
cur--;
i--;
} else if (key == CONFIG_MENU_DOWN_KEY) {
old = cur;
cur++;
i++;
}
while (cur < 0)
cur += numitems;
cur %= numitems;
if (key == CONFIG_MENU_UP_KEY || key == CONFIG_MENU_DOWN_KEY) {
if (cur == old)
break;
if (old >= 0 && old < numitems && cur != -1)
XFillRectangle(dpy, menuwin, gc, 0, old*high, wide, high);
if (cur >= 0 && cur < numitems && cur != -1)
XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high);
break;
}
if (warp)
restoremouse();
if (key == CONFIG_MENU_SELECT_KEY) {
if (strcmp(labels[cur], "exit") == 0) {
if (commands[cur] != labels[cur]) {
spawn(commands[cur]);
}
return;
}
spawn(commands[cur]);
}
if (popup)
return;
if (popdown)
XIconifyWindow(dpy, menuwin, screen);
break;
case LeaveNotify:
cur = old = -1;
XClearWindow(dpy, menuwin);
redraw(cur, high, wide);
break;
case ReparentNotify:
case ConfigureNotify:
/*
* ignore these, they come from XMoveWindow
* and are enabled by Struct..
*/
break;
case UnmapNotify:
ico = 1;
XClearWindow(dpy, menuwin);
break;
case MapNotify:
if (ico) {
if (teleport)
teleportmenu(i, wide, high);
else if (warp)
warpmouse(i, wide, high);
}
XClearWindow(dpy, menuwin);
redraw(cur = i, high, wide);
ico = 0;
break;
case Expose:
XClearWindow(dpy, menuwin);
redraw(cur, high, wide);
break;
case ClientMessage:
cmsg = &ev.xclient;
if (cmsg->message_type == wm_protocols
&& cmsg->data.l[0] == wm_delete_window)
return;
case MappingNotify: /* why do we get this? */
break;
}
}
}
/* set_wm_hints --- set all the window manager hints */
void
set_wm_hints(int wide, int high)
{
Pixmap iconpixmap;
XWMHints *wmhints;
XSizeHints *sizehints;
XClassHint *classhints;
XTextProperty wname, iname;
if ((sizehints = XAllocSizeHints()) == NULL)
memory("size hints");
if ((wmhints = XAllocWMHints()) == NULL)
memory("window manager hints");
if ((classhints = XAllocClassHint()) == NULL)
memory("class hints");
/* fill in hints in order to parse geometry spec */
sizehints->width = sizehints->min_width = sizehints->max_width = wide;
sizehints->height = sizehints->min_height = sizehints->max_height = high;
sizehints->flags = USSize|PSize|PMinSize|PMaxSize;
if (XWMGeometry(dpy, screen, geometry, "", 1, sizehints,
&sizehints->x, &sizehints->y,
&sizehints->width, &sizehints->height,
&sizehints->win_gravity) & (XValue|YValue))
sizehints->flags |= USPosition;
/* override -geometry for size of window */
sizehints->width = sizehints->min_width = sizehints->max_width = wide;
sizehints->height = sizehints->min_height = sizehints->max_height = high;
if (XStringListToTextProperty(& labelname, 1, & wname) == 0)
memory("window name structure");
if (XStringListToTextProperty(& labelname, 1, & iname) == 0)
memory("icon name structure");
menuwin = XCreateSimpleWindow(dpy, root, sizehints->x, sizehints->y,
sizehints->width, sizehints->height, 1, black, white);
iconpixmap = XCreateBitmapFromData(dpy, menuwin,
nine_menu_bits,
nine_menu_width,
nine_menu_height);
wmhints->icon_pixmap = iconpixmap;
wmhints->input = False; /* no keyboard input */
if (iconic)
wmhints->initial_state = IconicState;
else
wmhints->initial_state = NormalState;
wmhints->flags = IconPixmapHint | StateHint | InputHint;
classhints->res_name = progname;
classhints->res_class = "9menu";
#ifdef SET_PROPERTIES_MANUALLY
/*
* For some reason, XSetWMProperties (see below) is failing
* John O'Donnell replaces it with the following commands
* (this leaves out XSetWMClientMachine,
* and also environment variable checking from ClassHint)
*/
XSetWMName(dpy, menuwin, &wname);
XSetWMIconName(dpy, menuwin, &iname);
XSetCommand(dpy, menuwin, g_argv, g_argc);
XSetWMHints(dpy, menuwin, wmhints);
XSetClassHint(dpy, menuwin, classhints);
XSetWMNormalHints(dpy, menuwin, sizehints);
#else
XSetWMProperties(dpy, menuwin, & wname, & iname,
g_argv, g_argc, sizehints, wmhints, classhints);
#endif
}
/* ask_wm_for_delete --- jump through hoops to ask WM to delete us */
void
ask_wm_for_delete()
{
int status;
wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
status = XSetWMProtocols(dpy, menuwin, & wm_delete_window, 1);
if (status != True)
fprintf(stderr, "%s: could not ask for clean delete\n",
progname);
}
/* redraw --- actually redraw the menu */
void
redraw(int cur, 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;
XDrawString(dpy, menuwin, gc, tx, ty, labels[i], strlen(labels[i]));
}
if (cur >= 0 && cur < numitems)
XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high);
}
/* teleportmenu --- move the menu to the right place */
void
teleportmenu(int cur, int wide, int high)
{
int x, y, dummy;
Window wdummy;
if (XQueryPointer(dpy, menuwin, &wdummy, &wdummy, &x, &y,
&dummy, &dummy, &dummy))
XMoveWindow(dpy, menuwin, x-wide/2, y-cur*high-high/2);
}
/* warpmouse --- bring the mouse to the menu */
void
warpmouse(int cur, int wide, int high)
{
int dummy;
Window wdummy;
int offset;
/* move tip of pointer into middle of menu item */
offset = (font->ascent + font->descent + 1) / 2;
offset += 6; /* fudge factor */
if (XQueryPointer(dpy, menuwin, &wdummy, &wdummy, &savex, &savey,
&dummy, &dummy, &dummy))
XWarpPointer(dpy, None, menuwin, 0, 0, 0, 0,
wide/2, cur*high-high/2+offset);
}
/* restoremouse --- put the mouse back where it was */
void
restoremouse()
{
XWarpPointer(dpy, menuwin, root, 0, 0, 0, 0,
savex, savey);
}
EOF-9menu.c
echo - 'Imakefile'
cat << 'EOF-Imakefile' > 'Imakefile'
INCLUDES = -I$(TOP)
DEPLIBS = $(DEPXLIBONLY)
LOCAL_LIBRARIES = $(XLIBONLY)
DEFINES = -DSHAPE #-DDEBUG -DDEBUG_EV
SRCS = 9menu.c
OBJS = 9menu.o
ComplexProgramTarget(9menu)
EOF-Imakefile
echo - 'Makefile.noimake'
cat << 'EOF-Makefile.noimake' > 'Makefile.noimake'
# Makefile for 9menu.
#
# Edit to taste
#
# Arnold Robbins
# arnold@skeeve.atl.ga.us
CC = gcc
CFLAGS = -g -O
LIBS = -lX11
9menu: 9menu.c
$(CC) $(CFLAGS) 9menu.c $(LIBS) -o 9menu
EOF-Makefile.noimake