summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile34
-rw-r--r--xsnap.c178
3 files changed, 214 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..81bbe79
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+xsnap
+*.o
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..032c251
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+.POSIX:
+
+# This Makefile was generated by maje
+# See https://src.kaivo.net/dev/maje/ for more information
+# Do not edit this Makefile by hand
+
+CC=c99
+LD=$(CC)
+CFLAGS=-Wall -Wextra -Wpedantic -Werror -g
+LDFLAGS=
+LDLIBS= -lX11
+SRCDIR=.
+OBJDIR=.
+BINDIR=$(OBJDIR)
+LIBDIR=$(OBJDIR)
+DESTDIR=/usr/local
+
+all: $(BINDIR)/xsnap
+
+clean:
+ rm -f $(BINDIR)/xsnap $(OBJDIR)/*.o
+
+install: $(BINDIR)/xsnap
+ mkdir -p $(DESTDIR)/bin
+ cp $(BINDIR)/xsnap $(DESTDIR)/bin
+
+$(BINDIR)/xsnap: $(OBJDIR)/xsnap.o
+$(OBJDIR)/xsnap.o: $(SRCDIR)/xsnap.c
+ @mkdir -p $(@D)
+ $(CC) $(CFLAGS) -o $@ -c $(SRCDIR)/xsnap.c
+
+$(BINDIR)/xsnap:
+ @mkdir -p $(@D)
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/*.o $(LDLIBS)
diff --git a/xsnap.c b/xsnap.c
new file mode 100644
index 0000000..df5d55c
--- /dev/null
+++ b/xsnap.c
@@ -0,0 +1,178 @@
+#define _POSIX_C_SOURCE 200809L
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+
+/* MAJE_LDLIB=-lX11 */
+
+#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 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);
+}
+
+static int xsnap_error(Display *dpy, XErrorEvent *e)
+{
+ char buf[BUFSIZ];
+ XGetErrorText(dpy, e->error_code, buf, sizeof(buf));
+ fprintf(stderr, "xsnap: %s\n", buf);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ enum { NONE, LEFT, RIGHT, UP, DOWN } dir = NONE;
+
+ int c = 0;
+ while ((c = getopt(argc, argv, "lrud")) != -1) {
+ switch (c) {
+ case 'l':
+ dir = LEFT;
+ break;
+
+ case 'r':
+ dir = RIGHT;
+ break;
+
+ case 'u':
+ dir = UP;
+ break;
+
+ case 'd':
+ dir = DOWN;
+ break;
+
+ default:
+ return 1;
+ }
+ }
+
+ if (dir == NONE) {
+ fprintf(stderr, "xsnap: missing direction\n");
+ return 1;
+ }
+
+ /* TODO: if operand, use that, otherwise find focused */
+
+ Display *dpy = XOpenDisplay(NULL);
+ XSetErrorHandler(xsnap_error);
+
+ Window root = XDefaultRootWindow(dpy);
+
+ Window focused = root;
+ int revert = RevertToNone;
+ XGetInputFocus(dpy, &focused, &revert);
+ focused = find_normal_parent(dpy, focused);
+
+ XWindowAttributes rootattr = { 0 };
+ XGetWindowAttributes(dpy, root, &rootattr);
+
+ XWindowAttributes attributes = { 0 };
+ XGetWindowAttributes(dpy, focused, &attributes);
+
+ if (!attributes.override_redirect) {
+ fprintf(stderr, "can't override redirect\n");
+ exit(1);
+ }
+
+ int x = 0;
+ int y = 0;
+ unsigned w = 0;
+ unsigned h = 0;
+
+ switch (dir) {
+ case LEFT:
+ x = 0;
+ y = 0;
+ w = rootattr.width / 2;
+ h = rootattr.height;
+ break;
+
+ case RIGHT:
+ x = rootattr.width / 2;
+ y = 0;
+ w = rootattr.width / 2;
+ h = rootattr.height;
+ break;
+
+ case UP:
+ x = 0;
+ y = 0;
+ w = rootattr.width;
+ h = rootattr.height / 2;
+ break;
+
+ case DOWN:
+ x = 0;
+ y = rootattr.height / 2;
+ w = rootattr.width;
+ h = rootattr.height / 2;
+ break;
+
+ default:
+ fprintf(stderr, "somehow missing direction\n");
+ return 1;
+ break;
+ }
+
+ int r = XMoveResizeWindow(dpy, focused, x, y, w ? w : 1, h ? h : 1);
+ if (r != 0) {
+ XErrorEvent e = { .error_code = r };
+ xsnap_error(dpy, &e);
+ }
+
+ return 0;
+}