xmenu now can be operated by keyboard
This commit is contained in:
parent
54931d1465
commit
858338d978
|
@ -1,4 +1,4 @@
|
||||||
# progrma name
|
# program name
|
||||||
PROG = xmenu
|
PROG = xmenu
|
||||||
|
|
||||||
# paths
|
# paths
|
||||||
|
|
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.
|
The command is the string that will be output after selecting the item.
|
||||||
.IP
|
.IP
|
||||||
The newline terminates the item specification.
|
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
|
.SH RESOURCES
|
||||||
.B
|
.B
|
||||||
xmenu
|
xmenu
|
||||||
|
@ -83,7 +105,7 @@ The output is redirected to xargs to make a command to be run by the shell.
|
||||||
.EX
|
.EX
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
cat <<EOF | ./xmenu | xargs sh -c
|
cat <<EOF | xmenu | xargs sh -c
|
||||||
Applications
|
Applications
|
||||||
Web Browser firefox
|
Web Browser firefox
|
||||||
Image editor gimp
|
Image editor gimp
|
||||||
|
|
106
xmenu.c
106
xmenu.c
|
@ -6,6 +6,10 @@
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
#include <X11/Xresource.h>
|
#include <X11/Xresource.h>
|
||||||
|
#include <X11/XKBlib.h>
|
||||||
|
|
||||||
|
#define ITEMPREV 0
|
||||||
|
#define ITEMNEXT 1
|
||||||
|
|
||||||
/* macros */
|
/* macros */
|
||||||
#define LEN(x) (sizeof (x) / sizeof (x[0]))
|
#define LEN(x) (sizeof (x) / sizeof (x[0]))
|
||||||
|
@ -49,6 +53,7 @@ struct Item {
|
||||||
int y; /* item y position relative to menu */
|
int y; /* item y position relative to menu */
|
||||||
int h; /* item height */
|
int h; /* item height */
|
||||||
size_t labellen; /* strlen(label) */
|
size_t labellen; /* strlen(label) */
|
||||||
|
struct Item *prev; /* previous item */
|
||||||
struct Item *next; /* next item */
|
struct Item *next; /* next item */
|
||||||
struct Menu *submenu; /* submenu spawned by clicking on item */
|
struct Menu *submenu; /* submenu spawned by clicking on item */
|
||||||
};
|
};
|
||||||
|
@ -248,8 +253,13 @@ setupgeom(void)
|
||||||
static void
|
static void
|
||||||
setupgrab(void)
|
setupgrab(void)
|
||||||
{
|
{
|
||||||
XGrabPointer(dpy, rootwin, True, ButtonPressMask | ButtonReleaseMask,
|
if (XGrabPointer(dpy, rootwin, True, ButtonPressMask,
|
||||||
GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
|
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 */
|
/* allocate an item */
|
||||||
|
@ -360,6 +370,8 @@ parsestdin(void)
|
||||||
rootmenu = menu;
|
rootmenu = menu;
|
||||||
prevmenu = menu;
|
prevmenu = menu;
|
||||||
count = 1;
|
count = 1;
|
||||||
|
curritem->prev = NULL;
|
||||||
|
curritem->next = NULL;
|
||||||
} else if (level < prevmenu->level) { /* item is continuation of a parent menu*/
|
} else if (level < prevmenu->level) { /* item is continuation of a parent menu*/
|
||||||
for (menu = prevmenu, i = level;
|
for (menu = prevmenu, i = level;
|
||||||
menu != NULL && i < prevmenu->level;
|
menu != NULL && i < prevmenu->level;
|
||||||
|
@ -373,11 +385,19 @@ parsestdin(void)
|
||||||
;
|
;
|
||||||
|
|
||||||
item->next = curritem;
|
item->next = curritem;
|
||||||
|
|
||||||
|
curritem->prev = item;
|
||||||
|
curritem->next = NULL;
|
||||||
|
|
||||||
prevmenu = menu;
|
prevmenu = menu;
|
||||||
} else if (level == prevmenu->level) { /* item is a continuation of current menu */
|
} else if (level == prevmenu->level) { /* item is a continuation of current menu */
|
||||||
for (item = prevmenu->list; item->next != NULL; item = item->next)
|
for (item = prevmenu->list; item->next != NULL; item = item->next)
|
||||||
;
|
;
|
||||||
item->next = curritem;
|
item->next = curritem;
|
||||||
|
|
||||||
|
curritem->prev = item;
|
||||||
|
curritem->next = NULL;
|
||||||
|
|
||||||
} else if (level > prevmenu->level) { /* item begins a new menu */
|
} else if (level > prevmenu->level) { /* item begins a new menu */
|
||||||
menu = allocmenu(prevmenu, curritem, level);
|
menu = allocmenu(prevmenu, curritem, level);
|
||||||
|
|
||||||
|
@ -387,6 +407,9 @@ parsestdin(void)
|
||||||
item->submenu = menu;
|
item->submenu = menu;
|
||||||
menu->caller = item;
|
menu->caller = item;
|
||||||
|
|
||||||
|
curritem->prev = NULL;
|
||||||
|
curritem->next = NULL;
|
||||||
|
|
||||||
prevmenu = menu;
|
prevmenu = menu;
|
||||||
}
|
}
|
||||||
count++;
|
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 */
|
/* run event loop */
|
||||||
static void
|
static void
|
||||||
run(void)
|
run(void)
|
||||||
|
@ -623,11 +687,13 @@ run(void)
|
||||||
struct Menu *menu;
|
struct Menu *menu;
|
||||||
struct Item *item;
|
struct Item *item;
|
||||||
struct Item *previtem = NULL;
|
struct Item *previtem = NULL;
|
||||||
|
KeySym ksym;
|
||||||
XEvent ev;
|
XEvent ev;
|
||||||
|
|
||||||
while (!XNextEvent(dpy, &ev)) {
|
while (!XNextEvent(dpy, &ev)) {
|
||||||
switch(ev.type) {
|
switch(ev.type) {
|
||||||
case Expose:
|
case Expose:
|
||||||
|
if (ev.xexpose.count == 0)
|
||||||
drawmenu();
|
drawmenu();
|
||||||
break;
|
break;
|
||||||
case MotionNotify:
|
case MotionNotify:
|
||||||
|
@ -649,6 +715,7 @@ run(void)
|
||||||
case ButtonRelease:
|
case ButtonRelease:
|
||||||
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
|
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
|
||||||
if (menu != NULL && item != NULL) {
|
if (menu != NULL && item != NULL) {
|
||||||
|
selectitem:
|
||||||
if (item->label == NULL)
|
if (item->label == NULL)
|
||||||
break; /* ignore separators */
|
break; /* ignore separators */
|
||||||
if (item->submenu != NULL) {
|
if (item->submenu != NULL) {
|
||||||
|
@ -657,10 +724,45 @@ run(void)
|
||||||
printf("%s\n", item->output);
|
printf("%s\n", item->output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
currmenu->selected = currmenu->list;
|
||||||
drawmenu();
|
drawmenu();
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
return;
|
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;
|
break;
|
||||||
case LeaveNotify:
|
case LeaveNotify:
|
||||||
currmenu->selected = NULL;
|
currmenu->selected = NULL;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user