new wmiimenu, reads a list of newline-separated items from stdin,

and prints the user's selection on stdout. It seems to behave pretty well,
but some of the code definitaly needs a beauty makeover, I'll do that ASAP.
This commit is contained in:
Sander van Dijk 2006-02-10 23:57:21 +01:00
parent 6ce19c3999
commit 5dfffa855b
3 changed files with 127 additions and 300 deletions

View File

@ -18,8 +18,7 @@ OBJ_plumb = ${SRC_plumb:.c=.o}
SRC_warp = wmiiwarp.c
OBJ_warp = ${SRC_warp:.c=.o}
#all: wmiimenu wmiir wmiiplumb wmiiwarp
all: wmiir wmiiplumb wmiiwarp
all: wmiimenu wmiir wmiiplumb wmiiwarp
@echo built wmii commands
.c.o:

View File

@ -11,28 +11,14 @@ wmiimenu \- window manager improved 2 menu
is a generic, highly customizable, and efficient menu for the X Window System,
originally designed for
.BR wmii (1).
It supports arbitrary, user defined menu contents, which get executed.
Like wmii,
.B wmiimenu
also implements a socket-based fileserver, which is accessed to configure and
interoperate with other components.
It supports arbitrary, user defined menu contents, which get printed to stdout when selected.
.SS Options
.TP
.BI \-s " socketfile"
specifies the socketfile that
.B wmiimenu
should create.
.TP
.B \-v
prints version information to stderr, then exits.
.SS Customization
.B wmiimenu
is customized through manipulating its filesystem namespace.
In the default setup of
.BR wmii (1)
the namespace of
.B wmiimenu
can be found in /menu.
is customized by feeding it a list of newline-separated items from stdin.
.SH SEE ALSO
.BR wmiibar (1),
.BR wmiifs (1),

View File

