/* * 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 . */ #include #include #include #include #include #include #include #include #include "font.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(size_t 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, (off_t)size)) err(1, "Failed to truncate shm"); return fd; } static void RenderPui(const void* pui, int fd) { size_t width = PuiGetWidth(pui); size_t height = PuiGetHeight(pui); size_t size = width * 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 (size_t i = 0; i < width * height; i++) { uint32_t* ptr = (uint32_t*)buffer + i; *ptr = (*ptr << 16 & 0xff0000) | (*ptr >> 16 & 0xff) | (*ptr & 0xff00); } PuiStringRender("This is just a test string", buffer, width, 0); munmap(buffer, size); } static struct wl_buffer* PrepareBuffer(struct Context* ctx) { size_t width = PuiGetWidth(ctx->pui); size_t height = PuiGetHeight(ctx->pui); size_t stride = width * 4; size_t size = stride * height; int fd = CreateShm(size); struct wl_shm_pool* pool = wl_shm_create_pool(ctx->shm, fd, (int)size); struct wl_buffer* buffer = wl_shm_pool_create_buffer( pool, 0, (int)width, (int)height, (int)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"); }