linux-wallpaperengine/src/WallpaperEngine/Render/Drivers/Output/WaylandOutputViewport.cpp

196 lines
7.4 KiB
C++

#include "WallpaperEngine/Logging/Log.h"
#include "WaylandOutputViewport.h"
#define class _class
#define namespace _namespace
#define static
extern "C" {
#include "wlr-layer-shell-unstable-v1-protocol.h"
#include "xdg-shell-protocol.h"
}
#undef class
#undef namespace
#undef static
using namespace WallpaperEngine::Render::Drivers;
using namespace WallpaperEngine::Render::Drivers::Output;
static void handleLSConfigure (void* data, zwlr_layer_surface_v1* surface, uint32_t serial, uint32_t w, uint32_t h) {
const auto viewport = static_cast<WaylandOutputViewport*> (data);
viewport->size = {w, h};
viewport->viewport = {0, 0, viewport->size.x * viewport->scale, viewport->size.y * viewport->scale};
viewport->resize ();
zwlr_layer_surface_v1_ack_configure (surface, serial);
}
static void handleLSClosed (void* data, zwlr_layer_surface_v1* surface) {
const auto viewport = static_cast<WaylandOutputViewport*> (data);
viewport->getDriver ()->onLayerClose (viewport);
}
static void geometry (void* data, wl_output* output, int32_t x, int32_t y, int32_t width_mm, int32_t height_mm,
int32_t subpixel, const char* make, const char* model, int32_t transform) {
// ignored
}
static void mode (void* data, wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
const auto viewport = static_cast<WaylandOutputViewport*> (data);
// update viewport size too
viewport->size = {width, height};
viewport->viewport = {0, 0, viewport->size.x * viewport->scale, viewport->size.y * viewport->scale};
if (viewport->layerSurface)
viewport->resize ();
if (viewport->initialized)
viewport->getDriver ()->getOutput ().reset ();
}
static void done (void* data, wl_output* wl_output) {
static_cast<WaylandOutputViewport*> (data)->initialized = true;
}
static void scale (void* data, wl_output* wl_output, int32_t scale) {
const auto viewport = static_cast<WaylandOutputViewport*> (data);
viewport->scale = scale;
if (viewport->layerSurface)
viewport->resize ();
if (viewport->initialized)
viewport->getDriver ()->getOutput ().reset ();
}
static void name (void* data, wl_output* wl_output, const char* name) {
const auto viewport = static_cast<WaylandOutputViewport*> (data);
if (name)
viewport->name = name;
// ensure the output is updated with the new name too
viewport->getDriver ()->getOutput ().reset ();
}
static void description (void* data, wl_output* wl_output, const char* description) {
// ignored
}
static void surfaceFrameCallback (void* data, struct wl_callback* cb, uint32_t time) {
const auto viewport = static_cast<WaylandOutputViewport*> (data);
wl_callback_destroy (cb);
viewport->frameCallback = nullptr;
viewport->rendering = true;
viewport->getDriver ()->getApp ().update (viewport);
viewport->rendering = false;
}
constexpr struct wl_callback_listener frameListener = {.done = surfaceFrameCallback};
constexpr wl_output_listener outputListener = {
.geometry = geometry, .mode = mode, .done = done, .scale = scale, .name = name, .description = description};
constexpr struct zwlr_layer_surface_v1_listener layerSurfaceListener = {
.configure = handleLSConfigure,
.closed = handleLSClosed,
};
WaylandOutputViewport::WaylandOutputViewport (WaylandOpenGLDriver* driver, uint32_t waylandName,
struct wl_registry* registry) :
OutputViewport ({0, 0, 0, 0}, "", true),
size ({0, 0}),
waylandName (waylandName),
m_driver (driver) {
// setup output listener
this->output = static_cast<wl_output*> (wl_registry_bind (registry, waylandName, &wl_output_interface, 4));
wl_output_add_listener (output, &outputListener, this);
}
void WaylandOutputViewport::setupLS () {
surface = wl_compositor_create_surface (m_driver->getWaylandContext ()->compositor);
layerSurface =
zwlr_layer_shell_v1_get_layer_surface (m_driver->getWaylandContext ()->layerShell, surface, output,
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "linux-wallpaperengine");
if (!layerSurface)
sLog.exception ("Failed to get a layer surface");
wl_region* region = wl_compositor_create_region (m_driver->getWaylandContext ()->compositor);
wl_region_add (region, 0, 0, INT32_MAX, INT32_MAX);
zwlr_layer_surface_v1_set_size (layerSurface, 0, 0);
zwlr_layer_surface_v1_set_anchor (layerSurface,
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
zwlr_layer_surface_v1_set_keyboard_interactivity (layerSurface, false);
zwlr_layer_surface_v1_add_listener (layerSurface, &layerSurfaceListener, this);
zwlr_layer_surface_v1_set_exclusive_zone (layerSurface, -1);
wl_surface_set_input_region (surface, region);
wl_surface_commit (surface);
wl_display_roundtrip (m_driver->getWaylandContext ()->display);
eglWindow = wl_egl_window_create (surface, size.x * scale, size.y * scale);
eglSurface = m_driver->getEGLContext ()->eglCreatePlatformWindowSurfaceEXT (
m_driver->getEGLContext ()->display, m_driver->getEGLContext ()->config, eglWindow, nullptr);
wl_surface_commit (surface);
wl_display_roundtrip (m_driver->getWaylandContext ()->display);
wl_display_flush (m_driver->getWaylandContext ()->display);
static const auto XCURSORSIZE = getenv ("XCURSOR_SIZE") ? std::stoi (getenv ("XCURSOR_SIZE")) : 24;
const auto PRCURSORTHEME =
wl_cursor_theme_load (getenv ("XCURSOR_THEME"), XCURSORSIZE * scale, m_driver->getWaylandContext ()->shm);
if (!PRCURSORTHEME)
sLog.exception ("Failed to get a cursor theme");
pointer = wl_cursor_theme_get_cursor (PRCURSORTHEME, "left_ptr");
cursorSurface = wl_compositor_create_surface (m_driver->getWaylandContext ()->compositor);
if (!cursorSurface)
sLog.exception ("Failed to get a cursor surface");
if (eglMakeCurrent (m_driver->getEGLContext ()->display, eglSurface, eglSurface,
m_driver->getEGLContext ()->context) == EGL_FALSE)
sLog.exception ("Failed to make egl current");
this->m_driver->getOutput ().reset ();
}
WaylandOpenGLDriver* WaylandOutputViewport::getDriver () {
return this->m_driver;
}
void WaylandOutputViewport::makeCurrent () {
const EGLBoolean result = eglMakeCurrent (m_driver->getEGLContext ()->display, eglSurface, eglSurface,
m_driver->getEGLContext ()->context);
if (result == EGL_FALSE)
sLog.error ("Couldn't make egl current");
}
void WaylandOutputViewport::swapOutput () {
this->callbackInitialized = true;
this->makeCurrent ();
frameCallback = wl_surface_frame (surface);
wl_callback_add_listener (frameCallback, &frameListener, this);
eglSwapBuffers (m_driver->getEGLContext ()->display, this->eglSurface);
wl_surface_set_buffer_scale (surface, scale);
wl_surface_damage_buffer (surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit (surface);
}
void WaylandOutputViewport::resize () {
if (!this->eglWindow)
return;
wl_egl_window_resize (this->eglWindow, this->size.x * this->scale, this->size.y * this->scale, 0, 0);
this->getDriver ()->getOutput ().reset ();
}