stuff-from-scratch/src/windows/ui_interfaces/wayland/WaylandWindowInterface.h
2022-05-18 08:42:44 +01:00

263 lines
6.3 KiB
C++

#pragma once
#include "wayland-client.h"
#include "xdg-shell-client-protocol.h"
#include "Window.h"
#include "SharedMemory.h"
#include <iostream>
#include <cstring>
#include <sys/mman.h>
#include <unistd.h>
#include <memory>
class WaylandWindowInterface
{
public:
WaylandWindowInterface()
{
auto registry_handle_global = [](void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
auto thisClass = static_cast<WaylandWindowInterface*>(data);
printf("interface: '%s', version: %d, name: %d\n", interface, version, name);
if (strcmp(interface, wl_compositor_interface.name) == 0)
{
thisClass->setCompositor(static_cast<wl_compositor*>(wl_registry_bind(registry, name, &wl_compositor_interface, 4)));
}
else if (strcmp(interface, wl_shm_interface.name) == 0)
{
thisClass->setSharedMemory(static_cast<wl_shm*>(wl_registry_bind(registry, name, &wl_shm_interface, 1)));
}
else if (strcmp(interface, xdg_wm_base_interface.name) == 0)
{
thisClass->setXdgBase(static_cast<xdg_wm_base*>(wl_registry_bind(registry, name, &xdg_wm_base_interface, 1)));
}
};
auto registry_handle_global_remove = [](void *data, struct wl_registry *registry,
uint32_t name)
{
// This space deliberately left blank;
};
mRegistryListener.global = registry_handle_global;
mRegistryListener.global_remove = registry_handle_global_remove;
}
void setXdgBase(xdg_wm_base* xdg_base)
{
mXdgBase = xdg_base;
auto xdg_ping_handler = [](void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
{
auto thisClass = static_cast<WaylandWindowInterface*>(data);
thisClass->doXdgPong(serial);
};
mXdgBaseListener.ping = xdg_ping_handler;
xdg_wm_base_add_listener(mXdgBase, &mXdgBaseListener, this);
}
void doXdgPong(uint32_t serial)
{
xdg_wm_base_pong(mXdgBase, serial);
}
void setCompositor(wl_compositor* compositor)
{
mCompositor = compositor;
}
void setSharedMemory(wl_shm* shared_memory)
{
mWlSharedMemory = shared_memory;
}
void connect()
{
mDisplay = wl_display_connect(nullptr);
if (!mDisplay)
{
std::cout << "Display connect error" << std::endl;
}
auto registry = wl_display_get_registry(mDisplay);
if (!registry)
{
std::cout << "Failed to get registry" << std::endl;
}
wl_registry_add_listener(registry, &mRegistryListener, this);
wl_display_roundtrip(mDisplay);
}
void createSurface()
{
mSurface = wl_compositor_create_surface(mCompositor);
mXdgSurface = xdg_wm_base_get_xdg_surface(mXdgBase, mSurface);
auto xdg_surface_configure = [](void *data, struct xdg_surface *xdg_surface, uint32_t serial)
{
auto thisClass = static_cast<WaylandWindowInterface*>(data);
thisClass->onXdgSurfaceConfigure(data, xdg_surface, serial);
};
mXdgSurfaceListener.configure = xdg_surface_configure;
xdg_surface_add_listener(mXdgSurface, &mXdgSurfaceListener, this);
mXdgTopLevel = xdg_surface_get_toplevel(mXdgSurface);
xdg_toplevel_set_title(mXdgTopLevel, "Example client");
wl_surface_commit(mSurface);
}
void onXdgSurfaceConfigure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
{
xdg_surface_ack_configure(xdg_surface, serial);
auto buffer = draw_frame();
wl_surface_attach(mSurface, buffer, 0, 0);
//wl_surface_damage(mSurface, 0, 0, UINT32_MAX, UINT32_MAX);
wl_surface_commit(mSurface);
}
wl_buffer* draw_frame()
{
const auto width = mWindow->GetWidth();
const auto height = mWindow->GetHeight();
const int bitDepth = 4;
const int stride = width * bitDepth;
//const int numBuffers = 2; // i.e. front/back
const int numBuffers = 1;
const int shm_pool_size = height * stride * numBuffers;
initializeSharedBuffer(shm_pool_size);
if (!mSharedMemory->isValid())
{
std::cout << "Failed to allocate shared memory" << std::endl;
return nullptr;
}
if (!mPoolData)
{
std::cout << "Failed to mmap pool" << std::endl;
return nullptr;
}
auto pool = wl_shm_create_pool(mWlSharedMemory, mSharedMemory->getFileDescriptor(), shm_pool_size);
int index = 0;
// int offset = height * stride * index; // Two buffers, offset to starting point of second
int offset = 0;
mWorkingBuffer = wl_shm_pool_create_buffer(pool, offset, width, height, stride, WL_SHM_FORMAT_XRGB8888);
wl_shm_pool_destroy(pool);
close(mSharedMemory->getFileDescriptor());
drawCheckerboard(width, height, offset);
munmap(mPoolData, shm_pool_size);
auto wl_buffer_release = [](void *data, struct wl_buffer *wl_buffer)
{
wl_buffer_destroy(wl_buffer);
};
mBufferListener.release = wl_buffer_release;
wl_buffer_add_listener(mWorkingBuffer, &mBufferListener, nullptr);
return mWorkingBuffer;
}
void drawCheckerboard(int width, int height, int offset)
{
uint32_t *pixels = (uint32_t *)&mPoolData[offset];
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
if ((x + y / 8 * 8) % 16 < 8)
{
pixels[y * width + x] = 0xFF666666;
}
else
{
pixels[y * width + x] = 0xFFEEEEEE;
}
}
}
}
void disconnect()
{
if (mDisplay)
{
wl_display_disconnect(mDisplay);
}
}
void run()
{
while (wl_display_dispatch(mDisplay) != -1) {
/* This space deliberately left blank */
}
}
void initializeSharedBuffer(int size)
{
mSharedMemory = std::make_unique<SharedMemory>();
mSharedMemory->allocate("/wl_shm-XXXXXX", size);
if (!mSharedMemory->isValid())
{
return;
}
mPoolData = static_cast<uint8_t*>(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, mSharedMemory->getFileDescriptor(), 0));
if (mPoolData == MAP_FAILED)
{
close(mSharedMemory->getFileDescriptor());
mPoolData = nullptr;
}
}
void addWindow(mt::Window* window)
{
mWindow = window;
}
private:
mt::Window* mWindow{nullptr};
wl_display* mDisplay{nullptr};
wl_compositor* mCompositor{nullptr};
wl_registry_listener mRegistryListener;
xdg_wm_base* mXdgBase{nullptr};
xdg_wm_base_listener mXdgBaseListener;
wl_surface* mSurface{nullptr};
xdg_surface* mXdgSurface{nullptr};
xdg_surface_listener mXdgSurfaceListener{nullptr};
xdg_toplevel* mXdgTopLevel{nullptr};
wl_shm* mWlSharedMemory{nullptr};
std::unique_ptr<SharedMemory> mSharedMemory;
uint8_t* mPoolData{nullptr};
wl_shm_pool* mPool{nullptr};
wl_buffer* mWorkingBuffer{nullptr};
wl_buffer_listener mBufferListener;
};