commit 6578f25

uint  ·  2026-08-04 12:53:53 +0000 UTC
parent 9ae8147
update libmaus sources, configure script
20 files changed,  +2209, -452
+32, -31
 1@@ -2,63 +2,64 @@
 2 set -eu
 3 
 4 PLATFORM=$(uname -s)
 5+BACKEND=${1:-}
 6 CPPFLAGS=
 7 LDFLAGS="-Llibmaus/build"
 8 
 9+if [ "$#" -gt 1 ]; then
10+	echo "Usage: ./configure [x11|wayland|mac]" >&2
11+	exit 1
12+fi
13+
14 case "$PLATFORM" in
15 	Darwin)
16-		BACKEND=mac
17-		LDLIBS="-lmaus_mac \
18-		        -framework Cocoa \
19-		        -framework CoreVideo \
20-		        -framework IOKit \
21-		        -framework CoreGraphics \
22-		        -framework CoreFoundation \
23-		        -framework Carbon"
24+		: "${BACKEND:=mac}"
25 		;;
26-
27 	Linux|FreeBSD|OpenBSD)
28-		if [ -n "${WAYLAND_DISPLAY:-}" ]; then
29-			BACKEND=way
30-			CPPFLAGS=-DBACKEND_WAY
31-			LDLIBS="-lmaus_way -lwayland-client"
32-		elif [ -n "${DISPLAY:-}" ]; then
33+		if [ -z "$BACKEND" ] && [ -n "${WAYLAND_DISPLAY:-}" ]; then
34+			BACKEND=wayland
35+		elif [ -z "$BACKEND" ] && [ -n "${DISPLAY:-}" ]; then
36 			BACKEND=x11
37-			CPPFLAGS=-DBACKEND_X11
38-			LDLIBS="-lmaus_x11 -lX11 -lXext"
39-		else
40+		elif [ -z "$BACKEND" ]; then
41 			echo "No X11 or Wayland display found" >&2
42 			exit 1
43 		fi
44-
45-		case "$PLATFORM" in
46-			Linux)
47-				CPPFLAGS="-D_XOPEN_SOURCE=700 $CPPFLAGS"
48-				;;
49-			OpenBSD)
50-				CPPFLAGS="-I/usr/X11R6/include $CPPFLAGS"
51-				LDFLAGS="-L/usr/X11R6/lib $LDFLAGS"
52-				;;
53-		esac
54 		;;
55-
56 	*)
57 		echo "Unsupported platform: $PLATFORM" >&2
58 		exit 1
59 		;;
60 esac
61 
62+case "$PLATFORM" in
63+	Linux)
64+		CPPFLAGS="-D_XOPEN_SOURCE=700"
65+		;;
66+	OpenBSD)
67+		CPPFLAGS="-I/usr/X11R6/include"
68+		LDFLAGS="-L/usr/X11R6/lib $LDFLAGS"
69+		;;
70+esac
71+
72 (
73 	cd libmaus
74 	rm -rf build/
75-	./configure --backend="$BACKEND"
76+	./configure "$BACKEND"
77 	make
78 )
79 
80+CONF_CPPFLAGS=$(sed -n 's/^CONF_CPPFLAGS = //p' libmaus/config.mk)
81+CONF_LDFLAGS=$(sed -n 's/^CONF_LDFLAGS = //p' libmaus/config.mk)
82+CONF_LIB_NAME=$(sed -n 's/^CONF_LIB_NAME = //p' libmaus/config.mk)
83+CONF_LIB=${CONF_LIB_NAME#lib}
84+CONF_LIB=${CONF_LIB%.a}
85+
86 cat >platform.mk <<EOF
87-CPPFLAGS += $CPPFLAGS
88+CPPFLAGS += $CPPFLAGS $CONF_CPPFLAGS
89 LDFLAGS += $LDFLAGS
90-LDLIBS += $LDLIBS
91+LDLIBS += -l$CONF_LIB
92+LDLIBS += ${CONF_LDFLAGS}
93 EOF
94 
95 echo "Configuration done"
96+
+1, -0
1@@ -5,6 +5,7 @@
2 *.obj
3 *.lib
4 
5+build/
6 compile_flags.txt
7 
8 fenster/
+45, -7
 1@@ -1,9 +1,13 @@
 2+.POSIX:
 3+
 4 include config.mk
 5 
 6 CC = cc
 7 AR = ar
 8-CFLAGS = -std=c99 -Wall -Wextra -O2
 9-CPPFLAGS = ${CONF_CPPFLAGS} -Iinclude
10+ARFLAGS = -r -c
11+CFLAGS = -ansi -pedantic-errors -Wall -Wextra -O2
12+WL_CFLAGS = -std=c99 -Wall -Wextra -O2
13+CPPFLAGS = ${CONF_CPPFLAGS} -Iinclude -Ibuild
14 LDFLAGS = ${CONF_LDFLAGS}
15 
16 LIB_NAME = build/${CONF_LIB_NAME}
17@@ -19,21 +23,55 @@ LIB_OBJS = build/maus.o       \
18 all: ${LIB_NAME}
19 
20 ${LIB_NAME}: ${LIB_OBJS}
21-	${AR} -rcs $@ ${LIB_OBJS}
22+	${AR} ${ARFLAGS} $@ ${LIB_OBJS}
23 
24-build/maus.o: source/maus.c
25+build/maus.o: source/maus.c include/maus.h config.mk
26 	mkdir -p build
27 	${CC} ${CFLAGS} ${CPPFLAGS} -c source/maus.c -o $@
28-build/maus_font.o: source/maus_font.c
29+build/maus_font.o: source/maus_font.c include/maus.h include/maus_font.h config.mk
30 	mkdir -p build
31 	${CC} ${CFLAGS} ${CPPFLAGS} -c source/maus_font.c -o $@
32-build/utils.o: source/utils.c
33+build/utils.o: source/utils.c include/utils.h
34 	mkdir -p build
35 	${CC} ${CFLAGS} ${CPPFLAGS} -c source/utils.c -o $@
36-build/maus_x11.o: source/maus_x11.c
37+
38+# X11
39+build/maus_x11.o: source/maus_x11.c include/maus.h include/maus_x11.h config.mk
40 	mkdir -p build
41 	${CC} ${CFLAGS} ${CPPFLAGS} -c source/maus_x11.c -o $@
42 
43+# Wayland
44+build/maus_wayland.o: source/maus_wayland.c include/maus.h include/maus_wayland.h build/xdg-shell-client-protocol.h build/pointer-constraints-unstable-v1-client-protocol.h build/relative-pointer-unstable-v1-client-protocol.h config.mk
45+	mkdir -p build
46+	${CC} ${WL_CFLAGS} ${CPPFLAGS} -c source/maus_wayland.c -o $@
47+build/xdg-shell-client-protocol.h:
48+	mkdir -p build
49+	wayland-scanner client-header ${PROTOS}/stable/xdg-shell/xdg-shell.xml $@
50+build/xdg-shell-protocol.c:
51+	mkdir -p build
52+	wayland-scanner private-code ${PROTOS}/stable/xdg-shell/xdg-shell.xml $@
53+build/xdg-shell-protocol.o: build/xdg-shell-protocol.c build/xdg-shell-client-protocol.h
54+	mkdir -p build
55+	${CC} ${WL_CFLAGS} ${CPPFLAGS} -c build/xdg-shell-protocol.c -o $@
56+build/pointer-constraints-unstable-v1-client-protocol.h:
57+	mkdir -p build
58+	wayland-scanner client-header ${PROTOS}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@
59+build/pointer-constraints-unstable-v1-protocol.c:
60+	mkdir -p build
61+	wayland-scanner private-code ${PROTOS}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@
62+build/pointer-constraints-unstable-v1-protocol.o: build/pointer-constraints-unstable-v1-protocol.c build/pointer-constraints-unstable-v1-client-protocol.h
63+	mkdir -p build
64+	${CC} ${WL_CFLAGS} ${CPPFLAGS} -c build/pointer-constraints-unstable-v1-protocol.c -o $@
65+build/relative-pointer-unstable-v1-client-protocol.h:
66+	mkdir -p build
67+	wayland-scanner client-header ${PROTOS}/unstable/relative-pointer/relative-pointer-unstable-v1.xml $@
68+build/relative-pointer-unstable-v1-protocol.c:
69+	mkdir -p build
70+	wayland-scanner private-code ${PROTOS}/unstable/relative-pointer/relative-pointer-unstable-v1.xml $@
71+build/relative-pointer-unstable-v1-protocol.o: build/relative-pointer-unstable-v1-protocol.c build/relative-pointer-unstable-v1-client-protocol.h
72+	mkdir -p build
73+	${CC} ${WL_CFLAGS} ${CPPFLAGS} -c build/relative-pointer-unstable-v1-protocol.c -o $@
74+
75 clean:
76 	rm -rf build ${LIB_NAME} config.mk compile_flags.txt
77 
+8, -5
 1@@ -1,10 +1,10 @@
 2 ![logo](assets/logo_256x112.png)  
 3-is a small, cross-platform, software windowing library
 4+is a small, cross-platform, software windowing library **now in ANSI C**
 5 
 6 ### Building
 7 Unix:
 8 ```sh
 9-./configure --backend=[x11|wayland|mac]
10+./configure [x11|wayland|mac]
11 make # That's it!
12 ```
13 
14@@ -12,15 +12,18 @@ Windows:
15 ```
16 .\build.bat [msvc|gcc|clang]
17 ```
18+> If anyone is on Windows, please try to compile past the ANSI conversion.
19+> I pretty much converted it without checking so there may still be C99
20+> artefacts left behind.
21 
22 After building, you have a library which you link _your_ sources with. It is
23 located in `build/`.
24 
25 ### Usage
26 #### libmaus
27-Here is an annoted example for a basic window program which counts the number of
28-frames that have elapsed, draw the text and displays if you've pressed a key
29-(specifically 'A').
30+Here is an annoted (C99) example for a basic window program which counts the
31+number of frames that have elapsed, draw the text and displays if you've
32+pressed a key (specifically 'A').
33 
34 For the full list of functions and what they do, look at
35 [the header](include/maus.h).
+5, -0
 1@@ -1,4 +1,9 @@
 2 Maus:
 3+[ ] Relative cursor
 4+	[ ] Fill deltas
 5+[ ] Rename mw->ctx internally
 6+[ ] Move framebuffer creation out
 7+    of maus_init on platforms
 8 [?] Cursor warping
 9 [?] Custom cursor
10 
+12, -5
 1@@ -2,14 +2,16 @@
 2 
 3 for arg do
 4 	case "$arg" in
 5-		--backend=*) backend="${arg#*=}" ;;
 6 		--help)
 7-			echo "Usage: ./configure [--backend=x11|wayland|mac]"
 8+			echo "Usage: ./configure [x11|wayland|mac]"
 9 			exit 0
10 			;;
11 		*)
12-			echo "Unknown argument: $arg"
13-			exit 1
14+			if [ -n "$backend" ]; then
15+				echo "Usage: ./configure [x11|wayland|mac]"
16+				exit 1
17+			fi
18+			backend="$arg"
19 			;;
20 	esac
21 done
22@@ -28,7 +30,12 @@ case "$backend" in
23 		echo "CONF_LIB_NAME = libmaus_x11.a" >> config.mk
24 		;;
25 	wayland)
26-		# TODO
27+		echo "CONF_CPPFLAGS = -DBACKEND_WAY $(pkg-config --cflags wayland-client wayland-cursor xkbcommon)" >> config.mk
28+		echo "CONF_LDFLAGS = $(pkg-config --libs wayland-client wayland-cursor xkbcommon)" >> config.mk
29+		echo "CONF_SRC = source/maus_wayland.c build/xdg-shell-protocol.c build/pointer-constraints-unstable-v1-protocol.c build/relative-pointer-unstable-v1-protocol.c" >> config.mk
30+		echo "CONF_OBJS = build/maus_wayland.o build/xdg-shell-protocol.o build/pointer-constraints-unstable-v1-protocol.o build/relative-pointer-unstable-v1-protocol.o" >> config.mk
31+		echo "CONF_LIB_NAME = libmaus_wayland.a" >> config.mk
32+		echo "PROTOS = $(pkg-config --variable=pkgdatadir wayland-protocols)" >> config.mk
33 		;;
34 	mac)
35 		# TODO
+5, -3
 1@@ -1,8 +1,10 @@
 2+include ../../config.mk
 3+
 4 CC = cc
 5 CFLAGS = -std=c99 -Wall -Wextra -g
 6-CPPFLAGS = -DBACKEND_X11 -I../../include
 7-LIBS = -lX11 -lXext
 8-MAUS_LIB = ../../build/libmaus_x11.a
 9+CPPFLAGS = ${CONF_CPPFLAGS} -I../../include -I../../build
10+LIBS = ${CONF_LDFLAGS}
11+MAUS_LIB = ../../build/${CONF_LIB_NAME}
12 
13 SRC = main.c
14 
+23, -3
 1@@ -8,12 +8,14 @@
 2 int mx, my;
 3 bool cur_visible = true;
 4 bool cur_locked = false;
 5+bool cur_relative = false;
 6 bool mb1_pressed = false;
 7 char txtbuf[4096] = {'\0'};
 8 int current_char = 0;
 9 bool resize = false;
10 int rszw;
11 int rszh;
12+MausColor white = {255, 255, 255, 255};
13 
14 void handle_ev(Maus* mw, MausEvent* ev)
15 {
16@@ -25,7 +27,7 @@ void handle_ev(Maus* mw, MausEvent* ev)
17 		case MAUS_EV_KEY: {
18 			(void)0;
19 
20-			bool* keys = mw->key_syms;
21+			int8_t* keys = mw->key_syms;
22 			if (keys[MAUS_KEY_CONTROL_L] && keys[MAUS_KEY_Q]) {
23 				maus_close(mw);
24 				exit(EXIT_SUCCESS);
25@@ -44,6 +46,13 @@ void handle_ev(Maus* mw, MausEvent* ev)
26 
27 				cur_locked = !cur_locked;
28 			}
29+			if (keys[MAUS_KEY_CONTROL_L] && keys[MAUS_KEY_R]) {
30+				cur_relative ?
31+				maus_cur_set_mode(mw, MAUS_CURSOR_STATE_ABSOLUTE) :
32+				maus_cur_set_mode(mw, MAUS_CURSOR_STATE_RELATIVE);
33+
34+				cur_relative = !cur_relative;
35+			}
36 
37 			if (ev->key.pressed == false)
38 				break;
39@@ -59,6 +68,16 @@ void handle_ev(Maus* mw, MausEvent* ev)
40 			break;
41 		}
42 		case MAUS_EV_MOUSE_BUTTON: {
43+			if (ev->mouse.button.button == MAUS_MOUSE_BUTTON_SCROLL_UP) {
44+				white.r += 5;
45+				white.g += 5;
46+				white.b += 5;
47+			}
48+			else if (ev->mouse.button.button == MAUS_MOUSE_BUTTON_SCROLL_DOWN) {
49+				white.r -= 5;
50+				white.g -= 5;
51+				white.b -= 5;
52+			}
53 			mb1_pressed =
54 			mw->mouse_buttons[MAUS_MOUSE_BUTTON_LEFT] ?
55 			true : false;
56@@ -74,6 +93,8 @@ void handle_ev(Maus* mw, MausEvent* ev)
57 			rszw = ev->resize.width;
58 			rszh = ev->resize.height;
59 		} break;
60+		case MAUS_EV_REDRAW:
61+			break;
62 		case MAUS_EV_NONE:
63 			break;
64 	}
65@@ -102,7 +123,7 @@ int main(void)
66 			resize = false;
67 		}
68 
69-		maus_clear(mw, MAUS_COL_RGBA(255, 255, 255, 255));
70+		maus_clear(mw, white);
71 
72 		uint32_t red_unpacked = MAUS_UNPACK_COL(red);
73 		if (mx >= 0 && my >= 0 &&
74@@ -132,4 +153,3 @@ int main(void)
75 	maus_font_free(font);
76 	maus_close(mw);
77 }
78-
+56, -56
  1@@ -1,7 +1,6 @@
  2 #ifndef MAUSWIN_H
  3 #define MAUSWIN_H
  4 
  5-#include <stdbool.h>
  6 #include <stdio.h>
  7 #include <stdint.h>
  8 
  9@@ -36,7 +35,7 @@
 10 	#include "maus_x11.h"
 11 
 12 #elif defined(BACKEND_WAY)
 13-	/* ... */
 14+	#include "maus_wayland.h"
 15 
 16 #elif defined(BACKEND_WIN)
 17 	#include "maus_win.h"
 18@@ -53,7 +52,7 @@
 19 	 ((uint32_t)(c).r << 16) | \
 20 	 ((uint32_t)(c).g <<  8) | \
 21 	 ((uint32_t)(c).b))
 22-/* ... */
 23+
 24 #define MAUS_PIXEL_AT(mw, x, y) ((mw)->bfb[(y) * (mw)->stride + (x)])
 25 
 26 typedef enum {
 27@@ -63,66 +62,66 @@ typedef enum {
 28 	MAUS_EV_MOUSE_BUTTON,
 29 	MAUS_EV_MOUSE_MOTION,
 30 	MAUS_EV_RESIZE,
 31-	MAUS_EV_REDRAW, /* TODO: MAUS_EV_REDRAW for windows */
 32+	MAUS_EV_REDRAW /* TODO: MAUS_EV_REDRAW for windows */
 33 } MausEventType;
 34 
 35 typedef struct {
 36-	uint8_t        a;
 37-	uint8_t        r;
 38-	uint8_t        g;
 39-	uint8_t        b;
 40+	uint8_t a;
 41+	uint8_t r;
 42+	uint8_t g;
 43+	uint8_t b;
 44 } MausColor;
 45 
 46 typedef struct {
 47 	MausEventType type;
 48 
 49-	union {
 50-		struct {
 51-			uint32_t  code; /* raw, backend keycode */
 52-			MausKey   key;  /* logical, mapped key */
 53-			char      text; /* translated text from key */
 54-			bool      pressed;
 55-		} key;
 56+	struct {
 57+		uint32_t code; /* raw, backend keycode */
 58+		MausKey  key;  /* logical, mapped key */
 59+		char     text; /* translated text from key */
 60+		int8_t     pressed;
 61+	} key;
 62 
 63+	struct {
 64 		struct {
 65-			struct {
 66-				MausMouseButton button;
 67-				bool            pressed;
 68-			} button;
 69-
 70-			struct {
 71-				int32_t  x;
 72-				int32_t  y;
 73-			} motion;
 74-		} mouse;
 75+			MausMouseButton button;
 76+			int8_t            pressed;
 77+		} button;
 78 
 79 		struct {
 80-			uint32_t width;
 81-			uint32_t height;
 82-		} resize;
 83-	};
 84+			int32_t x;
 85+			int32_t y;
 86+			int32_t dx;
 87+			int32_t dy;
 88+		} motion;
 89+	} mouse;
 90+
 91+	struct {
 92+		uint32_t width;
 93+		uint32_t height;
 94+	} resize;
 95 } MausEvent;
 96 
 97 typedef struct {
 98-	MausBackend    backend;
 99-	uint64_t       frame_time_last;
100-	char*          clipboard;
101+	MausBackend backend;
102+	uint64_t    frame_time_last;
103+	char*       clipboard;
104 
105-	uint32_t*      fb;  /* front frame buffer */
106-	uint32_t*      bfb; /* back frame buffer */
107-	uint32_t       stride;
108+	uint32_t* fb;  /* front frame buffer */
109+	uint32_t* bfb; /* back frame buffer */
110+	uint32_t  stride;
111 
112-	const char*    title;
113-	uint32_t       width;
114-	uint32_t       height;
115-	int32_t        x;
116-	int32_t        y;
117+	const char* title;
118+	uint32_t    width;
119+	uint32_t    height;
120+	int32_t     x;
121+	int32_t     y;
122 
123-	bool           key_codes[MAUS_KEYCODE_LAST]; /* physical keys */
124-	bool           key_syms[MAUS_KEY_LAST];      /* logical keys */
125+	int8_t key_codes[MAUS_KEYCODE_LAST]; /* physical keys */
126+	int8_t key_syms[MAUS_KEY_LAST];      /* logical keys */
127 
128-	MausCursor     cursor;
129-	bool           mouse_buttons[MAUS_MOUSE_BUTTON_LAST];
130+	MausCursor cursor;
131+	int8_t       mouse_buttons[MAUS_MOUSE_BUTTON_LAST];
132 } Maus;
133 
134 /* clear screen with color: `col` */
135@@ -134,14 +133,15 @@ void maus_clipboard_set_text(Maus* mw, const char* text);
136 /* get text from system clipboard */
137 char* maus_clipboard_get_text(Maus* mw);
138 
139-/* close a Maus. returns false on fail */
140+/* close a Maus. returns 0 on fail
141+   TODO also free mw */
142 void maus_close(Maus* mw);
143 
144-/* close a window without the whole Maus. returns false on fail */
145-bool maus_close_window(Maus* mw);
146+/* close a window without the whole Maus. returns 0 on fail */
147+int8_t maus_close_window(Maus* mw);
148 
149-/* create window from Maus. returns false on fail */
150-bool maus_create_window(Maus* mw);
151+/* create window from Maus. returns 0 on fail */
152+int8_t maus_create_window(Maus* mw);
153 
154 /* log message to output `fd` and die */
155 void maus_die(const char* fmt, ...);
156@@ -156,20 +156,20 @@ Maus* maus_init(const char* title, int x, int y, int width, int height);
157 void maus_log(FILE* fd, const char* fmt, ...);
158 
159 /* poll for events then fill `ev` with retrieved events.
160-   returns true if event polled, else false.
161+   returns 1 if event polled, else 0.
162    note: can burn cpu cycles */
163-bool maus_event_poll(Maus* mw, MausEvent* ev);
164+int8_t maus_event_poll(Maus* mw, MausEvent* ev);
165 
166-/* poll for events then fill `ev` with retrieved events. returns true if event
167-   polled, else false. note, thread goes to sleep until an event arrives */
168+/* poll for events then fill `ev` with retrieved events. returns 1 if event
169+   polled, else 0. note, thread goes to sleep until an event arrives */
170 void maus_event_wait(Maus* mw, MausEvent* ev);
171 
172 /* present the pixelbuffer to the screen */
173 void maus_present(Maus* mw);
174 
175-/* resize window to specified width and height. returns true on resize success,
176-   else false */
177-bool maus_resize(Maus* mw, uint32_t width, uint32_t height);
178+/* resize window to specified width and height. returns 1 on resize success,
179+   else 0 */
180+int8_t maus_resize(Maus* mw, uint32_t width, uint32_t height);
181 
182 /* cap framerate to targetted fps */
183 void maus_target_fps(Maus* mw, uint32_t fps);
+13, -13
 1@@ -2,28 +2,28 @@
 2 #define MAUS_FONT_H
 3 
 4 #include <stdint.h>
 5-#include <stdbool.h>
 6 
 7 #include "maus.h"
 8 
 9 #define MAUS_BDF_GLYPHS_MAX 256
