commit 75a85a8

uint  ·  2026-06-06 15:25:25 +0000 UTC
parent d93fb26
Add framebuffer creation, presenting
3 files changed,  +249, -40
M maus.h
M maus.h
+37, -18
 1@@ -10,35 +10,41 @@
 2 /* auto selection */
 3 #if !defined(BACKEND_WIN) && !defined(BACKEND_MAC) && \
 4     !defined(BACKEND_X11) && !defined(BACKEND_WAY)
 5-
 6 #define MAUS_WARN_BACKEND_AUTO_SEL 1
 7+	#if defined(_WIN32)
 8+	#define BACKEND_WIN
 9 
10-#if defined(_WIN32)
11-#define BACKEND_WIN
12+	#elif defined(__APPLE__)
13+	#define BACKEND_MAC
14 
15-#elif defined(__APPLE__)
16-#define BACKEND_MAC
17+	#else /* default to X */
18+	#define BACKEND_X11
19 
20-#else /* default to X */
21-#define BACKEND_X11
22+	#endif
23+	#endif /* auto selection */
24 
25-#endif
26-#endif /* auto selection */
27+	#if defined(BACKEND_X11)
28+	#include "maus_x11.h"
29 
30-#if defined(BACKEND_X11)
31-#include "maus_x11.h"
32+	#elif defined(BACKEND_WAY)
33+	/* ... */
34 
35-#elif defined(BACKEND_WAY)
36-/* ... */
37+	#elif defined(BACKEND_WIN)
38+	/* ... */
39 
40-#elif defined(BACKEND_WIN)
41-/* ... */
42+	#elif defined(BACKEND_MAC)
43+	/* ... */
44+#endif
45 
46-#elif defined(BACKEND_MAC)
47+#define MAUS_COL_ARGB(a, r, g, b) (MausColor){a, r, g, b}
48+#define MAUS_COL_RGBA(r, g, b, a) (MausColor){a, r, g, b}
49+#define MAUS_UNPACK_COL(c) \
50+	(((uint32_t)(c).a << 24) | \
51+	 ((uint32_t)(c).r << 16) | \
52+	 ((uint32_t)(c).g <<  8) | \
53+	 ((uint32_t)(c).b))
54 /* ... */
55 
56-#endif
57-
58 typedef enum {
59 	MAUS_EV_NONE,
60 	MAUS_EV_CLOSE,
61@@ -48,6 +54,13 @@ typedef enum {
62 	MAUS_EV_RESIZE,
63 } MausEventType;
64 
65+typedef struct {
66+	uint8_t        a;
67+	uint8_t        r;
68+	uint8_t        g;
69+	uint8_t        b;
70+} MausColor;
71+
72 typedef struct {
73 	MausEventType type;
74 
75@@ -89,6 +102,9 @@ bool maus_create_window(Maus* mw);
76 /* log message to output `fd` and die */
77 void maus_die(const char* fmt, ...);
78 
79+/* paint framebuffer with color: `col` */
80+void maus_fb_clear(Maus* mw, MausColor col);
81+
82 /* initialise and fills the Maus. returns NULL on fail */
83 Maus* maus_init(const char* title, int x, int y, int width, int height);
84 
85@@ -105,5 +121,8 @@ bool maus_event_poll(Maus* mw, MausEvent* ev);
86    note: thread goes to sleep until an event arrives */
87 void maus_event_wait(Maus* mw, MausEvent* ev);
88 
89+/* present the pixelbuffer to the screen */
90+void maus_present(Maus* mw);
91+
92 #endif /* MAUSWIN_H */
93 
+204, -21
  1@@ -1,8 +1,12 @@
  2 #include <stdbool.h>
  3 #include <stdlib.h>
  4+#include <sys/shm.h>
  5+#include <sys/ipc.h>
  6 
  7 #include <X11/Xlib.h>
  8+#include <X11/Xutil.h>
  9 #include <X11/keysym.h>
 10+#include <X11/extensions/XShm.h>
 11 
 12 #include "maus.h"
 13 
 14@@ -38,9 +42,16 @@ static const KeyMapEntry keymap[] = {
 15 	{ XK_KP_Equal,     MAUS_KEY_KP_EQUAL },
 16 };
 17 
 18+static int xerrored;
 19+
 20 static void build_keymap(Maus* mw);
 21+static bool fb_create(Maus* mw);
 22+static bool fb_create_shm(Maus* mw);
 23+/* TODO? static bool fb_create_ximage(Maus* mw); */
 24+static void fb_destroy(Maus* mw);
 25 static bool handle_event(XEvent* xev, MausEvent* ev, Maus* mw);
 26 static MausKey keysym_to_mauskey(KeySym sym);
 27+static int xerr(Display* dpy, XErrorEvent* ev);
 28 
 29 static void build_keymap(Maus* mw)
 30 {
 31@@ -77,6 +88,130 @@ static void build_keymap(Maus* mw)
 32 	XFree(syms);
 33 }
 34 
 35+static bool fb_create(Maus* mw)
 36+{
 37+	mw->image = NULL;
 38+	mw->fb = NULL;
 39+	mw->stride = 0;
 40+	mw->shmat = false;
 41+	mw->shm.shmid = -1;
 42+	mw->shm.shmaddr = NULL;
 43+
 44+	return fb_create_shm(mw);
 45+}
 46+
 47+/* allocate and store a new shm for the framebuffer */
 48+static bool fb_create_shm(Maus* mw)
 49+{
 50+
 51+	if (!XShmQueryExtension(mw->display))
 52+		return false;
 53+
 54+	int scr = DefaultScreen(mw->display);
 55+	int depth = DefaultDepth(mw->display, scr);
 56+	Visual* vis = DefaultVisual(mw->display, scr);
 57+	if (!vis) {
 58+		maus_log(stderr, "could not get default visual");
 59+		return false;
 60+	}
 61+
 62+	mw->image = XShmCreateImage(
 63+		mw->display, vis, depth, ZPixmap, NULL,
 64+		&mw->shm, mw->width, mw->height
 65+	);
 66+	if (!mw->image) {
 67+		maus_log(stderr, "could not create XImage (shm)");
 68+		return false;
 69+	}
 70+
 71+	if (mw->image->bits_per_pixel != 32) {
 72+		XDestroyImage(mw->image);
 73+		mw->image = NULL;
 74+		maus_log(stderr, "XImage not 32bpp");
 75+		return false;
 76+	}
 77+
 78+	size_t size = mw->image->bytes_per_line * mw->image->height;
 79+	mw->shm.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600);
 80+	if (mw->shm.shmid < 0) {
 81+		XDestroyImage(mw->image);
 82+		mw->image = NULL;
 83+		maus_log(stderr, "shmget() failed");
 84+		return false;
 85+	}
 86+
 87+	mw->shm.shmaddr = shmat(mw->shm.shmid, NULL, 0);
 88+	if (mw->shm.shmaddr == (char*)-1) {
 89+		shmctl(mw->shm.shmid, IPC_RMID, NULL);
 90+		XDestroyImage(mw->image);
 91+		mw->image = NULL;
 92+		maus_log(stderr, "shmat() failed");
 93+		return false;
 94+	}
 95+
 96+	mw->shm.readOnly = False;
 97+	mw->image->data = mw->shm.shmaddr;
 98+
 99+	xerrored = 0;
100+	int (*old_xerr)(Display*, XErrorEvent*) = XSetErrorHandler(xerr);
101+
102+	if (!XShmAttach(mw->display, &mw->shm))
103+		xerrored = 1;
104+
105+	XSync(mw->display, False);
106+	XSetErrorHandler(old_xerr);
107+	if (xerrored) {
108+		shmdt(mw->shm.shmaddr);
109+		shmctl(mw->shm.shmid, IPC_RMID, NULL);
110+		XDestroyImage(mw->image);
111+
112+		mw->image = NULL;
113+		mw->fb = NULL;
114+		mw->shm.shmaddr = NULL;
115+		mw->shm.shmid = -1;
116+		maus_log(stderr, "XShmAttach failed");
117+		return false;
118+	}
119+
120+	shmctl(mw->shm.shmid, IPC_RMID, NULL);
121+
122+	mw->shmat = true;
123+	mw->fb = (uint32_t*) mw->image->data;
124+	mw->stride = mw->image->bytes_per_line / sizeof(uint32_t);
125+
126+	return true;
127+}
128+
129+static void fb_destroy(Maus* mw)
130+{
131+	Display* dpy = mw->display;
132+	if (mw->shmat) {
133+		XShmDetach(dpy, &mw->shm);
134+		XSync(dpy, False);
135+		mw->shmat = false;
136+	}
137+
138+	if (mw->image) {
139+		XDestroyImage(mw->image);
140+		mw->image = NULL;
141+	}
142+
143+	if (mw->shm.shmaddr && mw->shm.shmaddr != (char*) -1) {
144+		shmdt(mw->shm.shmaddr);
145+		mw->shm.shmaddr = NULL;
146+	}
147+
148+	/* normally already removed after attach but
149+	   can check anyways */
150+	if (mw->shm.shmid >= 0) {
151+		shmctl(mw->shm.shmid, IPC_RMID, NULL);
152+		mw->shm.shmid = -1;
153+	}
154+
155+	mw->fb = NULL;
156+	mw->stride = 0;
157+}
158+
159 static bool handle_event(XEvent* xev, MausEvent* ev, Maus* mw)
160 {
161 	uint32_t code = 0;
162@@ -145,8 +280,8 @@ static bool handle_event(XEvent* xev, MausEvent* ev, Maus* mw)
163 			ev->resize.height = xev->xconfigure.height;
164 
165 			/* TODO put into maus_resize */
166-			mw->width = xev->xconfigure.width;
167-			mw->height = xev->xconfigure.height;
168+			/* mw->width = xev->xconfigure.width; */
169+			/* mw->height = xev->xconfigure.height; */
170 			return true;
171 	}
172 
173@@ -177,8 +312,7 @@ static MausKey keysym_to_mauskey(KeySym sym)
174 
175 bool maus_close(Maus* mw)
176 {
177-	if (mw->pixels)
178-		free(mw->pixels);
179+	fb_destroy(mw);
180 	if (mw->win != None) {
181 		(void) maus_close_window(mw);
182 	}
183@@ -190,6 +324,11 @@ bool maus_close(Maus* mw)
184 
185 bool maus_close_window(Maus* mw)
186 {
187+	if (mw->gc) {
188+		XFreeGC(mw->display, mw->gc);
189+		mw->gc = 0;
190+	}
191+
192 	if (mw->win != None) {
193 		XUnmapWindow(mw->display, mw->win);
194 		XDestroyWindow(mw->display, mw->win);
195@@ -227,9 +366,25 @@ bool maus_create_window(Maus* mw)
196 	XMapWindow(dpy, *win);
197 	XFlush(dpy);
198 
199+	mw->gc = XCreateGC(dpy, *win, 0, NULL);
200+	if (!mw->gc) {
201+		maus_log(stderr, "failed to create window GC");
202+		maus_close_window(mw);
203+		return false;
204+	}
205+
206 	return true;
207 }
208 
209+void maus_fb_clear(Maus* mw, MausColor col)
210+{
211+	for (uint32_t y = 0; y < mw->height; y++) {
212+		uint32_t* row = mw->fb + (y * mw->stride);
213+		for (uint32_t x = 0; x < mw->width; x++)
214+			row[x] = (uint32_t) MAUS_UNPACK_COL(col);
215+	}
216+}
217+
218 Maus* maus_init(const char* title, int x, int y, int width, int height)
219 {
220 	if (MAUS_WARN_BACKEND_AUTO_SEL)
221@@ -241,26 +396,33 @@ Maus* maus_init(const char* title, int x, int y, int width, int height)
222 		return NULL;
223 	}
224 
225-	uint32_t* px = malloc(sizeof(uint32_t)*width*height);
226-	if (!px) {
227-		maus_log(stderr, "failed to allocate %dx%d pixel grid", width, height);
228+	Maus* mw = calloc(1, sizeof(Maus));
229+	mw->display = d;
230+	mw->root = DefaultRootWindow(d);
231+	mw->win = None;
232+	mw->title = title;
233+	mw->width = width;
234+	mw->height = height;
235+	mw->x = x;
236+	mw->y = y;
237+	mw->fb = NULL;
238+
239+	build_keymap(mw);
240+	
241+	mw->gc = 0;
242+	mw->image = NULL;
243+	mw->fb = NULL;
244+	mw->shmat = false;
245+	mw->shm.shmid = -1;
246+	mw->shm.shmaddr = NULL;
247+	if (!fb_create(mw)) {
248+		maus_log(stderr, "failed to create framebuffer");
249+		(void) maus_close(mw); /* TODO change to `void maus_close` */
250+		free(mw);
251 		return NULL;
252 	}
253 
254-	Maus* win = calloc(1, sizeof(Maus));
255-	win->display = d;
256-	win->root = DefaultRootWindow(d);
257-	win->win = None;
258-	win->title = title;
259-	win->width = width;
260-	win->height = height;
261-	win->x = x;
262-	win->y = y;
263-	win->pixels = px;
264-
265-	build_keymap(win);
266-
267-	return win;
268+	return mw;
269 }
270 
271 bool maus_event_poll(Maus* mw, MausEvent* ev)
272@@ -277,6 +439,14 @@ bool maus_event_poll(Maus* mw, MausEvent* ev)
273 	return false;
274 }
275 
276+static int xerr(Display* dpy, XErrorEvent* ev)
277+{
278+	(void) dpy;
279+	(void) ev;
280+	xerrored = 1;
281+	return 0;
282+}
283+
284 void maus_event_wait(Maus* mw, MausEvent* ev)
285 {
286 	XEvent xev;
287@@ -288,3 +458,16 @@ void maus_event_wait(Maus* mw, MausEvent* ev)
288 	}
289 }
290 
291+void maus_present(Maus* mw)
292+{
293+	if (!mw->image || mw->win == None)
294+		return;
295+
296+	XShmPutImage(
297+		mw->display, mw->win, mw->gc, mw->image,
298+		0, 0, 0, 0, mw->width, mw->height, False
299+	);
300+
301+	XFlush(mw->display);
302+}
303+
+8, -1
 1@@ -5,6 +5,7 @@
 2 #include <stdint.h>
 3 
 4 #include <X11/Xlib.h>
 5+#include <X11/extensions/XShm.h>
 6 
 7 #include "maus_keys.h"
 8 
 9@@ -20,6 +21,13 @@ typedef struct {
10 	Window         root;
11 	Window         win;
12 	Atom           atoms[MAUS_ATOM_LAST];
13+	GC             gc;
14+
15+	XImage*        image;
16+	XShmSegmentInfo shm;
17+	bool           shmat;
18+	uint32_t*      fb;
19+	uint32_t       stride;
20 
21 	const char*    title;
22 	uint32_t       width;
23@@ -27,7 +35,6 @@ typedef struct {
24 	int32_t        x;
25 	int32_t        y;
26 
27-	uint32_t*      pixels;
28 	bool           key_codes[MAUS_KEYCODE_LAST]; /* physical keys */
29 	bool           key_syms[MAUS_KEY_LAST];      /* logical keys */
30 	MausKey        keymap[MAUS_KEYCODE_LAST];    /* X11 keycode->MausKey */