/*
* 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");
}