diff --git a/README.md b/README.md index 39e1748..e6732ac 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,8 @@ x11 (FreeBSD, Linux, *nix) gcc and x11-dev libs needs to be installed. To build the code run tundra2 x11-gcc-debug and you should be able to run t2-output/x11-gcc-debug-default/noise +wayland (Linux) +-------------------------- + +Depends on gcc and wayland-client. Built using the wayland-gcc variants. + diff --git a/src/wayland/WaylandMiniFB.c b/src/wayland/WaylandMiniFB.c new file mode 100644 index 0000000..cbd7bf4 --- /dev/null +++ b/src/wayland/WaylandMiniFB.c @@ -0,0 +1,285 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +static struct wl +{ + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shell *shell; + struct wl_seat *seat; + struct wl_keyboard *keyboard; + struct wl_shm *shm; + struct wl_shm_pool *shm_pool; + struct wl_surface *surface; + struct wl_shell_surface *shell_surface; + + uint32_t seat_version; + uint32_t shm_format; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t *shm_ptr; + struct wl_buffer *buffer; + int should_close; +} wl; + +static void destroy(void) +{ + if (! wl.display) + return; + +#define KILL(NAME) \ + do \ + { \ + if (wl.NAME) \ + wl_##NAME##_destroy(wl.NAME); \ + } while (0) + KILL(shell_surface); + KILL(shell); + KILL(surface); + KILL(buffer); + KILL(shm_pool); + KILL(shm); + KILL(compositor); + KILL(keyboard); + KILL(seat); + KILL(registry); +#undef KILL + wl_display_disconnect(wl.display); + memset(&wl, 0, sizeof(wl)); +} + +static void nop() {} + +#define NO_FUNC (void (*)()) nop + +static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t time, uint32_t key, + uint32_t state) +{ + if (state == WL_KEYBOARD_KEY_STATE_RELEASED && key == KEY_ESC) + { + wl.should_close = 1; + } +} + +static const struct wl_keyboard_listener keyboard_listener = { + .keymap = NO_FUNC, + .enter = NO_FUNC, + .leave = NO_FUNC, + .key = keyboard_key, + .modifiers = NO_FUNC, + .repeat_info = NO_FUNC, +}; + +static void seat_capabilities(void *data, struct wl_seat *seat, + enum wl_seat_capability caps) +{ + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl.keyboard) + { + wl.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(wl.keyboard, &keyboard_listener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl.keyboard) + { + wl_keyboard_destroy(wl.keyboard); + wl.keyboard = NULL; + } +} + +static const struct wl_seat_listener seat_listener = { + .capabilities = seat_capabilities, .name = NO_FUNC, +}; + +static void shm_format(void *data, struct wl_shm *shm, uint32_t format) +{ + if (wl.shm_format == -1u) + { + switch (format) + { + // We could do RGBA, but that would not be what is expected from minifb... + /* case WL_SHM_FORMAT_ARGB8888: */ + case WL_SHM_FORMAT_XRGB8888: + wl.shm_format = format; + break; + + default: + break; + } + } +} + +static const struct wl_shm_listener shm_listener = {.format = shm_format}; + +static void registry_global(void *data, struct wl_registry *registry, + uint32_t id, char const *iface, uint32_t version) +{ + if (strcmp(iface, "wl_compositor") == 0) + { + wl.compositor = + wl_registry_bind(registry, id, &wl_compositor_interface, 1); + } + else if (strcmp(iface, "wl_shm") == 0) + { + wl.shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); + if (wl.shm) + wl_shm_add_listener(wl.shm, &shm_listener, NULL); + } + else if (strcmp(iface, "wl_shell") == 0) + { + wl.shell = wl_registry_bind(registry, id, &wl_shell_interface, 1); + } + else if (strcmp(iface, "wl_seat") == 0) + { + wl.seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); + if (wl.seat) + { + wl_seat_add_listener(wl.seat, &seat_listener, NULL); + } + } +} + +static const struct wl_registry_listener registry_listener = { + .global = registry_global, .global_remove = NO_FUNC, +}; + +int mfb_open(const char *title, int width, int height) +{ + int fd = -1; + + wl.display = wl_display_connect(NULL); + if (!wl.display) + return -1; + wl.registry = wl_display_get_registry(wl.display); + wl_registry_add_listener(wl.registry, ®istry_listener, NULL); + if (wl_display_roundtrip(wl.display) == -1 || + wl_display_roundtrip(wl.display) == -1) + { + return -1; + } + + // did not get a format we want... meh + if (wl.shm_format == -1) + goto out; + if (!wl.compositor) + goto out; + + char const *xdg_rt_dir = getenv("XDG_RUNTIME_DIR"); + char shmfile[PATH_MAX]; + int ret = snprintf(shmfile, sizeof(shmfile), "%s/WaylandMiniFB-SHM-XXXXXX", + xdg_rt_dir); + if (ret >= sizeof(shmfile)) + goto out; + + fd = mkstemp(shmfile); + if (fd == -1) + goto out; + unlink(shmfile); + + uint32_t length = sizeof(uint32_t) * width * height; + + if (ftruncate(fd, length) == -1) + goto out; + + wl.shm_ptr = mmap(NULL, length, PROT_WRITE, MAP_SHARED, fd, 0); + if (wl.shm_ptr == MAP_FAILED) + goto out; + + wl.width = width; + wl.height = height; + wl.stride = width * sizeof(uint32_t); + wl.shm_pool = wl_shm_create_pool(wl.shm, fd, length); + wl.buffer = wl_shm_pool_create_buffer(wl.shm_pool, 0, wl.width, wl.height, + wl.stride, wl.shm_format); + + close(fd); + fd = -1; + + wl.surface = wl_compositor_create_surface(wl.compositor); + if (!wl.surface) + goto out; + + // There should always be a shell, right? + if (wl.shell) + { + wl.shell_surface = wl_shell_get_shell_surface(wl.shell, wl.surface); + if (!wl.shell_surface) + goto out; + + wl_shell_surface_set_title(wl.shell_surface, title); + wl_shell_surface_set_toplevel(wl.shell_surface); + } + + wl_surface_attach(wl.surface, wl.buffer, 0, 0); + wl_surface_damage(wl.surface, 0, 0, width, height); + wl_surface_commit(wl.surface); + + return 1; + +out: + close(fd); + destroy(); + return 0; +} + +static void frame_done(void *data, struct wl_callback *callback, + uint32_t cookie) +{ + wl_callback_destroy(callback); + *(uint32_t *)data = 1; +} + +static const struct wl_callback_listener frame_listener = { + .done = frame_done, +}; + +int mfb_update(void *buffer) +{ + uint32_t done = 0; + + if (!wl.display || wl_display_get_error(wl.display) != 0) + return -1; + + if (wl.should_close) + return -1; + + // update shm buffer + memcpy(wl.shm_ptr, buffer, wl.stride * wl.height); + + wl_surface_attach(wl.surface, wl.buffer, 0, 0); + wl_surface_damage(wl.surface, 0, 0, wl.width, wl.height); + + struct wl_callback *frame = wl_surface_frame(wl.surface); + if (!frame) + return -1; + + wl_callback_add_listener(frame, &frame_listener, &done); + + wl_surface_commit(wl.surface); + + while (!done) + if (wl_display_dispatch(wl.display) == -1) + { + wl_callback_destroy(frame); + return -1; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void mfb_close(void) { destroy(); } diff --git a/src/x11/X11MiniFB.c b/src/x11/X11MiniFB.c index c25d9b5..d2b46f4 100644 --- a/src/x11/X11MiniFB.c +++ b/src/x11/X11MiniFB.c @@ -104,7 +104,7 @@ static int processEvents() KeySym sym; if (!XPending(s_display)) - return; + return 0; XNextEvent(s_display, &event); diff --git a/tundra.lua b/tundra.lua index 9fcdca1..02b11b0 100644 --- a/tundra.lua +++ b/tundra.lua @@ -32,7 +32,14 @@ local macosx = { } local x11 = { - Env = { CPPPATH = { "/usr/include", }, }, + Env = { + CPPPATH = { "/usr/include", }, + CCOPTS = { + "-Wpedantic", "-Werror", "-Wall", + { "-O0", "-g"; Config = "*-*-debug" }, + { "-O3"; Config = "*-*-release" }, + }, + }, } @@ -60,6 +67,7 @@ Build { Config { Name = "win64-msvc", Inherit = win32, Tools = { "msvc" }, SupportedHosts = { "windows" }, }, Config { Name = "macosx-clang", Inherit = macosx, Tools = { "clang-osx" }, SupportedHosts = { "macosx" },}, Config { Name = "x11-gcc", Inherit = x11, Tools = { "gcc" }, SupportedHosts = { "linux", "freebsd" },}, + Config { Name = "wayland-gcc", Inherit = x11, Tools = { "gcc" }, SupportedHosts = { "linux" },}, -- Config { Name = "x11-clang", Inherit = x11, Tools = { "clang" }, SupportedHosts = { "linux", "freebsd" },}, }, diff --git a/units.lua b/units.lua index a1c03c0..7db673e 100644 --- a/units.lua +++ b/units.lua @@ -11,6 +11,7 @@ StaticLibrary { { Pattern = "[/\\]windows[/\\]"; Config = { "win32-*", "win64-*" } }, { Pattern = "[/\\]macosx[/\\]"; Config = "mac*-*" }, { Pattern = "[/\\]x11[/\\]"; Config = { "x11-*" } }, + { Pattern = "[/\\]wayland[/\\]"; Config = { "wayland-*" } }, }, Recursive = true, @@ -36,7 +37,10 @@ Program { Depends = { "minifb" }, Sources = { "tests/noise.c" }, - Libs = { "X11"; Config = "x11-*" }, + Libs = { + { "X11"; Config = "x11-*" }, + { "wayland-client", "wayland-cursor"; Config = "wayland-*" }, + }, } Default "noise"