xmenu now can be operated by keyboard

This commit is contained in:
phillbush 2020-05-17 17:01:46 -03:00
parent 54931d1465
commit 858338d978
3 changed files with 129 additions and 5 deletions

View File

@ -1,4 +1,4 @@
# progrma name # program name
PROG = xmenu PROG = xmenu
# paths # paths

24
xmenu.1
View File

@ -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

108
xmenu.c
View File

@ -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,12 +687,14 @@ 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:
drawmenu(); if (ev.xexpose.count == 0)
drawmenu();
break; break;
case MotionNotify: case MotionNotify:
getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item); getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
@ -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;