diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b7fd218 --- /dev/null +++ b/Makefile @@ -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 diff --git a/example b/example new file mode 100755 index 0000000..2d2e3c7 Binary files /dev/null and b/example differ diff --git a/example.c b/example.c new file mode 100644 index 0000000..72c9d0f --- /dev/null +++ b/example.c @@ -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 +#include +#include +#include + +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; +} diff --git a/libwaymini.a b/libwaymini.a new file mode 100644 index 0000000..a5a964a Binary files /dev/null and b/libwaymini.a differ diff --git a/waymini b/waymini index 8a1e15f..351bb7a 100755 Binary files a/waymini and b/waymini differ diff --git a/waymini.c b/waymini.c index 3ce4cc5..4916681 100644 --- a/waymini.c +++ b/waymini.c @@ -1,6 +1,8 @@ #define _GNU_SOURCE #define _POSIX_C_SOURCE 200809L +#include "waymini.h" + #include #include #include @@ -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, ®istry_listener, app); - wl_display_roundtrip(app->display); + wm->registry = wl_display_get_registry(wm->display); + wl_registry_add_listener(wm->registry, ®istry_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); +} + \ No newline at end of file diff --git a/waymini.h b/waymini.h new file mode 100644 index 0000000..74b8b34 --- /dev/null +++ b/waymini.h @@ -0,0 +1,53 @@ +#ifndef WAYMINI_H +#define WAYMINI_H + +#include +#include + +#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 */ diff --git a/waymini.o b/waymini.o new file mode 100644 index 0000000..d469f0a Binary files /dev/null and b/waymini.o differ diff --git a/waymini_standalone.c b/waymini_standalone.c new file mode 100644 index 0000000..757682e --- /dev/null +++ b/waymini_standalone.c @@ -0,0 +1,39 @@ +/* Standalone demo equivalent to the original waymini.c main(). */ + +#include "waymini.h" + +#include +#include +#include + +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; +} diff --git a/xdg-shell-client-protocol.o b/xdg-shell-client-protocol.o new file mode 100644 index 0000000..3d321b7 Binary files /dev/null and b/xdg-shell-client-protocol.o differ