/* 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 frontFramebuffer = reinterpret_cast(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); if (frontFramebuffer == MAP_FAILED) { fprintf(stderr, "mmap failed: %m\n"); close(fd); } backFramebuffer = reinterpret_cast(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); if (backFramebuffer == 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); frontBuffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); backBuffer = 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 (frontBuffer == NULL || backBuffer == NULL) { exit(EXIT_FAILURE); } wl_surface_attach(surface, frontBuffer, 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) { std::vector drawOrder; drawOrder.reserve(elements.size()); for (UiElement& e : elements) drawOrder.push_back(&e); std::sort(drawOrder.begin(), drawOrder.end(), [](UiElement* a, UiElement* b){ return a->z < b->z; }); for(const UiElement* element : drawOrder) { std::cout << element->bufferWidth << std::endl; 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) { Pixel_BU8_GU8_RU8_AU8& dst = backFramebuffer[y * width + x]; const Pixel_BU8_GU8_RU8_AU8& src = scaled[(y - data.y) * data.width + (x - data.x)]; float srcA = src.a / 255.0f; float dstA = dst.a / 255.0f; float outA = srcA + dstA * (1.0f - srcA); if (outA > 0.0f) { dst.r = static_cast((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA); dst.g = static_cast((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA); dst.b = static_cast((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA); dst.a = static_cast(outA * 255); } } } } } wl_surface_attach(surface, backBuffer, 0, 0); wl_surface_damage(surface, 0, 0, width, height); wl_surface_commit(surface); std::swap(frontFramebuffer, backFramebuffer); std::swap(frontBuffer, backBuffer); } }