#include "WallpaperEngine/Application/CWallpaperApplication.h" #include "CWaylandOutputViewport.h" #include "WallpaperEngine/Logging/CLog.h" #define class _class #define namespace _namespace #define static extern "C" { #include "xdg-shell-protocol.h" #include "wlr-layer-shell-unstable-v1-protocol.h" } #undef class #undef namespace #undef static #include 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 (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 (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 (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 (data)->initialized = true; } static void scale(void* data, wl_output* wl_output, int32_t scale) { const auto viewport = static_cast (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 (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 (data); wl_callback_destroy(cb); viewport->frameCallback = nullptr; viewport->rendering = true; viewport->getDriver ()->getApp ().update (viewport); viewport->rendering = false; float renderTime = viewport->getDriver ()->getRenderTime(); if ((renderTime - viewport->lastTime) < viewport->minimumTime) usleep ((viewport->minimumTime - (renderTime - viewport->lastTime)) * CLOCKS_PER_SEC); viewport->lastTime = renderTime; } const struct wl_callback_listener frameListener = { .done = surfaceFrameCallback }; const wl_output_listener outputListener = { .geometry = geometry, .mode = mode, .done = done, .scale = scale, .name = name, .description = description }; const struct zwlr_layer_surface_v1_listener layerSurfaceListener = { .configure = handleLSConfigure, .closed = handleLSClosed, }; CWaylandOutputViewport::CWaylandOutputViewport (CWaylandOpenGLDriver* driver, uint32_t waylandName, struct wl_registry *registry) : m_driver (driver), waylandName (waylandName), COutputViewport ({0,0,0,0}, "", true) { // setup output listener this->output = (wl_output*) wl_registry_bind(registry, waylandName, &wl_output_interface, 4); this->name = ""; this->size = {0, 0}; wl_output_add_listener(output, &outputListener, this); } void CWaylandOutputViewport::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"); minimumTime = 1.0f / m_driver->getApp ().getContext().settings.render.maximumFPS; this->m_driver->getOutput ().reset (); } CWaylandOpenGLDriver* CWaylandOutputViewport::getDriver () { return this->m_driver; } void CWaylandOutputViewport::makeCurrent () { 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 CWaylandOutputViewport::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 CWaylandOutputViewport::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(); }