10 
11 typedef struct {
12-    uint8_t*    bmp;
13-    uint16_t    w;
14-    uint16_t    h;
15-    int16_t     xoff;
16-    int16_t     yoff;
17-    uint16_t    adv;
18-    bool        valid;
19+	uint8_t* bmp;
20+	uint16_t w;
21+	uint16_t h;
22+	int16_t  xoff;
23+	int16_t  yoff;
24+	uint16_t adv;
25+	int8_t   valid;
26 } MausGlyph;
27 
28 typedef struct {
29-    MausGlyph glyphs[MAUS_BDF_GLYPHS_MAX];
30-    int32_t     asc;
31-    int32_t     dsc;
32-    int32_t     cellh;
33-    int32_t     cellw;
34+	MausGlyph glyphs[MAUS_BDF_GLYPHS_MAX];
35+
36+	int32_t asc;
37+	int32_t dsc;
38+	int32_t cellh;
39+	int32_t cellw;
40 } MausFont;
41 
42 /* Loops through text passing parameters to glyph drawer */
+9, -5
 1@@ -59,19 +59,23 @@ typedef enum {
 2 	MAUS_KEY_KP_DECIMAL,    MAUS_KEY_KP_DIVIDE, MAUS_KEY_KP_MULTIPLY,
 3 	MAUS_KEY_KP_SUBTRACT,   MAUS_KEY_KP_ADD,    MAUS_KEY_KP_ENTER,  MAUS_KEY_KP_EQUAL,
 4 
 5-	MAUS_KEY_LAST,
 6+	MAUS_KEY_LAST
 7 } MausKey;
 8 
 9 typedef struct {
10-	int32_t        x;
11-	int32_t        y;
12+	int32_t x;
13+	int32_t y;
14 } MausCursor;
15 
16 typedef enum {
17 	MAUS_CURSOR_STATE_VISIBLE,
18 	MAUS_CURSOR_STATE_HIDDEN,
19-	MAUS_CURSOR_STATE_LOCKED,
20+
21 	MAUS_CURSOR_STATE_FREE,
22+	MAUS_CURSOR_STATE_LOCKED,
23+
24+	MAUS_CURSOR_STATE_ABSOLUTE,
25+	MAUS_CURSOR_STATE_RELATIVE
26 } MausCursorState;
27 
28 typedef enum {
29@@ -81,7 +85,7 @@ typedef enum {
30 	MAUS_MOUSE_BUTTON_RIGHT,
31 	MAUS_MOUSE_BUTTON_SCROLL_UP,
32 	MAUS_MOUSE_BUTTON_SCROLL_DOWN,
33-	MAUS_MOUSE_BUTTON_LAST,
34+	MAUS_MOUSE_BUTTON_LAST
35 } MausMouseButton;
36 
37 #endif /* MAUS_KEYS_H */
+116, -0
  1@@ -0,0 +1,116 @@
  2+#ifndef MAUS_WAYLAND_H
  3+#define MAUS_WAYLAND_H
  4+
  5+#include <stddef.h>
  6+#include <stdint.h>
  7+
  8+#define MAUS_EVQ_MAX 64
  9+
 10+/* must forward declare so that other
 11+   ANSI abiding files arent affected */
 12+struct wl_display;
 13+struct wl_buffer;
 14+struct wl_registry;
 15+struct wl_compositor;
 16+struct wl_surface;
 17+struct wl_shm;
 18+struct wl_seat;
 19+struct wl_pointer;
 20+struct wl_keyboard;
 21+struct wl_cursor_theme;
 22+struct wl_cursor;
 23+
 24+struct xdg_wm_base;
 25+struct xdg_surface;
 26+struct xdg_toplevel;
 27+
 28+struct zwp_pointer_constraints_v1;
 29+struct zwp_confined_pointer_v1;
 30+struct zwp_relative_pointer_manager_v1;
 31+struct zwp_relative_pointer_v1;
 32+
 33+struct xkb_context;
 34+struct xkb_keymap;
 35+struct xkb_state;
 36+
 37+typedef struct {
 38+	int8_t  pending;
 39+	int32_t type;
 40+
 41+	uint32_t width;
 42+	uint32_t height;
 43+
 44+	int32_t  mouse_x;
 45+	int32_t  mouse_y;
 46+	int32_t  mouse_dx;
 47+	int32_t  mouse_dy;
 48+	uint32_t mouse_button;
 49+	uint8_t  mouse_pressed;
 50+
 51+	uint32_t key_code;
 52+	uint32_t key_sym;
 53+	char     key_text;
 54+	uint8_t  key_pressed;
 55+} MausEventPending;
 56+
 57+typedef struct {
 58+	struct wl_buffer* buffer;
 59+	void*             data;
 60+	size_t            size;
 61+	int8_t            busy;
 62+} MausWLBuffer;
 63+
 64+typedef struct {
 65+	struct wl_display*    display;
 66+	struct wl_compositor* compositor;
 67+	struct wl_surface*    surface;
 68+
 69+	struct wl_shm* shm;
 70+	MausWLBuffer   buffers[2];
 71+
 72+	struct wl_seat*     seat;
 73+	struct wl_pointer*  pointer;
 74+	struct wl_keyboard* keyboard;
 75+
 76+	struct wl_cursor_theme* cursor_theme;
 77+	struct wl_cursor*       cursor;
 78+	struct wl_surface*      cursor_surface;
 79+	uint32_t                pointer_enter_serial;
 80+	int32_t                 mouse_x;
 81+	int32_t                 mouse_y;
 82+	int8_t                  mouse_pos_set;
 83+	int8_t                  cursor_state;
 84+
 85+	struct zwp_pointer_constraints_v1*      pointer_constraints;
 86+	struct zwp_confined_pointer_v1*         locked_pointer;
 87+	struct zwp_relative_pointer_manager_v1* relative_pointer_manager;
 88+	struct zwp_relative_pointer_v1*         relative_pointer;
 89+
 90+	struct xkb_context* xkb_context;
 91+	struct xkb_keymap*  xkb_keymap;
 92+	struct xkb_state*   xkb_state;
 93+
 94+
 95+	MausEventPending pending;
 96+	MausEventPending evq[MAUS_EVQ_MAX];
 97+	uint32_t evq_head;
 98+	uint32_t evq_tail;
 99+	uint32_t evq_count;
100+
101+	int32_t  repeat_rate;
102+	int32_t  repeat_delay;
103+	uint32_t repeat_key_code;
104+	uint32_t repeat_key_sym;
105+	char     repeat_key_text;
106+	uint64_t repeat_next_ns;
107+
108+	struct xdg_wm_base*  wm_base;
109+	struct xdg_surface*  xdg_surface;
110+	struct xdg_toplevel* xdg_toplevel;
111+	int8_t               configured;
112+
113+	struct wl_registry*          registry;
114+} MausBackend;
115+
116+#endif /* MAUS_WAYLAND_H */
117+
+8, -9
 1@@ -1,20 +1,19 @@
 2 #ifndef MAUS_WIN_H
 3 #define MAUS_WIN_H
 4 
 5-#include <stdbool.h>
 6 #include <stdint.h>
 7 
 8 #include <windows.h>
 9 
10 typedef struct {
11-	HWND           hwnd;
12-	HINSTANCE      hInst;
13-	HDC            hdc;
14-	HBITMAP        hbm;
15-	HDC            memdc;
16-	bool           resized;
17-	uint32_t       rw;
18-	uint32_t       rh;
19+	HWND      hwnd;
20+	HINSTANCE hInst;
21+	HDC       hdc;
22+	HBITMAP   hbm;
23+	HDC       memdc;
24+	int8_t    resized;
25+	uint32_t  rw;
26+	uint32_t  rh;
27 } MausBackend;
28 
29 #endif /* MAUS_WIN_H */
+15, -9
 1@@ -1,26 +1,32 @@
 2 #ifndef MAUS_X11_H
 3 #define MAUS_X11_H
 4 
 5-#include <stdbool.h>
 6+#include <stdint.h>
 7 
 8 #include <X11/Xlib.h>
 9 #include <X11/extensions/XShm.h>
10 
11 typedef enum {
12 	MAUS_ATOM_WM_DELETE_WINDOW,
13-	MAUS_ATOM_LAST,
14+	MAUS_ATOM_LAST
15 } MausX11Atoms;
16 
17 typedef struct {
18-	Display*       display;
19-	Window         root;
20-	Window         win;
21-	Atom           atoms[MAUS_ATOM_LAST];
22-	GC             gc;
23+	Display* display;
24+	Window   root;
25+	Window   win;
26+	Atom     atoms[MAUS_ATOM_LAST];
27+	GC       gc;
28+
29+	int8_t cur_rel; /* relative cursor */
30+	int8_t ignore_warp;
31+	int32_t mouse_x;
32+	int32_t mouse_y;
33+	int8_t  mouse_pos_set;
34 
35-	XImage*        image;
36 	XShmSegmentInfo shm;
37-	bool           shmat;
38+	int8_t          shmat;
39+	XImage*         image; /* TODO: XImage path */
40 } MausBackend;
41 
42 #endif /* MAUS_X11_H */
+28, -15
 1@@ -22,6 +22,7 @@ static void vlog(FILE* fd, const char* fmt, va_list ap)
 2 void maus_die(const char* fmt, ...)
 3 {
 4 	va_list ap;
 5+
 6 	va_start(ap, fmt);
 7 	vlog(stderr, fmt, ap);
 8 	va_end(ap);
 9@@ -34,25 +35,29 @@ uint64_t maus_get_time_ns(void)
10 #if !defined(_WIN32)
11 	struct timespec ts;
12 	clock_gettime(CLOCK_MONOTONIC, &ts);
13-	return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
14+	return ts.tv_sec * 1000000000 + ts.tv_nsec;
15 #else
16 	static LARGE_INTEGER frequency = {0};
17+	LARGE_INTEGER counter;
18+	uint64_t sec;
19+	uint64_t rem;
20+
21 	if (frequency.QuadPart == 0)
22 		QueryPerformanceFrequency(&frequency);
23 
24-	LARGE_INTEGER counter;
25 	QueryPerformanceCounter(&counter);
26 
27-	uint64_t sec = counter.QuadPart / frequency.QuadPart;
28-	uint64_t rem = counter.QuadPart % frequency.QuadPart;
29+	sec = counter.QuadPart / frequency.QuadPart;
30+	rem = counter.QuadPart % frequency.QuadPart;
31 
32-	return (sec * 1000000000ULL) + ((rem * 1000000000ULL) / frequency.QuadPart);
33+	return (sec * 1000000000) + ((rem * 1000000000) / frequency.QuadPart);
34 #endif
35 }
36 
37 void maus_log(FILE* fd, const char* fmt, ...)
38 {
39 	va_list ap;
40+
41 	va_start(ap, fmt);
42 	vlog(fd, fmt, ap);
43 	fputc('\n', fd);
44@@ -61,24 +66,32 @@ void maus_log(FILE* fd, const char* fmt, ...)
45 
46 void maus_target_fps(Maus* mw, uint32_t fps)
47 {
48+	uint64_t ns_frame;
49+	uint64_t cur_time;
50+	uint64_t elapsed;
51+	uint64_t sleep;
52+
53+	#if !defined(_WIN32)
54+	struct timespec ts;
55+	#endif
56+
57 	if (fps == 0)
58 		return;
59 
60-	uint64_t ns_frame = 1000000000ULL / fps; /* max ns a frame can take */
61-	uint64_t cur_time = maus_get_time_ns();
62-	uint64_t elapsed = cur_time - mw->frame_time_last;
63+	ns_frame = 1000000000 / fps; /* max ns a frame can take */
64+	cur_time = maus_get_time_ns();
65+	elapsed = cur_time - mw->frame_time_last;
66 
67 	/* if early finish, sleep for the remaining time balance */
68 	if (elapsed < ns_frame) {
69-		uint64_t sleep = ns_frame - elapsed;
70+		sleep = ns_frame - elapsed;
71 	#if !defined(_WIN32)
72-		struct timespec ts;
73-		ts.tv_sec = sleep / 1000000000ULL;
74-		ts.tv_nsec = sleep % 1000000000ULL;
75+		ts.tv_sec = sleep / 1000000000;
76+		ts.tv_nsec = sleep % 1000000000;
77 		nanosleep(&ts, NULL);
78-	#else /* unix */
79-		Sleep((DWORD)((sleep + 999999ULL) / 1000000ULL));
80-	#endif /* platform */
81+	#else
82+		Sleep((DWORD)((sleep + 999999) / 1000000));
83+	#endif
84 	}
85 
86 	mw->frame_time_last = maus_get_time_ns();
+177, -122
  1@@ -5,17 +5,152 @@
  2 #include "maus.h"
  3 #include "maus_font.h"
  4 
  5+static void glyph_to_buf(int32_t sgx, int32_t egx, int32_t sgy, int32_t egy,
  6+                         int32_t gox, int32_t goy, MausGlyph* g, uint32_t col_up,
  7+                         uint32_t* buf, uint32_t stride);
  8+
  9+/* "blit" a glpyh to some buffer */
 10+static void glyph_to_buf(int32_t sgx, int32_t egx, int32_t sgy, int32_t egy,
 11+                         int32_t gox, int32_t goy, MausGlyph* g, uint32_t col_up,
 12+                         uint32_t* buf, uint32_t stride)
 13+{
 14+	int gy;
 15+	int gx;
 16+	int32_t dstx;
 17+
 18+	uint32_t* row;
 19+	int32_t offset;
 20+
 21+	for (gy = sgy; gy < egy; gy++) {
 22+		int dsty = goy + gy;
 23+
 24+		/* calculate row pointer once per scanline */
 25+		row = buf + (dsty * stride);
 26+		offset = gy * g->w;
 27+
 28+		for (gx = sgx; gx < egx; gx++) {
 29+			if (g->bmp[offset + gx] > 0) {
 30+				dstx = gox + gx;
 31+				row[dstx] = col_up;
 32+			}
 33+		}
 34+	}
 35+}
 36+
 37+static int8_t parse_glyphs(FILE* f, MausFont* font, char* line, size_t line_size,
 38+                           int32_t* encoding, int32_t* advance, int32_t* bw, int32_t* bh,
 39+                           int32_t* xoff, int32_t* yoff, int8_t* seen)
 40+{
 41+	int32_t rowbits;
 42+
 43+	int32_t x;
 44+	int32_t y;
 45+	uint64_t bits;
 46+
 47+	MausGlyph* g;
 48+
 49+	while (fgets(line, line_size, f)) {
 50+		if (sscanf(line, "ENCODING %d", encoding) == 1)
 51+			continue;
 52+		if (sscanf(line, "DWIDTH %d", advance) == 1)
 53+			continue;
 54+		if (sscanf(line, "BBX %d %d %d %d", bw, bh, xoff, yoff) == 4)
 55+			continue;
 56+
 57+		if (strncmp(line, "BITMAP", 6) == 0) {
 58+			/* invalid character code
 59+			   TODO: just skip it */
 60+			if (*encoding < 0 || *encoding >= MAUS_BDF_GLYPHS_MAX)
 61+				break;
 62+
 63+			/* empty/invalid size */
 64+			if (*bw <= 0 || *bh <= 0)
 65+				break;
 66+
 67+			g = &font->glyphs[*encoding];
 68+			free(g->bmp); /* prevent leak on hot reload */
 69+			memset(g, 0, sizeof(*g));
 70+
 71+			g->w = *bw;
 72+			g->h = *bh;
 73+			g->xoff = *xoff;
 74+			g->yoff = *yoff;
 75+			g->adv = *advance;
 76+
 77+			g->bmp = calloc((size_t)(*bw) * (*bh), 1);
 78+			if (!g->bmp) {
 79+				fclose(f);
 80+				maus_font_free(font);
 81+				return 0;
 82+			}
 83+
 84+			/* each bmp row in BDF is padded as a multiple
 85+			   of 8 bits. this rounds width up to the next
 86+			   multiple of 8 */
 87+			rowbits = (*bw + 7) & ~7;
 88+
 89+			for (y = 0; y < *bh; y++) {
 90+				/* missing row */
 91+				if (!fgets(line, line_size, f)) {
 92+					fclose(f);
 93+					maus_font_free(font);
 94+					return 0;
 95+				}
 96+
 97+				bits = strtoull(line, NULL, 16); /* hex to bits */
 98+				for (x = 0; x < *bw; x++) {
 99+					int bit = rowbits - 1 - x;
100+
101+					if ((bits >> bit) & 1)
102+						g->bmp[y * (*bw) + x] = 255; /* solid */
103+				}
104+			}
105+
106+			g->valid = 1;
107+			if (*advance > font->cellw)
108+				font->cellw = *advance;
109+			*seen = 1;
110+
111+			continue;
112+		}
113+
114+		if (strncmp(line, "ENDCHAR", 7) == 0)
115+			break;
116+	}
117+
118+	return 1;
119+}
120+
121 void maus_draw_text(Maus* mw, MausFont* font, int32_t x, int32_t y,
122                     const char* text, MausColor col)
123 {
124+	uint32_t col_up = MAUS_UNPACK_COL(col);
125+
126+	size_t i;
127+
128+	/* current coordinates */
129+	int32_t cx = x;
130+	int32_t cy = y;
131+
132+	/* origins to start drawing glyph from */
133+	int32_t gox;
134+	int32_t goy;
135+
136+	/* starting glyph coordinates */
137+	int32_t sgx;
138+	int32_t sgy;
139+
140+	/* ending glyph coordinates */
141+	int32_t egx;
142+	int32_t egy;
143+
144+	/* current glyph pointer */
145+	MausGlyph* g;
146+
147 	if (!mw || !mw->bfb || !font || !text)
148 		return;
149 
150-	uint32_t col_up = MAUS_UNPACK_COL(col);
151-	int32_t cx = x; /* current x */
152-	int32_t cy = y; /* current y */
153-
154-	for (size_t i = 0; text[i] != '\0'; i++) {
155+	for (i = 0; text[i] != '\0'; i++) {
156 		char c = text[i];
157 
158 		if (c == '\n') {
159@@ -24,7 +159,7 @@ void maus_draw_text(Maus* mw, MausFont* font, int32_t x, int32_t y,
160 			continue;
161 		}
162 
163-		MausGlyph* g = &font->glyphs[(uint8_t)c];
164+		g = &font->glyphs[(uint8_t)c];
165 		if (!g->valid) {
166 			/* still advance cx if character invalid */
167 			cx += (font->cellw > 0 ? font->cellw : 8);
168@@ -32,70 +167,57 @@ void maus_draw_text(Maus* mw, MausFont* font, int32_t x, int32_t y,
169 		}
170 
171 		/* origins to start drawing glyph from */
172-		int32_t gox = cx + g->xoff;
173-		int32_t goy = cy + font->asc - g->h - g->yoff;
174+		gox = cx + g->xoff;
175+		goy = cy + font->asc - g->h - g->yoff;
176 
177 		/* starting coordinates */
178-		int sgx = (-gox > 0) ? -gox : 0;
179-		int sgy = (-goy > 0) ? -goy : 0;
180+		sgx = (-gox > 0) ? -gox : 0;
181+		sgy = (-goy > 0) ? -goy : 0;
182 
183-		/* ending coordinates. if glyph runs over right/bottom, clamp it */
184-		int end_gx = (mw->width - gox < g->w) ? (mw->width - gox) : g->w;
185-		int end_gy = (mw->height - goy < g->h) ?(mw->height - goy) : g->h;
186+		egx = (mw->width - gox < g->w) ? (mw->width - gox) : g->w;
187+		egy = (mw->height - goy < g->h) ?(mw->height - goy) : g->h;
188 
189 		/* cull glyphs which start off screen */
190-		if (sgx >= end_gx || sgy >= end_gy) {
191+		if (sgx >= egx || sgy >= egy) {
192 			cx += g->adv;
193 			continue;
194 		}
195 
196-		/* "blit" font to framebuffer */
197-		for (int gy = sgy; gy < end_gy; gy++) {
198-			int dsty = goy + gy;
199-
200-			/* calculate row pointer once per scanline */
201-			uint32_t* row = mw->bfb + (dsty * mw->stride);
202-			int offset = gy * g->w;
203-
204-			for (int gx = sgx; gx < end_gx; gx++) {
205-				if (g->bmp[offset + gx] > 0) {
206-					int dstx = gox + gx;
207-					row[dstx] = col_up;
208-				}
209-			}
210-		}
211-
212+		glyph_to_buf(sgx, egx, sgy, egy, gox, goy, g, col_up, mw->bfb, mw->stride);
213 		cx += g->adv;
214 	}
215 }
216 
217 MausFont* maus_font_load(const char* path)
218 {
219-	FILE* fp = fopen(path, "r");
220-	if (!fp) {
221+	FILE* f = fopen(path, "r");
222+	MausFont* font = calloc(1, sizeof(MausFont));
223+	char line[1024];
224+
225+	int32_t asc = -1;
226+	int32_t dsc = -1;
227+	int32_t encoding = -1;
228+	int32_t advance = 0;
229+	int32_t bw = 0; /* bmp width */
230+	int32_t bh = 0; /* bmp height */
231+	int32_t xoff = 0;
232+	int32_t yoff = 0;
233+	int8_t seen = 0;
234+
235+	int i;
236+
237+	if (!f) {
238 		maus_log(stderr, "failed to load font \"%s\"", path);
239 		return NULL;
240 	}
241 
242-	MausFont* font = calloc(1, sizeof(MausFont));
243 	if (!font) {
244 		maus_log(stderr, "failed to calloc font");
245-		fclose(fp);
246+		fclose(f);
247 		return NULL;
248 	}
249 
250-	char line[1024];
251-	int asc = -1;
252-	int dsc = -1;
253-	int encoding = -1;
254-	int advance = 0;
255-	int bw = 0; /* bmp width */
256-	int bh = 0; /* bmp height */
257-	int xoff = 0;
258-	int yoff = 0;
259-	bool seen_any_glyph = 0;
260-
261-	while (fgets(line, sizeof(line), fp)) {
262+	while (fgets(line, sizeof(line), f)) {
263 		if (sscanf(line, "FONT_ASCENT %d", &asc) == 1)
264 			continue;
265 		if (sscanf(line, "FONT_DESCENT %d", &dsc) == 1)
266@@ -113,83 +235,14 @@ MausFont* maus_font_load(const char* path)
267 		xoff = 0;
268 		yoff = 0;
269 
270-		while (fgets(line, sizeof(line), fp)) {
271-			if (sscanf(line, "ENCODING %d", &encoding) == 1)
272-				continue;
273-			if (sscanf(line, "DWIDTH %d", &advance) == 1)
274-				continue;
275-			if (sscanf(line, "BBX %d %d %d %d", &bw, &bh, &xoff, &yoff) == 4)
276-				continue;
277-
278-			if (strncmp(line, "BITMAP", 6) == 0) {
279-				int rowbits;
280-
281-				/* invalid character code
282-				   TODO: just skip it */
283-				if (encoding < 0 || encoding >= MAUS_BDF_GLYPHS_MAX)
284-					break;
285-
286-				/* empty/invalid size */
287-				if (bw <= 0 || bh <= 0)
288-					break;
289-
290-				MausGlyph* g = &font->glyphs[encoding];
291-				free(g->bmp); /* prevent leak on hot reload */
292-				memset(g, 0, sizeof(*g));
293-
294-				g->w = bw;
295-				g->h = bh;
296-				g->xoff = xoff;
297-				g->yoff = yoff;
298-				g->adv = advance;
299-
300-				g->bmp = calloc((size_t)bw * bh, 1);
301-				if (!g->bmp) {
302-					fclose(fp);
303-					maus_font_free(font);
304-					return NULL;
305-				}
306-
307-				/* each bmp row in BDF is padded as a multiple
308-				   of 8 bits. this rounds width up to the next
309-				   multiple of 8 */
310-				rowbits = (bw + 7) & ~7;
311-
312-				for (int y = 0; y < bh; y++) {
313-					uint64_t bits;
314-
315-					/* missing row */
316-					if (!fgets(line, sizeof(line), fp)) {
317-						fclose(fp);
318-						maus_font_free(font);
319-						return NULL;
320-					}
321-
322-					bits = strtoull(line, NULL, 16); /* hex to bits */
323-					for (int x = 0; x < bw; x++) {
324-						int bit = rowbits - 1 - x;
325-
326-						if ((bits >> bit) & 1)
327-							g->bmp[y * bw + x] = 255; /* solid */
328-					}
329-				}
330-
331-				g->valid = true;
332-				if (advance > font->cellw)
333-					font->cellw = advance;
334-				seen_any_glyph = true;
335-
336-				continue;
337-			}
338-
339-			if (strncmp(line, "ENDCHAR", 7) == 0)
340-				break;
341-		}
342+		if (!parse_glyphs(f, font, line, sizeof(line), &encoding, &advance, &bw, &bh, &xoff, &yoff, &seen))
343+			return NULL;
344 	}
345 
346-	fclose(fp);
347 
348-	if (!seen_any_glyph) {
349+	fclose(f);
350+
351+	if (!seen) {
352 		free(font);
353 		return NULL;
354 	}
355@@ -199,7 +252,7 @@ MausFont* maus_font_load(const char* path)
356 		asc = 0;
357 		dsc = 0;
358 
359-		for (int i = 0; i < MAUS_BDF_GLYPHS_MAX; i++) {
360+		for (i = 0; i < MAUS_BDF_GLYPHS_MAX; i++) {
361 			MausGlyph* g = &font->glyphs[i];
362 
363 			/* unloaded glpyh */
364@@ -230,10 +283,12 @@ MausFont* maus_font_load(const char* path)
365 
366 void maus_font_free(MausFont* font)
367 {
368+	int i;
369+
370 	if (!font)
371 		return;
372 
373-	for (int i = 0; i < MAUS_BDF_GLYPHS_MAX; i++)
374+	for (i = 0; i < MAUS_BDF_GLYPHS_MAX; i++)
375 		free(font->glyphs[i].bmp);
376 
377 	free(font);
+1351, -0
   1@@ -0,0 +1,1351 @@
   2+#define _POSIX_C_SOURCE 200809L
   3+
   4+#include <fcntl.h>
   5+#include <poll.h>
   6+#include <stdlib.h>
   7+#include <string.h>
   8+#include <sys/mman.h>
   9+#include <unistd.h>
  10+
  11+#include <wayland-client.h>
  12+#include <wayland-cursor.h>
  13+#include <xdg-shell-client-protocol.h>
  14+#include <pointer-constraints-unstable-v1-client-protocol.h>
  15+#include <relative-pointer-unstable-v1-client-protocol.h>
  16+#include <xkbcommon/xkbcommon.h>
  17+
  18+#include "maus.h"
  19+#include "maus_wayland.h"
  20+
  21+static void registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name);
  22+static void xdg_surface_configure(void* data, struct xdg_surface* xdg_surface, uint32_t serial);
  23+static void xdg_toplevel_configure(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height, struct wl_array* states);
  24+static void xdg_toplevel_close(void* data, struct xdg_toplevel* xdg_toplevel);
  25+static void xdg_toplevel_configure_bounds(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height);
  26+static void xdg_toplevel_wm_capabilities(void* data, struct xdg_toplevel* xdg_toplevel, struct wl_array* capabilities);
  27+static void wm_base_ping(void* data, struct xdg_wm_base* wm_base, uint32_t serial);
  28+static void seat_capabilities(void* data, struct wl_seat* seat, uint32_t capabilities);
  29+static void seat_name(void* data, struct wl_seat* seat, const char* name);
  30+static void pointer_enter(void* data, struct wl_pointer* pointer, uint32_t serial, struct wl_surface* surface, wl_fixed_t sx, wl_fixed_t sy);
  31+static void pointer_leave(void* data, struct wl_pointer* pointer, uint32_t serial, struct wl_surface* surface);
  32+static void pointer_motion(void* data, struct wl_pointer* pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy);
  33+static void pointer_button(void* data, struct wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state);
  34+static void pointer_axis(void* data, struct wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value);
  35+static void pointer_frame(void* data, struct wl_pointer* pointer);
  36+static void pointer_axis_source(void* data, struct wl_pointer* pointer, uint32_t axis_source);
  37+static void pointer_axis_stop(void* data, struct wl_pointer* pointer, uint32_t time, uint32_t axis);
  38+static void pointer_axis_discrete(void* data, struct wl_pointer* pointer, uint32_t axis, int32_t discrete);
  39+static void pointer_axis_value120(void* data, struct wl_pointer* pointer, uint32_t axis, int32_t value120);
  40+static void pointer_axis_relative_direction(void* data, struct wl_pointer* pointer, uint32_t axis, uint32_t direction);
  41+static void relative_pointer_motion(void* data, struct zwp_relative_pointer_v1* relative_pointer, uint32_t utime_hi, uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel);
  42+static void keyboard_keymap(void* data, struct wl_keyboard* keyboard, uint32_t format, int32_t fd, uint32_t size);
  43+static void keyboard_enter(void* data, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface, struct wl_array* keys);
  44+static void keyboard_leave(void* data, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface);
  45+static void keyboard_key(void* data, struct wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state);
  46+static void keyboard_modifiers(void* data, struct wl_keyboard* keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group);
  47+static void keyboard_repeat_info(void* data, struct wl_keyboard* keyboard, int32_t rate, int32_t delay);
  48+static void registry_global(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
  49+
  50+static int8_t cursor_init(Maus* mw);
  51+static void cursor_destroy(Maus* mw);
  52+static void cursor_lock(Maus* mw);
  53+static void cursor_unlock(Maus* mw);
  54+static void cursor_show(Maus* mw);
  55+static void cursor_hide(Maus* mw);
  56+static void cursor_relative(Maus* mw);
  57+static void cursor_absolute(Maus* mw);
  58+static void event_push_pending(Maus* mw);
  59+static int8_t event_pop(Maus* mw, MausEventPending* ev);
  60+static void event_push_key(Maus* mw, uint32_t code, uint32_t sym, char text, uint8_t pressed);
  61+static int8_t repeat_push_due(Maus* mw);
  62+static int32_t repeat_timeout_ms(Maus* mw);
  63+static int8_t display_read(Maus* mw, int timeout_ms);
  64+static MausKey keysym_to_mauskey(xkb_keysym_t sym);
  65+static MausMouseButton wl_button_to_maus(uint32_t button);
  66+static void wl_buffer_release(void* data, struct wl_buffer* buffer);
  67+
  68+static struct wl_registry_listener registry_listener = { registry_global, registry_global_remove };
  69+static struct xdg_surface_listener xdg_surface_listener = { xdg_surface_configure };
  70+static struct xdg_toplevel_listener xdg_toplevel_listener = {
  71+	xdg_toplevel_configure, xdg_toplevel_close,
  72+	xdg_toplevel_configure_bounds, xdg_toplevel_wm_capabilities
  73+};
  74+static struct xdg_wm_base_listener xdg_wm_base_listener = { wm_base_ping };
  75+static struct wl_seat_listener seat_listener = { seat_capabilities, seat_name };
  76+static struct wl_pointer_listener pointer_listener = {
  77+	pointer_enter, pointer_leave, pointer_motion, pointer_button,
  78+	pointer_axis, pointer_frame, pointer_axis_source, pointer_axis_stop,
  79+	pointer_axis_discrete, pointer_axis_value120, pointer_axis_relative_direction
  80+};
  81+static struct wl_keyboard_listener keyboard_listener = {
  82+	keyboard_keymap, keyboard_enter, keyboard_leave, keyboard_key,
  83+	keyboard_modifiers, keyboard_repeat_info
  84+};
  85+static struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
  86+	relative_pointer_motion
  87+};
  88+static struct wl_buffer_listener wl_buffer_listener = { wl_buffer_release };
  89+
  90+static void wl_buffer_release(void* data, struct wl_buffer* buffer)
  91+{
  92+	MausWLBuffer* wb = data;
  93+
  94+	(void)buffer;
  95+
  96+	wb->busy = 0;
  97+}
  98+
  99+static void event_push_pending(Maus* mw)
 100+{
 101+	MausBackend* be = &mw->backend;
 102+
 103+	if (!be->pending.pending)
 104+		return;
 105+
 106+	if (be->evq_count == MAUS_EVQ_MAX) {
 107+		be->evq_head = (be->evq_head + 1) % MAUS_EVQ_MAX;
 108+		be->evq_count--;
 109+	}
 110+
 111+	be->evq[be->evq_tail] = be->pending;
 112+	be->evq_tail = (be->evq_tail + 1) % MAUS_EVQ_MAX;
 113+	be->evq_count++;
 114+	be->pending.pending = 0;
 115+}
 116+
 117+static int8_t event_pop(Maus* mw, MausEventPending* ev)
 118+{
 119+	MausBackend* be = &mw->backend;
 120+
 121+	if (be->evq_count == 0)
 122+		return 0;
 123+
 124+	*ev = be->evq[be->evq_head];
 125+	be->evq_head = (be->evq_head + 1) % MAUS_EVQ_MAX;
 126+	be->evq_count--;
 127+	return 1;
 128+}
 129+
 130+static void event_push_key(Maus* mw, uint32_t code, uint32_t sym, char text, uint8_t pressed)
 131+{
 132+	MausBackend* be = &mw->backend;
 133+
 134+	be->pending.pending = 1;
 135+	be->pending.type = MAUS_EV_KEY;
 136+	be->pending.key_code = code;
 137+	be->pending.key_sym = sym;
 138+	be->pending.key_text = text;
 139+	be->pending.key_pressed = pressed;
 140+	event_push_pending(mw);
 141+}
 142+
 143+static void registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name)
 144+{
 145+	(void)data;
 146+	(void)wl_registry;
 147+	(void)name;
 148+}
 149+
 150+static void xdg_surface_configure(void* data, struct xdg_surface* xdg_surface, uint32_t serial)
 151+{
 152+	Maus* mw = data;
 153+	xdg_surface_ack_configure(xdg_surface, serial);
 154+	mw->backend.configured = 1;
 155+	if (!mw->backend.pending.pending) {
 156+		mw->backend.pending.pending = 1;
 157+		mw->backend.pending.type = MAUS_EV_REDRAW;
 158+	}
 159+	event_push_pending(mw);
 160+}
 161+
 162+static void xdg_toplevel_configure(void* data, struct xdg_toplevel* xdg_toplevel,
 163+                                   int32_t width, int32_t height, struct wl_array* states)
 164+{
 165+	Maus* mw = data;
 166+
 167+	(void)xdg_toplevel;
 168+	(void)states;
 169+
 170+	if (width <= 0 || height <= 0)
 171+		return;
 172+
 173+	mw->backend.pending.pending = 1;
 174+	mw->backend.pending.type = MAUS_EV_RESIZE;
 175+	mw->backend.pending.width = width;
 176+	mw->backend.pending.height = height;
 177+	event_push_pending(mw);
 178+}
 179+
 180+static void xdg_toplevel_close(void* data, struct xdg_toplevel* xdg_toplevel)
 181+{
 182+	Maus* mw = data;
 183+
 184+	(void)xdg_toplevel;
 185+
 186+	mw->backend.pending.pending = 1;
 187+	mw->backend.pending.type = MAUS_EV_CLOSE;
 188+	event_push_pending(mw);
 189+}
 190+
 191+static void xdg_toplevel_configure_bounds(void* data, struct xdg_toplevel* xdg_toplevel,
 192+                                          int32_t width, int32_t height)
 193+{
 194+	(void)data;
 195+	(void)xdg_toplevel;
 196+	(void)width;
 197+	(void)height;
 198+}
 199+
 200+static void xdg_toplevel_wm_capabilities(void* data, struct xdg_toplevel* xdg_toplevel,
 201+                                         struct wl_array* capabilities)
 202+{
 203+	(void)data;
 204+	(void)xdg_toplevel;
 205+	(void)capabilities;
 206+}
 207+
 208+static void wm_base_ping(void* data, struct xdg_wm_base* wm_base, uint32_t serial)
 209+{
 210+	(void)data;
 211+	xdg_wm_base_pong(wm_base, serial);
 212+}
 213+
 214+static void registry_global(void* data, struct wl_registry* registry,
 215+                            uint32_t name, const char* interface,
 216+                            uint32_t version)
 217+{
 218+	Maus* mw = data;
 219+	MausBackend* be = &mw->backend;
 220+
 221+	(void) version;
 222+
 223+	if (strcmp(interface, wl_compositor_interface.name) == 0)
 224+		be->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
 225+	else if (strcmp(interface, wl_shm_interface.name) == 0)
 226+		be->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
 227+	else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
 228+		be->wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
 229+		xdg_wm_base_add_listener(be->wm_base, &xdg_wm_base_listener, mw);
 230+	}
 231+	else if (strcmp(interface, wl_seat_interface.name) == 0) {
 232+		be->seat = wl_registry_bind(registry, name, &wl_seat_interface, 5);
 233+		wl_seat_add_listener(be->seat, &seat_listener, mw);
 234+	}
 235+	else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0)
 236+		be->pointer_constraints = wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, 1);
 237+	else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0)
 238+		be->relative_pointer_manager = wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, 1);
 239+}
 240+
 241+static void seat_capabilities(void* data, struct wl_seat* seat, uint32_t capabilities)
 242+{
 243+	Maus* mw = data;
 244+	MausBackend* be = &mw->backend;
 245+
 246+	if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !be->pointer) {
 247+		be->pointer = wl_seat_get_pointer(seat);
 248+		wl_pointer_add_listener(be->pointer, &pointer_listener, mw);
 249+	}
 250+	else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && be->pointer) {
 251+		cursor_absolute(mw);
 252+		wl_pointer_destroy(be->pointer);
 253+		be->pointer = NULL;
 254+	}
 255+
 256+	if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !be->keyboard) {
 257+		be->keyboard = wl_seat_get_keyboard(seat);
 258+		wl_keyboard_add_listener(be->keyboard, &keyboard_listener, mw);
 259+	}
 260+	else if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && be->keyboard) {
 261+		wl_keyboard_destroy(be->keyboard);
 262+		be->keyboard = NULL;
 263+	}
 264+}
 265+
 266+static void seat_name(void* data, struct wl_seat* seat, const char* name)
 267+{
 268+	(void)data;
 269+	(void)seat;
 270+	(void)name;
 271+}
 272+
 273+static void pointer_enter(void* data, struct wl_pointer* pointer, uint32_t serial,
 274+                          struct wl_surface* surface, wl_fixed_t sx, wl_fixed_t sy)
 275+{
 276+	Maus* mw = data;
 277+
 278+	(void)pointer;
 279+	(void)surface;
 280+	(void)sx;
 281+	(void)sy;
 282+
 283+	mw->backend.pointer_enter_serial = serial;
 284+	if (mw->backend.cursor_state == MAUS_CURSOR_STATE_HIDDEN)
 285+		cursor_hide(mw);
 286+	else
 287+		cursor_show(mw);
 288+}
 289+
 290+static void pointer_leave(void* data, struct wl_pointer* pointer, uint32_t serial,
 291+                          struct wl_surface* surface)
 292+{
 293+	(void)data;
 294+	(void)pointer;
 295+	(void)serial;
 296+	(void)surface;
 297+}
 298+
 299+static void pointer_motion(void* data, struct wl_pointer* pointer, uint32_t time,
 300+                           wl_fixed_t sx, wl_fixed_t sy)
 301+{
 302+	Maus* mw = data;
 303+	MausBackend* be = &mw->backend;
 304+	int32_t x;
 305+	int32_t y;
 306+
 307+	(void)pointer;
 308+	(void)time;
 309+
 310+	if (be->relative_pointer)
 311+		return;
 312+
 313+	x = wl_fixed_to_int(sx);
 314+	y = wl_fixed_to_int(sy);
 315+
 316+	be->pending.pending = 1;
 317+	be->pending.type = MAUS_EV_MOUSE_MOTION;
 318+	be->pending.mouse_x = x;
 319+	be->pending.mouse_y = y;
 320+	be->pending.mouse_dx = be->mouse_pos_set ? x - be->mouse_x : 0;
 321+	be->pending.mouse_dy = be->mouse_pos_set ? y - be->mouse_y : 0;
 322+	be->mouse_x = x;
 323+	be->mouse_y = y;
 324+	be->mouse_pos_set = 1;
 325+	event_push_pending(mw);
 326+}
 327+
 328+static void pointer_button(void* data, struct wl_pointer* pointer, uint32_t serial,
 329+                           uint32_t time, uint32_t button, uint32_t state)
 330+{
 331+	Maus* mw = data;
 332+	MausMouseButton mb;
 333+
 334+	(void)pointer;
 335+	(void)serial;
 336+	(void)time;
 337+
 338+	mb = wl_button_to_maus(button);
 339+	mw->backend.pending.pending = 1;
 340+	mw->backend.pending.type = MAUS_EV_MOUSE_BUTTON;
 341+	mw->backend.pending.mouse_button = mb;
 342+	mw->backend.pending.mouse_pressed = state == WL_POINTER_BUTTON_STATE_PRESSED;
 343+
 344+	if (mb != MAUS_MOUSE_BUTTON_NONE)
 345+		mw->mouse_buttons[mb] = mw->backend.pending.mouse_pressed;
 346+	event_push_pending(mw);
 347+}
 348+
 349+static void pointer_axis(void* data, struct wl_pointer* pointer, uint32_t time,
 350+                         uint32_t axis, wl_fixed_t value)
 351+{
 352+	Maus* mw = data;
 353+
 354+	(void)pointer;
 355+	(void)time;
 356+
 357+	if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
 358+		return;
 359+
 360+	mw->backend.pending.pending = 1;
 361+	mw->backend.pending.type = MAUS_EV_MOUSE_BUTTON;
 362+	mw->backend.pending.mouse_button = wl_fixed_to_int(value) > 0
 363+		? MAUS_MOUSE_BUTTON_SCROLL_DOWN
 364+		: MAUS_MOUSE_BUTTON_SCROLL_UP;
 365+	mw->backend.pending.mouse_pressed = 1;
 366+	event_push_pending(mw);
 367+}
 368+
 369+static void pointer_frame(void* data, struct wl_pointer* pointer)
 370+{
 371+	(void)data;
 372+	(void)pointer;
 373+}
 374+
 375+static void pointer_axis_source(void* data, struct wl_pointer* pointer, uint32_t axis_source)
 376+{
 377+	(void)data;
 378+	(void)pointer;
 379+	(void)axis_source;
 380+}
 381+
 382+static void pointer_axis_stop(void* data, struct wl_pointer* pointer, uint32_t time, uint32_t axis) {
 383+	(void)data;
 384+	(void)pointer;
 385+	(void)time;
 386+	(void)axis;
 387+}
 388+
 389+static void pointer_axis_discrete(void* data, struct wl_pointer* pointer, uint32_t axis,
 390+                                  int32_t discrete)
 391+{
 392+	(void)data;
 393+	(void)pointer;
 394+	(void)axis;
 395+	(void)discrete;
 396+}
 397+
 398+static void pointer_axis_value120(void* data, struct wl_pointer* pointer, uint32_t axis,
 399+                                  int32_t value120)
 400+{
 401+	(void)data;
 402+	(void)pointer;
 403+	(void)axis;
 404+	(void)value120;
 405+}
 406+
 407+static void pointer_axis_relative_direction(void* data, struct wl_pointer* pointer,
 408+                                            uint32_t axis, uint32_t direction)
 409+{
 410+	(void)data;
 411+	(void)pointer;
 412+	(void)axis;
 413+	(void)direction;
 414+}
 415+
 416+static void relative_pointer_motion(void* data, struct zwp_relative_pointer_v1* relative_pointer,
 417+                                    uint32_t utime_hi, uint32_t utime_lo,
 418+                                    wl_fixed_t dx, wl_fixed_t dy,
 419+                                    wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel)
 420+{
 421+	Maus* mw = data;
 422+
 423+	(void)relative_pointer;
 424+	(void)utime_hi;
 425+	(void)utime_lo;
 426+	(void)dx_unaccel;
 427+	(void)dy_unaccel;
 428+
 429+	mw->backend.pending.pending = 1;
 430+	mw->backend.pending.type = MAUS_EV_MOUSE_MOTION;
 431+	mw->backend.pending.mouse_x = wl_fixed_to_int(dx);
 432+	mw->backend.pending.mouse_y = wl_fixed_to_int(dy);
 433+	mw->backend.pending.mouse_dx = wl_fixed_to_int(dx);
 434+	mw->backend.pending.mouse_dy = wl_fixed_to_int(dy);
 435+	event_push_pending(mw);
 436+}
 437+
 438+static void keyboard_keymap(void* data, struct wl_keyboard* keyboard, uint32_t format,
 439+                            int32_t fd, uint32_t size)
 440+{
 441+	Maus* mw = data;
 442+	MausBackend* be = &mw->backend;
 443+	char* map;
 444+
 445+	(void)keyboard;
 446+
 447+	if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
 448+		close(fd);
 449+		return;
 450+	}
 451+
 452+	map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 453+	if (map == MAP_FAILED) {
 454+		close(fd);
 455+		return;
 456+	}
 457+
 458+	if (be->xkb_state)
 459+		xkb_state_unref(be->xkb_state);
 460+	if (be->xkb_keymap)
 461+		xkb_keymap_unref(be->xkb_keymap);
 462+
 463+	be->xkb_keymap = xkb_keymap_new_from_string(
 464+		be->xkb_context, map,
 465+		XKB_KEYMAP_FORMAT_TEXT_V1,
 466+		XKB_KEYMAP_COMPILE_NO_FLAGS
 467+	);
 468+
 469+	munmap(map, size);
 470+	close(fd);
 471+
 472+	if (!be->xkb_keymap)
 473+		return;
 474+
 475+	be->xkb_state = xkb_state_new(be->xkb_keymap);
 476+}
 477+
 478+static void keyboard_enter(void* data, struct wl_keyboard* keyboard, uint32_t serial,
 479+                           struct wl_surface* surface, struct wl_array* keys)
 480+{
 481+	(void)data;
 482+	(void)keyboard;
 483+	(void)serial;
 484+	(void)surface;
 485+	(void)keys;
 486+}
 487+
 488+static void keyboard_leave(void* data, struct wl_keyboard* keyboard, uint32_t serial,
 489+                           struct wl_surface* surface)
 490+{
 491+	Maus* mw = data;
 492+
 493+	(void)keyboard;
 494+	(void)serial;
 495+	(void)surface;
 496+
 497+	mw->backend.repeat_key_code = 0;
 498+}
 499+
 500+static void keyboard_key(void* data, struct wl_keyboard* keyboard, uint32_t serial,
 501+                         uint32_t time, uint32_t key, uint32_t state)
 502+{
 503+	Maus* mw = data;
 504+	MausBackend* be = &mw->backend;
 505+	uint32_t code;
 506+	xkb_keysym_t sym;
 507+	MausKey maus_key;
 508+	char text[8];
 509+	int len;
 510+	uint8_t pressed;
 511+
 512+	(void)keyboard;
 513+	(void)serial;
 514+	(void)time;
 515+
 516+	code = key + 8;
 517+	sym = XKB_KEY_NoSymbol;
 518+	text[0] = 0;
 519+
 520+	if (be->xkb_state) {
 521+		sym = xkb_state_key_get_one_sym(be->xkb_state, code);
 522+		if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
 523+			len = xkb_state_key_get_utf8(be->xkb_state, code, text, sizeof(text));
 524+			if (len <= 0)
 525+				text[0] = 0;
 526+		}
 527+	}
 528+
 529+	maus_key = keysym_to_mauskey(sym);
 530+	if (maus_key == MAUS_KEY_ENTER)
 531+		text[0] = '\n';
 532+	pressed = state == WL_KEYBOARD_KEY_STATE_PRESSED;
 533+	event_push_key(mw, code, maus_key, text[0], pressed);
 534+
 535+	if (code < MAUS_KEYCODE_LAST)
 536+		mw->key_codes[code] = pressed;
 537+	if (maus_key != MAUS_KEY_NONE)
 538+		mw->key_syms[maus_key] = pressed;
 539+
 540+	if (!pressed) {
 541+		if (be->repeat_key_code == code)
 542+			be->repeat_key_code = 0;
 543+		return;
 544+	}
 545+
 546+	if (be->repeat_rate > 0 && be->xkb_keymap &&
 547+	    xkb_keymap_key_repeats(be->xkb_keymap, code)) {
 548+		be->repeat_key_code = code;
 549+		be->repeat_key_sym = maus_key;
 550+		be->repeat_key_text = text[0];
 551+		be->repeat_next_ns = maus_get_time_ns() + be->repeat_delay * 1000000;
 552+	}
 553+	else
 554+		be->repeat_key_code = 0;
 555+}
 556+
 557+static void keyboard_modifiers(void* data, struct wl_keyboard* keyboard, uint32_t serial,
 558+                               uint32_t mods_depressed, uint32_t mods_latched,
 559+                               uint32_t mods_locked, uint32_t group)
 560+{
 561+	Maus* mw = data;
 562+
 563+	(void)keyboard;
 564+	(void)serial;
 565+
 566+	if (!mw->backend.xkb_state)
 567+		return;
 568+
 569+	xkb_state_update_mask(
 570+		mw->backend.xkb_state, mods_depressed,
 571+		mods_latched, mods_locked,
 572+		0, 0, group
 573+	);
 574+}
 575+
 576+static void keyboard_repeat_info(void* data, struct wl_keyboard* keyboard,
 577+                                 int32_t rate, int32_t delay)
 578+{
 579+	Maus* mw = data;
 580+
 581+	(void)keyboard;
 582+
 583+	mw->backend.repeat_rate = rate;
 584+	mw->backend.repeat_delay = delay;
 585+	if (rate <= 0)
 586+		mw->backend.repeat_key_code = 0;
 587+}
 588+
 589+static int8_t repeat_push_due(Maus* mw)
 590+{
 591+	MausBackend* be = &mw->backend;
 592+	uint64_t now;
 593+	uint64_t interval;
 594+
 595+	if (be->repeat_key_code == 0 || be->repeat_rate <= 0)
 596+		return 0;
 597+
 598+	now = maus_get_time_ns();
 599+	if (now < be->repeat_next_ns)
 600+		return 0;
 601+
 602+	event_push_key(mw, be->repeat_key_code, be->repeat_key_sym, be->repeat_key_text, 1);
 603+	interval = 1000000000 / be->repeat_rate;
 604+	do {
 605+		be->repeat_next_ns += interval;
 606+	} while (be->repeat_next_ns <= now);
 607+
 608+	return 1;
 609+}
 610+
 611+static int32_t repeat_timeout_ms(Maus* mw)
 612+{
 613+	MausBackend* be = &mw->backend;
 614+	uint64_t now;
 615+	uint64_t diff;
 616+
 617+	if (be->repeat_key_code == 0 || be->repeat_rate <= 0)
 618+		return -1;
 619+
 620+	now = maus_get_time_ns();
 621+	if (now >= be->repeat_next_ns)
 622+		return 0;
 623+
 624+	diff = be->repeat_next_ns - now;
 625+	/* round xto nearest ms */
 626+	return (int32_t)((diff + 999999) / 1000000);
 627+}
 628+
 629+static int8_t display_read(Maus* mw, int timeout_ms)
 630+{
 631+	MausBackend* be = &mw->backend;
 632+	struct pollfd pfd;
 633+	int ret;
 634+
 635+	if (wl_display_prepare_read(be->display) != 0) {
 636+		wl_display_dispatch_pending(be->display);
 637+		return 1;
 638+	}
 639+
 640+	wl_display_flush(be->display);
 641+
 642+	pfd.fd = wl_display_get_fd(be->display);
 643+	pfd.events = POLLIN;
 644+	pfd.revents = 0;
 645+
 646+	ret = poll(&pfd, 1, timeout_ms);
 647+	if (ret > 0 && (pfd.revents & POLLIN)) {
 648+		if (wl_display_read_events(be->display) == -1) {
 649+			wl_display_cancel_read(be->display);
 650+			return -1;
 651+		}
 652+		wl_display_dispatch_pending(be->display);
 653+		return 1;
 654+	}
 655+
 656+	wl_display_cancel_read(be->display);
 657+	return 0;
 658+}
 659+
 660+
 661+static int8_t cursor_init(Maus* mw)
 662+{
 663+	MausBackend* be = &mw->backend;
 664+
 665+	be->cursor_theme = wl_cursor_theme_load(NULL, 24, be->shm);
 666+	if (!be->cursor_theme)
 667+		return 0;
 668+
 669+	be->cursor = wl_cursor_theme_get_cursor(be->cursor_theme, "left_ptr");
 670+	if (!be->cursor)
 671+		return 0;
 672+
 673+	be->cursor_surface = wl_compositor_create_surface(be->compositor);
 674+	if (!be->cursor_surface)
 675+		return 0;
 676+
 677+	return 1;
 678+}
 679+
 680+static void cursor_destroy(Maus* mw)
 681+{
 682+	MausBackend* be = &mw->backend;
 683+
 684+	if (be->cursor_surface)
 685+		wl_surface_destroy(be->cursor_surface);
 686+	if (be->cursor_theme)
 687+		wl_cursor_theme_destroy(be->cursor_theme);
 688+
 689+	be->cursor_surface = NULL;
 690+	be->cursor_theme = NULL;
 691+	be->cursor = NULL;
 692+}
 693+
 694+static void cursor_hide(Maus* mw)
 695+{
 696+	MausBackend* be = &mw->backend;
 697+
 698+	if (!be->pointer)
 699+		return;
 700+
 701+	wl_pointer_set_cursor(be->pointer, be->pointer_enter_serial, NULL, 0, 0);
 702+}
 703+
 704+static void cursor_lock(Maus* mw)
 705+{
 706+	MausBackend* be = &mw->backend;
 707+
 708+	if (be->locked_pointer)
 709+		return;
 710+	if (!be->pointer_constraints || !be->surface || !be->pointer)
 711+		return;
 712+
 713+	be->locked_pointer = zwp_pointer_constraints_v1_confine_pointer(
 714+		be->pointer_constraints, be->surface, be->pointer, NULL,
 715+		ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT
 716+	);
 717+}
 718+
 719+static void cursor_unlock(Maus* mw)
 720+{
 721+	MausBackend* be = &mw->backend;
 722+
 723+	if (be->locked_pointer) {
 724+		zwp_confined_pointer_v1_destroy(be->locked_pointer);
 725+		be->locked_pointer = NULL;
 726+	}
 727+
 728+	if (be->cursor_state == MAUS_CURSOR_STATE_VISIBLE)
 729+		cursor_show(mw);
 730+}
 731+
 732+static void cursor_show(Maus* mw)
 733+{
 734+	MausBackend* be = &mw->backend;
 735+	struct wl_cursor_image* image;
 736+	struct wl_buffer* buffer;
 737+
 738+	if (!be->pointer || !be->cursor || !be->cursor_surface)
 739+		return;
 740+
 741+	image = be->cursor->images[0];
 742+	buffer = wl_cursor_image_get_buffer(image);
 743+	if (!buffer)
 744+		return;
 745+
 746+	wl_pointer_set_cursor(
 747+		be->pointer, be->pointer_enter_serial,
 748+		be->cursor_surface, image->hotspot_x, image->hotspot_y
 749+	);
 750+	wl_surface_attach(be->cursor_surface, buffer, 0, 0);
 751+	wl_surface_damage_buffer(be->cursor_surface, 0, 0, image->width, image->height);
 752+	wl_surface_commit(be->cursor_surface);
 753+}
 754+
 755+static void cursor_relative(Maus* mw)
 756+{
 757+	MausBackend* be = &mw->backend;
 758+	if (be->relative_pointer)
 759+		return;
 760+	if (!be->relative_pointer_manager || !be->pointer)
 761+		return;
 762+
 763+	cursor_lock(mw);
 764+	be->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
 765+		be->relative_pointer_manager, be->pointer
 766+	);
 767+	zwp_relative_pointer_v1_add_listener(
 768+		be->relative_pointer, &relative_pointer_listener, mw
 769+	);
 770+}
 771+
 772+static void cursor_absolute(Maus* mw)
 773+{
 774+	MausBackend* be = &mw->backend;
 775+
 776+	if (be->relative_pointer) {
 777+		zwp_relative_pointer_v1_destroy(be->relative_pointer);
 778+		be->relative_pointer = NULL;
 779+	}
 780+
 781+	cursor_unlock(mw);
 782+}
 783+
 784+static int8_t fb_create(Maus* mw);
 785+static int8_t fb_create_buffer(Maus* mw, MausWLBuffer* wb);
 786+static int8_t fb_create_shm(size_t size);
 787+static void fb_destroy(Maus* mw);
 788+static int8_t handle_event(Maus* mw, MausEvent* ev);
 789+
 790+static int8_t fb_create(Maus* mw)
 791+{
 792+	MausBackend* be = &mw->backend;
 793+
 794+	if (!fb_create_buffer(mw, &be->buffers[0])) {
 795+		fb_destroy(mw);
 796+		return 0;
 797+	}
 798+	if (!fb_create_buffer(mw, &be->buffers[1])) {
 799+		fb_destroy(mw);
 800+		return 0;
 801+	}
 802+
 803+	mw->fb = be->buffers[0].data;
 804+	mw->stride = mw->width;
 805+	return 1;
 806+}
 807+
 808+static int8_t fb_create_buffer(Maus* mw, MausWLBuffer* wb)
 809+{
 810+	MausBackend* be = &mw->backend;
 811+	struct wl_shm_pool* pool;
 812+
 813+	int fd;
 814+	int stride;
 815+	int size;
 816+
 817+	stride = mw->width * 4;
 818+	size = stride * mw->height;
 819+
 820+	fd = fb_create_shm(size);
 821+	if (fd < 0)
 822+		return 0;
 823+
 824+	wb->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 825+	if (wb->data == MAP_FAILED) {
 826+		wb->data = NULL;
 827+		close(fd);
 828+		return 0;
 829+	}
 830+
 831+	pool = wl_shm_create_pool(be->shm, fd, size);
 832+	wb->buffer = wl_shm_pool_create_buffer(
 833+		pool, 0, mw->width, mw->height,
 834+		stride, WL_SHM_FORMAT_XRGB8888
 835+	);
 836+
 837+	wl_shm_pool_destroy(pool);
 838+	close(fd);
 839+
 840+	if (!wb->buffer) {
 841+		munmap(wb->data, size);
 842+		wb->data = NULL;
 843+		return 0;
 844+	}
 845+
 846+	wl_buffer_add_listener(wb->buffer, &wl_buffer_listener, wb);
 847+	wb->size = size;
 848+	wb->busy = 0;
 849+
 850+	return 1;
 851+}
 852+
 853+static int8_t fb_create_shm(size_t size)
 854+{
 855+	char name[64];
 856+	int fd;
 857+
 858+	int i;
 859+
 860+	for (i = 0; i < 100; i++) {
 861+		snprintf(name, sizeof(name), "/maus-%ld-%d", (long)getpid(), i);
 862+
 863+		fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
 864+		if (fd >= 0) {
 865+			shm_unlink(name);
 866+
 867+			if (ftruncate(fd, size) < 0) {
 868+				close(fd);
 869+				return -1;
 870+			}
 871+
 872+			return fd;
 873+		}
 874+	}
 875+
 876+	return -1;
 877+}
 878+
 879+static void fb_destroy(Maus* mw)
 880+{
 881+	MausBackend* be = &mw->backend;
 882+
 883+	uint32_t i;
 884+
 885+	for (i = 0; i < 2; i++) {
 886+		if (be->buffers[i].buffer)
 887+			wl_buffer_destroy(be->buffers[i].buffer);
 888+
 889+		if (be->buffers[i].data && be->buffers[i].size > 0)
 890+			munmap(be->buffers[i].data, be->buffers[i].size);
 891+
 892+		be->buffers[i].buffer = NULL;
 893+		be->buffers[i].data = NULL;
 894+		be->buffers[i].size = 0;
 895+		be->buffers[i].busy = 0;
 896+	}
 897+	mw->fb = NULL;
 898+}
 899+
 900+static int8_t handle_event(Maus* mw, MausEvent* ev)
 901+{
 902+	MausEventPending pending;
 903+
 904+	if (!event_pop(mw, &pending))
 905+		return 0;
 906+
 907+	ev->type = pending.type;
 908+
 909+	switch (ev->type) {
 910+	case MAUS_EV_CLOSE:
 911+		return 1;
 912+
 913+	case MAUS_EV_RESIZE:
 914+		ev->resize.width = pending.width;
 915+		ev->resize.height = pending.height;
 916+		return 1;
 917+
 918+	case MAUS_EV_MOUSE_MOTION:
 919+		ev->mouse.motion.x = pending.mouse_x;
 920+		ev->mouse.motion.y = pending.mouse_y;
 921+		ev->mouse.motion.dx = pending.mouse_dx;
 922+		ev->mouse.motion.dy = pending.mouse_dy;
 923+		return 1;
 924+
 925+	case MAUS_EV_MOUSE_BUTTON:
 926+		ev->mouse.button.button = pending.mouse_button;
 927+		ev->mouse.button.pressed = pending.mouse_pressed;
 928+		return 1;
 929+
 930+	case MAUS_EV_KEY:
 931+		ev->key.code = pending.key_code;
 932+		ev->key.pressed = pending.key_pressed;
 933+		ev->key.key = pending.key_sym;
 934+		ev->key.text = pending.key_text;
 935+		return 1;
 936+
 937+	default:
 938+		return 1;
 939+	}
 940+}
 941+
 942+static MausMouseButton wl_button_to_maus(uint32_t button)
 943+{
 944+	switch (button) {
 945+		case 0x110: return MAUS_MOUSE_BUTTON_LEFT;
 946+		case 0x111: return MAUS_MOUSE_BUTTON_RIGHT;
 947+		case 0x112: return MAUS_MOUSE_BUTTON_MIDDLE;
 948+		default:    return MAUS_MOUSE_BUTTON_NONE;
 949+	}
 950+}
 951+
 952+static MausKey keysym_to_mauskey(xkb_keysym_t sym)
 953+{
 954+	if (sym >= 0x20 && sym <= 0x7E)
 955+		return (MausKey)sym;
 956+
 957+	if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F12)
 958+		return MAUS_KEY_F1 + (sym - XKB_KEY_F1);
 959+	if (sym >= XKB_KEY_KP_0 && sym <= XKB_KEY_KP_9)
 960+		return MAUS_KEY_KP_0 + (sym - XKB_KEY_KP_0);
 961+
 962+	switch (sym) {
 963+		case XKB_KEY_BackSpace:    return MAUS_KEY_BACKSPACE;
 964+		case XKB_KEY_Tab:          return MAUS_KEY_TAB;
 965+		case XKB_KEY_Return:       return MAUS_KEY_ENTER;
 966+		case XKB_KEY_Escape:       return MAUS_KEY_ESCAPE;
 967+		case XKB_KEY_Delete:       return MAUS_KEY_DELETE;
 968+		case XKB_KEY_Left:         return MAUS_KEY_LEFT;
 969+		case XKB_KEY_Right:        return MAUS_KEY_RIGHT;
 970+		case XKB_KEY_Up:           return MAUS_KEY_UP;
 971+		case XKB_KEY_Down:         return MAUS_KEY_DOWN;
 972+		case XKB_KEY_Home:         return MAUS_KEY_HOME;
 973+		case XKB_KEY_End:          return MAUS_KEY_END;
 974+		case XKB_KEY_Page_Up:      return MAUS_KEY_PAGE_UP;
 975+		case XKB_KEY_Page_Down:    return MAUS_KEY_PAGE_DOWN;
 976+		case XKB_KEY_Insert:       return MAUS_KEY_INSERT;
 977+		case XKB_KEY_Shift_L:      return MAUS_KEY_SHIFT_L;
 978+		case XKB_KEY_Shift_R:      return MAUS_KEY_SHIFT_R;
 979+		case XKB_KEY_Control_L:    return MAUS_KEY_CONTROL_L;
 980+		case XKB_KEY_Control_R:    return MAUS_KEY_CONTROL_R;
 981+		case XKB_KEY_Alt_L:        return MAUS_KEY_ALT_L;
 982+		case XKB_KEY_Alt_R:        return MAUS_KEY_ALT_R;
 983+		case XKB_KEY_Super_L:      return MAUS_KEY_SUPER_L;
 984+		case XKB_KEY_Super_R:      return MAUS_KEY_SUPER_R;
 985+		case XKB_KEY_Caps_Lock:    return MAUS_KEY_CAPS_LOCK;
 986+		case XKB_KEY_Num_Lock:     return MAUS_KEY_NUM_LOCK;
 987+		case XKB_KEY_Scroll_Lock:  return MAUS_KEY_SCROLL_LOCK;
 988+		case XKB_KEY_Print:        return MAUS_KEY_PRINT_SCREEN;
 989+		case XKB_KEY_Pause:        return MAUS_KEY_PAUSE;
 990+		case XKB_KEY_Menu:         return MAUS_KEY_MENU;
 991+		case XKB_KEY_KP_Decimal:   return MAUS_KEY_KP_DECIMAL;
 992+		case XKB_KEY_KP_Divide:    return MAUS_KEY_KP_DIVIDE;
 993+		case XKB_KEY_KP_Multiply:  return MAUS_KEY_KP_MULTIPLY;
 994+		case XKB_KEY_KP_Subtract:  return MAUS_KEY_KP_SUBTRACT;
 995+		case XKB_KEY_KP_Add:       return MAUS_KEY_KP_ADD;
 996+		case XKB_KEY_KP_Enter:     return MAUS_KEY_KP_ENTER;
 997+		case XKB_KEY_KP_Equal:     return MAUS_KEY_KP_EQUAL;
 998+		default:                   return MAUS_KEY_NONE;
 999+	}
