Skip to content

Instantly share code, notes, and snippets.

@Cloudef
Created October 17, 2011 17:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Cloudef/1293166 to your computer and use it in GitHub Desktop.
Save Cloudef/1293166 to your computer and use it in GitHub Desktop.
Menu patch for dwm-6 (hg) (Keybindings, few fixes)
changeset: 1590:260e6f30aa70
tag: tip
user: cloudef
date: Sat Dec 17 22:34:07 2011 +0200
summary: dwm-menu-patch
diff -r 40bff70c312f -r 260e6f30aa70 config.def.h
--- a/config.def.h Tue Nov 15 20:16:58 2011 +0100
+++ b/config.def.h Sat Dec 17 22:34:07 2011 +0200
@@ -36,6 +36,7 @@
/* key definitions */
#define MODKEY Mod1Mask
+#define CTRLKEY ControlMask
#define TAGKEYS(KEY,TAG) \
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
@@ -45,10 +46,34 @@
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+/* menus */
+#define MENUEND { NULL, NULL, NULL, {0} }
+#define MENUSEP { "" , NULL, NULL, {0} }
+
+/* title, submenu, function, argument */
+static const menuCtx rootMenu[] = {
+ MENUSEP,
+ { "EMPTY", NULL, NULL, {0} },
+ MENUSEP,
+ MENUEND,
+};
+
/* commands */
static const char *dmenucmd[] = { "dmenu_run", "-fn", font, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor, NULL };
static const char *termcmd[] = { "uxterm", NULL };
+
+/* menu key bindings
+ * only grabbed when menu is alive. */
+static Key menu_keys[] = {
+ { 0, XK_Up, menu_up, {0} },
+ { 0, XK_Down, menu_down, {0} },
+ { 0, XK_Right, menu_next, {0} },
+ { 0, XK_Left, menu_prev, {0} },
+ { 0, XK_Return, menu_accept, {0} },
+ { 0, XK_Escape, togglemenu, {0} }
+};
+
static Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
@@ -74,6 +99,7 @@
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+ { CTRLKEY, XK_space, togglemenu, {0} },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
@@ -101,5 +127,6 @@
{ ClkTagBar, 0, Button3, toggleview, {0} },
{ ClkTagBar, MODKEY, Button1, tag, {0} },
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
+ { ClkRootWin, 0, Button3, togglemenu, {0} },
};
diff -r 40bff70c312f -r 260e6f30aa70 dwm.c
--- a/dwm.c Tue Nov 15 20:16:58 2011 +0100
+++ b/dwm.c Sat Dec 17 22:34:07 2011 +0200
@@ -54,6 +54,7 @@
#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
#define TAGMASK ((1 << LENGTH(tags)) - 1)
#define TEXTW(X) (textnw(X, strlen(X)) + dc.font.height)
+#define TEXTW2(X) (textnw(X, strlen(X)))
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
@@ -154,7 +155,32 @@
int monitor;
} Rule;
+typedef struct menuCtx {
+ char *title;
+ const struct menuCtx *ctx;
+ void (*func)(const Arg *);
+ const Arg arg;
+} menuCtx;
+typedef struct menu_t {
+ Window win;
+ int x, y, w, h;
+ Drawable drawable;
+ struct menu_t *next, *child;
+ const struct menuCtx *ctx;
+ int sel;
+ int sely; /* hack :) */
+} menu_t;
+static menu_t *menu = NULL;
+static menu_t *cmenu = NULL;
+
/* function declarations */
+static void togglemenu(const Arg *arg);
+static void menu_up(const Arg *arg);
+static void menu_down(const Arg *arg);
+static void menu_next(const Arg *arg);
+static void menu_prev(const Arg *arg);
+static void menu_accept(const Arg *arg);
+static void motionnotify(XEvent *e);
static void applyrules(Client *c);
static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact);
static void arrange(Monitor *m);
@@ -423,15 +449,342 @@
c->mon->stack = c;
}
+/* 0 = DEFAULT
+ * 1 = FANCY STATUS COLORS */
+#define MENU_COLOR 0
+static menu_t* openmenupos( const menuCtx *ctx, int x, int y );
+static void closemenu( menu_t *target );
+static void
+updatemenu( menu_t *m, int x, int y ) {
+ Drawable dble = dc.drawable;
+ int ow = dc.w, oh = dc.h, ox = dc.x, oy = dc.y;
+ unsigned long *col;
+
+ if(!m) return;
+ const int osel = m->sel;
+
+ dc.drawable = m->drawable;
+#ifdef XFT
+ XftDrawChange(dc.xftdrawable, dc.drawable);
+#endif
+ size_t i;
+ dc.x = 0; dc.y = 0;
+ dc.w = m->w;
+
+ for(i = 0; m->ctx[i].title; ++i)
+ {
+ if (x > 0 && x < m->w &&
+ y > dc.y && y < dc.y + dc.font.height &&
+ strlen(m->ctx[i].title))
+ m->sel = i;
+
+ /* this check cause there is keyboard input too */
+#if MENU_COLOR
+ if(m->sel == i) { col = dc.colors[1]; m->sely = dc.y; }
+ else col = dc.colors[0];
+#else
+ if(m->sel == i) { col = dc.sel; m->sely = dc.y; }
+ else col = dc.norm;
+#endif
+
+ if(strlen(m->ctx[i].title))
+ {
+ dc.h = dc.font.height;
+ drawtext(m->ctx[i].title, col, False);
+ }
+ else
+ {
+ dc.h = 4;
+
+ /* draw line */
+ XGCValues gcv;
+ XRectangle r = { dc.x , dc.y, dc.w , dc.h };
+
+#if MENU_COLOR
+ XSetForeground(dpy, dc.gc, dc.colors[1][ColBG]);
+#else
+ XSetForeground(dpy, dc.gc, dc.sel[ColBG]);
+#endif
+ XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
+
+#if MENU_COLOR
+ gcv.foreground = dc.colors[1][ColBG];
+#else
+ gcv.foreground = dc.sel[ColBG];
+#endif
+
+ XChangeGC(dpy, dc.gc, GCForeground, &gcv);
+ XDrawLine(dpy, dc.drawable, dc.gc, dc.x, dc.y + dc.h / 2, dc.w, dc.y + dc.h / 2);
+ }
+ dc.y += dc.h;
+ }
+
+ XCopyArea(dpy, dc.drawable, m->win, dc.gc, 0, 0, m->w, m->h, 0, 0);
+ XSync(dpy, False);
+
+ dc.w = ow; dc.h = oh; dc.x = ox; dc.y = oy;
+ dc.drawable = dble;
+#ifdef XFT
+ XftDrawChange(dc.xftdrawable, dc.drawable);
+#endif
+
+ /* only for mouse */
+ if(m->sel != osel)
+ {
+ if(m->child) { closemenu(m->child); m->child = NULL; }
+ if(m->ctx[m->sel].ctx != NULL)
+ m->child = openmenupos( m->ctx[m->sel].ctx, m->x + m->w, m->y + m->sely );
+ }
+}
+
+menu_t*
+wintomenu( Window win )
+{
+ menu_t *m;
+
+ if(!menu) return(NULL);
+ for( m = menu; m; m = m->next )
+ if(m->win == win) return(m);
+
+ return(NULL);
+}
+
+menu_t*
+ctxtomenu( const menuCtx *ctx )
+{
+ menu_t *m;
+
+ if(!menu) return(NULL);
+ for( m = menu; m; m = m->next )
+ if(m->ctx == ctx) return(m);
+
+ return(NULL);
+}
+
+static int
+createmenu( menu_t *m, const menuCtx *ctx, int x, int y ) {
+ int valid = 0;
+ size_t i, j;
+ unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
+ KeyCode code;
+
+ if(ctxtomenu(ctx)) return(-1);
+
+ m->x = x;
+ m->y = y;
+ m->w = 0;
+ m->h = 0;
+ m->ctx = ctx;
+ m->sel = -1;
+ m->child= NULL;
+
+ for(i = 0; m->ctx[i].title; ++i)
+ {
+#ifdef XFT
+ if(m->w < TEXTW2(m->ctx[i].title))
+ m->w = TEXTW2(m->ctx[i].title);
+#else
+ if(m->w < TEXTW(m->ctx[i].title))
+ m->w = TEXTW(m->ctx[i].title);
+#endif
+ if(strlen(m->ctx[i].title)) { m->h += dc.font.height; valid = 1; }
+ else m->h += 4;
+ }
+ if(i == 0 || !valid) return(-1);
+
+ XSetWindowAttributes wattr;
+ wattr.override_redirect = True;
+ wattr.background_pixmap = ParentRelative;
+ wattr.event_mask = ButtonPressMask|ExposureMask;
+
+ m->win = XCreateWindow(dpy, root, m->x, m->y, m->w, m->h, 0, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWBackPixmap|CWOverrideRedirect|CWEventMask, &wattr);
+
+ XSelectInput(dpy, m->win, PointerMotionMask|ButtonPressMask|KeyPressMask);
+
+ XDefineCursor(dpy, m->win, cursor[CurNormal]);
+ XMapRaised(dpy, m->win);
+
+ m->drawable = XCreatePixmap(dpy, root, m->w, m->h, DefaultDepth(dpy, screen));
+ updatemenu(m, m->x, m->y);
+
+ if(selmon && selmon->sel)
+ { grabbuttons(selmon->sel,False); XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); XSync(dpy,False); }
+
+ /* keyboard input */
+ updatenumlockmask();
+ {
+ XUngrabKey(dpy, AnyKey, AnyModifier, m->win);
+ for(i = 0; i < LENGTH(menu_keys); i++) {
+ if((code = XKeysymToKeycode(dpy, menu_keys[i].keysym)))
+ for(j = 0; j < LENGTH(modifiers); j++)
+ XGrabKey(dpy, code, menu_keys[i].mod | modifiers[j], m->win,
+ True, GrabModeAsync, GrabModeAsync);
+ }
+ }
+
+ XSetInputFocus(dpy, m->win, RevertToPointerRoot, CurrentTime);
+
+ return(0);
+}
+
+static menu_t*
+openmenupos( const menuCtx *ctx, int x, int y ) {
+ menu_t **m, *mm;
+
+ m = &menu;
+ mm = menu;
+ for(; mm; mm = mm->next)
+ m = &mm->next;
+
+ *m = calloc(1, sizeof(menu_t));
+ if(!*m)
+ return NULL;
+
+ (*m)->next = NULL;
+ if(createmenu(*m, ctx, x, y) == -1)
+ { free(*m); *m = NULL; }
+
+ return *m;
+}
+
+static menu_t*
+openmenu( const menuCtx *ctx ) {
+ int x, y;
+ getrootptr( &x, &y );
+ return openmenupos( ctx, x, y );
+}
+
+static void
+closemenu( menu_t *target )
+{
+ menu_t **m, *mm;
+
+ if(!target) return;
+
+ /* this is not valid child anymore */
+ for( mm = menu; mm; mm = mm->next )
+ if(mm->child == target) mm->child = NULL;
+
+ /* close childs */
+ for( mm = target->child; mm; mm = mm->child )
+ closemenu( mm );
+
+ /* remove */
+ m = &menu;
+ for( mm = menu; mm && mm->next && mm->next != target; mm = mm->next )
+ m = &mm;
+
+ (*m)->next = target->next;
+ XSetInputFocus(dpy, (*m)->win, RevertToPointerRoot, CurrentTime);
+
+ XFreePixmap(dpy, target->drawable);
+ XDestroyWindow(dpy, target->win);
+ free(target);
+ XSync(dpy, False);
+}
+
+static void
+closemenus(void) {
+ if(!menu) return;
+
+ menu_t *m = menu, *next = NULL;
+ while(m)
+ {
+ XDestroyWindow(dpy, m->win);
+
+ next = m->next; free(m);
+ m = next;
+ }
+ XSync(dpy, False);
+ menu = NULL;
+}
+
+static void
+buttonmenu( menu_t *m, int x, int y )
+{
+ if(!m) return;
+ updatemenu(m, x, y);
+ if(m->sel == -1) return;
+ if(!m->ctx[m->sel].func) return;
+
+ m->ctx[m->sel].func(&m->ctx[m->sel].arg);
+ closemenus();
+}
+
+void
+togglemenu(const Arg *arg) {
+ if(!menu)
+ openmenu(&rootMenu[0]);
+ else
+ closemenus();
+}
+
+void
+menu_up(const Arg *arg) {
+ size_t i;
+ cmenu->sel--;
+ if(cmenu->sel < 0)
+ for(i = 0; cmenu->ctx[i].title; i++) cmenu->sel = i;
+ updatemenu(cmenu, 0, 0);
+}
+
+void
+menu_down(const Arg *arg) {
+ cmenu->sel++;
+ if(!cmenu->ctx[cmenu->sel].title) cmenu->sel = 0;
+ updatemenu(cmenu, 0, 0);
+}
+
+void
+menu_next(const Arg *arg) {
+ if(cmenu->sel > -1)
+ {
+ if(cmenu->child) { closemenu(cmenu->child); cmenu->child = NULL; }
+ if(cmenu->ctx[cmenu->sel].ctx)
+ {
+ cmenu->child = openmenupos( cmenu->ctx[cmenu->sel].ctx, cmenu->x + cmenu->w, cmenu->y + cmenu->sely );
+ if(!cmenu->child) return;
+ cmenu->child->sel = 0;
+ updatemenu(cmenu->child, 0, 0);
+ }
+ }
+}
+
+void
+menu_prev(const Arg *arg) {
+ if(cmenu != menu) closemenu(cmenu);
+}
+
+void
+menu_accept(const Arg *arg) {
+ buttonmenu(cmenu, 0, 0);
+}
+
void
buttonpress(XEvent *e) {
unsigned int i, x, click;
Arg arg = {0};
Client *c;
Monitor *m;
+ menu_t *mm;
XButtonPressedEvent *ev = &e->xbutton;
click = ClkRootWin;
+
+ /* do action on Button1, or close menu if menu isn't clicked
+ * with Button1 */
+ if(ev->button == Button1)
+ {
+ if(menu)
+ {
+ if(!(mm = wintomenu(ev->window)))
+ {
+ closemenus();
+ } else
+ { buttonmenu(mm, ev->x, ev->y); return; }
+ }
+ }
+
/* focus monitor if necessary */
if((m = wintomon(ev->window)) && m != selmon) {
unfocus(selmon->sel, True);
@@ -842,6 +1195,7 @@
void
focus(Client *c) {
+ if(menu) return;
if(!c || !ISVISIBLE(c))
for(c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
/* was if(selmon->sel) */
@@ -1090,6 +1444,16 @@
&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
&& keys[i].func)
keys[i].func(&(keys[i].arg));
+
+ if(menu && (cmenu = wintomenu(ev->window)))
+ {
+ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
+ for(i = 0; i < LENGTH(menu_keys); i++)
+ if(keysym == menu_keys[i].keysym
+ && CLEANMASK(menu_keys[i].mod) == CLEANMASK(ev->state)
+ && menu_keys[i].func)
+ menu_keys[i].func(&(menu_keys[i].arg));
+ }
}
void
@@ -1208,6 +1572,13 @@
static Monitor *mon = NULL;
Monitor *m;
XMotionEvent *ev = &e->xmotion;
+ menu_t *mm;
+
+ if( (mm = wintomenu(ev->window)) )
+ {
+ updatemenu(mm, ev->x, ev->y);
+ return;
+ }
if(ev->window != root)
return;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment