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
+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 */