1000+}
1001+
1002+void maus_clear(Maus* mw, MausColor col)
1003+{
1004+	uint32_t up = MAUS_UNPACK_COL(col);
1005+	uint32_t pxs = mw->height * mw->width;
1006+
1007+	uint32_t i;
1008+
1009+	for (i = 0; i < pxs; i++)
1010+		mw->bfb[i] = up;
1011+}
1012+
1013+/* TODO */
1014+void maus_clipboard_set_text(Maus* mw, const char* text)
1015+{
1016+	(void)mw;
1017+	(void)text;
1018+}
1019+
1020+/* TODO */
1021+char* maus_clipboard_get_text(Maus* mw)
1022+{
1023+	(void)mw;
1024+	return NULL;
1025+}
1026+
1027+void maus_close(Maus* mw)
1028+{
1029+	MausBackend* be;
1030+
1031+	if (!mw)
1032+		return;
1033+
1034+	be = &mw->backend;
1035+
1036+	fb_destroy(mw);
1037+	cursor_absolute(mw);
1038+
1039+	if (be->xdg_toplevel)
1040+		xdg_toplevel_destroy(be->xdg_toplevel);
1041+
1042+	if (be->xdg_surface)
1043+		xdg_surface_destroy(be->xdg_surface);
1044+
1045+	if (be->surface)
1046+		wl_surface_destroy(be->surface);
1047+
1048+	if (be->keyboard)
1049+		wl_keyboard_destroy(be->keyboard);
1050+
1051+	if (be->pointer)
1052+		wl_pointer_destroy(be->pointer);
1053+
1054+	if (be->seat)
1055+		wl_seat_destroy(be->seat);
1056+
1057+	if (be->pointer_constraints)
1058+		zwp_pointer_constraints_v1_destroy(be->pointer_constraints);
1059+
1060+	if (be->relative_pointer_manager)
1061+		zwp_relative_pointer_manager_v1_destroy(be->relative_pointer_manager);
1062+
1063+	if (be->xkb_state)
1064+		xkb_state_unref(be->xkb_state);
1065+
1066+	if (be->xkb_keymap)
1067+		xkb_keymap_unref(be->xkb_keymap);
1068+
1069+	if (be->xkb_context)
1070+		xkb_context_unref(be->xkb_context);
1071+
1072+	cursor_destroy(mw);
1073+
1074+	if (be->wm_base)
1075+		xdg_wm_base_destroy(be->wm_base);
1076+
1077+	if (be->shm)
1078+		wl_shm_destroy(be->shm);
1079+
1080+	if (be->compositor)
1081+		wl_compositor_destroy(be->compositor);
1082+
1083+	if (be->registry)
1084+		wl_registry_destroy(be->registry);
1085+
1086+	if (be->display) {
1087+		wl_display_flush(be->display);
1088+		wl_display_disconnect(be->display);
1089+	}
1090+
1091+	free(mw->bfb);
1092+	free(mw);
1093+}
1094+
1095+int8_t maus_close_window(Maus* mw)
1096+{
1097+	MausBackend* be = &mw->backend;
1098+
1099+	cursor_unlock(mw);
1100+	fb_destroy(mw);
1101+
1102+	if (be->xdg_toplevel)
1103+		xdg_toplevel_destroy(be->xdg_toplevel);
1104+	if (be->xdg_surface)
1105+		xdg_surface_destroy(be->xdg_surface);
1106+	if (be->surface)
1107+		wl_surface_destroy(be->surface);
1108+
1109+	be->xdg_toplevel = NULL;
1110+	be->xdg_surface = NULL;
1111+	be->surface = NULL;
1112+	be->configured = 0;
1113+
1114+	return 1;
1115+}
1116+
1117+int8_t maus_create_window(Maus* mw)
1118+{
1119+	MausBackend* be = &mw->backend;
1120+
1121+	be->surface = wl_compositor_create_surface(be->compositor);
1122+	if (!be->surface)
1123+		return 0;
1124+
1125+	be->xdg_surface = xdg_wm_base_get_xdg_surface(be->wm_base, be->surface);
1126+	if (!be->xdg_surface)
1127+		return 0;
1128+
1129+	xdg_surface_add_listener(be->xdg_surface, &xdg_surface_listener, mw);
1130+
1131+	be->xdg_toplevel = xdg_surface_get_toplevel(be->xdg_surface);
1132+	if (!be->xdg_toplevel)
1133+		return 0;
1134+
1135+	xdg_toplevel_add_listener(be->xdg_toplevel, &xdg_toplevel_listener, mw);
1136+	xdg_toplevel_set_title(be->xdg_toplevel, mw->title);
1137+
1138+	if (!fb_create(mw))
1139+		return 0;
1140+
1141+	wl_surface_commit(be->surface);
1142+
1143+	while (!be->configured) {
1144+		if (wl_display_dispatch(be->display) == -1)
1145+			return 0;
1146+	}
1147+
1148+	return 1;
1149+}
1150+
1151+Maus* maus_init(const char* title, int x, int y, int width, int height)
1152+{
1153+	Maus* mw = calloc(1, sizeof(Maus));
1154+	MausBackend* be = &mw->backend;
1155+
1156+	if (!mw)
1157+		return NULL;
1158+
1159+	be->display = wl_display_connect(NULL);
1160+	if (!be->display) {
1161+		free(mw);
1162+		return NULL;
1163+	}
1164+
1165+	be->registry = wl_display_get_registry(be->display);
1166+	wl_registry_add_listener(be->registry, &registry_listener, mw);
1167+	wl_display_roundtrip(be->display);
1168+
1169+	be->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1170+	if (!be->xkb_context)
1171+		goto close;
1172+
1173+	if (!be->compositor || !be->shm || !be->wm_base) {
1174+		maus_close(mw);
1175+		free(mw);
1176+		return NULL;
1177+	}
1178+
1179+	if (!cursor_init(mw))
1180+		goto close;
1181+
1182+	mw->frame_time_last = maus_get_time_ns();
1183+	mw->title = title;
1184+	mw->x = x;
1185+	mw->y = y;
1186+	mw->width = width;
1187+	mw->height = height;
1188+	mw->stride = width;
1189+	be->cursor_state = MAUS_CURSOR_STATE_VISIBLE;
1190+
1191+	mw->bfb = calloc(mw->stride * mw->height, sizeof(uint32_t));
1192+	if (!mw->bfb)
1193+		goto close;
1194+
1195+	return mw;
1196+close:
1197+	maus_close(mw);
1198+	free(mw);
1199+	return NULL;
1200+}
1201+
1202+int8_t maus_event_poll(Maus* mw, MausEvent* ev)
1203+{
1204+	MausBackend* be = &mw->backend;
1205+	int8_t read;
1206+
1207+	ev->type = MAUS_EV_NONE;
1208+
1209+	if (repeat_push_due(mw) && handle_event(mw, ev))
1210+		return 1;
1211+
1212+	if (handle_event(mw, ev))
1213+		return 1;
1214+
1215+	wl_display_dispatch_pending(be->display);
1216+	if (handle_event(mw, ev))
1217+		return 1;
1218+
1219+	read = display_read(mw, 0);
1220+	if (read == -1) {
1221+		ev->type = MAUS_EV_CLOSE;
1222+		return 1;
1223+	}
1224+	if (read == 0)
1225+		return 0;
1226+	if (repeat_push_due(mw) && handle_event(mw, ev))
1227+		return 1;
1228+
1229+	return handle_event(mw, ev);
1230+}
1231+
1232+void maus_event_wait(Maus* mw, MausEvent* ev)
1233+{
1234+	MausBackend* be = &mw->backend;
1235+	int32_t timeout;
1236+	int8_t read;
1237+
1238+	ev->type = MAUS_EV_NONE;
1239+
1240+	for (;;) {
1241+		if (repeat_push_due(mw) && handle_event(mw, ev))
1242+			return;
1243+
1244+		if (handle_event(mw, ev))
1245+			return;
1246+
1247+		timeout = repeat_timeout_ms(mw);
1248+		if (timeout >= 0) {
1249+			read = display_read(mw, timeout);
1250+			if (read == -1) {
1251+				ev->type = MAUS_EV_CLOSE;
1252+				return;
1253+			}
1254+			continue;
1255+		}
1256+
1257+		if (wl_display_dispatch(be->display) == -1) {
1258+			ev->type = MAUS_EV_CLOSE;
1259+			return;
1260+		}
1261+	}
1262+}
1263+
1264+void maus_present(Maus* mw)
1265+{
1266+	MausBackend* be = &mw->backend;
1267+	MausWLBuffer* wb = NULL;
1268+	size_t bytes;
1269+
1270+	uint32_t i;
1271+
1272+	for (i = 0; i < 2; i++) {
1273+		if (be->buffers[i].buffer && !be->buffers[i].busy) {
1274+			wb = &be->buffers[i];
1275+			break;
1276+		}
1277+	}
1278+
1279+	if (!wb)
1280+		return;
1281+
1282+	bytes = mw->stride * mw->height * sizeof(uint32_t);
1283+	memcpy(wb->data, mw->bfb, bytes);
1284+	mw->fb = wb->data;
1285+	wb->busy = 1;
1286+
1287+	wl_surface_attach(be->surface, wb->buffer, 0, 0);
1288+	wl_surface_damage_buffer(be->surface, 0, 0, mw->width, mw->height);
1289+	wl_surface_commit(be->surface);
1290+	wl_display_flush(be->display);
1291+}
1292+
1293+int8_t maus_resize(Maus* mw, uint32_t width, uint32_t height)
1294+{
1295+	uint32_t* bfb;
1296+
1297+	if (width == 0 || height == 0)
1298+		return 0;
1299+
1300+	bfb = calloc(width * height, sizeof(uint32_t));
1301+	if (!bfb)
1302+		return 0;
1303+
1304+	fb_destroy(mw);
1305+	free(mw->bfb);
1306+
1307+	mw->width = width;
1308+	mw->height = height;
1309+	mw->stride = width;
1310+	mw->bfb = bfb;
1311+
1312+	if (!fb_create(mw)) {
1313+		free(mw->bfb);
1314+		mw->bfb = NULL;
1315+		return 0;
1316+	}
1317+
1318+	return 1;
1319+}
1320+
1321+void maus_cur_set_mode(Maus* mw, MausCursorState state)
1322+{
1323+	switch (state) {
1324+	case MAUS_CURSOR_STATE_VISIBLE:
1325+		cursor_show(mw);
1326+		mw->backend.cursor_state = state;
1327+		break;
1328+	case MAUS_CURSOR_STATE_HIDDEN:
1329+		cursor_hide(mw);
1330+		mw->backend.cursor_state = state;
1331+		break;
1332+	case MAUS_CURSOR_STATE_LOCKED:
1333+		mw->backend.cursor_state = state;
1334+		cursor_lock(mw);
1335+		break;
1336+	case MAUS_CURSOR_STATE_FREE:
1337+		cursor_unlock(mw);
1338+		mw->backend.cursor_state = state;
1339+		break;
1340+	case MAUS_CURSOR_STATE_RELATIVE:
1341+		cursor_relative(mw);
1342+		mw->backend.cursor_state = state;
1343+		break;
1344+	case MAUS_CURSOR_STATE_ABSOLUTE:
1345+		cursor_absolute(mw);
1346+		mw->backend.cursor_state = state;
1347+		break;
1348+	default:
1349+		break;
1350+	}
1351+}
1352+
+106, -77
  1@@ -10,9 +10,9 @@ typedef struct {
  2 	MausKey maus;
  3 } KeyMapEntry;
  4 
  5-static bool fb_create(Maus* mw);
  6+static int8_t fb_create(Maus* mw);
  7 static void fb_destroy(Maus* mw);
  8-static bool handle_event(const MSG* msg, MausEvent* ev, Maus* mw);
  9+static int8_t handle_event(const MSG* msg, MausEvent* ev, Maus* mw);
 10 static MausKey vk_to_mauskey(UINT vk);
 11 static UINT resolve_lr(UINT vk, LPARAM lparam);
 12 static char translate_text(UINT vk, UINT scan);
 13@@ -38,14 +38,16 @@ static const KeyMapEntry keymap[] = {
 14 	{ VK_DIVIDE,   MAUS_KEY_KP_DIVIDE }, { VK_DECIMAL,  MAUS_KEY_KP_DECIMAL },
 15 };
 16 
 17-static bool fb_create(Maus* mw)
 18+static int8_t fb_create(Maus* mw)
 19 {
 20 	MausBackend* be = &mw->backend;
 21+	BITMAPINFO bmi = {0};
 22+	HDC dc = GetDC(NULL);
 23+
 24 	be->hbm = NULL;
 25 	mw->fb = NULL;
 26 	mw->stride = 0;
 27 
 28-	BITMAPINFO bmi = {0};
 29 	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 30 	bmi.bmiHeader.biWidth = mw->width;
 31 	bmi.bmiHeader.biHeight = -mw->height;
 32@@ -53,7 +55,6 @@ static bool fb_create(Maus* mw)
 33 	bmi.bmiHeader.biBitCount = 32;
 34 	bmi.bmiHeader.biCompression = BI_RGB;
 35 
 36-	HDC dc = GetDC(NULL);
 37 	be->memdc = CreateCompatibleDC(dc);
 38 	be->hbm = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, (void**)&mw->fb, NULL, 0);
 39 	ReleaseDC(NULL, dc);
 40@@ -62,13 +63,13 @@ static bool fb_create(Maus* mw)
 41 		if (be->memdc)
 42 			DeleteDC(be->memdc);
 43 		maus_log(stderr, "CreateDIBSection failed");
 44-		return false;
 45+		return 0;
 46 	}
 47 
 48 	SelectObject(be->memdc, be->hbm);
 49 
 50 	mw->stride = mw->width;
 51-	return true;
 52+	return 1;
 53 }
 54 
 55 static void fb_destroy(Maus* mw)
 56@@ -88,7 +89,7 @@ static void fb_destroy(Maus* mw)
 57 	mw->stride = 0;
 58 }
 59 
 60-static bool handle_event(const MSG* msg, MausEvent* ev, Maus* mw)
 61+static int8_t handle_event(const MSG* msg, MausEvent* ev, Maus* mw)
 62 {
 63 	UINT scan;
 64 	UINT vk;
 65@@ -98,52 +99,52 @@ static bool handle_event(const MSG* msg, MausEvent* ev, Maus* mw)
 66 	switch (msg->message) {
 67 		case WM_QUIT:
 68 			ev->type = MAUS_EV_CLOSE;
 69-			return true;
 70+			return 1;
 71 
 72 		case WM_MOUSEMOVE:
 73 			ev->type = MAUS_EV_MOUSE_MOTION;
 74 			ev->mouse.motion.x = GET_X_LPARAM(msg->lParam);
 75 			ev->mouse.motion.y = GET_Y_LPARAM(msg->lParam);
 76-			return true;
 77+			return 1;
 78 
 79 		case WM_LBUTTONDOWN:
 80 			ev->type = MAUS_EV_MOUSE_BUTTON;
 81 			ev->mouse.button.button = MAUS_MOUSE_BUTTON_LEFT;
 82-			ev->mouse.button.pressed = true;
 83-			mw->mouse_buttons[MAUS_MOUSE_BUTTON_LEFT] = true;
 84-			return true;
 85+			ev->mouse.button.pressed = 1;
 86+			mw->mouse_buttons[MAUS_MOUSE_BUTTON_LEFT] = 1;
 87+			return 1;
 88 		case WM_LBUTTONUP:
 89 			ev->type = MAUS_EV_MOUSE_BUTTON;
 90 			ev->mouse.button.button = MAUS_MOUSE_BUTTON_LEFT;
 91-			ev->mouse.button.pressed = false;
 92-			mw->mouse_buttons[MAUS_MOUSE_BUTTON_LEFT] = false;
 93-			return true;
 94+			ev->mouse.button.pressed = 0;
 95+			mw->mouse_buttons[MAUS_MOUSE_BUTTON_LEFT] = 0;
 96+			return 1;
 97 
 98 		case WM_RBUTTONDOWN:
 99 			ev->type = MAUS_EV_MOUSE_BUTTON;
100 			ev->mouse.button.button = MAUS_MOUSE_BUTTON_RIGHT;
101-			ev->mouse.button.pressed = true;
102-			mw->mouse_buttons[MAUS_MOUSE_BUTTON_RIGHT] = true;
103-			return true;
104+			ev->mouse.button.pressed = 1;
105+			mw->mouse_buttons[MAUS_MOUSE_BUTTON_RIGHT] = 1;
106+			return 1;
107 		case WM_RBUTTONUP:
108 			ev->type = MAUS_EV_MOUSE_BUTTON;
109 			ev->mouse.button.button = MAUS_MOUSE_BUTTON_RIGHT;
110-			ev->mouse.button.pressed = false;
111-			mw->mouse_buttons[MAUS_MOUSE_BUTTON_RIGHT] = false;
112-			return true;
113+			ev->mouse.button.pressed = 0;
114+			mw->mouse_buttons[MAUS_MOUSE_BUTTON_RIGHT] = 0;
115+			return 1;
116 
117 		case WM_MBUTTONDOWN:
118 			ev->type = MAUS_EV_MOUSE_BUTTON;
119 			ev->mouse.button.button = MAUS_MOUSE_BUTTON_MIDDLE;
120-			ev->mouse.button.pressed = true;
121-			mw->mouse_buttons[MAUS_MOUSE_BUTTON_MIDDLE] = true;
122-			return true;
123+			ev->mouse.button.pressed = 1;
124+			mw->mouse_buttons[MAUS_MOUSE_BUTTON_MIDDLE] = 1;
125+			return 1;
126 		case WM_MBUTTONUP:
127 			ev->type = MAUS_EV_MOUSE_BUTTON;
128 			ev->mouse.button.button = MAUS_MOUSE_BUTTON_MIDDLE;
129-			ev->mouse.button.pressed = false;
130-			mw->mouse_buttons[MAUS_MOUSE_BUTTON_MIDDLE] = false;
131-			return true;
132+			ev->mouse.button.pressed = 0;
133+			mw->mouse_buttons[MAUS_MOUSE_BUTTON_MIDDLE] = 0;
134+			return 1;
135 
136 		case WM_MOUSEWHEEL:
137 			delta = GET_WHEEL_DELTA_WPARAM(msg->wParam);
138@@ -151,8 +152,8 @@ static bool handle_event(const MSG* msg, MausEvent* ev, Maus* mw)
139 			                     : MAUS_MOUSE_BUTTON_SCROLL_DOWN;
140 			ev->type = MAUS_EV_MOUSE_BUTTON;
141 			ev->mouse.button.button = mbtype;
142-			ev->mouse.button.pressed = true;
143-			return true;
144+			ev->mouse.button.pressed = 1;
145+			return 1;
146 
147 		case WM_KEYDOWN:
148 		case WM_SYSKEYDOWN:
149@@ -161,15 +162,15 @@ static bool handle_event(const MSG* msg, MausEvent* ev, Maus* mw)
150 
151 			ev->type = MAUS_EV_KEY;
152 			ev->key.code = (uint32_t)msg->wParam;
153-			ev->key.pressed = true;
154+			ev->key.pressed = 1;
155 			ev->key.key = vk_to_mauskey(vk);
156 			ev->key.text = translate_text((UINT)msg->wParam, scan);
157 
158 			if (ev->key.code < MAUS_KEYCODE_LAST)
159-				mw->key_codes[ev->key.code] = true;
160+				mw->key_codes[ev->key.code] = 1;
161 			if (ev->key.key != MAUS_KEY_NONE)
162-				mw->key_syms[ev->key.key] = true;
163-			return true;
164+				mw->key_syms[ev->key.key] = 1;
165+			return 1;
166 
167 		case WM_KEYUP:
168 		case WM_SYSKEYUP:
169@@ -177,18 +178,18 @@ static bool handle_event(const MSG* msg, MausEvent* ev, Maus* mw)
170 
171 			ev->type = MAUS_EV_KEY;
172 			ev->key.code = (uint32_t)msg->wParam;
173-			ev->key.pressed = false;
174+			ev->key.pressed = 0;
175 			ev->key.key = vk_to_mauskey(vk);
176 			ev->key.text = 0;
177 
178 			if (ev->key.code < MAUS_KEYCODE_LAST)
179-				mw->key_codes[ev->key.code] = false;
180+				mw->key_codes[ev->key.code] = 0;
181 			if (ev->key.key != MAUS_KEY_NONE)
182-				mw->key_syms[ev->key.key] = false;
183-			return true;
184+				mw->key_syms[ev->key.key] = 0;
185+			return 1;
186 	}
187 
188-	return false;
189+	return 0;
190 }
191 
192 static MausKey vk_to_mauskey(UINT vk)
193@@ -228,11 +229,13 @@ static UINT resolve_lr(UINT vk, LPARAM lparam)
194 static char translate_text(UINT vk, UINT scan)
195 {
196 	BYTE state[256];
197+	WCHAR buf[4];
198+	int n;
199+
200+	n = ToUnicode(vk, scan, state, buf, 4, 0);
201+
202 	if (!GetKeyboardState(state))
203 		return 0;
204-
205-	WCHAR buf[4];
206-	int n = ToUnicode(vk, scan, state, buf, 4, 0);
207 	if (n == 1 && buf[0] < 0x80)
208 		return (buf[0] == '\r') ? '\n' : (char)buf[0];
209 
210@@ -241,16 +244,19 @@ static char translate_text(UINT vk, UINT scan)
211 
212 static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
213 {
214+	Maus* mw;
215+	MausBackend* be;
216+
217 	if (msg == WM_DESTROY) {
218 		PostQuitMessage(EXIT_SUCCESS);
219 		return 0;
220 	}
221 
222 	if (msg == WM_SIZE) {
223-		Maus* mw = (Maus*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
224+		mw = (Maus*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
225 		if (mw) {
226-			MausBackend* be = &mw->backend;
227-			be->resized = true;
228+			be = &mw->backend;
229+			be->resized = 1;
230 			be->rw = LOWORD(lp);
231 			be->rh = HIWORD(lp);
232 		}
233@@ -264,20 +270,27 @@ void maus_clear(Maus* mw, MausColor col)
234 {
235 	uint32_t col_up = MAUS_UNPACK_COL(col);
236 	uint32_t pxs = mw->height * mw->width;
237-	for (uint32_t i = 0; i < pxs; i++)
238+	uint32_t i;
239+
240+	for (i = 0; i < pxs; i++)
241 		mw->bfb[i] = col_up;
242 }
243 
244 void maus_clipboard_set_text(Maus* mw, const char* text)
245 {
246 	MausBackend* be = &mw->backend;
247+	size_t len = strlen(text) + 1;
248+	HGLOBAL hmem;
249+
250+	void* ptr;
251+
252 	if (!text || !OpenClipboard(be->hwnd))
253 		return;
254+
255 	EmptyClipboard();
256-	size_t len = strlen(text) + 1;
257-	HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE, len);
258+	hmem = GlobalAlloc(GMEM_MOVEABLE, len);
259 	if (hmem) {
260-		void* ptr = GlobalLock(hmem);
261+		ptr = GlobalLock(hmem);
262 		if (ptr) {
263 			memcpy(ptr, text, len);
264 			GlobalUnlock(hmem);
265@@ -296,23 +309,28 @@ void maus_clipboard_set_text(Maus* mw, const char* text)
266 char* maus_clipboard_get_text(Maus* mw)
267 {
268 	MausBackend* be = &mw->backend;
269+	HANDLE hmem;
270+
271+	const char* text;
272+	size_t len;
273+
274 	if (!OpenClipboard(be->hwnd))
275 		return NULL;
276 
277-	HANDLE hmem = GetClipboardData(CF_TEXT);
278+	hmem = GetClipboardData(CF_TEXT);
279 	if (!hmem) {
280 		CloseClipboard();
281 		return NULL;
282 	}
283 
284-	const char* text = GlobalLock(hmem);
285+	text = GlobalLock(hmem);
286 	if (text) {
287 		if (mw->clipboard) {
288 			free(mw->clipboard);
289 			mw->clipboard = NULL;
290 		}
291 
292-		size_t len = strlen(text) + 1;
293+		len = strlen(text) + 1;
294 		mw->clipboard = malloc(len);
295 		if (mw->clipboard)
296 			memcpy(mw->clipboard, text, len);
297@@ -327,6 +345,7 @@ char* maus_clipboard_get_text(Maus* mw)
298 void maus_close(Maus* mw)
299 {
300 	MausBackend* be = &mw->backend;
301+
302 	fb_destroy(mw);
303 	if (mw->bfb) {
304 		free(mw->bfb);
305@@ -340,34 +359,35 @@ void maus_close(Maus* mw)
306 		(void) maus_close_window(mw);
307 }
308 
309-bool maus_close_window(Maus* mw)
310+int8_t maus_close_window(Maus* mw)
311 {
312 	MausBackend* be = &mw->backend;
313 
314 	if (be->hwnd) {
315 		DestroyWindow(be->hwnd);
316 		be->hwnd = NULL;
317-		return true;
318+		return 1;
319 	}
320 
321-	return false;
322+	return 0;
323 }
324 
325 
326-bool maus_create_window(Maus* mw)
327+int8_t maus_create_window(Maus* mw)
328 {
329 	MausBackend* be = &mw->backend;
330 	be->hInst = GetModuleHandle(NULL);
331-
332 	WNDCLASS wc = {0};
333+
334+	RECT r = { 0, 0, mw->width, mw->height };
335+
336 	wc.lpfnWndProc = WndProc;
337 	wc.hInstance = be->hInst;
338 	wc.lpszClassName = "MausWindow";
339 	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
340 	if (!RegisterClass(&wc))
341-		return false;
342+		return 0;
343 
344-	RECT r = { 0, 0, mw->width, mw->height };
345 	AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE);
346 
347 	be->hwnd = CreateWindowEx(
348@@ -378,12 +398,12 @@ bool maus_create_window(Maus* mw)
349 	);
350 
351 	if (!be->hwnd)
352-		return false;
353+		return 0;
354 
355 	SetWindowLongPtr(be->hwnd, GWLP_USERDATA, (LONG_PTR)mw);
356 	ShowWindow(be->hwnd, SW_SHOW);
357 	UpdateWindow(be->hwnd);
358-	return true;
359+	return 1;
360 }
361 
362 Maus* maus_init(const char* title, int x, int y, int width, int height)
363@@ -418,29 +438,30 @@ Maus* maus_init(const char* title, int x, int y, int width, int height)
364 	return mw;
365 }
366 
367-bool maus_event_poll(Maus* mw, MausEvent* ev)
368+int8_t maus_event_poll(Maus* mw, MausEvent* ev)
369 {
370 	MausBackend* be = &mw->backend;
371 	MSG msg;
372+
373 	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
374 		TranslateMessage(&msg);
375 		DispatchMessage(&msg);
376 		ev->type = MAUS_EV_NONE;
377 
378 		if (handle_event(&msg, ev, mw))
379-			return true;
380+			return 1;
381 	}
382 
383 	/* WM_SIZE doesnt is handled separately */
384 	if (be->resized) {
385-		be->resized = false;
386+		be->resized = 0;
387 		ev->type = MAUS_EV_RESIZE;
388 		ev->resize.width = be->rw;
389 		ev->resize.height = be->rh;
390-		return true;
391+		return 1;
392 	}
393 
394-	return false;
395+	return 0;
396 }
397 
398 void maus_event_wait(Maus* mw, MausEvent* ev)
399@@ -448,8 +469,10 @@ void maus_event_wait(Maus* mw, MausEvent* ev)
400 	MausBackend* be = &mw->backend;
401 	MSG msg;
402 
403+	int r;
404+
405 	for (;;) {
406-		int r = GetMessage(&msg, NULL, 0, 0);
407+		r = GetMessage(&msg, NULL, 0, 0);
408 		if (r <= 0) {
409 			ev->type = MAUS_EV_CLOSE;
410 			return;
411@@ -461,7 +484,7 @@ void maus_event_wait(Maus* mw, MausEvent* ev)
412 
413 		/* WM_SIZE doesnt is handled separately */
414                 if (be->resized) {
415-			be->resized = false;
416+			be->resized = 0;
417 			ev->type = MAUS_EV_RESIZE;
418 			ev->resize.width = be->rw;
419 			ev->resize.height = be->rh;
420@@ -476,22 +499,27 @@ void maus_event_wait(Maus* mw, MausEvent* ev)
421 void maus_present(Maus* mw)
422 {
423 	MausBackend* be = &mw->backend;
424+	HDC wdc;
425+
426+	uint32_t bytes;
427+
428 	if (!be->hwnd || !be->memdc)
429 		return;
430-	HDC wdc = GetDC(be->hwnd);
431 
432-	uint32_t bytes = mw->stride * mw->height * sizeof(uint32_t);
433+	wdc = GetDC(be->hwnd);
434+
435+	bytes = mw->stride * mw->height * sizeof(uint32_t);
436 	memcpy(mw->fb, mw->bfb, bytes);
437 
438 	BitBlt(wdc, 0, 0, mw->width, mw->height, be->memdc, 0, 0, SRCCOPY);
439 	ReleaseDC(be->hwnd, wdc);
440 }
441 
442-bool maus_resize(Maus* mw, uint32_t width, uint32_t height)
443+int8_t maus_resize(Maus* mw, uint32_t width, uint32_t height)
444 {
445 	if (width == 0 || height == 0 ||
446-	    (mw->width == width && mw->height == height))
447-		return false;
448+	   (mw->width == width && mw->height == height))
449+		return 0;
450 
451 	fb_destroy(mw);
452 	if (mw->bfb) {
453@@ -503,13 +531,13 @@ bool maus_resize(Maus* mw, uint32_t width, uint32_t height)
454 	mw->height = height;
455 
456 	if (!fb_create(mw))
457-		return false;
458+		return 0;
459 
460 	mw->bfb = calloc(mw->stride * mw->height, sizeof(uint32_t));
461 	if (!mw->bfb)
462-		return false;
463+		return 0;
464 
465-	return true;
466+	return 1;
467 }
468 
469 void maus_cur_set_mode(Maus* mw, MausCursorState state)
470@@ -517,6 +545,8 @@ void maus_cur_set_mode(Maus* mw, MausCursorState state)
471 	MausBackend* be = &mw->backend;
472 	HWND hwnd = be->hwnd;
473 
474+	RECT r;
475+
476 	if (!hwnd) {
477 		maus_log(stderr, "hwnd is NULL");
478 		return;
479@@ -537,7 +567,6 @@ void maus_cur_set_mode(Maus* mw, MausCursorState state)
480 
481 	/* lock cursor */
482 	if (state == MAUS_CURSOR_STATE_LOCKED) {
483-		RECT r;
484 		GetClientRect(hwnd, &r);
485 		MapWindowPoints(hwnd, NULL, (POINT*)&r, 2);
486 
+195, -90
  1@@ -1,5 +1,4 @@
  2 #include <limits.h>
  3-#include <stdbool.h>
  4 #include <stdlib.h>
  5 #include <string.h>
  6 #include <sys/shm.h>
  7@@ -21,11 +20,11 @@ typedef struct {
  8 	MausKey maus;
  9 } KeyMapEntry;
 10 
 11-static bool fb_create(Maus* mw);
 12-static bool fb_create_shm(Maus* mw);
 13-/* TODO? static bool fb_create_ximage(Maus* mw); */
 14+static int8_t fb_create(Maus* mw);
 15+static int8_t fb_create_shm(Maus* mw);
 16+/* TODO static int8_t fb_create_ximage(Maus* mw); */
 17 static void fb_destroy(Maus* mw);
 18-static bool handle_event(XEvent* xev, MausEvent* ev, Maus* mw);
 19+static int8_t handle_event(XEvent* xev, MausEvent* ev, Maus* mw);
 20 static MausKey keysym_to_mauskey(KeySym sym);
 21 static MausMouseButton mouse_button_to_maus(int btn);
 22 static int xerr(Display* dpy, XErrorEvent* ev);
 23@@ -59,11 +58,12 @@ static const KeyMapEntry keymap[] = {
 24 
 25 static int xerrored;
 26 
 27-static bool fb_create(Maus* mw)
 28+static int8_t fb_create(Maus* mw)
 29 {
 30 	MausBackend* be = &mw->backend;
 31+
 32 	be->image = NULL;
 33-	be->shmat = false;
 34+	be->shmat = 0;
 35 	be->shm.shmid = -1;
 36 	be->shm.shmaddr = NULL;
 37 	mw->fb = NULL;
 38@@ -73,43 +73,48 @@ static bool fb_create(Maus* mw)
 39 }
 40 
 41 /* allocate and store a new shm for the framebuffer */
 42-static bool fb_create_shm(Maus* mw)
 43+static int8_t fb_create_shm(Maus* mw)
 44 {
 45 	MausBackend* be = &mw->backend;
 46-	if (!XShmQueryExtension(be->display))
 47-		return false;
 48-
 49 	int scr = DefaultScreen(be->display);
 50 	int depth = DefaultDepth(be->display, scr);
 51 	Visual* vis = DefaultVisual(be->display, scr);
 52+
 53+	size_t size;
 54+	int (*old_xerr)(Display*, XErrorEvent*);
 55+
 56+	if (!XShmQueryExtension(be->display))
 57+		return 0;
 58+
 59 	if (!vis) {
 60 		maus_log(stderr, "could not get default visual");
 61-		return false;
 62+		return 0;
 63 	}
 64 
 65 	be->image = XShmCreateImage(
 66 		be->display, vis, depth, ZPixmap, NULL,
 67 		&be->shm, mw->width, mw->height
 68 	);
 69+
 70 	if (!be->image) {
 71 		maus_log(stderr, "could not create XImage (shm)");
 72-		return false;
 73+		return 0;
 74 	}
 75 
 76 	if (be->image->bits_per_pixel != 32) {
 77 		XDestroyImage(be->image);
 78 		be->image = NULL;
 79 		maus_log(stderr, "XImage not 32bpp");
 80-		return false;
 81+		return 0;
 82 	}
 83 
 84-	size_t size = be->image->bytes_per_line * be->image->height;
 85+	size = be->image->bytes_per_line * be->image->height;
 86 	be->shm.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600);
 87 	if (be->shm.shmid < 0) {
 88 		XDestroyImage(be->image);
 89 		be->image = NULL;
 90 		maus_log(stderr, "shmget() failed");
 91-		return false;
 92+		return 0;
 93 	}
 94 
 95 	be->shm.shmaddr = shmat(be->shm.shmid, NULL, 0);
 96@@ -118,14 +123,14 @@ static bool fb_create_shm(Maus* mw)
 97 		XDestroyImage(be->image);
 98 		be->image = NULL;
 99 		maus_log(stderr, "shmat() failed");
100-		return false;
101+		return 0;
102 	}
103 
104 	be->shm.readOnly = False;
105 	be->image->data = be->shm.shmaddr;
106 
107 	xerrored = 0;
108-	int (*old_xerr)(Display*, XErrorEvent*) = XSetErrorHandler(xerr);
109+	old_xerr = XSetErrorHandler(xerr);
110 
111 	if (!XShmAttach(be->display, &be->shm))
112 		xerrored = 1;
113@@ -142,26 +147,27 @@ static bool fb_create_shm(Maus* mw)
114 		be->shm.shmaddr = NULL;
115 		be->shm.shmid = -1;
116 		maus_log(stderr, "XShmAttach failed");
117-		return false;
118+		return 0;
119 	}
120 
121 	shmctl(be->shm.shmid, IPC_RMID, NULL);
122 
123-	be->shmat = true;
124+	be->shmat = 1;
125 	mw->fb = (uint32_t*) be->image->data;
126 	mw->stride = be->image->bytes_per_line / sizeof(uint32_t);
127 
128-	return true;
129+	return 1;
130 }
131 
132 static void fb_destroy(Maus* mw)
133 {
134 	MausBackend* be = &mw->backend;
135 	Display* dpy = be->display;
136+
137 	if (be->shmat) {
138 		XShmDetach(dpy, &be->shm);
139 		XSync(dpy, False);
140-		be->shmat = false;
141+		be->shmat = 0;
142 	}
143 
144 	if (be->image) {
145@@ -185,73 +191,85 @@ static void fb_destroy(Maus* mw)
146 	mw->stride = 0;
147 }
148 
149-static bool handle_event(XEvent* xev, MausEvent* ev, Maus* mw)
150+static int8_t handle_event(XEvent* xev, MausEvent* ev, Maus* mw)
151 {
152 	MausBackend* be = &mw->backend;
153 	uint32_t code = 0;
154 	unsigned int mb;
155 	MausMouseButton mbtype;
156 
157+	char buf[8] = {0};
158+	int len;
159+
160+	KeySym sym;
161+
162+	XSelectionRequestEvent* req;
163+	XEvent rsp; /* response */
164+	Atom utf8_string;
165+	Atom clipboard;
166+
167+	int cx;
168+	int cy;
169+
170 	switch (xev->type) {
171 		case Expose:
172 			if (xev->xexpose.count != 0)
173-				return false;
174+				return 0;
175 			ev->type = MAUS_EV_REDRAW;
176-			return true;
177+			return 1;
178 
179 		case ClientMessage:
180 			if ((Atom)xev->xclient.data.l[0] == be->atoms[MAUS_ATOM_WM_DELETE_WINDOW]) {
181 				ev->type = MAUS_EV_CLOSE;
182-				return true;
183+				return 1;
184 			}
185 			break;
186 
187 		case MappingNotify:
188 			XRefreshKeyboardMapping(&xev->xmapping);
189-			return true;
190+			return 1;
191 			break;
192 
193 		case KeyPress: {
194 			ev->type = MAUS_EV_KEY;
195 			code = xev->xkey.keycode;
196 			ev->key.code = code;
197-			ev->key.pressed = true;
198+			ev->key.pressed = 1;
199 			if (code < MAUS_KEYCODE_LAST)
200-			        mw->key_codes[code] = true;
201+			        mw->key_codes[code] = 1;
202 
203 			/* grab the raw, base sym */
204-			KeySym sym = XLookupKeysym(&xev->xkey, 0);
205+			sym = XLookupKeysym(&xev->xkey, 0);
206 			ev->key.key = keysym_to_mauskey(sym);
207 			if (ev->key.key != MAUS_KEY_NONE)
208-			        mw->key_syms[ev->key.key] = true;
209+			        mw->key_syms[ev->key.key] = 1;
210 
211-			char buf[8] = {0};
212-			int len = XLookupString(&xev->xkey, buf, sizeof(buf), NULL, NULL);
213+			len = XLookupString(&xev->xkey, buf, sizeof(buf), NULL, NULL);
214 			if (len > 0)
215-			        ev->key.text = (buf[0] == '\r') ? '\n' : buf[0];
216+				ev->key.text = (buf[0] == '\r') ? '\n' : buf[0];
217 			else
218-			        ev->key.text = 0;
219+				ev->key.text = 0;
220 
221-			return true;
222+			return 1;
223 		}
224 
225 		case KeyRelease: {
226 			ev->type = MAUS_EV_KEY;
227 			code = xev->xkey.keycode;
228 			ev->key.code = code;
229-			ev->key.pressed = false;
230+			ev->key.pressed = 0;
231 			ev->key.text = 0;
232 
233 			if (code < MAUS_KEYCODE_LAST)
234-			        mw->key_codes[code] = false;
235+			        mw->key_codes[code] = 0;
236 
237 			/* unset base sym set on `KeyPress` */
238-			KeySym sym = XLookupKeysym(&xev->xkey, 0);
239+			sym = XLookupKeysym(&xev->xkey, 0);
240 			ev->key.key = keysym_to_mauskey(sym);
241 			if (ev->key.key != MAUS_KEY_NONE)
242-			        mw->key_syms[ev->key.key] = false;
243+			        mw->key_syms[ev->key.key] = 0;
244 
245-			return true;
246+			return 1;
247 		}
248 
249 		case ButtonPress:
250@@ -259,37 +277,67 @@ static bool handle_event(XEvent* xev, MausEvent* ev, Maus* mw)
251 			mb = xev->xbutton.button;
252 			mbtype = mouse_button_to_maus(mb);
253 			ev->mouse.button.button = mbtype;
254-			ev->mouse.button.pressed = true;
255-			mw->mouse_buttons[mbtype] = true;
256-			return true;
257+			ev->mouse.button.pressed = 1;
258+			mw->mouse_buttons[mbtype] = 1;
259+			return 1;
260 
261 		case ButtonRelease:
262 			ev->type = MAUS_EV_MOUSE_BUTTON;
263 			mb = xev->xbutton.button;
264 			mbtype = mouse_button_to_maus(mb);
265 			ev->mouse.button.button = mbtype;
266-			ev->mouse.button.pressed = false;
267-			mw->mouse_buttons[mbtype] = false;
268-			return true;
269+			ev->mouse.button.pressed = 0;
270+			mw->mouse_buttons[mbtype] = 0;
271+			return 1;
272 
273 		case MotionNotify:
274+			if (be->cur_rel) {
275+				cx = mw->width / 2;
276+				cy = mw->height / 2;
277+
278+				if (be->ignore_warp && xev->xmotion.x == cx && xev->xmotion.y == cy) {
279+					be->ignore_warp = 0;
280+					return 0;
281+				}
282+
283+				ev->type = MAUS_EV_MOUSE_MOTION;
284+				ev->mouse.motion.x = xev->xmotion.x - cx;
285+				ev->mouse.motion.y = xev->xmotion.y - cy;
286+				ev->mouse.motion.dx = ev->mouse.motion.x;
287+				ev->mouse.motion.dy = ev->mouse.motion.y;
288+
289+				if (ev->mouse.motion.x != 0 || ev->mouse.motion.y != 0) {
290+					be->ignore_warp = 1;
291+					XWarpPointer(
292+						be->display, None, be->win,
293+						0, 0, 0, 0, cx, cy
294+					);
295+					XFlush(be->display);
296+				}
297+
298+				return ev->mouse.motion.x != 0 || ev->mouse.motion.y != 0;
299+			}
300 			ev->type = MAUS_EV_MOUSE_MOTION;
301 			ev->mouse.motion.x = xev->xmotion.x;
302 			ev->mouse.motion.y = xev->xmotion.y;
303-			return true;
304+			ev->mouse.motion.dx = be->mouse_pos_set ? xev->xmotion.x - be->mouse_x : 0;
305+			ev->mouse.motion.dy = be->mouse_pos_set ? xev->xmotion.y - be->mouse_y : 0;
306+			be->mouse_x = xev->xmotion.x;
307+			be->mouse_y = xev->xmotion.y;
308+			be->mouse_pos_set = 1;
309+			return 1;
310 
311 		case ConfigureNotify:
312 			ev->type = MAUS_EV_RESIZE;
313 			ev->resize.width = xev->xconfigure.width;
314 			ev->resize.height = xev->xconfigure.height;
315-			return true;
316+			return 1;
317 
318 		case SelectionRequest: {
319-			XSelectionRequestEvent* req = &xev->xselectionrequest;
320-			XEvent rsp; /* response */
321+			req = &xev->xselectionrequest;
322 
323-			Atom utf8_string = XInternAtom(be->display, "UTF8_STRING", False);
324-			Atom clipboard = XInternAtom(be->display, "CLIPBOARD", False);
325+			utf8_string = XInternAtom(be->display, "UTF8_STRING", False);
326+			clipboard = XInternAtom(be->display, "CLIPBOARD", False);
327 
328 			rsp.type = SelectionNotify;
329 			rsp.xselection.display = req->display;
330@@ -317,15 +365,17 @@ static bool handle_event(XEvent* xev, MausEvent* ev, Maus* mw)
331 			XSendEvent(req->display, req->requestor, False, 0, &rsp);
332 			XFlush(be->display);
333 
334-			return false; /* skip maus polling it */
335+			return 0; /* skip maus polling it */
336 		}
337 	}
338 
339-	return false;
340+	return 0;
341 }
342 
343 static MausKey keysym_to_mauskey(KeySym sym)
344 {
345+	size_t i;
346+
347 	if (sym >= 0x20 && sym <= 0x7E)
348 		return (MausKey)sym;
349 
350@@ -334,7 +384,7 @@ static MausKey keysym_to_mauskey(KeySym sym)
351 	if (sym >= XK_KP_0 && sym <= XK_KP_9)
352 		return MAUS_KEY_KP_0 + (sym - XK_KP_0);
353 
354-	for (size_t i = 0; i < sizeof(keymap)/ sizeof(keymap[0]); i++) {
355+	for (i = 0; i < sizeof(keymap)/ sizeof(keymap[0]); i++) {
356 		if (keymap[i].x11 == sym)
357 			return keymap[i].maus;
358 	}
359@@ -365,6 +415,8 @@ static int xerr(Display* dpy, XErrorEvent* ev)
360 void maus_clipboard_set_text(Maus* mw, const char* text)
361 {
362 	MausBackend* be = &mw->backend;
363+	Atom clipboard;
364+
365 	if (!be->display || be->win == None)
366 		return;
367 
368@@ -377,7 +429,7 @@ void maus_clipboard_set_text(Maus* mw, const char* text)
369 	if (text)
370 		mw->clipboard = maus_strdup(text);
371 
372-	Atom clipboard = XInternAtom(be->display, "CLIPBOARD", False);
373+	clipboard = XInternAtom(be->display, "CLIPBOARD", False);
374 	XSetSelectionOwner(be->display, clipboard, be->win, CurrentTime);
375 	XFlush(be->display);
376 }
377@@ -389,6 +441,19 @@ char* maus_clipboard_get_text(Maus* mw)
378 	Atom utf8_string = XInternAtom(be->display, "UTF8_STRING", False);
379 	Atom maus_clipboard_prop = XInternAtom(be->display, "MAUS_CLIPBOARD_PROP", False);
380 
381+	XEvent xev;
382+	int8_t ok = 0;
383+
384+	int timeout;
385+
386+	Atom actual_type;
387+	int actual_fmt;
388+	unsigned long nitems;
389+	unsigned long bytes_after;
390+	unsigned char* prop_data = NULL;
391+
392+	char* res = NULL;
393+
394 	/* fetch clipboard data to `maus_clipboard_prop`
395 	   window property */
396 	XConvertSelection(
397@@ -398,12 +463,10 @@ char* maus_clipboard_get_text(Maus* mw)
398 	XFlush(be->display);
399 
400 	/* check for selection response */
401-	XEvent xev;
402-	bool ok = false;
403-	for (int timeout = 0; timeout < 10000; timeout++) { 
404+	for (timeout = 0; timeout < 10000; timeout++) { 
405 		if (XCheckTypedWindowEvent(be->display, be->win, SelectionNotify, &xev)) {
406 			if (xev.xselection.property != None)
407-				ok = true;
408+				ok = 1;
409 			break;
410 		}
411 	}
412@@ -411,18 +474,12 @@ char* maus_clipboard_get_text(Maus* mw)
413 		return NULL;
414 
415 	/* read string off property */
416-	Atom actual_type;
417-	int actual_fmt;
418-	unsigned long nitems;
419-	unsigned long bytes_after;
420-	unsigned char* prop_data = NULL;
421 	XGetWindowProperty(
422 		be->display, be->win, maus_clipboard_prop, 0,LONG_MAX,
423 		True, utf8_string, &actual_type, &actual_fmt, &nitems,
424 		&bytes_after, &prop_data
425 	);
426 
427-	char* res = NULL;
428 	if (prop_data) {
429 		res = maus_strdup((char*)prop_data);
430 		XFree(prop_data);
431@@ -434,6 +491,7 @@ char* maus_clipboard_get_text(Maus* mw)
432 void maus_close(Maus* mw)
433 {
434 	MausBackend* be = &mw->backend;
435+
436 	fb_destroy(mw);
437 	if (mw->bfb) {
438 		free(mw->bfb);
439@@ -450,9 +508,10 @@ void maus_close(Maus* mw)
440 		XCloseDisplay(be->display);
441 }
442 
443-bool maus_close_window(Maus* mw)
444+int8_t maus_close_window(Maus* mw)
445 {
446 	MausBackend* be = &mw->backend;
447+
448 	if (be->gc) {
449 		XFreeGC(be->display, be->gc);
450 		be->gc = 0;
451@@ -463,10 +522,10 @@ bool maus_close_window(Maus* mw)
452 		XDestroyWindow(be->display, be->win);
453 	}
454 
455-	return true;
456+	return 1;
457 }
458 
459-bool maus_create_window(Maus* mw)
460+int8_t maus_create_window(Maus* mw)
461 {
462 	MausBackend* be = &mw->backend;
463 	Display* dpy = be->display;
464@@ -480,12 +539,13 @@ bool maus_create_window(Maus* mw)
465 	);
466 
467 	if (*win == None)
468-		return false;
469+		return 0;
470 
471 	XSelectInput(dpy, *win,
472 		ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask |
473 		ButtonReleaseMask | PointerMotionMask | StructureNotifyMask
474 	);
475+
476 	/* no black flashing when resizing */
477 	XSetWindowBackgroundPixmap(dpy, *win, None);
478 
479@@ -502,36 +562,44 @@ bool maus_create_window(Maus* mw)
480 	if (!be->gc) {
481 		maus_log(stderr, "failed to create window GC");
482 		maus_close_window(mw);
483-		return false;
484+		return 0;
485 	}
486 
487-	return true;
488+	return 1;
489 }
490 
491 void maus_clear(Maus* mw, MausColor col)
492 {
493 	uint32_t col_up = MAUS_UNPACK_COL(col);
494 	uint32_t pxs = mw->height * mw->width;
495-	for (uint32_t i = 0; i < pxs; i++)
496+	uint32_t i;
497+	for (i = 0; i < pxs; i++)
498 		mw->bfb[i] = col_up;
499 }
500 
501 Maus* maus_init(const char* title, int x, int y, int width, int height)
502 {
503+	Display* d = XOpenDisplay(NULL);
504+	Maus* mw = calloc(1, sizeof(Maus));
505+	MausBackend* be = &mw->backend;
506+
507 	if (MAUS_WARN_BACKEND_AUTO_SEL)
508 		maus_log(stderr, "backend auto-selected: X11");
509 
510-	Display* d = XOpenDisplay(NULL);
511 	if (!d) {
512 		maus_die("failed to open X11 display");
513 		return NULL;
514 	}
515 
516-	Maus* mw = calloc(1, sizeof(Maus));
517-	MausBackend* be = &mw->backend;
518 	be->display = d;
519 	be->root = DefaultRootWindow(d);
520 	be->win = None;
521+	be->cur_rel = 0;
522+	be->ignore_warp = 0;
523+	be->mouse_x = 0;
524+	be->mouse_y = 0;
525+	be->mouse_pos_set = 0;
526+
527 	mw->frame_time_last = maus_get_time_ns();
528 	mw->title = title;
529 	mw->width = width;
530@@ -542,7 +610,7 @@ Maus* maus_init(const char* title, int x, int y, int width, int height)
531 
532 	be->gc = 0;
533 	be->image = NULL;
534-	be->shmat = false;
535+	be->shmat = 0;
536 	be->shm.shmid = -1;
537 	be->shm.shmaddr = NULL;
538 	if (!fb_create(mw)) {
539@@ -562,25 +630,27 @@ Maus* maus_init(const char* title, int x, int y, int width, int height)
540 	return mw;
541 }
542 
543-bool maus_event_poll(Maus* mw, MausEvent* ev)
544+int8_t maus_event_poll(Maus* mw, MausEvent* ev)
545 {
546 	MausBackend* be = &mw->backend;
547 	XEvent xev;
548+
549 	while (XPending(be->display)) {
550 		XNextEvent(be->display, &xev);
551 		ev->type = MAUS_EV_NONE;
552 		if (handle_event(&xev, ev, mw))
553-			return true;
554+			return 1;
555 
556 	}
557 
558-	return false;
559+	return 0;
560 }
561 
562 void maus_event_wait(Maus* mw, MausEvent* ev)
563 {
564 	MausBackend* be = &mw->backend;
565 	XEvent xev;
566+
567 	for (;;) {
568 		XNextEvent(be->display, &xev);
569 		ev->type = MAUS_EV_NONE;
570@@ -592,10 +662,12 @@ void maus_event_wait(Maus* mw, MausEvent* ev)
571 void maus_present(Maus* mw)
572 {
573 	MausBackend* be = &mw->backend;
574+	uint32_t bytes;
575+
576 	if (!be->image || be->win == None)
577 		return;
578 
579-	uint32_t bytes = mw->stride * mw->height * sizeof(uint32_t);
580+	bytes = mw->stride * mw->height * sizeof(uint32_t);
581 	memcpy(mw->fb, mw->bfb, bytes);
582 
583 	XShmPutImage(
584@@ -606,12 +678,13 @@ void maus_present(Maus* mw)
585 	XFlush(be->display);
586 }
587 
588-bool maus_resize(Maus* mw, uint32_t width, uint32_t height)
589+int8_t maus_resize(Maus* mw, uint32_t width, uint32_t height)
590 {
591 	MausBackend* be = &mw->backend;
592+
593 	if (width == 0 || height == 0 ||
594 	    (mw->width == width && mw->height == height))
595-		return false;
596+		return 0;
597 
598 	fb_destroy(mw);
599 	if (mw->bfb) {
600@@ -626,13 +699,13 @@ bool maus_resize(Maus* mw, uint32_t width, uint32_t height)
601 	XFlush(be->display);
602 
603 	if (!fb_create(mw))
604-		return false;
605+		return 0;
606 
607 	mw->bfb = calloc(mw->stride * mw->height, sizeof(uint32_t));
608 	if (!mw->bfb)
609-		return false;
610+		return 0;
611 
612-	return true;
613+	return 1;
614 }
615 
616 void maus_cur_set_mode(Maus* mw, MausCursorState state)
617@@ -641,6 +714,9 @@ void maus_cur_set_mode(Maus* mw, MausCursorState state)
618 	Display* dpy = be->display;
619 	Window win = be->win;
620 
621+	Mask mask;
622+	int grab;
623+
624 	if (!dpy) {
625 		maus_log(stderr, "display is NULL");
626 		return;
627@@ -660,7 +736,7 @@ void maus_cur_set_mode(Maus* mw, MausCursorState state)
628 	/* hide cursor */
629 	if (state == MAUS_CURSOR_STATE_HIDDEN) {
630 		Pixmap blank_pm = XCreatePixmap(dpy, win, 1, 1, 1);
631-		XColor black = {.red=0, .green=0, .blue=0};
632+		XColor black = {0, 0, 0, 0, 0, 0};
633 		Cursor blank_cur = XCreatePixmapCursor(
634 			dpy, blank_pm, blank_pm, &black, &black, 0, 0
635 		);
636@@ -674,8 +750,8 @@ void maus_cur_set_mode(Maus* mw, MausCursorState state)
637 
638 	/* lock cursor */
639 	if (state == MAUS_CURSOR_STATE_LOCKED) {
640-		Mask mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
641-		int grab = XGrabPointer(
642+		mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
643+		grab = XGrabPointer(
644 			dpy, win, True, mask, GrabModeAsync,
645 			GrabModeAsync, win, None, CurrentTime
646 		);
647@@ -697,5 +773,34 @@ void maus_cur_set_mode(Maus* mw, MausCursorState state)
648 		XFlush(dpy);
649 		return;
650 	}
651+
652+	/* relative cursor */
653+	if (state == MAUS_CURSOR_STATE_RELATIVE) {
654+		mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
655+		grab = XGrabPointer(
656+			dpy, win, True, mask, GrabModeAsync,
657+			GrabModeAsync, win, None, CurrentTime
658+		);
659+
660+		if (grab != GrabSuccess) {
661+			maus_log(stderr, "failed to grab mouse pointer");
662+			return;
663+		}
664+
665+		be->cur_rel = 1;
666+		be->ignore_warp = 1;
667+		XWarpPointer(dpy, None, win, 0, 0, 0, 0, mw->width/2, mw->height/2);
668+		XFlush(dpy);
669+		return;
670+	}
671+
672+	/* absolute cursor */
673+	if (state == MAUS_CURSOR_STATE_ABSOLUTE) {
674+		be->cur_rel = 0;
675+		be->ignore_warp = 0;
676+		XUngrabPointer(dpy, CurrentTime);
677+		XFlush(dpy);
678+		return;
679+	}
680 }
681 
+4, -2
 1@@ -5,11 +5,13 @@
 2 
 3 char* maus_strdup(const char* src)
 4 {
 5+	size_t len;
 6+	char* dst;
 7 	if (src == NULL)
 8 		return NULL;
 9 
10-	size_t len = strlen(src);
11-	char* dst = malloc(len + 1);
12+	len = strlen(src);
13+	dst = malloc(len + 1);
14 	if (dst == NULL)
15 		return NULL;
16