changeset 0:f0372d3356b4 default tip

initial import
author Yasuri <linuxoverwindows@charliesmp.net>
date Sun, 30 Aug 2026 12:47:10 -0500
parents
children
files .hgignore LICENCE Makefile README.md cursors/92h.sh cursors/9cursors.c src/config.h src/include/types.h src/include/util.h src/include/yawc.h src/nein_cursor.h src/util.c src/yawc.c
diffstat 13 files changed, 1688 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,7 @@
+syntax: glob
+*.swp
+*.o
+yawc
+compile_flags.txt
+*.log
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENCE	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,7 @@
+ISC License
+
+Copyright 2026 uint
+
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,27 @@
+CC      ?= cc
+CFLAGS  ?= -D_POSIX_C_SOURCE=200809L -Isrc/include -O2 -std=c99
+LDFLAGS ?= 
+LIBS    ?= $(shell pkg-config --static --libs swc wayland-server xkbcommon libinput pixman-1 libdrm wld libudev)
+
+PREFIX  ?= /usr/local
+DESTDIR ?= 
+BINDIR   = $(DESTDIR)$(PREFIX)/bin
+
+OBJS = src/yawc.o src/util.o
+
+.PHONY: all clean install
+
+all: yawc
+
+yawc: $(OBJS)
+	$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+.c.o:
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+install: yawc
+	mkdir -p $(BINDIR)
+	cp yawc $(BINDIR)/
+
+clean:
+	rm -f yawc $(OBJS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,47 @@
+yawc
+----
+
+Yet Another Wayland Compositor. This was forked from [tohu](https://srcdump.net/shrub/tohu/),
+and it doesn't really add much aside from removing server side decoration support in favour 
+of dual borders and an autostart mechanism, hence why it is Yet Another Wayland Compositor,
+as it doesn't actually add much.
+(this version has plan9 cursors from [hevel](https://git.sr.ht/~dlm/hevel))
+build
+-----
+yawc builds using make (bmake doesn't work):
+```
+make
+make install
+```
+
+binds
+-----
+mod+return to create terminal
+
+mod+space to run launcher
+
+mod+w to close window
+
+mod+m1 to move
+
+mod+m3 to resize
+
+mod+f to fullscreen
+
+mod+number to workspace
+
+customise in source/config.h
+
+credits
+-------
+yawc was originally forked from [tohu](https://srcdump.net/shrub/tohu/) 
+
+tohu was originally forked from [wsxwm](https://git.sr.ht/~uint/wsxwm) 
+
+neuswc is a fork of [swc](github.com/michaelforney/swc) by [michael forney](https://mforney.org/) 
+
+TODO
+-------
+
+- [x] Autostart
+- [ ] Lua Configuration
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cursors/92h.sh	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,224 @@
+#!/bin/sh
+# convert plan 9 cursor definitions in a C file to packed argb cursor_data[] + cursor_metadata[] for swc
+# asage:
+#   ./92h.sh 9cursors.c > out.c
+#   ./92h.sh 9cursors.c tl t tr l r bl b br > out.c   # explicit order/subset
+
+infile=${1:-/dev/stdin}
+shift 2>/dev/null || true
+
+# remaining args are optional, desired cursor names in output order.
+order="$*"
+
+awk -v ORDER="$order" '
+function hexval(c) {
+  c = toupper(c)
+  if (c >= "0" && c <= "9") return c + 0
+  return 10 + index("ABCDEF", c) - 1
+}
+function hex2dec(s,    i,n) {
+  sub(/^0[xX]/, "", s)
+  n = 0
+  for (i = 1; i <= length(s); i++)
+    n = n * 16 + hexval(substr(s, i, 1))
+  return n
+}
+function pow2(n,    i,p) { p=1; for(i=0;i<n;i++) p*=2; return p }
+function bit16msb(v, col,    shift) {
+  shift = 15 - col
+  return int(v / P2[shift]) % 2
+}
+function pix(setb, clrb) {
+  #   clr=0,set=0 => transparent
+  #   clr=1,set=0 => black
+  #   clr=0,set=1 => white
+  #   clr=1,set=1 => invert (approximate as white for ARGB cursors)
+  if (!clrb && !setb) return "0x00000000"
+  if (clrb && !setb) return "0xff000000"
+  if (!clrb && setb) return "0xffffffff"
+  return "0xffffffff"
+}
+function min(a,b){ return (a<b)?a:b }
+function max(a,b){ return (a>b)?a:b }
+
+BEGIN {
+  # precompute powers of two up to 15
+  for (i=0;i<16;i++) P2[i]=pow2(i)
+
+  want_cnt = 0
+  if (ORDER != "") {
+    n = split(ORDER, want, /[[:space:]]+/)
+    for (i=1;i<=n;i++) if (want[i] != "") { want_cnt++; wantlist[want[i]]=i }
+  }
+
+  in_cursor = 0
+  arr = 0
+  c1 = c2 = 0
+  name_cnt = 0
+}
+
+# start of a cursor block: cursor foo = {
+match($0, /^[[:space:]]*Cursor[[:space:]]+[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/) {
+  # extract the name
+  line = $0
+  sub(/^[[:space:]]*Cursor[[:space:]]+/, "", line)
+  cur = line
+  sub(/[[:space:]]*=.*/, "", cur)
+
+  in_cursor = 1
+  got_hot = 0
+  arr = 0
+  c1 = 0
+  c2 = 0
+  next
+}
+
+# inside cursor: hotspot like {-7, -7}, compute it properly so it offsets,
+in_cursor && !got_hot && match($0, /\{[[:space:]]*-?[0-9]+[[:space:]]*,[[:space:]]*-?[0-9]+[[:space:]]*\}/) {
+  s = substr($0, RSTART+1, RLENGTH-2)
+  gsub(/[[:space:]]*/, "", s)
+  split(s, a, ",")
+  hx[cur] = a[1] + 0
+  hy[cur] = a[2] + 0
+  got_hot = 1
+}
+
+in_cursor {
+  while (match($0, /0[xX][0-9A-Fa-f]+/)) {
+    tok = substr($0, RSTART, RLENGTH)
+    val = hex2dec(tok)
+
+    if (arr == 0) arr = 1
+    if (arr == 1) {
+      clr[cur, c1++] = val
+      if (c1 >= 32) arr = 2
+    } else {
+      set[cur, c2++] = val
+    }
+
+    $0 = substr($0, RSTART + RLENGTH)
+  }
+}
+
+# end of cursor block
+in_cursor && /};/ {
+  in_cursor = 0
+
+  if (!(seen[cur]++)) {
+    name_cnt++
+    names[name_cnt] = cur
+  }
+
+  if (c1 < 32 || c2 < 32) {
+    warn[cur] = 1
+  }
+}
+
+END {
+  # decide output order
+  outn = 0
+  if (want_cnt > 0) {
+    for (i=1; i<=want_cnt; i++) out[++outn] = want[i]
+  } else {
+    for (i=1; i<=name_cnt; i++) out[++outn] = names[i]
+  }
+
+  print "/* generated from plan 9 cursor data */"
+  print "#include <stdint.h>"
+  print "#include <stddef.h>"
+  print ""
+  print "enum {"
+  for (oi=1; oi<=outn; oi++) {
+    n = out[oi]
+    if (n == "") continue
+    name = toupper(n)
+    gsub(/[^A-Z0-9]+/, "_", name)
+    printf "\tNEIN_CURSOR_%s = %d,\n", name, oi-1
+  }
+  print "};"
+  print ""
+  print "static const uint32_t nein_cursor_data[] = {"
+
+  off = 0
+  perline = 0
+
+  for (oi=1; oi<=outn; oi++) {
+    n = out[oi]
+    if (n == "") continue
+    if (!(seen[n])) {
+      printf "/* WARNING: cursor %s not found */\n", n
+      continue
+    }
+    if (warn[n]) {
+      printf "/* WARNING: cursor %s looked incomplete (expected 32 clr + 32 set bytes) */\n", n
+    }
+
+    # build full 16x16 pixels and compute tight bbox
+    minx=16; miny=16; maxx=-1; maxy=-1
+    for (y=0; y<16; y++) {
+      rowc = clr[n,2*y]*256 + clr[n,2*y+1]
+      rows = set[n,2*y]*256 + set[n,2*y+1]
+      for (x=0; x<16; x++) {
+        pb = pix(bit16msb(rows,x), bit16msb(rowc,x))
+        full[n,y,x] = pb
+        if (pb != "0x00000000") {
+          minx = min(minx, x); maxx = max(maxx, x)
+          miny = min(miny, y); maxy = max(maxy, y)
+        }
+      }
+    }
+
+    # if entirely transparent, fall back to 1x1
+    if (maxx < 0) { minx=0; maxx=0; miny=0; maxy=0 }
+
+    w = maxx - minx + 1
+    h = maxy - miny + 1
+
+    #offset hostspot
+    xhot = -hx[n]
+    yhot = -hy[n]
+
+    hx2 = xhot - minx
+    hy2 = yhot - miny
+
+    meta_name[oi] = n
+    meta_w[oi] = w
+    meta_h[oi] = h
+    meta_hx[oi] = hx2
+    meta_hy[oi] = hy2
+    meta_off[oi] = off
+
+    for (y=miny; y<=maxy; y++) {
+      for (x=minx; x<=maxx; x++) {
+        v = full[n,y,x]
+        if (perline == 0) printf "\t"
+        printf "%s,", v
+        perline++
+        off++
+        if (perline >= 6) { printf "\n"; perline = 0 }
+        else printf " "
+      }
+    }
+    if (perline != 0) { printf "\n"; perline = 0 }
+  }
+
+  print "};"
+  print ""
+  print "struct nein_cursor_meta {"
+  print "\tuint32_t width, height;"
+  print "\tint32_t hotspot_x, hotspot_y;"
+  print "\tsize_t offset;"
+  print "};"
+  print ""
+  print "static const struct nein_cursor_meta nein_cursor_metadata[] = {"
+
+  for (oi=1; oi<=outn; oi++) {
+    n = meta_name[oi]
+    if (n == "") continue
+    printf "\t{ %d, %d, %d, %d, %d }, /* %s */\n",
+      meta_w[oi], meta_h[oi], meta_hx[oi], meta_hy[oi], meta_off[oi], n
+  }
+
+  print "};"
+}
+' "$infile"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cursors/9cursors.c	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,234 @@
+#include "dat.h"
+#include "fns.h"
+#include <cursor.h>
+#include <draw.h>
+#include <fcall.h>
+#include <frame.h>
+#include <keyboard.h>
+#include <libc.h>
+#include <mouse.h>
+#include <thread.h>
+#include <u.h>
+
+Cursor crosscursor = {
+    {-7, -7},
+    {
+        0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03,
+        0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0,
+        0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
+    },
+    {
+        0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+        0x80, 0x01, 0x80, 0x7F, 0xFE, 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80,
+        0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00,
+    }};
+
+Cursor boxcursor = {
+    {-7, -7},
+    {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
+        0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    },
+    {
+        0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x70, 0x0E, 0x70,
+        0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
+        0x70, 0x0E, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00,
+    }};
+
+Cursor sightcursor = {
+    {-7, -7},
+    {
+        0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, 0xF3, 0xCF, 0xE3,
+        0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7,
+        0xF3, 0xCF, 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,
+    },
+    {
+        0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, 0x41, 0x82, 0x41,
+        0x82, 0x41, 0x82, 0x7F, 0xFE, 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82,
+        0x41, 0x82, 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,
+    }};
+
+Cursor whitearrow = {
+    {0, 0},
+    {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 0xFF, 0xF0, 0xFF,
+        0xF0, 0xFF, 0xF8, 0xFF, 0xFC, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE,
+        0xFF, 0xFC, 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40,
+    },
+    {
+        0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C, 0xC0, 0x30, 0xC0,
+        0x30, 0xC0, 0x38, 0xC0, 0x1C, 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E,
+        0xDF, 0x1C, 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40,
+    }};
+
+Cursor query = {
+    {-7, -7},
+    {
+        0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0x7c, 0x7e, 0x78,
+        0x7e, 0x00, 0xfc, 0x01, 0xf8, 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0,
+        0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0,
+    },
+    {
+        0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, 0x38, 0x1c, 0x00,
+        0x3c, 0x00, 0x78, 0x00, 0xf0, 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80,
+        0x03, 0x80, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00,
+    }};
+
+Cursor tl = {
+    {-4, -4},
+    {
+        0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff, 0xa0, 0x01, 0xb0,
+        0x01, 0xd0, 0x01, 0x11, 0xff, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
+        0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00,
+    },
+    {
+        0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00, 0x5f, 0xfe, 0x4f,
+        0xfe, 0x0f, 0xfe, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
+        0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00,
+    }};
+
+Cursor skull = {
+    {-7, -7},
+    {
+        0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7, 0xff, 0xff, 0xff,
+        0xff, 0x3f, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff,
+        0xff, 0xff, 0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,
+    },
+    {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xE7, 0xE7, 0x3F,
+        0xFC, 0x0F, 0xF0, 0x0D, 0xB0, 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC,
+        0xE4, 0x27, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    }};
+
+Cursor t = {
+    {-7, -8},
+    {
+        0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0, 0x1c, 0x70, 0x10,
+        0x10, 0x0c, 0x60, 0xfc, 0x7f, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+        0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    },
+    {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x80, 0x0f,
+        0xe0, 0x03, 0x80, 0x03, 0x80, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    }};
+
+Cursor tr = {
+    {-11, -4},
+    {
+        0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1, 0x80, 0x05, 0x80,
+        0x0d, 0x80, 0x0b, 0xff, 0x88, 0x00, 0x88, 0x0,  0x88, 0x00, 0x88,
+        0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8,
+    },
+    {
+        0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e, 0x7f, 0xfa, 0x7f,
+        0xf2, 0x7f, 0xf0, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
+        0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00,
+    }};
+
+Cursor r = {
+    {-8, -7},
+    {
+        0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58, 0x04, 0x68, 0x04,
+        0x6c, 0x04, 0x06, 0x04, 0x02, 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68,
+        0x04, 0x58, 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0,
+    },
+    {
+        0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x90, 0x03,
+        0x90, 0x03, 0xf8, 0x03, 0xfc, 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90,
+        0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00,
+    }};
+
+Cursor br = {
+    {-11, -11},
+    {
+        0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00,
+        0x88, 0x00, 0x88, 0x00, 0x88, 0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d,
+        0x80, 0x05, 0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f,
+    },
+    {
+        0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x0,  0x70, 0x00,
+        0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2,
+        0x7f, 0xfa, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00,
+    }};
+
+Cursor b = {
+    {-7, -7},
+    {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x80,
+        0x01, 0x80, 0x01, 0x80, 0x01, 0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10,
+        0x1c, 0x70, 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00,
+    },
+    {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
+        0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0,
+        0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    }};
+
+Cursor bl = {
+    {-4, -11},
+    {
+        0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11,
+        0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01,
+        0xa0, 0x01, 0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00,
+    },
+    {
+        0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e,
+        0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe,
+        0x5f, 0xfe, 0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0,
+    }};
+
+Cursor l = {
+    {-7, -7},
+    {
+        0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20, 0x16, 0x20, 0x36,
+        0x20, 0x60, 0x20, 0x40, 0x20, 0x60, 0x20, 0x36, 0x20, 0x16, 0x20,
+        0x1a, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0,
+    },
+    {
+        0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x09, 0xc0, 0x09,
+        0xc0, 0x1f, 0xc0, 0x3f, 0xc0, 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0,
+        0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00,
+    }};
+
+Cursor *corners[9] = {
+    &tl, &t, &tr, &l, nil, &r, &bl, &b, &br,
+};
+
+void
+iconinit(void)
+{
+  background =
+      allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x777777FF);
+
+  /* greys are multiples of 0x11111100+0xFF, 14* being palest */
+  cols[BACK] =
+      allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0xFFFFFFFF ^ reverse);
+  cols[BORD] =
+      allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x999999FF ^ reverse);
+  cols[TEXT] =
+      allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x000000FF ^ reverse);
+  cols[HTEXT] = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x000000FF);
+  if (!reverse) {
+    cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0xCCCCCCFF);
+    titlecol = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DGreygreen);
+    lighttitlecol =
+        allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DPalegreygreen);
+  } else {
+    cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DPurpleblue);
+    titlecol = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DPurpleblue);
+    lighttitlecol = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x222222FF);
+  }
+  dholdcol = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DMedblue);
+  lightholdcol = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DGreyblue);
+  paleholdcol = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DPalegreyblue);
+  paletextcol =
+      allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x666666FF ^ reverse);
+  sizecol = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DRed);
+
+  if (reverse == 0)
+    holdcol = dholdcol;
+  else
+    holdcol = paleholdcol;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config.h	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,60 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <xkbcommon/xkbcommon-keysyms.h>
+
+#include "types.h"
+#include "yawc.h"
+
+static const struct config cfg = {
+	.motion_throttle_hz = 60,
+	.border_col_active = 0xfffde9ca,
+	.border_col_normal = 0xff76532B,
+	.border_width = 3,
+	.border_col_active_outer = 0xff44270a,
+	.border_col_normal_outer = 0xff44270a,
+	.border_width_outer = 3,
+	.gaps = 0,
+	.spawn_w = 640,
+	.spawn_h = 480,
+	.repeat_rate = 50,
+	.repeat_delay = 225,
+};
+
+static const char* termcmd[] = { "st-wl", NULL };
+static const char* menucmd[] = { "bemenu-run", "--fn", "Spleen 14", NULL };
+static struct bind binds[] = {
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_Return,	   { .v = termcmd }, spawn },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_space,  { .v = menucmd }, spawn },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_e,      { .v = NULL },    quit },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_Tab,    { .v = NULL },    focus_next },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_f,      { .v = NULL },    fullscreen },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_w,      { .v = NULL },    kill_sel },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_1,      { .ui = 1 },      workspace_goto },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_2,      { .ui = 2 },      workspace_goto },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_3,      { .ui = 3 },      workspace_goto },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_4,      { .ui = 4 },      workspace_goto },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_5,      { .ui = 5 },      workspace_goto },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_6,      { .ui = 6 },      workspace_goto },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_7,      { .ui = 7 },      workspace_goto },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_8,      { .ui = 8 },      workspace_goto },
+	{ SWC_BINDING_KEY,    MOD4,        XKB_KEY_9,      { .ui = 9 },      workspace_goto },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_1,      { .ui = 1 },      workspace_moveto },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_2,      { .ui = 2 },      workspace_moveto },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_3,      { .ui = 3 },      workspace_moveto },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_4,      { .ui = 4 },      workspace_moveto },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_5,      { .ui = 5 },      workspace_moveto },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_6,      { .ui = 6 },      workspace_moveto },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_7,      { .ui = 7 },      workspace_moveto },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_8,      { .ui = 8 },      workspace_moveto },
+	{ SWC_BINDING_KEY,    MOD4|SHFT,   XKB_KEY_9,      { .ui = 9 },      workspace_moveto },
+	{ SWC_BINDING_BUTTON, MOD4,        BTN_LEFT,       { .v = NULL },    mouse_move },
+	{ SWC_BINDING_BUTTON, MOD4,        BTN_RIGHT,      { .v = NULL },    mouse_resize },
+};
+
+static const char* autostart[] = {
+/*	"pgrep -x pipewire || pipewire", */
+	"swaybg -i $HOME/wall.jpg",
+ }; 
+
+#endif /* CONFIG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/include/types.h	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,95 @@
+#ifndef TYPES_H
+#define TYPES_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <swc.h>
+#include <wayland-server.h>
+
+enum {
+	BTN_LEFT   = 0x110,
+	BTN_RIGHT  = 0x111,
+	BTN_MIDDLE = 0x112,
+};
+
+enum {
+	MOD1 = SWC_MOD_ALT,
+	MOD4 = SWC_MOD_LOGO,
+	SHFT = SWC_MOD_SHIFT,
+	CTRL = SWC_MOD_CTRL,
+};
+
+union arg {
+	int            i;
+	uint32_t       ui;
+	float          f;
+	const void*    v;
+};
+
+struct bind {
+	uint32_t       type;
+	uint32_t       mods;
+	uint32_t       ksym;
+	union arg      arg;
+	void           (*fn)(void* data, uint32_t time, uint32_t value, uint32_t state);
+};
+
+struct client {
+	struct wl_list link;
+	struct swc_window* win;
+	struct screen* scr;
+	bool           mapped;
+	bool           floating;
+	bool           fullscreen;
+	int32_t        x;
+	int32_t        y;
+	uint32_t       w;
+	uint32_t       h;
+	uint32_t       ws;
+};
+
+struct config {
+	uint32_t       motion_throttle_hz;
+	uint32_t       border_col_active;
+	uint32_t       border_col_normal;
+	uint32_t       border_width;
+	uint32_t       border_col_active_outer;
+	uint32_t       border_col_normal_outer;
+	uint32_t       border_width_outer;
+	uint32_t       gaps;
+	uint32_t       spawn_w;
+	uint32_t       spawn_h;
+	int32_t        repeat_rate;
+	int32_t        repeat_delay;
+};
+
+struct grab {
+	bool           active;
+	bool           resize;
+	struct client* c;
+};
+
+struct screen {
+	struct wl_list link;
+	struct swc_screen* scr;
+	int32_t        x;
+	int32_t        y;
+	uint32_t       w;
+	uint32_t       h;
+};
+
+struct wm {
+	struct wl_display* dpy;
+	struct wl_event_loop* ev_loop;
+
+	struct wl_list screens;
+	struct wl_list clients;
+
+	struct screen* sel_screen;
+	struct client* sel_client;
+	struct grab    grab;
+	uint8_t        ws;
+};
+
+#endif /* TYPES_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/include/util.h	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,18 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <swc.h>
+#include <wayland-server.h>
+
+void die(int ret, const char* fmt, ...);
+void _log(FILE* fd, const char* fmt, ...);
+void sig_handler(int s);
+
+#define LENGTH(x) (sizeof(x) / sizeof((x)[0]))
+
+#endif /* UTIL_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/include/yawc.h	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,23 @@
+#ifndef TOHU_H
+#define TOHU_H
+
+#include <stdint.h>
+
+#include "types.h"
+
+extern void focus_next(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern void focus_prev(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern void kill_sel(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern void fullscreen(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern void mouse_move(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern void mouse_resize(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern void new_screen(struct swc_screen* scr);
+extern void new_window(struct swc_window* win);
+extern void new_device(struct libinput_device* dev);
+extern void quit(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern void spawn(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern void workspace_goto(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern void workspace_moveto(void* data, uint32_t time, uint32_t value, uint32_t state);
+extern struct wm wm;
+
+#endif /* YAWC_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nein_cursor.h	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,254 @@
+/* generated from Plan 9 Cursor data */
+#include <stddef.h>
+#include <stdint.h>
+
+enum {
+  NEIN_CURSOR_WHITEARROW = 0,
+  NEIN_CURSOR_BOXCURSOR = 1,
+  NEIN_CURSOR_CROSSCURSOR = 2,
+  NEIN_CURSOR_SIGHTCURSOR = 3,
+  NEIN_CURSOR_T = 4,
+  NEIN_CURSOR_B = 5,
+};
+
+static const uint32_t nein_cursor_data[] = {
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0x00000000,
+    0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff,
+    0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff,
+    0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+    0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff,
+    0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+    0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff,
+    0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
+    0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff, 0xffffffff,
+    0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000,
+    0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0x00000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, 0xff000000,
+    0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff,
+    0xff000000, 0xff000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000,
+    0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0x00000000,
+    0x00000000, 0x00000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff, 0xff000000,
+    0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff,
+    0xff000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000,
+    0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000,
+    0x00000000, 0x00000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000,
+    0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xffffffff, 0xff000000,
+    0x00000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0x00000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, 0xff000000,
+    0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0x00000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff,
+    0xff000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000,
+    0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000,
+    0xff000000, 0xff000000, 0xff000000, 0xff000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0xff000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+    0xffffffff, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000,
+    0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xffffffff,
+    0xff000000, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+struct nein_cursor_meta {
+  uint32_t width, height;
+  int32_t hotspot_x, hotspot_y;
+  size_t offset;
+};
+
+static const struct nein_cursor_meta nein_cursor_metadata[] = {
+    {16, 16, 0, 0, 0},    /* whitearrow */
+    {16, 16, 7, 7, 256},  /* boxcursor */
+    {16, 16, 7, 7, 512},  /* crosscursor */
+    {16, 16, 7, 7, 768},  /* sightcursor */
+    {16, 10, 7, 6, 1024}, /* t */
+    {16, 10, 7, 3, 1184}, /* b */
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/util.c	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,44 @@
+#include <stdlib.h>
+
+#include <swc.h>
+
+#include "util.h"
+#include "yawc.h"
+
+void die(int ret, const char* fmt, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "tohu: ");
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+
+	fputc('\n', stderr);
+	fflush(stderr);
+
+	exit(ret);
+}
+
+void _log(FILE* fd, const char* fmt, ...)
+{
+	va_list ap;
+
+	fprintf(fd, "tohu: ");
+
+	va_start(ap, fmt);
+	vfprintf(fd, fmt, ap);
+	va_end(ap);
+
+	fputc('\n', fd);
+	fflush(fd);
+}
+
+void sig_handler(int s)
+{
+	(void)s;
+
+	if (wm.dpy)
+		wl_display_terminate(wm.dpy);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/yawc.c	Sun Aug 30 12:47:10 2026 -0500
@@ -0,0 +1,648 @@
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "nein_cursor.h"
+#include <swc.h>
+#include <wayland-server.h>
+#include <wayland-util.h>
+#include <xkbcommon/xkbcommon-keysyms.h>
+
+#include "config.h"
+#include "types.h"
+#include "util.h"
+#include "yawc.h"
+
+static void focus(struct client* c);
+static void apply_decor(struct client* c, bool active);
+static struct client* first_client(struct screen* s);
+static bool is_ws_client(const struct client* c, const struct screen* s);
+static void on_screen_destroy(void* data);
+static void on_win_destroy(void* data);
+static void on_win_entered(void* data);
+static void on_win_metadata_changed(void* data);
+static void run_autostart(void);
+static void setup(void);
+static void setup_binds(void);
+static void setup_cursor(void);
+static void sync_window_visibility(void);
+
+struct wm wm;
+const struct swc_manager manager = {
+	.new_screen = new_screen, .new_window = new_window, .new_device = new_device,
+};
+struct swc_window_handler window_handler = {
+	.destroy = on_win_destroy,
+	.title_changed = on_win_metadata_changed,
+	.app_id_changed = on_win_metadata_changed,
+	.entered = on_win_entered,
+};
+struct swc_screen_handler screen_handler = {
+	.destroy = on_screen_destroy,
+};
+
+static void focus(struct client* c)
+{
+	if (wm.sel_client) {
+		swc_window_set_border(
+			wm.sel_client->win,
+			cfg.border_col_normal, cfg.border_width,
+			cfg.border_col_normal_outer, cfg.border_width_outer
+		);
+	}
+
+	if (c) {
+		swc_window_set_border(
+			c->win,
+			cfg.border_col_active, cfg.border_width,
+			cfg.border_col_active_outer, cfg.border_width_outer
+		);
+	}
+
+	swc_window_focus(c ? c->win : NULL);
+	wm.sel_client = c;
+}
+
+static void apply_decor(struct client* c, bool active)
+{
+	(void)active;
+
+	if (!c)
+		return;
+	swc_window_set_decor(c->win, NULL);
+}
+
+static struct client* first_client(struct screen* s)
+{
+	struct client* c;
+
+	wl_list_for_each(c, &wm.clients, link) {
+		if (is_ws_client(c, s))
+			return c;
+	}
+
+	return NULL;
+}
+
+static bool is_ws_client(const struct client* c, const struct screen* s)
+{
+	return c && c->ws == wm.ws && (!s || c->scr == s);
+}
+
+static void on_screen_destroy(void* data)
+{
+	struct screen* s = data;
+
+	if (!s)
+		return;
+
+	wl_list_remove(&s->link);
+
+	if (wm.sel_screen == s) {
+		if (wl_list_empty(&wm.screens))
+			wm.sel_screen = NULL;
+		else
+			wm.sel_screen = wl_container_of(wm.screens.next, wm.sel_screen, link);
+	}
+
+	free(s);
+}
+
+static void on_win_destroy(void* data)
+{
+	struct client* c = data;
+	struct client* next;
+
+	if (!c)
+		return;
+
+	if (wm.grab.active && wm.grab.c == c) {
+		wm.grab.active = false;
+		wm.grab.c = NULL;
+	}
+
+	if (wm.sel_client == c) {
+		wm.sel_client = NULL;
+	}
+
+	wl_list_remove(&c->link);
+	free(c);
+
+	next = first_client(wm.sel_screen);
+	if (!next)
+		next = first_client(NULL);
+	focus(next);
+}
+
+static void on_win_entered(void* data)
+{
+	if (wm.grab.active)
+		return;
+
+	struct client* c = data;
+	if (!is_ws_client(c, NULL))
+		return;
+
+	focus(c);
+}
+
+static void on_win_metadata_changed(void* data)
+{
+	struct client* c = data;
+
+	if (!c)
+		return;
+
+	apply_decor(c, c == wm.sel_client);
+}
+static void setup_cursor(void)
+{
+	const struct nein_cursor_meta *arrow = &nein_cursor_metadata[NEIN_CURSOR_WHITEARROW];
+
+	swc_set_cursor_mode(SWC_CURSOR_MODE_COMPOSITOR);
+	swc_set_cursor_image(SWC_CURSOR_DEFAULT,
+		&nein_cursor_data[arrow->offset],
+		arrow->width, arrow->height,
+		arrow->hotspot_x, arrow->hotspot_y);
+}
+static void setup(void)
+{
+	/* display */
+	wm.dpy = wl_display_create();
+	if (!wm.dpy)
+		die(EXIT_FAILURE, "wl_display_create failed");
+
+	/* variables */
+	wl_list_init(&wm.screens);
+	wl_list_init(&wm.clients);
+	wm.sel_client = NULL;
+	wm.sel_screen = NULL;
+	wm.grab.active = false;
+	wm.grab.resize = false;
+	wm.grab.c = NULL;
+	wm.ws = 1;
+
+	/* event loop */
+	wm.ev_loop = wl_display_get_event_loop(wm.dpy);
+	if (!swc_initialize(wm.dpy, wm.ev_loop, &manager))
+		die(EXIT_FAILURE, "swc_initialize failed\n");
+
+	swc_repeat_rate = cfg.repeat_rate;
+	swc_repeat_delay = cfg.repeat_delay;
+	 setup_cursor();
+	setup_binds();
+
+	/* display socket */
+	const char* sock;
+	sock = wl_display_add_socket_auto(wm.dpy);
+	if (!sock)
+		die(EXIT_FAILURE, "wl_display_add_socket_auto failed\n");
+	setenv("WAYLAND_DISPLAY", sock, 1);
+	_log(stderr, "WAYLAND_DISPLAY=%s\n", sock);
+
+	run_autostart();
+
+	/* signals */
+	signal(SIGINT,  sig_handler);
+	signal(SIGTERM, sig_handler);
+	signal(SIGQUIT, sig_handler);
+}
+
+static void run_autostart(void)
+{
+	const char* shell = getenv("SHELL");
+
+	if (!shell || !shell[0])
+		shell = "/bin/sh";
+
+	for (size_t i = 0; i < LENGTH(autostart); i++) {
+		const char* cmd = autostart[i];
+		pid_t pid;
+
+		if (!cmd || !cmd[0])
+			continue;
+
+		pid = fork();
+		if (pid < 0) {
+			_log(stderr, "autostart fork failed for: %s\n", cmd);
+			continue;
+		}
+
+		if (pid == 0) {
+			setsid();
+			execl(shell, shell, "-c", cmd, (char*)NULL);
+			_exit(127);
+		}
+	}
+}
+
+static void setup_binds(void)
+{
+	for (size_t i = 0; i < LENGTH(binds); i++) {
+		const struct bind* b = &binds[i];
+		swc_add_binding(b->type, b->mods, b->ksym, b->fn, (void*)&b->arg);
+	}
+}
+
+static void sync_window_visibility(void)
+{
+	struct client* c;
+
+	wl_list_for_each(c, &wm.clients, link) {
+		if (c->ws == wm.ws)
+			swc_window_show(c->win);
+		else
+			swc_window_hide(c->win);
+	}
+}
+
+void focus_next(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	(void)data;
+	(void)time;
+	(void)value;
+
+	struct client* c = NULL;
+
+	if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
+		return;
+
+	if (wl_list_empty(&wm.clients))
+		return;
+
+	if (!wm.sel_client || !is_ws_client(wm.sel_client, wm.sel_screen)) {
+		c = first_client(wm.sel_screen);
+		if (!c)
+			c = first_client(NULL);
+		focus(c);
+		return;
+	}
+
+	struct wl_list* start = wm.sel_client->link.next;
+	struct wl_list* it = start;
+
+	do {
+		if (it == &wm.clients)
+			it = wm.clients.next;
+		if (it == &wm.clients)
+			break;
+
+		c = wl_container_of(it, c, link);
+		if (is_ws_client(c, wm.sel_screen)) {
+			focus(c);
+			return;
+		}
+		it = it->next;
+	} while (it != start);
+
+	c = first_client(wm.sel_screen);
+	if (!c)
+		c = first_client(NULL);
+	focus(c);
+}
+
+void focus_prev(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	struct client* c = NULL;
+
+	(void)data;
+	(void)time;
+	(void)value;
+
+	if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
+		return;
+
+	if (wl_list_empty(&wm.clients))
+		return;
+
+	if (!wm.sel_client || !is_ws_client(wm.sel_client, wm.sel_screen)) {
+		c = first_client(wm.sel_screen);
+		if (!c)
+			c = first_client(NULL);
+		focus(c);
+		return;
+	}
+
+	struct wl_list* start = wm.sel_client->link.prev;
+	struct wl_list* it = start;
+
+	do {
+		if (it == &wm.clients)
+			it = wm.clients.prev;
+		if (it == &wm.clients)
+			break;
+
+		c = wl_container_of(it, c, link);
+		if (is_ws_client(c, wm.sel_screen)) {
+			focus(c);
+			return;
+		}
+		it = it->prev;
+	} while (it != start);
+
+	c = first_client(wm.sel_screen);
+	if (!c)
+		c = first_client(NULL);
+	focus(c);
+}
+
+void kill_sel(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	(void)data;
+	(void)time;
+	(void)value;
+
+	if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
+		return;
+
+	if (!wm.sel_client)
+		return;
+
+	swc_window_close(wm.sel_client->win);
+}
+
+void fullscreen(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	struct swc_rectangle geom;
+
+	(void)data;
+	(void)time;
+	(void)value;
+
+	if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
+		return;
+
+	if (!wm.sel_client)
+		return;
+
+	if (wm.sel_client->fullscreen) {
+		wm.sel_client->fullscreen = false;
+		swc_window_set_stacked(wm.sel_client->win);
+		apply_decor(wm.sel_client, true);
+
+		if (wm.sel_client->w > 0 && wm.sel_client->h > 0) {
+			geom.x = wm.sel_client->x;
+			geom.y = wm.sel_client->y;
+			geom.width = wm.sel_client->w;
+			geom.height = wm.sel_client->h;
+			swc_window_set_geometry(wm.sel_client->win, &geom);
+		}
+		return;
+	}
+
+	if (!wm.sel_client->scr || !wm.sel_client->scr->scr)
+		return;
+
+	if (swc_window_get_geometry(wm.sel_client->win, &geom)) {
+		wm.sel_client->x = geom.x;
+		wm.sel_client->y = geom.y;
+		wm.sel_client->w = geom.width;
+		wm.sel_client->h = geom.height;
+	}
+
+	wm.sel_client->fullscreen = true;
+	apply_decor(wm.sel_client, true);
+	swc_window_set_fullscreen(wm.sel_client->win, wm.sel_client->scr->scr);
+}
+
+void mouse_move(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	(void)data;
+	(void)time;
+	(void)value;
+
+	if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+		if (!wm.sel_client)
+			return;
+
+		if (!wm.sel_client->floating) {
+			wm.sel_client->floating = true;
+			swc_window_set_stacked(wm.sel_client->win);
+		}
+
+		wm.grab.active = true;
+		wm.grab.resize = false;
+		wm.grab.c = wm.sel_client;
+
+		swc_window_begin_move(wm.grab.c->win);
+	}
+	else {
+		if (!wm.grab.active || wm.grab.resize || !wm.grab.c)
+			return;
+
+		swc_window_end_move(wm.grab.c->win);
+
+		wm.grab.active = false;
+		wm.grab.c = NULL;
+	}
+}
+
+void mouse_resize(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	(void)data;
+	(void)time;
+	(void)value;
+
+	if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+		if (!wm.sel_client)
+			return;
+
+		if (!wm.sel_client->floating) {
+			wm.sel_client->floating = true;
+			swc_window_set_stacked(wm.sel_client->win);
+		}
+
+		wm.grab.active = true;
+		wm.grab.resize = true;
+		wm.grab.c = wm.sel_client;
+
+		swc_window_begin_resize(
+			wm.grab.c->win,
+			SWC_WINDOW_EDGE_RIGHT | SWC_WINDOW_EDGE_BOTTOM
+		);
+	}
+	else {
+		if (!wm.grab.active || !wm.grab.resize || !wm.grab.c)
+			return;
+
+		swc_window_end_resize(wm.grab.c->win);
+
+		wm.grab.active = false;
+		wm.grab.c = NULL;
+	}
+}
+
+void new_screen(struct swc_screen* scr)
+{
+	struct screen* s;
+
+	s = malloc(sizeof(*s));
+	if (!s)
+		die(EXIT_FAILURE, "new screen calloc failed");
+
+	s->scr = scr;
+
+	s->x = 0;
+	s->y = 0;
+	s->w = 0;
+	s->h = 0;
+
+	wl_list_insert(&wm.screens, &s->link);
+
+	if (!wm.sel_screen)
+		wm.sel_screen = s;
+
+	swc_screen_set_handler(scr, &screen_handler, s);
+
+	_log(stderr, "new_screen=%p\n", (void*)scr);
+}
+
+void new_window(struct swc_window* win)
+{
+	struct client* c;
+
+	c = malloc(sizeof(*c));
+	if (!c)
+		die(EXIT_FAILURE, "malloc client failed");
+
+	win->motion_throttle_ms = 1000 / cfg.motion_throttle_hz;
+	win->min_width = 1;
+	win->min_height = 1;
+	win->max_width = 0;
+	win->max_height = 0;
+
+	c->win = win;
+	c->scr = wm.sel_screen;
+	c->mapped = 0;
+	c->floating = true;
+	c->fullscreen = 0;
+	c->ws = wm.ws;
+	c->x = 0;
+	c->y = 0;
+	c->w = 0;
+	c->h = 0;
+
+	wl_list_insert(&wm.clients, &c->link);
+	swc_window_set_handler(win, &window_handler, c);
+	swc_window_set_stacked(win);
+	apply_decor(c, false);
+	{
+		/* swc reports cursor coordinates in wl_fixed_t (24.8) units */
+		int32_t cx_fixed = 0;
+		int32_t cy_fixed = 0;
+
+		if (swc_cursor_position(&cx_fixed, &cy_fixed)) {
+			int32_t cx = cx_fixed / 256;
+			int32_t cy = cy_fixed / 256;
+
+			swc_window_set_position(
+				win,
+				cx - (int32_t)(cfg.spawn_w / 2),
+				cy - (int32_t)(cfg.spawn_h / 2)
+			);
+		}
+	}
+	swc_window_show(win);
+	focus(c);
+
+	_log(stderr, "new_window=%p\n", (void*)win);
+}
+
+void new_device(struct libinput_device* dev)
+{
+	(void)dev;
+}
+
+void quit(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	(void)data;
+	(void)time;
+	(void)value;
+	(void)state;
+
+	wl_display_terminate(wm.dpy);
+}
+
+void spawn(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	union arg* a = data;
+	char* const* cmd = (char* const*)a->v;
+
+	(void)time;
+	(void)value;
+
+	if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
+		return;
+
+	if (fork() == 0) {
+		execvp(cmd[0], cmd);
+		_exit(127);
+	}
+}
+
+void workspace_goto(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	union arg* a = data;
+	struct client* c;
+
+	(void)time;
+	(void)value;
+
+	if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
+		return;
+
+	if (a->ui < 1 || a->ui > 9 || a->ui == wm.ws)
+		return;
+
+	wm.ws = a->ui;
+	sync_window_visibility();
+
+	c = first_client(wm.sel_screen);
+	if (!c)
+		c = first_client(NULL);
+	focus(c);
+}
+
+void workspace_moveto(void* data, uint32_t time, uint32_t value, uint32_t state)
+{
+	union arg* a = data;
+	struct client* c;
+	struct client* next;
+
+	(void)time;
+	(void)value;
+
+	if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
+		return;
+
+	if (!wm.sel_client)
+		return;
+
+	if (a->ui < 1 || a->ui > 9)
+		return;
+
+	c = wm.sel_client;
+	if (c->ws == a->ui)
+		return;
+
+	c->ws = a->ui;
+	if (c->ws == wm.ws)
+		swc_window_show(c->win);
+	else
+		swc_window_hide(c->win);
+
+	next = first_client(wm.sel_screen);
+	if (!next)
+		next = first_client(NULL);
+	focus(next);
+}
+
+int main(void)
+{
+	setup();
+	wl_display_run(wm.dpy);
+	swc_finalize();
+	wl_display_destroy(wm.dpy);
+	return EXIT_SUCCESS;
+}