Crafter.Graphics/implementations/Crafter.Graphics-WindowWaylandWayland.cpp
2025-11-16 20:42:48 +01:00

202 lines
No EOL
6.2 KiB
C++

/*
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 <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-cursor.h>
#include <xkbcommon/xkbcommon.h>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_wayland.h>
#include <wayland-client.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <print>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include <linux/input-event-codes.h>
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<Pixel_BU8_GU8_RU8_AU8*>(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<Pixel_BU8_GU8_RU8_AU8*>(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<UiElement*> 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<Pixel_BU8_GU8_RU8_AU8> 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<uint8_t>((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA);
dst.g = static_cast<uint8_t>((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA);
dst.b = static_cast<uint8_t>((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA);
dst.a = static_cast<uint8_t>(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);
}
}