summaryrefslogtreecommitdiff
path: root/xnext.c
diff options
context:
space:
mode:
Diffstat (limited to 'xnext.c')
-rw-r--r--xnext.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/xnext.c b/xnext.c
new file mode 100644
index 0000000..d02230d
--- /dev/null
+++ b/xnext.c
@@ -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;
+}