/* Crafter®.Graphics Copyright (C) 2025 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include export module Crafter.Graphics:WindowWaylandWayland_impl; import :WindowWaylandWayland; import std; import Crafter.Event; using namespace Crafter; static void randname(char *buf) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); long r = ts.tv_nsec; for (int i = 0; i < 6; ++i) { buf[i] = 'A'+(r&15)+(r&16)*2; r >>= 5; } } static int anonymous_shm_open(void) { char name[] = "/hello-wayland-XXXXXX"; int retries = 100; do { randname(name + strlen(name) - 6); --retries; // shm_open guarantees that O_CLOEXEC is set int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd >= 0) { shm_unlink(name); return fd; } } while (retries > 0 && errno == EEXIST); return -1; } int create_shm_file(off_t size) { int fd = anonymous_shm_open(); if (fd < 0) { return fd; } if (ftruncate(fd, size) < 0) { close(fd); return -1; } return fd; } void ScaleBitmapR8G8B8(Pixel_BU8_GU8_RU8_AU8* dst, const Pixel_BU8_GU8_RU8_AU8* src, std::uint32_t srcWidth, std::uint32_t srcHeight, std::uint32_t dstWidth, std::uint32_t dstHeight) { for (std::uint32_t y = 0; y < dstHeight; y++) { std::uint32_t srcY = y * srcHeight / dstHeight; for (std::uint32_t x = 0; x < dstWidth; x++) { std::uint32_t srcX = x * srcWidth / dstWidth; const Pixel_BU8_GU8_RU8_AU8* srcPixel = src + (srcY * srcWidth + srcX); Pixel_BU8_GU8_RU8_AU8* dstPixel = dst + (y * dstWidth + x); dstPixel[0] = srcPixel[0]; } } } WindowWaylandWayland::WindowWaylandWayland(std::string name, std::uint32_t width, std::uint32_t height) : WindowWayland(name, width, height) { // Create a wl_buffer, attach it to the surface and commit the surface int stride = width * 4; int size = stride * height; // Allocate a shared memory file with the right size int fd = create_shm_file(size); if (fd < 0) { fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size); } // Map the shared memory file framebuffer = reinterpret_cast(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); if (framebuffer == MAP_FAILED) { fprintf(stderr, "mmap failed: %m\n"); close(fd); } // Create a wl_buffer from our shared memory file descriptor wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); wl_shm_pool_destroy(pool); // Now that we've mapped the file and created the wl_buffer, we no longer // need to keep file descriptor opened close(fd); if (buffer == NULL) { exit(EXIT_FAILURE); } wl_surface_attach(surface, buffer, 0, 0); wl_surface_commit(surface); } WindowWaylandWayland::~WindowWaylandWayland() { open = false; thread.join(); } void WindowWaylandWayland::StartAsync() { thread = std::thread(&WindowWaylandWayland::StartSync, this); } void WindowWaylandWayland::StartSync() { while (open && wl_display_dispatch(display) != -1) { wl_surface_attach(surface, buffer, 0, 0); for(const UiElement& element : elements) { ScaleData data = ScaleElement(element); std::vector scaled(data.width*data.height); ScaleBitmapR8G8B8(scaled.data(), element.buffer.data(), element.bufferWidth, element.bufferHeight, data.width, data.height); for(std::int32_t x = data.x; x-data.x < data.width; x++) { for(std::int32_t y = data.y; y-data.y < data.height; y++) { if(x > 0 && x < width && y > 0 && y < height) { framebuffer[y*width+x] = scaled[(y-data.y)*data.width+(x-data.x)]; } } } wl_surface_damage(surface, data.x, data.y, data.width, data.height); } wl_surface_commit(surface); } }