summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..5cc4299
--- /dev/null
+++ b/main.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2020 Mikhail Burakov. This file is part of Pui.
+ *
+ * Pui is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Pui is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Pui. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <wayland-client.h>
+
+#include "pui.h"
+#include "xdg-shell.h"
+
+struct Context {
+ const void* pui;
+
+ struct wl_display* display;
+ struct wl_registry* registry;
+
+ struct wl_shm* shm;
+ struct wl_compositor* compositor;
+ struct xdg_wm_base* wm_base;
+
+ struct wl_surface* surface;
+ struct xdg_surface* xdg_surface;
+ struct xdg_toplevel* xdg_toplevel;
+};
+
+// === Wayland registry ===
+
+static void OnRegistryGlobal(void* data, struct wl_registry* registry,
+ uint32_t name, const char* interface,
+ uint32_t version) {
+ (void)version;
+ struct Context* ctx = data;
+ if (!strcmp(interface, wl_compositor_interface.name))
+ ctx->compositor =
+ wl_registry_bind(registry, name, &wl_compositor_interface, 4);
+ else if (!strcmp(interface, wl_shm_interface.name))
+ ctx->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ else if (!strcmp(interface, xdg_wm_base_interface.name))
+ ctx->wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 2);
+}
+
+static void OnRegistryGlobalRemove(void* data, struct wl_registry* registry,
+ uint32_t name) {
+ (void)data;
+ (void)registry;
+ (void)name;
+}
+
+static const struct wl_registry_listener g_registry_listener = {
+ .global = OnRegistryGlobal, .global_remove = OnRegistryGlobalRemove};
+
+// === Wayland WM base listener ===
+
+static void OnPing(void* data, struct xdg_wm_base* xdg_wm_base,
+ uint32_t serial) {
+ (void)data;
+ xdg_wm_base_pong(xdg_wm_base, serial);
+}
+
+static const struct xdg_wm_base_listener g_wm_base_listener = {.ping = OnPing};
+
+// === Wayland buffer ===
+
+static void OnBufferRelease(void* data, struct wl_buffer* wl_buffer) {
+ (void)data;
+ wl_buffer_destroy(wl_buffer);
+}
+
+static const struct wl_buffer_listener g_buffer_listener = {
+ .release = OnBufferRelease};
+
+// === Wayland XDG surface ===
+
+static void OnSurfaceConfigure(void* data, struct xdg_surface* xdg_surface,
+ uint32_t serial) {
+ (void)data;
+ xdg_surface_ack_configure(xdg_surface, serial);
+}
+
+static const struct xdg_surface_listener g_surface_listener = {
+ .configure = OnSurfaceConfigure};
+
+// === Helper functions ===
+
+static const void* CreatePui(const char* filename) {
+ int fd = open(filename, O_RDONLY);
+ if (fd == -1) err(1, "Failed to open pui file");
+ struct stat buf;
+ if (fstat(fd, &buf)) err(1, "Failed to stat pui file");
+ const void* result =
+ mmap(NULL, (size_t)buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (result == MAP_FAILED) err(1, "Failed to mmap pui file");
+ return result;
+}
+
+static int CreateShm(int size) {
+ static const char kShmName[] = "/wl_shm-pui";
+ int fd = shm_open(kShmName, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd == -1) err(1, "Failed to create shm");
+ shm_unlink(kShmName);
+ if (ftruncate(fd, size)) err(1, "Failed to truncate shm");
+ return fd;
+}
+
+static void RenderPui(const void* pui, int fd) {
+ int width = PuiGetWidth(pui);
+ int height = PuiGetHeight(pui);
+ size_t size = (size_t)width * (size_t)height * 4;
+ void* buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (buffer == MAP_FAILED) err(1, "Failed to map shm");
+ PuiRender(pui, buffer, width, 0);
+ for (int i = 0; i < width * height; i++) {
+ uint32_t* ptr = (uint32_t*)buffer + i;
+ *ptr = (*ptr << 16 & 0xff0000) | (*ptr >> 16 & 0xff) | (*ptr & 0xff00);
+ }
+ munmap(buffer, size);
+}
+
+static struct wl_buffer* PrepareBuffer(struct Context* ctx) {
+ int width = PuiGetWidth(ctx->pui);
+ int height = PuiGetHeight(ctx->pui);
+ int stride = width * 4;
+ int size = stride * height;
+ int fd = CreateShm(size);
+ struct wl_shm_pool* pool = wl_shm_create_pool(ctx->shm, fd, size);
+ struct wl_buffer* buffer = wl_shm_pool_create_buffer(
+ pool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888);
+ wl_shm_pool_destroy(pool);
+ RenderPui(ctx->pui, fd);
+ close(fd);
+ return buffer;
+}
+
+int main(int argc, char** argv) {
+ if (argc < 2) err(1, "Usage: %s [file.pui]", argv[0]);
+ struct Context ctx = {.pui = CreatePui(argv[1]),
+ .display = wl_display_connect(NULL)};
+ if (!ctx.pui) err(1, "Failed to create pui");
+ if (!ctx.display) err(1, "Failed to connect to wayland display");
+ ctx.registry = wl_display_get_registry(ctx.display);
+ if (!ctx.registry) err(1, "Failed to get wayland registry");
+ if (wl_registry_add_listener(ctx.registry, &g_registry_listener, &ctx))
+ err(1, "Failed to add wayland registry listener");
+ if (wl_display_roundtrip(ctx.display) == -1)
+ err(1, "Failed to roundtrip wayland display");
+
+ if (xdg_wm_base_add_listener(ctx.wm_base, &g_wm_base_listener, &ctx))
+ err(1, "Failed to add wayland wm base listener");
+ ctx.surface = wl_compositor_create_surface(ctx.compositor);
+ if (!ctx.surface) err(1, "Failed to create wayland surface");
+ ctx.xdg_surface = xdg_wm_base_get_xdg_surface(ctx.wm_base, ctx.surface);
+ if (!ctx.xdg_surface) err(1, "Failed to get wayland xdg surface");
+ if (xdg_surface_add_listener(ctx.xdg_surface, &g_surface_listener, &ctx))
+ err(1, "Failed to add wayland xdg surface listener");
+ ctx.xdg_toplevel = xdg_surface_get_toplevel(ctx.xdg_surface);
+ if (!ctx.xdg_toplevel) err(1, "Failed to get wayland xdg toplevel");
+ xdg_toplevel_set_title(ctx.xdg_toplevel, "Pui");
+ wl_surface_commit(ctx.surface);
+ if (wl_display_roundtrip(ctx.display) == -1)
+ err(1, "Failed to roundtrip wayland display");
+
+ struct wl_buffer* buffer = PrepareBuffer(&ctx);
+ wl_buffer_add_listener(buffer, &g_buffer_listener, &ctx);
+ wl_surface_attach(ctx.surface, buffer, 0, 0);
+ wl_surface_commit(ctx.surface);
+
+ while (wl_display_dispatch(ctx.display) != -1)
+ ;
+ err(1, "Failed to dispatch wayland display");
+}