Created
October 17, 2011 17:22
-
-
Save Cloudef/1293166 to your computer and use it in GitHub Desktop.
Menu patch for dwm-6 (hg) (Keybindings, few fixes)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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