Refactor into libwaymini library with public API, example, and standalone demo

This commit is contained in:
2026-07-03 02:27:00 -07:00
parent 2a8d309923
commit 616ecbf6b4
10 changed files with 299 additions and 110 deletions
+33
View File
@@ -0,0 +1,33 @@
CC ?= gcc
CFLAGS := -Wall -Wextra -O2 $(shell pkg-config --cflags wayland-client 2>/dev/null)
LDFLAGS := $(shell pkg-config --libs wayland-client 2>/dev/null)
# Fallback if pkg-config is unavailable or returns nothing.
ifeq ($(LDFLAGS),)
LDFLAGS := -lwayland-client -lm
endif
LIB_SRC := waymini.c xdg-shell-client-protocol.c
LIB_OBJ := $(LIB_SRC:.c=.o)
.PHONY: all clean
all: libwaymini.a example waymini
libwaymini.a: $(LIB_OBJ)
ar rcs $@ $^
example: example.c libwaymini.a
$(CC) $(CFLAGS) -o $@ example.c -L. -lwaymini $(LDFLAGS)
waymini: waymini_standalone.c waymini.c xdg-shell-client-protocol.c
$(CC) $(CFLAGS) -o $@ waymini_standalone.c waymini.c xdg-shell-client-protocol.c $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(LIB_OBJ) libwaymini.a example waymini
xdg-shell-client-protocol.c xdg-shell-client-protocol.h: gen-protocols.sh
bash gen-protocols.sh
Executable
BIN
View File
Binary file not shown.
+53
View File
@@ -0,0 +1,53 @@
/* Example program that links against the waymini library.
* No external dependencies beyond waymini itself and the C standard library.
*/
#include "waymini.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void on_key(void *user, uint32_t keycode, uint32_t state) {
(void)user;
const char *label = state ? "down" : "up ";
printf("key %s: %u\n", label, keycode);
fflush(stdout);
}
int main(void) {
uint32_t w = 320, h = 200;
struct waymini *wm = waymini_create(w, h, on_key, NULL);
if (!wm) return 1;
printf("surface: %ux%u stride=%u\n",
waymini_get_width(wm), waymini_get_height(wm), waymini_get_stride(wm));
uint8_t *pixels = (uint8_t *)waymini_get_pixels(wm);
if (!pixels) {
waymini_destroy(wm);
return 1;
}
/* Fill a simple gradient. Each row is stride bytes. */
for (uint32_t y = 0; y < h; y++) {
for (uint32_t x = 0; x < w; x++) {
size_t i = (size_t)y * waymini_get_stride(wm) + (size_t)x * 4;
pixels[i + 0] = 0x20; /* B */
pixels[i + 1] = (uint8_t)(y * 255 / h); /* G */
pixels[i + 2] = (uint8_t)(x * 255 / w); /* R */
pixels[i + 3] = 0xff; /* A (ignored for XRGB) */
}
}
waymini_present(wm);
while (!waymini_should_close(wm)) {
if (waymini_dispatch(wm) < 0) break;
}
waymini_destroy(wm);
return 0;
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+121 -110
View File
@@ -1,6 +1,8 @@
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include "waymini.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
@@ -17,8 +19,6 @@
#include "xdg-shell-client-protocol.h"
enum fmt { FMT_RGBA32 = 4 };
struct shm_state {
int fd;
size_t size;
@@ -44,11 +44,11 @@ struct input_state {
struct wl_seat *seat;
struct wl_keyboard *keyboard;
void (*on_keycode)(void *user, uint32_t keycode, uint32_t state);
waymini_key_cb on_keycode;
void *user;
};
struct app {
struct waymini {
struct wl_display *display;
struct wl_registry *registry;
@@ -56,17 +56,17 @@ struct app {
struct wl_shm *shm_iface;
struct xdg_wm_base *wm_base;
struct wl_event_queue *queue;
struct shm_state shm;
struct window_state win;
struct input_state in;
int should_close;
};
static void on_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) {
struct app *app = data;
struct waymini *wm = data;
xdg_surface_ack_configure(xdg_surface, serial);
app->win.configured = 1;
wm->win.configured = 1;
}
static const struct xdg_surface_listener xdg_surface_listener = {
@@ -80,7 +80,9 @@ static void on_toplevel_configure(void *data, struct xdg_toplevel *toplevel,
}
static void on_toplevel_close(void *data, struct xdg_toplevel *toplevel) {
(void)data; (void)toplevel;
(void)toplevel;
struct waymini *wm = data;
wm->should_close = 1;
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
@@ -105,7 +107,6 @@ static void on_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format,
close(fd);
return;
}
// Minimal: drain keymap then close.
char *buf = malloc(size);
if (buf) {
size_t off = 0;
@@ -116,7 +117,6 @@ static void on_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format,
}
free(buf);
} else {
// If alloc fails, still drain.
(void)read(fd, buf, 0);
}
close(fd);
@@ -183,20 +183,20 @@ static const struct wl_seat_listener seat_listener = {
static void on_registry_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
struct app *app = data;
struct waymini *wm = data;
(void)registry;
if (strcmp(interface, "wl_compositor") == 0) {
app->compositor = wl_registry_bind(app->registry, name, &wl_compositor_interface, 4);
wm->compositor = wl_registry_bind(wm->registry, name, &wl_compositor_interface, 4);
} else if (strcmp(interface, "wl_shm") == 0) {
app->shm_iface = wl_registry_bind(app->registry, name, &wl_shm_interface, 1);
app->shm.shm_iface = app->shm_iface;
wm->shm_iface = wl_registry_bind(wm->registry, name, &wl_shm_interface, 1);
wm->shm.shm_iface = wm->shm_iface;
} else if (strcmp(interface, "xdg_wm_base") == 0) {
app->wm_base = wl_registry_bind(app->registry, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(app->wm_base, &wm_base_listener, app);
wm->wm_base = wl_registry_bind(wm->registry, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(wm->wm_base, &wm_base_listener, wm);
} else if (strcmp(interface, "wl_seat") == 0) {
struct wl_seat *seat = wl_registry_bind(app->registry, name, &wl_seat_interface, version);
wl_seat_add_listener(seat, &seat_listener, &app->in);
struct wl_seat *seat = wl_registry_bind(wm->registry, name, &wl_seat_interface, version);
wl_seat_add_listener(seat, &seat_listener, &wm->in);
}
}
@@ -209,11 +209,11 @@ static const struct wl_registry_listener registry_listener = {
.global_remove = on_registry_global_remove,
};
static void waymini_destroy(struct app *app);
static void waymini_destroy_internal(struct waymini *wm);
static int create_shm_buffer(struct app *app) {
struct shm_state *s = &app->shm;
struct window_state *w = &app->win;
static int create_shm_buffer(struct waymini *wm) {
struct shm_state *s = &wm->shm;
struct window_state *w = &wm->win;
s->size = (size_t)w->stride * (size_t)w->height;
s->fd = memfd_create("waymini-shm", MFD_CLOEXEC);
@@ -234,122 +234,133 @@ static int create_shm_buffer(struct app *app) {
return 0;
}
static struct app *waymini_create(uint32_t width, uint32_t height,
void (*on_keycode)(void*, uint32_t, uint32_t),
void *user) {
struct app *app = calloc(1, sizeof(*app));
if (!app) return NULL;
struct waymini *waymini_create(uint32_t width, uint32_t height,
waymini_key_cb key_cb, void *user) {
struct waymini *wm = calloc(1, sizeof(*wm));
if (!wm) return NULL;
app->display = wl_display_connect(NULL);
if (!app->display) { fprintf(stderr, "wl_display_connect failed\n"); free(app); return NULL; }
wm->display = wl_display_connect(NULL);
if (!wm->display) { fprintf(stderr, "wl_display_connect failed\n"); free(wm); return NULL; }
app->registry = wl_display_get_registry(app->display);
wl_registry_add_listener(app->registry, &registry_listener, app);
wl_display_roundtrip(app->display);
wm->registry = wl_display_get_registry(wm->display);
wl_registry_add_listener(wm->registry, &registry_listener, wm);
wl_display_roundtrip(wm->display);
if (!app->compositor || !app->shm_iface || !app->wm_base) {
if (!wm->compositor || !wm->shm_iface || !wm->wm_base) {
fprintf(stderr, "missing globals\n");
waymini_destroy(app);
waymini_destroy_internal(wm);
return NULL;
}
app->win.compositor = app->compositor;
app->win.surface = wl_compositor_create_surface(app->compositor);
wm->win.compositor = wm->compositor;
wm->win.surface = wl_compositor_create_surface(wm->compositor);
app->win.xdg_surface = xdg_wm_base_get_xdg_surface(app->wm_base, app->win.surface);
xdg_surface_add_listener(app->win.xdg_surface, &xdg_surface_listener, app);
wm->win.xdg_surface = xdg_wm_base_get_xdg_surface(wm->wm_base, wm->win.surface);
xdg_surface_add_listener(wm->win.xdg_surface, &xdg_surface_listener, wm);
app->win.xdg_toplevel = xdg_surface_get_toplevel(app->win.xdg_surface);
xdg_toplevel_add_listener(app->win.xdg_toplevel, &xdg_toplevel_listener, app);
xdg_toplevel_set_title(app->win.xdg_toplevel, "waymini");
xdg_toplevel_set_app_id(app->win.xdg_toplevel, "waymini");
wm->win.xdg_toplevel = xdg_surface_get_toplevel(wm->win.xdg_surface);
xdg_toplevel_add_listener(wm->win.xdg_toplevel, &xdg_toplevel_listener, wm);
xdg_toplevel_set_title(wm->win.xdg_toplevel, "waymini");
xdg_toplevel_set_app_id(wm->win.xdg_toplevel, "waymini");
app->in.on_keycode = on_keycode;
app->in.user = user;
wm->in.on_keycode = key_cb;
wm->in.user = user;
app->win.width = width;
app->win.height = height;
app->win.bpp = 4;
app->win.stride = width * app->win.bpp;
wm->win.width = width;
wm->win.height = height;
wm->win.bpp = 4;
wm->win.stride = width * wm->win.bpp;
/* Initial commit without a buffer, as required by xdg-shell. */
wl_surface_commit(app->win.surface);
wl_surface_commit(wm->win.surface);
/* Wait for the first xdg_surface.configure. */
while (!app->win.configured) {
if (wl_display_dispatch(app->display) < 0) {
while (!wm->win.configured) {
if (wl_display_dispatch(wm->display) < 0) {
fprintf(stderr, "dispatch failed waiting for configure\n");
waymini_destroy(app);
waymini_destroy_internal(wm);
return NULL;
}
}
if (create_shm_buffer(app) < 0) {
if (create_shm_buffer(wm) < 0) {
fprintf(stderr, "create_shm_buffer failed\n");
waymini_destroy(app);
waymini_destroy_internal(wm);
return NULL;
}
return app;
return wm;
}
static void waymini_present(struct app *app) {
if (!app->win.configured) return;
wl_surface_attach(app->win.surface, app->shm.wl_buffer, 0, 0);
wl_surface_commit(app->win.surface);
uint32_t waymini_get_width(const struct waymini *wm) { return wm ? wm->win.width : 0; }
uint32_t waymini_get_height(const struct waymini *wm) { return wm ? wm->win.height : 0; }
uint32_t waymini_get_stride(const struct waymini *wm) { return wm ? wm->win.stride : 0; }
void *waymini_get_pixels(const struct waymini *wm) { return wm ? wm->shm.map : NULL; }
void waymini_present(struct waymini *wm) {
if (!wm || !wm->win.configured) return;
wl_surface_attach(wm->win.surface, wm->shm.wl_buffer, 0, 0);
wl_surface_commit(wm->win.surface);
wl_display_flush(wm->display);
}
static void waymini_destroy(struct app *app) {
if (!app) return;
if (app->in.keyboard) wl_keyboard_destroy(app->in.keyboard);
if (app->in.seat) wl_seat_destroy(app->in.seat);
if (app->shm.wl_buffer) wl_buffer_destroy(app->shm.wl_buffer);
if (app->shm.map && app->shm.size) munmap(app->shm.map, app->shm.size);
if (app->shm.fd >= 0) close(app->shm.fd);
if (app->win.xdg_toplevel) xdg_toplevel_destroy(app->win.xdg_toplevel);
if (app->win.xdg_surface) xdg_surface_destroy(app->win.xdg_surface);
if (app->win.surface) wl_surface_destroy(app->win.surface);
if (app->wm_base) xdg_wm_base_destroy(app->wm_base);
if (app->compositor) wl_compositor_destroy(app->compositor);
if (app->shm_iface) wl_shm_destroy(app->shm_iface);
if (app->registry) wl_registry_destroy(app->registry);
if (app->display) wl_display_disconnect(app->display);
free(app);
int waymini_dispatch(struct waymini *wm) {
if (!wm || !wm->display) return -1;
return wl_display_dispatch(wm->display);
}
static void on_keycode(void *user, uint32_t keycode, uint32_t state) {
(void)user;
printf("key=%u state=%u\n", keycode, state);
fflush(stdout);
}
int waymini_poll(struct waymini *wm) {
if (!wm || !wm->display) return -1;
wl_display_dispatch_pending(wm->display);
wl_display_flush(wm->display);
int ret = wl_display_prepare_read(wm->display);
if (ret < 0) return wl_display_dispatch_pending(wm->display) >= 0 ? 1 : -1;
int main() {
uint32_t w = 800, h = 480;
struct app *app = waymini_create(w, h, on_keycode, NULL);
if (!app) return 1;
// Fill RGBA-ish bytes, but we're attaching as XRGB8888; color order is compositor-dependent.
uint8_t *p = (uint8_t*)app->shm.map;
for (uint32_t y = 0; y < h; y++) {
for (uint32_t x = 0; x < w; x++) {
size_t i = (size_t)y * app->win.stride + (size_t)x * 4;
p[i + 0] = 0x20;
p[i + 1] = (uint8_t)(y * 255 / h);
p[i + 2] = (uint8_t)(x * 255 / w);
p[i + 3] = 0xff;
}
int fd = wl_display_get_fd(wm->display);
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
struct timeval tv = { 0, 0 };
ret = select(fd + 1, &rfds, NULL, NULL, &tv);
if (ret > 0) {
wl_display_read_events(wm->display);
wl_display_dispatch_pending(wm->display);
return 1;
}
waymini_present(app);
while (wl_display_dispatch(app->display) != -1) {
/* Optionally re-present here if content changes. */
}
waymini_destroy(app);
wl_display_cancel_read(wm->display);
return 0;
}
void waymini_request_close(struct waymini *wm) {
if (wm) wm->should_close = 1;
}
int waymini_should_close(const struct waymini *wm) {
return wm ? wm->should_close : 0;
}
static void waymini_destroy_internal(struct waymini *wm) {
if (!wm) return;
if (wm->in.keyboard) wl_keyboard_destroy(wm->in.keyboard);
if (wm->in.seat) wl_seat_destroy(wm->in.seat);
if (wm->shm.wl_buffer) wl_buffer_destroy(wm->shm.wl_buffer);
if (wm->shm.map && wm->shm.size) munmap(wm->shm.map, wm->shm.size);
if (wm->shm.fd >= 0) close(wm->shm.fd);
if (wm->win.xdg_toplevel) xdg_toplevel_destroy(wm->win.xdg_toplevel);
if (wm->win.xdg_surface) xdg_surface_destroy(wm->win.xdg_surface);
if (wm->win.surface) wl_surface_destroy(wm->win.surface);
if (wm->wm_base) xdg_wm_base_destroy(wm->wm_base);
if (wm->compositor) wl_compositor_destroy(wm->compositor);
if (wm->shm_iface) wl_shm_destroy(wm->shm_iface);
if (wm->registry) wl_registry_destroy(wm->registry);
if (wm->display) wl_display_disconnect(wm->display);
free(wm);
}
void waymini_destroy(struct waymini *wm) {
waymini_destroy_internal(wm);
}
+53
View File
@@ -0,0 +1,53 @@
#ifndef WAYMINI_H
#define WAYMINI_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
struct waymini;
typedef void (*waymini_key_cb)(void *user, uint32_t keycode, uint32_t state);
/* Create a Wayland surface of the requested dimensions.
* key_cb may be NULL if keyboard input is not needed.
*/
struct waymini *waymini_create(uint32_t width, uint32_t height,
waymini_key_cb key_cb, void *user);
/* Dimensions and pixel access. */
uint32_t waymini_get_width(const struct waymini *wm);
uint32_t waymini_get_height(const struct waymini *wm);
uint32_t waymini_get_stride(const struct waymini *wm);
void *waymini_get_pixels(const struct waymini *wm);
/* Mark the SHM buffer as changed and commit it to the compositor. */
void waymini_present(struct waymini *wm);
/* Dispatch one or more Wayland events. Returns 0 on success, -1 on error.
* Blocks until at least one event is processed.
*/
int waymini_dispatch(struct waymini *wm);
/* Non-blocking poll for events. Returns 1 if events were dispatched, 0 if no
* events were available, and -1 on error.
*/
int waymini_poll(struct waymini *wm);
/* Request a graceful close (e.g. from the toplevel close button). */
void waymini_request_close(struct waymini *wm);
/* Returns non-zero when the compositor asked us to close. */
int waymini_should_close(const struct waymini *wm);
/* Clean up. */
void waymini_destroy(struct waymini *wm);
#ifdef __cplusplus
}
#endif
#endif /* WAYMINI_H */
BIN
View File
Binary file not shown.
+39
View File
@@ -0,0 +1,39 @@
/* Standalone demo equivalent to the original waymini.c main(). */
#include "waymini.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
static void on_keycode(void *user, uint32_t keycode, uint32_t state) {
(void)user;
printf("key=%u state=%u\n", keycode, state);
fflush(stdout);
}
int main(void) {
uint32_t w = 320, h = 200;
struct waymini *wm = waymini_create(w, h, on_keycode, NULL);
if (!wm) return 1;
uint8_t *p = (uint8_t *)waymini_get_pixels(wm);
for (uint32_t y = 0; y < h; y++) {
for (uint32_t x = 0; x < w; x++) {
size_t i = (size_t)y * waymini_get_stride(wm) + (size_t)x * 4;
p[i + 0] = 0x20;
p[i + 1] = (uint8_t)(y * 255 / h);
p[i + 2] = (uint8_t)(x * 255 / w);
p[i + 3] = 0xff;
}
}
waymini_present(wm);
while (!waymini_should_close(wm)) {
if (waymini_dispatch(wm) < 0) break;
}
waymini_destroy(wm);
return 0;
}
Binary file not shown.