diff options
Diffstat (limited to 'xnext.c')
-rw-r--r-- | xnext.c | 162 |
1 files changed, 162 insertions, 0 deletions
@@ -0,0 +1,162 @@ +#define _POSIX_C_SOURCE 200809L +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <X11/Xlib.h> + +#define WM_STATE_FORMAT 32 + +static Bool is_normal(Display *dpy, Window win) +{ + static Atom state = None; + if (state == None) { + state = XInternAtom(dpy, "WM_STATE", True); + if (state == None) { + return False; + } + } + + Atom type = AnyPropertyType; + int format = WM_STATE_FORMAT; + unsigned long nitems = 0; + unsigned long bytes = 0; + unsigned char *prop = NULL; + + int r = XGetWindowProperty(dpy, + win, + state, + 0, + 0, + False, + type, + &type, + &format, + &nitems, + &bytes, + &prop); + + if (r == Success && prop != NULL) { + XFree(prop); + return True; + } + + return False; +} + +static int compar(const void *a, const void *b) +{ + const Window *wina = a; + const Window *winb = b; + if (*wina > *winb) { + return 1; + } + if (*wina < *winb) { + return -1; + } + return 0; +} + +static Window *walk_tree(Display *dpy, Window win, Window *list, size_t *nwin) +{ + Window root = win, parent = win, *children = NULL; + unsigned int nchildren = 0; + if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) { + return NULL; + } + + for (unsigned int i = 0; i < nchildren; i++) { + list = walk_tree(dpy, children[i], list, nwin); + + if (is_normal(dpy, children[i])) { + list = realloc(list, sizeof(*list) * (*nwin + 1)); + if (list == NULL) { + return NULL; + } + list[*nwin] = children[i]; + (*nwin)++; + } + } + + return list; +} + +int switch_to(Display *dpy, Window root, Window prev, Window win) +{ + Atom ActiveWindowAtom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + XEvent xev = { + .xclient.type = ClientMessage, + .xclient.window = win, + .xclient.message_type = ActiveWindowAtom, + .xclient.format = 32, + .xclient.data.l = { 1, 1, 0, 0 }, + }; + XSendEvent(dpy, root, False, SubstructureRedirectMask, &xev); + XMapRaised(dpy, win); + XRaiseWindow(dpy, win); + XWarpPointer(dpy, prev, win, 0, 0, 0, 0, 0, 0); + XSetInputFocus(dpy, win, RevertToNone, CurrentTime); + XCloseDisplay(dpy); + return 0; +} + +Window find_normal_parent(Display *dpy, Window win) +{ + if (win == PointerRoot || win == None || is_normal(dpy, win)) { + return win; + } + + Window root = win, parent = win, *children = NULL; + unsigned int nchildren = 0; + if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) { + return None; + } + + if (parent == win || is_normal(dpy, parent)) { + return parent; + } + + return find_normal_parent(dpy, win); +} + +int main(int argc, char *argv[]) +{ + int previous = 0; + + int c = 0; + while ((c = getopt(argc, argv, "p")) != -1) { + switch (c) { + case 'p': + previous = 1; + break; + default: + return 1; + } + } + + Display *dpy = XOpenDisplay(""); + Window root = XDefaultRootWindow(dpy); + + Window focused = root; + int revert = RevertToNone; + XGetInputFocus(dpy, &focused, &revert); + focused = find_normal_parent(dpy, focused); + + size_t nwin = 0; + Window *list = walk_tree(dpy, root, NULL, &nwin); + if (list == NULL) { + return 1; + } + + qsort(list, nwin, sizeof(*list), compar); + if (focused == PointerRoot || focused == None) { + return switch_to(dpy, root, focused, list[previous ? nwin - 1 : 0]); + } + + for (size_t i = 0; i < nwin; i++) { + if (list[i] == focused) { + return switch_to(dpy, root, focused, list[(i + (previous ? -1 : 1)) % nwin]); + } + } + + return 1; +} |