UI rewrite 3rd attempt

This commit is contained in:
Jorijn van der Graaf 2026-05-02 21:08:20 +02:00
commit 1f5697326c
48 changed files with 2155 additions and 6190 deletions

View file

@ -516,109 +516,113 @@ void Window::SetTitle(const std::string_view title) {
#endif
}
void Window::SetCusorImage(std::uint16_t sizeX, std::uint16_t sizeY) {
void Window::SetCursorImage(std::uint16_t width, std::uint16_t height,
std::uint16_t hotspotX, std::uint16_t hotspotY,
const std::uint8_t* pixels) {
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
if(cursorSurface == nullptr) {
if (width == 0 || height == 0 || pixels == nullptr) {
SetDefaultCursor();
return;
}
if (cursorSurface == nullptr) {
cursorSurface = wl_compositor_create_surface(Device::compositor);
}
int stride = width * 4;
int size = stride * height;
// Reuse the existing mmap+buffer if the size is unchanged; otherwise
// tear down and re-allocate.
if (cursorWlBuffer != nullptr &&
cursorBufferOldSize == static_cast<std::uint32_t>(size)) {
// size unchanged — keep the buffer and mmap.
} else {
if (cursorMmap_) {
munmap(cursorMmap_, cursorBufferOldSize);
cursorMmap_ = nullptr;
}
if (cursorWlBuffer) {
wl_buffer_destroy(cursorWlBuffer);
cursorWlBuffer = nullptr;
}
int fd = create_shm_file(size);
if (fd < 0) {
throw std::runtime_error(std::format(
"Window::SetCursorImage: shm allocation for {}B failed", size));
}
void* mapped = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED) {
close(fd);
throw std::runtime_error("Window::SetCursorImage: mmap failed");
}
cursorMmap_ = static_cast<std::uint8_t*>(mapped);
wl_shm_pool* pool = wl_shm_create_pool(Device::shm, fd, size);
cursorWlBuffer = wl_shm_pool_create_buffer(
pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
close(fd);
cursorBufferOldSize = static_cast<std::uint32_t>(size);
}
// Convert the user's straight-alpha RGBA8 pixels into the compositor's
// expected premultiplied BGRA8 (= ARGB8888 little-endian byte order).
for (int i = 0; i < width * height; ++i) {
std::uint8_t r = pixels[i * 4 + 0];
std::uint8_t g = pixels[i * 4 + 1];
std::uint8_t b = pixels[i * 4 + 2];
std::uint8_t a = pixels[i * 4 + 3];
cursorMmap_[i * 4 + 0] = static_cast<std::uint8_t>((b * a) / 255);
cursorMmap_[i * 4 + 1] = static_cast<std::uint8_t>((g * a) / 255);
cursorMmap_[i * 4 + 2] = static_cast<std::uint8_t>((r * a) / 255);
cursorMmap_[i * 4 + 3] = a;
}
cursorHotspotX_ = hotspotX;
cursorHotspotY_ = hotspotY;
wl_surface_attach(cursorSurface, cursorWlBuffer, 0, 0);
wl_surface_damage(cursorSurface, 0, 0, width, height);
wl_surface_commit(cursorSurface);
// If the pointer is currently inside our window, re-apply the cursor
// so the new hotspot takes effect immediately. Otherwise the next
// pointer-enter event will pick it up.
if (Device::wlPointer && Device::focusedWindow == this && lastPointerSerial_) {
wl_pointer_set_cursor(Device::wlPointer, lastPointerSerial_,
cursorSurface, hotspotX, hotspotY);
}
#endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
// Win32 cursor support is not implemented for the v2 Window.
(void)width; (void)height; (void)hotspotX; (void)hotspotY; (void)pixels;
#endif
}
void Window::SetDefaultCursor() {
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
if (cursorMmap_) {
munmap(cursorMmap_, cursorBufferOldSize);
cursorMmap_ = nullptr;
}
if (cursorWlBuffer) {
wl_buffer_destroy(cursorWlBuffer);
cursorWlBuffer = nullptr;
}
int stride = sizeX * 4;
int size = stride * sizeY;
cursorBufferOldSize = size;
// Allocate a shared memory file with the right size
int fd = create_shm_file(size);
if (fd < 0) {
throw std::runtime_error(std::format("creating a buffer file for {}B failed", size));
}
wl_shm_pool *pool = wl_shm_create_pool(Device::shm, fd, size);
cursorWlBuffer = wl_shm_pool_create_buffer(pool, 0, sizeX, sizeY, stride, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
close(fd);
wl_surface_attach(cursorSurface, cursorWlBuffer, 0, 0);
wl_surface_damage(cursorSurface, 0, 0, sizeX, sizeY);
wl_surface_commit(cursorSurface);
#endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
if (cursorBitmap) {
DeleteObject(cursorBitmap);
if (cursorSurface) {
wl_surface_destroy(cursorSurface);
cursorSurface = nullptr;
}
if (cursorHandle) {
DestroyCursor(cursorHandle);
cursorHandle = nullptr;
}
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = sizeX;
bmi.bmiHeader.biHeight = -(int)sizeY; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
HDC hdc = GetDC(nullptr);
cursorBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, reinterpret_cast<void**>(&cursorRenderer.buffer[0]), nullptr, 0);
ReleaseDC(nullptr, hdc);
if (!cursorBitmap) {
throw std::runtime_error("CreateDIBSection failed for cursor");
}
cursorSizeX = sizeX;
cursorSizeY = sizeY;
#endif
}
void Window::SetCusorImageDefault() {
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
wl_buffer_destroy(cursorWlBuffer);
wl_surface_destroy(cursorSurface);
cursorSurface = nullptr;
#endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
if (cursorHandle) {
DestroyCursor(cursorHandle);
cursorHandle = nullptr;
}
if (cursorBitmap) {
DeleteObject(cursorBitmap);
cursorBitmap = nullptr;
}
// Setting nullptr will make WM_SETCURSOR fall through to the default
#endif
}
void Window::UpdateCursorImage() {
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
wl_surface_attach(cursorSurface, cursorWlBuffer, 0, 0);
wl_surface_damage(cursorSurface, 0, 0, 9999999, 99999999);
wl_surface_commit(cursorSurface);
#endif
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
// Create a mask bitmap (all zeros = fully opaque, alpha comes from color bitmap)
HBITMAP hMask = CreateBitmap(cursorSizeX, cursorSizeY, 1, 1, nullptr);
ICONINFO ii = {};
ii.fIcon = FALSE;
ii.xHotspot = 0;
ii.yHotspot = 0;
ii.hbmMask = hMask;
ii.hbmColor = cursorBitmap;
if (cursorHandle) {
DestroyCursor(cursorHandle);
}
cursorHandle = (HCURSOR)CreateIconIndirect(&ii);
DeleteObject(hMask);
if (cursorHandle) {
SetCursor(cursorHandle);
cursorBufferOldSize = 0;
cursorHotspotX_ = 0;
cursorHotspotY_ = 0;
// Tell the compositor to drop our cursor surface — passing nullptr
// makes it fall back to the system default.
if (Device::wlPointer && Device::focusedWindow == this && lastPointerSerial_) {
wl_pointer_set_cursor(Device::wlPointer, lastPointerSerial_, nullptr, 0, 0);
}
#endif
}