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
# 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.
.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
View File

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