diff --git a/config.def.h b/config.def.h index 9efa774..95499bd 100644 --- a/config.def.h +++ b/config.def.h @@ -2,6 +2,8 @@ /* appearance */ static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int tabModKey = 0x40; +static const unsigned int tabCycleKey = 0x17; static const unsigned int snap = 32; /* snap pixel */ static const int showbar = 1; /* 0 means no bar */ static const int topbar = 1; /* 0 means bottom bar */ @@ -95,6 +97,8 @@ static const Key keys[] = { TAGKEYS( XK_8, 7) TAGKEYS( XK_9, 8) { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY, XK_o, winview, {0} }, + { Mod1Mask, XK_Tab, alttab, {0} }, }; /* button definitions */ diff --git a/dwm.1 b/dwm.1 index ddc8321..f8a809e 100644 --- a/dwm.1 +++ b/dwm.1 @@ -110,6 +110,9 @@ Increase master area size. .B Mod1\-h Decrease master area size. .TP +.B Mod1\-o +Select view of the window in focus. The list of tags to be displayed is matched to the window tag list. +.TP .B Mod1\-Return Zooms/cycles focused window to/from master area (tiled layouts only). .TP diff --git a/dwm.c b/dwm.c index f1d86b2..71d0ebc 100644 --- a/dwm.c +++ b/dwm.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -142,6 +143,7 @@ typedef struct { } Rule; /* function declarations */ +static void alttab(const Arg *arg); static void applyrules(Client *c); static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); static void arrange(Monitor *m); @@ -168,6 +170,7 @@ static void expose(XEvent *e); static void focus(Client *c); static void focusin(XEvent *e); static void focusmon(const Arg *arg); +static void focusnext(const Arg *arg); static void focusstack(const Arg *arg); static Atom getatomprop(Client *c, Atom prop); static int getrootptr(int *x, int *y); @@ -229,6 +232,7 @@ static void updatewmhints(Client *c); static void view(const Arg *arg); static Client *wintoclient(Window w); static Monitor *wintomon(Window w); +static void winview(const Arg* arg); static int xerror(Display *dpy, XErrorEvent *ee); static int xerrordummy(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee); @@ -268,6 +272,8 @@ static Drw *drw; static Monitor *mons, *selmon; static Window root, wmcheckwin; +static int alt_tab_direction = 0; + /* configuration, allows nested code to access above variables */ #include "config.h" @@ -275,6 +281,79 @@ static Window root, wmcheckwin; struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; /* function implementations */ + +static void +alttab(const Arg *arg) { + + view(&(Arg){ .ui = ~0 }); + focusnext(&(Arg){ .i = alt_tab_direction }); + + int grabbed = 1; + int grabbed_keyboard = 1000; + for (int i = 0; i < 100; i += 1) { + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + + if (grabbed_keyboard != GrabSuccess) { + grabbed_keyboard = XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, + GrabModeAsync, GrabModeAsync, CurrentTime); + } + if (grabbed_keyboard == GrabSuccess) { + XGrabButton(dpy, AnyButton, AnyModifier, None, False, + BUTTONMASK, GrabModeAsync, GrabModeAsync, + None, None); + break; + } + nanosleep(&ts, NULL); + if (i == 100 - 1) + grabbed = 0; + } + + XEvent event; + Client *c; + Monitor *m; + XButtonPressedEvent *ev; + + while (grabbed) { + XNextEvent(dpy, &event); + switch (event.type) { + case KeyPress: + if (event.xkey.keycode == tabCycleKey) + focusnext(&(Arg){ .i = alt_tab_direction }); + break; + case KeyRelease: + if (event.xkey.keycode == tabModKey) { + XUngrabKeyboard(dpy, CurrentTime); + XUngrabButton(dpy, AnyButton, AnyModifier, None); + grabbed = 0; + alt_tab_direction = !alt_tab_direction; + winview(0); + } + break; + case ButtonPress: + ev = &(event.xbutton); + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if ((c = wintoclient(ev->window))) + focus(c); + XAllowEvents(dpy, AsyncBoth, CurrentTime); + break; + case ButtonRelease: + XUngrabKeyboard(dpy, CurrentTime); + XUngrabButton(dpy, AnyButton, AnyModifier, None); + grabbed = 0; + alt_tab_direction = !alt_tab_direction; + winview(0); + break; + } + } + return; +} + void applyrules(Client *c) { @@ -835,6 +914,28 @@ focusmon(const Arg *arg) focus(NULL); } +static void +focusnext(const Arg *arg) { + Monitor *m; + Client *c; + m = selmon; + c = m->sel; + + if (arg->i) { + if (c->next) + c = c->next; + else + c = m->clients; + } else { + Client *last = c; + if (last == m->clients) + last = NULL; + for (c = m->clients; c->next != last; c = c->next); + } + focus(c); + return; +} + void focusstack(const Arg *arg) { @@ -2092,6 +2193,26 @@ wintomon(Window w) return selmon; } +/* Selects for the view of the focused window. The list of tags */ +/* to be displayed is matched to the focused window tag list. */ +void +winview(const Arg* arg){ + Window win, win_r, win_p, *win_c; + unsigned nc; + int unused; + Client* c; + Arg a; + + if (!XGetInputFocus(dpy, &win, &unused)) return; + while(XQueryTree(dpy, win, &win_r, &win_p, &win_c, &nc) + && win_p != win_r) win = win_p; + + if (!(c = wintoclient(win))) return; + + a.ui = c->tags; + view(&a); +} + /* There's no way to check accesses to destroyed windows, thus those cases are * ignored (especially on UnmapNotify's). Other types of errors call Xlibs * default error handler, which may call exit. */