xmenu now can be operated by keyboard
This commit is contained in:
parent
54931d1465
commit
858338d978
24
xmenu.1
24
xmenu.1
|
@ -37,6 +37,28 @@ separating the item above from the item below.
|
|||
The command is the string that will be output after selecting the item.
|
||||
.IP
|
||||
The newline terminates the item specification.
|
||||
.SH USAGE
|
||||
.B xmenu
|
||||
is controlled by the mouse,
|
||||
but can also be controlled by the keyboard.
|
||||
Items can be selected using the arrow keys,
|
||||
Tab (with and without Shift),
|
||||
Enter and Esc.
|
||||
.TP
|
||||
.BR Down ", " Tab
|
||||
Cycle through the items in the regular direction.
|
||||
.TP
|
||||
.BR Up ", " Shift-Tab
|
||||
Cycle through the items in the reverse direction.
|
||||
.TP
|
||||
.BR Right ", " Enter
|
||||
Select the highlighted item.
|
||||
.TP
|
||||
.B Left
|
||||
Go to the menu above.
|
||||
.TP
|
||||
.B Esc
|
||||
Go to the menu above or exit xmenu.
|
||||
.SH RESOURCES
|
||||
.B
|
||||
xmenu
|
||||
|
@ -83,7 +105,7 @@ The output is redirected to xargs to make a command to be run by the shell.
|
|||
.EX
|
||||
#!/bin/sh
|
||||
|
||||
cat <<EOF | ./xmenu | xargs sh -c
|
||||
cat <<EOF | xmenu | xargs sh -c
|
||||
Applications
|
||||
Web Browser firefox
|
||||
Image editor gimp
|
||||
|
|
108
xmenu.c
108
xmenu.c
|
@ -6,6 +6,10 @@
|
|||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/XKBlib.h>
|
||||
|
||||
#define ITEMPREV 0
|
||||
#define ITEMNEXT 1
|
||||
|
||||
/* macros */
|
||||
#define LEN(x) (sizeof (x) / sizeof (x[0]))
|
||||
|
@ -49,6 +53,7 @@ struct Item {
|
|||
int y; /* item y position relative to menu */
|
||||
int h; /* item height */
|
||||
size_t labellen; /* strlen(label) */
|
||||
struct Item *prev; /* previous item */
|
||||
struct Item *next; /* next item */
|
||||
struct Menu *submenu; /* submenu spawned by clicking on item */
|
||||
};
|
||||
|
@ -248,8 +253,13 @@ setupgeom(void)
|
|||
static void
|
||||
setupgrab(void)
|
||||
{
|
||||
XGrabPointer(dpy, rootwin, True, ButtonPressMask | ButtonReleaseMask,
|
||||
GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
|
||||
if (XGrabPointer(dpy, rootwin, True, ButtonPressMask,
|
||||
GrabModeAsync, GrabModeAsync, None,
|
||||
None, CurrentTime) != GrabSuccess)
|
||||
errx(1, "cannot grab pointer");
|
||||
if (XGrabKeyboard(dpy, rootwin, True, GrabModeAsync,
|
||||
GrabModeAsync, CurrentTime) != GrabSuccess)
|
||||
errx(1, "cannot grab keyboard");
|
||||
}
|
||||
|
||||
/* allocate an item */
|
||||
|
@ -360,6 +370,8 @@ parsestdin(void)
|
|||
rootmenu = menu;
|
||||
prevmenu = menu;
|
||||
count = 1;
|
||||
curritem->prev = NULL;
|
||||
curritem->next = NULL;
|
||||
} else if (level < prevmenu->level) { /* item is continuation of a parent menu*/
|
||||
for (menu = prevmenu, i = level;
|
||||
menu != NULL && i < prevmenu->level;
|
||||
|
@ -373,11 +385,19 @@ parsestdin(void)
|
|||
;
|
||||
|
||||
item->next = curritem;
|
||||
|
||||
curritem->prev = item;
|
||||
curritem->next = NULL;
|
||||
|
||||
prevmenu = menu;
|
||||
} else if (level == prevmenu->level) { /* item is a continuation of current menu */
|
||||
for (item = prevmenu->list; item->next != NULL; item = item->next)
|
||||
;
|
||||
item->next = curritem;
|
||||
|
||||
curritem->prev = item;
|
||||
curritem->next = NULL;
|
||||
|
||||
} else if (level > prevmenu->level) { /* item begins a new menu */
|
||||
menu = allocmenu(prevmenu, curritem, level);
|
||||
|
||||
|
@ -387,6 +407,9 @@ parsestdin(void)
|
|||
item->submenu = menu;
|
||||
menu->caller = item;
|
||||
|
||||
curritem->prev = NULL;
|
||||
curritem->next = NULL;
|
||||
|
||||
prevmenu = menu;
|
||||
}
|
||||
count++;
|
||||
|
@ -616,6 +639,47 @@ drawmenu(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* cycle through the items; non-zero direction is next, zero is prev */
|
||||
static struct Item *
|
||||
itemcycle(int direction)
|
||||
{
|
||||
struct Item *item;
|
||||
struct Item *lastitem;
|
||||
|
||||
item = NULL;
|
||||
|
||||
if (direction == ITEMNEXT) {
|
||||
if (currmenu->selected == NULL)
|
||||
item = currmenu->list;
|
||||
else if (currmenu->selected->next != NULL)
|
||||
item = currmenu->selected->next;
|
||||
|
||||
while (item != NULL && item->label == NULL)
|
||||
item = item->next;
|
||||
|
||||
if (item == NULL)
|
||||
item = currmenu->list;
|
||||
} else {
|
||||
for (lastitem = currmenu->list;
|
||||
lastitem != NULL && lastitem->next != NULL;
|
||||
lastitem = lastitem->next)
|
||||
;
|
||||
|
||||
if (currmenu->selected == NULL)
|
||||
item = lastitem;
|
||||
else if (currmenu->selected->prev != NULL)
|
||||
item = currmenu->selected->prev;
|
||||
|
||||
while (item != NULL && item->label == NULL)
|
||||
item = item->prev;
|
||||
|
||||
if (item == NULL)
|
||||
item = lastitem;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/* run event loop */
|
||||
static void
|
||||
run(void)
|
||||
|
@ -623,12 +687,14 @@ run(void)
|
|||
struct Menu *menu;
|
||||
struct Item *item;
|
||||
struct Item *previtem = NULL;
|
||||
KeySym ksym;
|
||||
XEvent ev;
|
||||
|
||||
while (!XNextEvent(dpy, &ev)) {
|
||||
switch(ev.type) {
|
||||
case Expose:
|
||||
drawmenu();
|
||||
if (ev.xexpose.count == 0)
|
||||
drawmenu();
|
||||
break;
|
||||
case MotionNotify:
|
||||
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
|
||||
|
@ -649,6 +715,7 @@ run(void)
|
|||
case ButtonRelease:
|
||||
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
|
||||
if (menu != NULL && item != NULL) {
|
||||
selectitem:
|
||||
if (item->label == NULL)
|
||||
break; /* ignore separators */
|
||||
if (item->submenu != NULL) {
|
||||
|
@ -657,10 +724,45 @@ run(void)
|
|||
printf("%s\n", item->output);
|
||||
return;
|
||||
}
|
||||
currmenu->selected = currmenu->list;
|
||||
drawmenu();
|
||||
break;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
case ButtonPress:
|
||||
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
|
||||
if (menu == NULL || item == NULL)
|
||||
return;
|
||||
break;
|
||||
case KeyPress:
|
||||
ksym = XkbKeycodeToKeysym(dpy, ev.xkey.keycode, 0, 0);
|
||||
|
||||
if (ksym == XK_Escape && currmenu == rootmenu)
|
||||
return;
|
||||
|
||||
/* Shift-Tab = ISO_Left_Tab */
|
||||
if (ksym == XK_Tab && (ev.xkey.state & ShiftMask))
|
||||
ksym = XK_ISO_Left_Tab;
|
||||
|
||||
/* cycle through menu */
|
||||
item = NULL;
|
||||
if (ksym == XK_ISO_Left_Tab || ksym == XK_Up) {
|
||||
item = itemcycle(ITEMPREV);
|
||||
} else if (ksym == XK_Tab || ksym == XK_Down) {
|
||||
item = itemcycle(ITEMNEXT);
|
||||
} else if ((ksym == XK_Return || ksym == XK_Right) &&
|
||||
currmenu->selected != NULL) {
|
||||
item = currmenu->selected;
|
||||
goto selectitem;
|
||||
} else if ((ksym == XK_Escape || ksym == XK_Left) &&
|
||||
currmenu->parent != NULL) {
|
||||
item = currmenu->parent->selected;
|
||||
setcurrmenu(currmenu->parent);
|
||||
} else
|
||||
break;
|
||||
currmenu->selected = item;
|
||||
drawmenu();
|
||||
break;
|
||||
case LeaveNotify:
|
||||
currmenu->selected = NULL;
|
||||
|
|
Loading…
Reference in New Issue
Block a user