@ -1,5 +1,6 @@
/*
* (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
* (C)opyright MMVI Sander van Dijk <a dot h dot vandijk at gmail dot com>
* See LICENSE file for license details.
*/
@ -10,6 +11,7 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
@ -17,25 +19,8 @@
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include "wmii.h"
#include "blitz.h"
/* array indexes for file pointers */
typedef enum {
M_CTL,
M_GEOMETRY,
M_PRE_COMMAND,
M_COMMAND,
M_HISTORY,
M_LOOKUP,
M_FONT,
M_SEL_BG_COLOR,
M_SEL_TEXT_COLOR,
M_SEL_BORDER_COLOR,
M_NORM_BG_COLOR,
M_NORM_TEXT_COLOR,
M_NORM_BORDER_COLOR,
M_LAST
} InputIndexes;
enum {
OFF_NEXT, OFF_PREV, OFF_CURR, OFF_LAST
@ -43,102 +28,62 @@ enum {
typedef struct Item Item;
struct Item {
File *file;
char *name;
Item *next;
Item *prev;
};
static IXPServer *ixps = 0;
static char *command = 0;
static Item *allitems = nil;
static char *fontstr = 0;
static char *normcolstr = 0;
static char *selcolstr = 0;
static int done = 0;
static int retvalue = 0;
static Display *dpy;
static GC gc;
static Window win;
static XRectangle rect;
static XRectangle mrect;
static int screen;
static char *sockfile = 0;
static File *files[M_LAST];
static size_t nitems = 0;
static Item *sel = nil;
static Item *items = nil;
static Item *selhist = nil;
static Item *history = nil;
static Item *offset[OFF_LAST];
static unsigned int cmdw = 0;
static Pixmap pmap;
static const int seek = 30; /* 30px */
static XFontStruct *font;
static Align align = SOUTH;
static void check_event(Connection * c);
static void check_event(void);
static void draw_menu(void);
static void handle_kpress(XKeyEvent * e);
static void set_text(char *text);
static void quit(void *obj, char *arg);
static void display(void *obj, char *arg);
static size_t update_items(char *prefix);
static Action acttbl[2] = {
{"quit", quit},
{"display", display}
};
static char version[] = "wmiimenu - " VERSION ", (C)opyright MMIV-MMVI Anselm R. Garbe\n";
static void
usage()
{
fprintf(stderr, "%s", "usage: wmiimenu [-a <address>] [-r] [-v]\n");
fprintf(stderr, "%s", "usage: wmiimenu [-v]\n");
exit(1);
}
static void
add_history(char *cmd)
{
Item *h, *new;
char buf[MAX_BUF];
snprintf(buf, MAX_BUF, "/history/%ld", (long) time(0));
new = cext_emallocz(sizeof(Item));
new->file = wmii_create_ixpfile(ixps, buf, cmd);
for(h = history; h && h->next; h = h->next);
if(!h)
history = new;
else {
h->next = new;
new->prev = h;
}
selhist = new;
}
static void
exec_item(char *cmd)
return_item(char *cmd)
{
char *rc = cmd;
if(!cmd || cmd[0] == 0)
return;
if(sel && sel->file->size)
rc = cmd = sel->file->content;
add_history(cmd);
if(sel && strlen(sel->name))
rc = cmd = sel->name;
if(files[M_PRE_COMMAND]->content) {
size_t len = strlen(cmd) + files[M_PRE_COMMAND]->size + 2;
rc = cext_emallocz(len);
snprintf(rc, len, "%s %s", (char *) files[M_PRE_COMMAND]->content,
cmd);
}
/* fallback */
wmii_spawn(dpy, rc);
/* cleanup */
if(files[M_PRE_COMMAND]->content)
free(rc);
}
static void
quit(void *obj, char *arg)
{
ixps->runlevel = SHUTDOWN;
fprintf(stdout, "%s", rc);
done = 1;
}
static void
@ -147,12 +92,13 @@ show()
set_text(0);
XMapRaised(dpy, win);
XSync(dpy, False);
update_items(files[M_COMMAND]->content);
update_items(command);
draw_menu();
while(XGrabKeyboard
(dpy, RootWindow(dpy, screen), True, GrabModeAsync,
GrabModeAsync, CurrentTime) != GrabSuccess)
usleep(1000);
usleep(1000);
}
static void
@ -163,29 +109,15 @@ hide()
XSync(dpy, False);
}
static void
display(void *obj, char *arg)
{
if(!arg)
return;
if(blitz_strtonum(arg, 0, 1))
show();
else
hide();
check_event(0);
}
void
set_text(char *text)
{
if(files[M_COMMAND]->content) {
free(files[M_COMMAND]->content);
files[M_COMMAND]->content = 0;
files[M_COMMAND]->size = 0;
if(command) {
free(command);
command = 0;
}
if(text && strlen(text)) {
files[M_COMMAND]->content = strdup(text);
files[M_COMMAND]->size = strlen(text);
command = strdup(text);
}
}
@ -200,8 +132,8 @@ update_offsets()
/* calc next offset */
for(i = offset[OFF_CURR]; i; i = i->next) {
w += XTextWidth(font, i->file->content,
strlen(i->file->content)) + mrect.height;
w += XTextWidth(font, i->name,
strlen(i->name)) + mrect.height;
if(w > mrect.width)
break;
}
@ -209,8 +141,8 @@ update_offsets()
w = cmdw + 2 * seek;
for(i = offset[OFF_CURR]->prev; i && i->prev; i = i->prev) {
w += XTextWidth(font, i->file->content,
strlen(i->file->content)) + mrect.height;
w += XTextWidth(font, i->name,
strlen(i->name)) + mrect.height;
if(w > mrect.width)
break;
}
@ -222,14 +154,7 @@ update_items(char *pattern)
{
size_t plen = pattern ? strlen(pattern) : 0, len, max = 0;
int matched = pattern ? plen == 0 : 1;
File *f, *p, *maxitem;
Item *i, *new;
if(!files[M_LOOKUP]->content)
return 0;
f = ixp_walk(ixps, files[M_LOOKUP]->content);
if(!f || !is_directory(f))
return 0;
Item *i, *new, *p, *maxitem = 0;
cmdw = 0;
offset[OFF_CURR] = offset[OFF_PREV] = offset[OFF_NEXT] = nil;
@ -242,7 +167,7 @@ update_items(char *pattern)
nitems = 0;
/* build new items */
for(p = f->content; p; p = p->next) {
for(p = allitems; p; p = p->next) {
len = strlen(p->name);
if(max < len) {
maxitem = p;
@ -253,36 +178,40 @@ update_items(char *pattern)
if(maxitem)
cmdw = XTextWidth(font, maxitem->name, max) + mrect.height;
for(p = f->content; p; p = p->next) {
for(p = allitems; p; p = p->next) {
if(matched || !strncmp(pattern, p->name, plen)) {
new = cext_emallocz(sizeof(Item));
new->file = p;
new->name = strdup(p->name);
new->next=0;
new->prev=0;
nitems++;
if(!items)
offset[OFF_CURR] = sel = items = i = new;
else {
else {
i->next = new;
new->prev = i;
i = new;
}
p->parent = 0; /* HACK to prevent doubled items */
}
}
for(p = f->content; p; p = p->next) {
if(p->parent && strstr(p->name, pattern)) {
for(p = allitems; p; p = p->next) {
if(pattern && strstr(p->name, pattern)) {
new = cext_emallocz(sizeof(Item));
new->file = p;
new->name = strdup(p->name);
new->next=0;
new->prev=0;
nitems++;
if(!items)
offset[OFF_CURR] = sel = items = i = new;
else {
i->next = new;
new->prev = i;
i = new;
if(strncmp(pattern, p->name, plen)) { /* yuck... */
i->next = new;
new->prev = i;
i = new;
}
}
} else
p->parent = f; /* restore HACK */
}
}
update_offsets();
@ -303,20 +232,13 @@ draw_menu()
d.rect = mrect;
d.rect.x = 0;
d.rect.y = 0;
d.bg =
blitz_loadcolor(dpy, screen, files[M_NORM_BG_COLOR]->content);
d.border =
blitz_loadcolor(dpy, screen,
files[M_NORM_BORDER_COLOR]->content);
blitz_loadcolor(dpy, screen, normcolstr, &(d.color));
blitz_drawlabelnoborder(dpy, &d);
/* print command */
d.align = WEST;
d.font = font;
d.fg =
blitz_loadcolor(dpy, screen,
files[M_NORM_TEXT_COLOR]->content);
d.data = files[M_COMMAND]->content;
d.data = command;
if(cmdw && sel)
d.rect.width = cmdw;
offx += d.rect.width;
@ -324,12 +246,7 @@ draw_menu()
d.align = CENTER;
if(sel) {
d.bg =
blitz_loadcolor(dpy, screen,
files[M_NORM_BG_COLOR]->content);
d.fg =
blitz_loadcolor(dpy, screen,
files[M_NORM_TEXT_COLOR]->content);
blitz_loadcolor(dpy, screen, normcolstr, &(d.color));
d.data = offset[OFF_PREV] ? "<" : nil;
d.rect.x = offx;
d.rect.width = seek;
@ -338,43 +255,21 @@ draw_menu()
/* determine maximum items */
for(i = offset[OFF_CURR]; i && i != offset[OFF_NEXT]; i = i->next) {
d.data = i->file->name;
d.data = i->name;
d.rect.x = offx;
d.rect.width =
XTextWidth(d.font, d.data, strlen(d.data)) + mrect.height;
offx += d.rect.width;
/*fprintf(stderr, "%s (%d, %d, %d, %d)\n", item->name, d.rect.x, d.rect.y, d.rect.width, d.rect.height); */
if(sel == i) {
d.bg =
blitz_loadcolor(dpy, screen,
files[M_SEL_BG_COLOR]->content);
d.fg =
blitz_loadcolor(dpy, screen,
files[M_SEL_TEXT_COLOR]->content);
d.border =
blitz_loadcolor(dpy, screen,
files[M_SEL_BORDER_COLOR]->content);
blitz_loadcolor(dpy, screen, selcolstr, &(d.color));
blitz_drawlabel(dpy, &d);
} else {
d.bg =
blitz_loadcolor(dpy, screen,
files[M_NORM_BG_COLOR]->content);
d.fg =
blitz_loadcolor(dpy, screen,
files[M_NORM_TEXT_COLOR]->content);
d.border =
blitz_loadcolor(dpy, screen,
files[M_NORM_BORDER_COLOR]->content);
blitz_loadcolor(dpy, screen, normcolstr, &(d.color));
blitz_drawlabelnoborder(dpy, &d);
}
}
d.bg =
blitz_loadcolor(dpy, screen,
files[M_NORM_BG_COLOR]->content);
d.fg =
blitz_loadcolor(dpy, screen,
files[M_NORM_TEXT_COLOR]->content);
blitz_loadcolor(dpy, screen, normcolstr, &(d.color));
d.data = offset[OFF_NEXT] ? ">" : nil;
d.rect.x = mrect.width - seek;
d.rect.width = seek;
@ -394,8 +289,8 @@ handle_kpress(XKeyEvent * e)
size_t len = 0;
text[0] = 0;
if(files[M_COMMAND]->content) {
cext_strlcpy(text, files[M_COMMAND]->content, sizeof(text));
if(command) {
cext_strlcpy(text, command, sizeof(text));
len = strlen(text);
}
buf[0] = 0;
@ -448,8 +343,8 @@ handle_kpress(XKeyEvent * e)
case XK_Tab:
if(!sel)
return;
set_text(sel->file->name);
update_items(files[M_COMMAND]->content);
set_text(sel->name);
update_items(command);
break;
case XK_Right:
if(!sel)
@ -459,29 +354,15 @@ handle_kpress(XKeyEvent * e)
} else
return;
break;
case XK_Down:
if(selhist) {
set_text(selhist->file->content);
if(selhist->next)
selhist = selhist->next;
}
update_items(files[M_COMMAND]->content);
break;
case XK_Up:
if(selhist) {
set_text(selhist->file->content);
if(selhist->prev)
selhist = selhist->prev;
}
update_items(files[M_COMMAND]->content);
break;
case XK_Return:
if(sel)
exec_item(sel->file->name);
return_item(sel->name);
else if(text)
exec_item(text);
return_item(text);
break;
case XK_Escape:
hide();
retvalue = 1;
done = 1;
break;
case XK_BackSpace:
if(len) {
@ -493,7 +374,7 @@ handle_kpress(XKeyEvent * e)
while((prev_nitems = nitems) && i && prev_nitems == update_items(text));
}
set_text(text);
update_items(files[M_COMMAND]->content);
update_items(command);
}
break;
default:
@ -504,7 +385,7 @@ handle_kpress(XKeyEvent * e)
else
cext_strlcpy(text, buf, sizeof(text));
set_text(text);
update_items(files[M_COMMAND]->content);
update_items(command);
}
}
if(sel) {
@ -520,7 +401,7 @@ handle_kpress(XKeyEvent * e)
}
static void
check_event(Connection * c)
check_event()
{
XEvent ev;
@ -546,8 +427,7 @@ update_geometry()
{
mrect = rect;
mrect.height = font->ascent + font->descent + 4;
if(align == SOUTH)
mrect.y = rect.height - mrect.height;
mrect.y = rect.height - mrect.height;
XMoveResizeWindow(dpy, win, mrect.x, mrect.y, mrect.width,
mrect.height);
XSync(dpy, False);
@ -558,59 +438,37 @@ update_geometry()
XSync(dpy, False);
}
static void
handle_after_write(IXPServer * s, File * f)
void
init_allitems()
{
int i;
size_t len;
if(files[M_CTL] == f) {
for(i = 0; i < 2; i++) {
len = strlen(acttbl[i].name);
if(!strncmp(acttbl[i].name, (char *) f->content, len)) {
if(strlen(f->content) > len) {
acttbl[i].func(0, &((char *) f->content)[len + 1]);
} else {
acttbl[i].func(0, 0);
}
break;
}
char text[4096];
allitems = cext_emallocz(sizeof(Item));
allitems->prev = 0;
Item *curitem = allitems;
char ch;
int i = 0;
while ((ch = (char)fgetc(stdin)) != EOF) {
if (!iscntrl(ch) && i<4095) {
text[i]=ch;
i++;
} else {
text[i] = 0;
if (strlen(text)) {
curitem->name = strdup(text);
curitem->next = cext_emallocz(sizeof(Item));
curitem->next->prev = curitem;
curitem = curitem->next;
}
i=0;
}
} else if(files[M_GEOMETRY] == f) {
if(f->content) {
if(!strncmp(f->content, "south", 6))
align = SOUTH;
else if(!strncmp(f->content, "north", 6))
align = NORTH;
update_geometry();
draw_menu();
}
} else if(files[M_FONT] == f) {
XFreeFont(dpy, font);
font = blitz_getfont(dpy, files[M_FONT]->content);
update_geometry();
draw_menu();
} else if(files[M_COMMAND] == f) {
update_items(files[M_COMMAND]->content);
draw_menu();
}
check_event(0);
if (curitem == allitems)
allitems = 0;
if (curitem->prev)
curitem->prev->next = 0;
free(curitem);
}
static void
handle_before_read(IXPServer * s, File * f)
{
char buf[64];
if(f != files[M_GEOMETRY])
return;
snprintf(buf, sizeof(buf), "%d %d %d %d", mrect.x, mrect.y,
mrect.width, mrect.height);
if(f->content)
free(f->content);
f->content = strdup(buf);
f->size = strlen(buf);
}
int
main(int argc, char *argv[])
{
@ -622,15 +480,9 @@ main(int argc, char *argv[])
for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version[0]);
fprintf(stdout, "%s", version);
exit(0);
break;
case 's':
if(i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
default:
usage();
break;
@ -644,41 +496,19 @@ main(int argc, char *argv[])
}
screen = DefaultScreen(dpy);
ixps = wmii_setup_server(sockfile);
/* set font and colors */
fontstr = getenv("WMII_FONT");
if (!fontstr)
fontstr = strdup(BLITZ_FONT);
font = blitz_getfont(dpy, fontstr);
normcolstr = getenv("WMII_NORMCOLORS");
if (!normcolstr || strlen(normcolstr) != 23)
normcolstr = strdup(BLITZ_NORMCOLORS);
selcolstr = getenv("WMII_SELCOLORS");
if (!selcolstr || strlen(selcolstr) != 23)
selcolstr = strdup(BLITZ_SELCOLORS);
/* init */
if(!(files[M_CTL] = ixp_create(ixps, "/ctl"))) {
perror("wmiimenu: cannot connect IXP server");
exit(1);
}
files[M_CTL]->after_write = handle_after_write;
files[M_GEOMETRY] = ixp_create(ixps, "/geometry");
files[M_GEOMETRY]->before_read = handle_before_read;
files[M_GEOMETRY]->after_write = handle_after_write;
files[M_PRE_COMMAND] = ixp_create(ixps, "/precmd");
files[M_COMMAND] = ixp_create(ixps, "/cmd");
files[M_COMMAND]->after_write = handle_after_write;
files[M_HISTORY] = ixp_create(ixps, "/history");
add_history("");
files[M_LOOKUP] = ixp_create(ixps, "/lookup");
files[M_FONT] = wmii_create_ixpfile(ixps, "/font", BLITZ_FONT);
files[M_FONT]->after_write = handle_after_write;
font = blitz_getfont(dpy, files[M_FONT]->content);
files[M_SEL_BG_COLOR] =
wmii_create_ixpfile(ixps, "/sstyle/bgcolor", BLITZ_SEL_BG_COLOR);
files[M_SEL_TEXT_COLOR] =
wmii_create_ixpfile(ixps, "/sstyle/fgcolor", BLITZ_SEL_FG_COLOR);
files[M_SEL_BORDER_COLOR] =
wmii_create_ixpfile(ixps, "/sstyle/bordercolor",
BLITZ_SEL_BORDER_COLOR);
files[M_NORM_BG_COLOR] =
wmii_create_ixpfile(ixps, "/nstyle/bgcolor", BLITZ_NORM_BG_COLOR);
files[M_NORM_TEXT_COLOR] =
wmii_create_ixpfile(ixps, "/nstyle/fgcolor", BLITZ_NORM_FG_COLOR);
files[M_NORM_BORDER_COLOR] =
wmii_create_ixpfile(ixps, "/nstyle/bordercolor",
BLITZ_NORM_BORDER_COLOR);
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask
@ -709,13 +539,25 @@ main(int argc, char *argv[])
XCreatePixmap(dpy, win, mrect.width, mrect.height,
DefaultDepth(dpy, screen));
/* initialize some more stuff */
update_geometry();
init_allitems();
if(!allitems) {
fprintf(stderr, "%s", "wmiimenu: feed me newline-separated items on stdin please.\n");
exit(1);
}
/* main event loop */
run_server_with_fd_support(ixps, ConnectionNumber(dpy), check_event,
0);
deinit_server(ixps);
show();
while (!done) {
check_event();
usleep(1000);
}
hide();
XFreePixmap(dpy, pmap);
XFreeGC(dpy, gc);
XCloseDisplay(dpy);
return 0;
return retvalue;
}