| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- #include "WaylandOpenGLDriver.h"
- #include "VideoFactories.h"
- #include "WallpaperEngine/Application/WallpaperApplication.h"
- #include "WallpaperEngine/Logging/Log.h"
- #define class _class
- #define namespace _namespace
- #define static
- extern "C" {
- #include "color-management-v1-protocol.h"
- #include "wlr-layer-shell-unstable-v1-protocol.h"
- #include "xdg-output-unstable-v1-protocol.h"
- #include "xdg-shell-protocol.h"
- #ifdef ENABLE_KDE_FEATURES
- #include "plasma-shell-protocol.h"
- #endif
- #include <linux/input-event-codes.h>
- }
- #undef class
- #undef namespace
- #undef static
- #include <algorithm>
- #include <cerrno>
- #include <poll.h>
- #include <string.h>
- #include <unistd.h>
- using namespace WallpaperEngine::Render::Drivers;
- constexpr int EVENT_WAIT_TIMEOUT_MS = 100;
- constexpr auto MISSED_FRAME_TIMEOUT = std::chrono::seconds (1);
- static void handlePointerEnter (
- void* data, struct wl_pointer* wl_pointer, uint32_t serial, struct wl_surface* surface, wl_fixed_t surface_x,
- wl_fixed_t surface_y
- ) {
- const auto driver = static_cast<WaylandOpenGLDriver*> (data);
- const auto viewport = driver->surfaceToViewport (surface);
- driver->viewportInFocus = viewport;
- wl_surface_set_buffer_scale (viewport->cursorSurface, viewport->scale);
- wl_surface_attach (viewport->cursorSurface, wl_cursor_image_get_buffer (viewport->pointer->images[0]), 0, 0);
- wl_pointer_set_cursor (
- wl_pointer, serial, viewport->cursorSurface, viewport->pointer->images[0]->hotspot_x,
- viewport->pointer->images[0]->hotspot_y
- );
- wl_surface_commit (viewport->cursorSurface);
- }
- static void
- handlePointerLeave (void* data, struct wl_pointer* wl_pointer, uint32_t serial, struct wl_surface* surface) { }
- static void handlePointerAxis (void* data, wl_pointer* wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { }
- static void handlePointerMotion (
- void* data, struct wl_pointer* wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y
- ) {
- const auto driver = static_cast<WaylandOpenGLDriver*> (data);
- const auto x = wl_fixed_to_double (surface_x);
- auto y = wl_fixed_to_double (surface_y);
- if (!driver->viewportInFocus) {
- return;
- }
- // Wayland has Y=0 at the top, OpenGL has Y=0 at the bottom
- const double viewportHeight = static_cast<double> (driver->viewportInFocus->size.y);
- y = viewportHeight - y;
- driver->viewportInFocus->mousePos = { x * driver->viewportInFocus->scale, y * driver->viewportInFocus->scale };
- }
- static void handlePointerButton (
- void* data, struct wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state
- ) {
- const auto driver = static_cast<WaylandOpenGLDriver*> (data);
- if (!driver->viewportInFocus) {
- return;
- }
- if (button == BTN_LEFT) {
- if (button_state == WL_POINTER_BUTTON_STATE_PRESSED) {
- driver->viewportInFocus->leftClick = WallpaperEngine::Input::MouseClickStatus::Clicked;
- } else if (button_state == WL_POINTER_BUTTON_STATE_RELEASED) {
- driver->viewportInFocus->leftClick = WallpaperEngine::Input::MouseClickStatus::Released;
- }
- } else if (button == BTN_RIGHT) {
- if (button_state == WL_POINTER_BUTTON_STATE_PRESSED) {
- driver->viewportInFocus->rightClick = WallpaperEngine::Input::MouseClickStatus::Clicked;
- } else if (button_state == WL_POINTER_BUTTON_STATE_RELEASED) {
- driver->viewportInFocus->rightClick = WallpaperEngine::Input::MouseClickStatus::Released;
- }
- }
- }
- constexpr struct wl_pointer_listener pointerListener = { .enter = handlePointerEnter,
- .leave = handlePointerLeave,
- .motion = handlePointerMotion,
- .button = handlePointerButton,
- .axis = handlePointerAxis };
- static void handleCapabilities (void* data, wl_seat* wl_seat, uint32_t capabilities) {
- if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
- wl_pointer_add_listener (wl_seat_get_pointer (wl_seat), &pointerListener, data);
- }
- }
- constexpr struct wl_seat_listener seatListener = { .capabilities = handleCapabilities };
- static void colorManagerSupportedIntent (void*, wp_color_manager_v1*, uint32_t) { }
- static void colorManagerSupportedFeature (void* data, wp_color_manager_v1*, uint32_t feature) {
- if (feature == WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC) {
- static_cast<WaylandOpenGLDriver*> (data)->colorSupport.parametric = true;
- }
- }
- static void colorManagerSupportedTransferFunction (void* data, wp_color_manager_v1*, uint32_t tf) {
- if (tf == WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ) {
- static_cast<WaylandOpenGLDriver*> (data)->colorSupport.pq = true;
- }
- }
- static void colorManagerSupportedPrimaries (void* data, wp_color_manager_v1*, uint32_t primaries) {
- if (primaries == WP_COLOR_MANAGER_V1_PRIMARIES_BT2020) {
- static_cast<WaylandOpenGLDriver*> (data)->colorSupport.bt2020 = true;
- }
- }
- static void colorManagerDone (void*, wp_color_manager_v1*) { }
- constexpr struct wp_color_manager_v1_listener colorManagerListener = {
- .supported_intent = colorManagerSupportedIntent,
- .supported_feature = colorManagerSupportedFeature,
- .supported_tf_named = colorManagerSupportedTransferFunction,
- .supported_primaries_named = colorManagerSupportedPrimaries,
- .done = colorManagerDone,
- };
- static void hdrDescriptionFailed (void*, wp_image_description_v1*, uint32_t cause, const char* message) {
- sLog.error ("--hdr: the compositor refused a PQ image description (", cause, "): ", message);
- }
- static void hdrDescriptionReady (void* data, wp_image_description_v1*, uint32_t) {
- static_cast<WaylandOpenGLDriver*> (data)->hdrDescriptionReady = true;
- }
- constexpr struct wp_image_description_v1_listener hdrDescriptionListener = {
- .failed = hdrDescriptionFailed,
- .ready = hdrDescriptionReady,
- };
- static void
- handleGlobal (void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
- const auto driver = static_cast<WaylandOpenGLDriver*> (data);
- if (strcmp (interface, wl_compositor_interface.name) == 0) {
- driver->getWaylandContext ()->compositor
- = static_cast<wl_compositor*> (wl_registry_bind (registry, name, &wl_compositor_interface, 4));
- } else if (strcmp (interface, wl_shm_interface.name) == 0) {
- driver->getWaylandContext ()->shm
- = static_cast<wl_shm*> (wl_registry_bind (registry, name, &wl_shm_interface, 1));
- } else if (strcmp (interface, wl_output_interface.name) == 0) {
- driver->m_screens.emplace_back (
- new WallpaperEngine::Render::Drivers::Output::WaylandOutputViewport (driver, name, registry)
- );
- } else if (strcmp (interface, zwlr_layer_shell_v1_interface.name) == 0) {
- driver->getWaylandContext ()->layerShell
- = static_cast<zwlr_layer_shell_v1*> (wl_registry_bind (registry, name, &zwlr_layer_shell_v1_interface, 1));
- } else if (strcmp (interface, wl_seat_interface.name) == 0) {
- driver->getWaylandContext ()->seat
- = static_cast<wl_seat*> (wl_registry_bind (registry, name, &wl_seat_interface, 1));
- wl_seat_add_listener (driver->getWaylandContext ()->seat, &seatListener, driver);
- } else if (strcmp (interface, zxdg_output_manager_v1_interface.name) == 0) {
- driver->getWaylandContext ()->xdgOutputManager = static_cast<zxdg_output_manager_v1*> (
- wl_registry_bind (registry, name, &zxdg_output_manager_v1_interface, std::min (version, 3u))
- );
- } else if (
- strcmp (interface, wp_color_manager_v1_interface.name) == 0
- && driver->getApp ().getContext ().settings.render.hdr
- ) {
- driver->getWaylandContext ()->colorManager
- = static_cast<wp_color_manager_v1*> (wl_registry_bind (registry, name, &wp_color_manager_v1_interface, 1));
- wp_color_manager_v1_add_listener (driver->getWaylandContext ()->colorManager, &colorManagerListener, driver);
- #ifdef ENABLE_KDE_FEATURES
- } else if (strcmp (interface, org_kde_plasma_shell_interface.name) == 0) {
- driver->getWaylandContext ()->plasmaShell = static_cast<org_kde_plasma_shell*> (
- wl_registry_bind (registry, name, &org_kde_plasma_shell_interface, std::min (version, 8u))
- );
- #endif
- }
- }
- static void handleGlobalRemoved (void* data, struct wl_registry* registry, uint32_t id) {
- const auto driver = static_cast<WaylandOpenGLDriver*> (data);
- // a monitor being unplugged/disabled removes its global; leaving the viewport around leaks its
- // layer-shell/EGL surfaces for the rest of this client's connection since nothing else disconnects it
- const auto it
- = std::ranges::find_if (driver->m_screens, [id] (const auto* viewport) { return viewport->waylandName == id; });
- if (it != driver->m_screens.end ()) {
- driver->onLayerClose (*it);
- }
- }
- constexpr struct wl_registry_listener registryListener = {
- .global = handleGlobal,
- .global_remove = handleGlobalRemoved,
- };
- void WaylandOpenGLDriver::initEGL () {
- const char* CLIENT_EXTENSIONS = eglQueryString (EGL_NO_DISPLAY, EGL_EXTENSIONS);
- if (!CLIENT_EXTENSIONS) {
- sLog.exception ("Failed to query EGL Extensions");
- }
- const auto CLIENTEXTENSIONS = std::string (CLIENT_EXTENSIONS);
- if (CLIENTEXTENSIONS.find ("EGL_EXT_platform_base") == std::string::npos) {
- sLog.exception ("EGL_EXT_platform_base not supported by EGL!");
- }
- if (CLIENTEXTENSIONS.find ("EGL_EXT_platform_wayland") == std::string::npos) {
- sLog.exception ("EGL_EXT_platform_wayland not supported by EGL!");
- }
- const auto eglGetPlatformDisplayEXT
- = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC> (eglGetProcAddress ("eglGetPlatformDisplayEXT"));
- m_eglContext.eglCreatePlatformWindowSurfaceEXT = reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC> (
- eglGetProcAddress ("eglCreatePlatformWindowSurfaceEXT")
- );
- if (!eglGetPlatformDisplayEXT || !m_eglContext.eglCreatePlatformWindowSurfaceEXT) {
- sLog.exception ("EGL did not return EXT proc pointers!");
- }
- m_eglContext.display = eglGetPlatformDisplayEXT (EGL_PLATFORM_WAYLAND_EXT, m_waylandContext.display, nullptr);
- if (m_eglContext.display == EGL_NO_DISPLAY) {
- this->finishEGL ();
- sLog.exception ("eglGetPlatformDisplayEXT failed!");
- }
- if (!eglInitialize (m_eglContext.display, nullptr, nullptr)) {
- this->finishEGL ();
- sLog.exception ("eglInitialize failed!");
- }
- const auto CLIENTEXTENSIONSPOSTINIT = std::string (eglQueryString (m_eglContext.display, EGL_EXTENSIONS));
- if (CLIENTEXTENSIONSPOSTINIT.find ("EGL_KHR_create_context") == std::string::npos) {
- this->finishEGL ();
- sLog.exception ("EGL_KHR_create_context not supported!");
- }
- EGLint matchedConfigs = 0;
- const EGLint CONFIG_ATTRIBUTES[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_SAMPLES, 4,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE,
- };
- // PQ in 8 bits bands visibly, so HDR outputs want a 10 bit buffer (fine for the SDR ones too)
- if (this->isHDRAvailable ()) {
- const EGLint HDR_CONFIG_ATTRIBUTES[] = {
- EGL_SURFACE_TYPE,
- EGL_WINDOW_BIT,
- EGL_RED_SIZE,
- 10,
- EGL_GREEN_SIZE,
- 10,
- EGL_BLUE_SIZE,
- 10,
- EGL_SAMPLES,
- 4,
- EGL_RENDERABLE_TYPE,
- EGL_OPENGL_BIT,
- EGL_NONE,
- };
- EGLConfig configs[64];
- EGLint count = 0;
- if (eglChooseConfig (m_eglContext.display, HDR_CONFIG_ATTRIBUTES, configs, 64, &count)) {
- for (EGLint i = 0; i < count && matchedConfigs == 0; i++) {
- EGLint red = 0;
- eglGetConfigAttrib (m_eglContext.display, configs[i], EGL_RED_SIZE, &red);
- if (red == 10) {
- m_eglContext.config = configs[i];
- matchedConfigs = 1;
- }
- }
- }
- if (matchedConfigs == 0) {
- sLog.error ("--hdr: no 10 bit EGL config, HDR output will band");
- }
- }
- if (matchedConfigs == 0
- && !eglChooseConfig (m_eglContext.display, CONFIG_ATTRIBUTES, &m_eglContext.config, 1, &matchedConfigs)) {
- this->finishEGL ();
- sLog.exception ("eglChooseConfig failed!");
- }
- if (matchedConfigs == 0) {
- this->finishEGL ();
- sLog.exception ("eglChooseConfig failed! (matched 0 configs)");
- }
- if (!eglBindAPI (EGL_OPENGL_API)) {
- this->finishEGL ();
- sLog.exception ("eglBindAPI failed!");
- }
- const EGLint CONTEXT_ATTRIBUTES[] = {
- EGL_CONTEXT_MAJOR_VERSION_KHR,
- 3,
- EGL_CONTEXT_MINOR_VERSION_KHR,
- 3,
- EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
- EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
- // required for glDebugMessageCallback (WallpaperApplication::setupOpenGLDebugging) on drivers that
- // only emit KHR_debug output when the context is created with this flag; Mesa tends to be lenient
- EGL_CONTEXT_FLAGS_KHR,
- EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
- EGL_NONE,
- };
- m_eglContext.context
- = eglCreateContext (m_eglContext.display, m_eglContext.config, EGL_NO_CONTEXT, CONTEXT_ATTRIBUTES);
- if (m_eglContext.context == EGL_NO_CONTEXT) {
- this->finishEGL ();
- sLog.error ("eglCreateContext error " + std::to_string (eglGetError ()));
- sLog.exception ("eglCreateContext failed!");
- }
- }
- void WaylandOpenGLDriver::finishEGL () const {
- eglMakeCurrent (EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- if (m_eglContext.display) {
- eglTerminate (m_eglContext.display);
- }
- eglReleaseThread ();
- }
- void WaylandOpenGLDriver::onLayerClose (Output::WaylandOutputViewport* viewport) {
- sLog.error ("Compositor closed our LS, freeing data...");
- if (viewport->frameCallback) {
- wl_callback_destroy (viewport->frameCallback);
- viewport->frameCallback = nullptr;
- }
- if (viewport->eglSurface) {
- eglDestroySurface (m_eglContext.display, viewport->eglSurface);
- }
- if (viewport->eglWindow) {
- wl_egl_window_destroy (viewport->eglWindow);
- }
- if (viewport->colorSurface) {
- wp_color_management_surface_v1_destroy (viewport->colorSurface);
- viewport->colorSurface = nullptr;
- }
- if (viewport->colorOutput) {
- wp_color_management_output_v1_destroy (viewport->colorOutput);
- viewport->colorOutput = nullptr;
- }
- if (viewport->layerSurface) {
- zwlr_layer_surface_v1_destroy (viewport->layerSurface);
- }
- #ifdef ENABLE_KDE_FEATURES
- if (viewport->plasmaSurface) {
- org_kde_plasma_surface_destroy (viewport->plasmaSurface);
- viewport->plasmaSurface = nullptr;
- }
- #endif
- if (viewport->xdgOutput) {
- zxdg_output_v1_destroy (viewport->xdgOutput);
- viewport->xdgOutput = nullptr;
- }
- if (viewport->surface) {
- wl_surface_destroy (viewport->surface);
- }
- if (viewport->cursorSurface) {
- wl_surface_destroy (viewport->cursorSurface);
- }
- if (viewport->cursorTheme) {
- wl_cursor_theme_destroy (viewport->cursorTheme);
- }
- if (viewport->output) {
- wl_output_release (viewport->output);
- }
- std::erase (this->m_screens, viewport);
- this->getOutput ().reset ();
- delete viewport;
- }
- WaylandOpenGLDriver::WaylandOpenGLDriver (ApplicationContext& context, WallpaperApplication& app) :
- VideoDriver (app, m_mouseInput), m_output (context, *this), m_requestedExit (false), m_frameCounter (0),
- m_context (context), m_mouseInput (*this) {
- initWaylandRegistry ();
- initColorManagement ();
- initEGL ();
- setupOutputLayerSurfaces ();
- initGLEW ();
- }
- void WaylandOpenGLDriver::initWaylandRegistry () {
- m_waylandContext.display = wl_display_connect (nullptr);
- if (!m_waylandContext.display) {
- sLog.exception ("Failed to query wayland display");
- }
- m_waylandContext.registry = wl_display_get_registry (m_waylandContext.display);
- wl_registry_add_listener (m_waylandContext.registry, ®istryListener, this);
- wl_display_dispatch (m_waylandContext.display);
- wl_display_roundtrip (m_waylandContext.display);
- if (!m_waylandContext.compositor || !m_waylandContext.shm || !m_waylandContext.layerShell
- || this->m_screens.empty ()) {
- sLog.exception ("Failed to bind to required interfaces");
- }
- if (m_waylandContext.xdgOutputManager) {
- for (const auto& o : this->m_screens) {
- o->setupXdgOutput (m_waylandContext.xdgOutputManager);
- }
- wl_display_roundtrip (m_waylandContext.display);
- } else if (!m_context.settings.general.spanGroups.empty ()) {
- sLog.error ("zxdg_output_manager_v1 is unavailable; screen-span positions will be incorrect.");
- }
- }
- void WaylandOpenGLDriver::initColorManagement () {
- if (!m_context.settings.render.hdr) {
- return;
- }
- if (!m_waylandContext.colorManager) {
- sLog.error ("--hdr: the compositor has no color management protocol (wp_color_manager_v1), staying SDR");
- return;
- }
- // the supported_* events and done follow the bind
- wl_display_roundtrip (m_waylandContext.display);
- if (!colorSupport.parametric || !colorSupport.pq || !colorSupport.bt2020) {
- sLog.error ("--hdr: the compositor can't take PQ with BT.2020 primaries, staying SDR");
- return;
- }
- auto* creator = wp_color_manager_v1_create_parametric_creator (m_waylandContext.colorManager);
- wp_image_description_creator_params_v1_set_tf_named (creator, WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ);
- wp_image_description_creator_params_v1_set_primaries_named (creator, WP_COLOR_MANAGER_V1_PRIMARIES_BT2020);
- this->m_hdrDescription = wp_image_description_creator_params_v1_create (creator);
- wp_image_description_v1_add_listener (this->m_hdrDescription, &hdrDescriptionListener, this);
- wl_display_roundtrip (m_waylandContext.display);
- if (!this->hdrDescriptionReady) {
- return;
- }
- // every output reports whether it runs in HDR, and again whenever that changes
- for (const auto& o : this->m_screens) {
- o->setupColorManagement ();
- }
- // image description ready -> get_information -> its events
- wl_display_roundtrip (m_waylandContext.display);
- wl_display_roundtrip (m_waylandContext.display);
- for (const auto& o : this->m_screens) {
- sLog.out ("Output ", o->name, o->isOutputHDR () ? " runs in HDR, drawing PQ" : " runs in SDR");
- }
- }
- bool WaylandOpenGLDriver::isHDRAvailable () const { return this->hdrDescriptionReady; }
- wp_image_description_v1* WaylandOpenGLDriver::getHDRDescription () const { return this->m_hdrDescription; }
- void WaylandOpenGLDriver::setupOutputLayerSurfaces () {
- bool any = false;
- for (const auto& o : this->m_screens) {
- if (!isScreenRequested (o->name)) {
- continue;
- }
- o->setupLS ();
- any = true;
- }
- if (!any) {
- sLog.error ("No outputs could be initialized, please check the parameters and try again");
- sLog.error ("Detected outputs:");
- for (const auto& o : this->m_screens) {
- sLog.error (" ", o->name);
- }
- sLog.error ("Requested: ");
- for (const auto& o : m_context.settings.general.screenBackgrounds | std::views::keys) {
- sLog.error (" ", o);
- }
- for (const auto& spanGroup : m_context.settings.general.spanGroups) {
- for (const auto& screen : spanGroup.screens) {
- sLog.error (" ", screen, " (span group)");
- }
- }
- sLog.exception ("Cannot continue...");
- }
- this->m_layerSurfacesReady = true;
- }
- bool WaylandOpenGLDriver::isScreenRequested (const std::string& name) const {
- if (m_context.settings.general.screenBackgrounds.contains (name)) {
- return true;
- }
- return std::ranges::any_of (m_context.settings.general.spanGroups, [&name] (const auto& spanGroup) {
- return std::ranges::find (spanGroup.screens, name) != spanGroup.screens.end ();
- });
- }
- void WaylandOpenGLDriver::setupLateOutputs () {
- // KDE often drops and re-adds outputs while it applies the screen layout at login, and monitors can
- // wake up after we started; the wallpapers stay registered by screen name, only the surface is missing
- for (const auto& o : this->m_screens) {
- if (o->layerSurface || !o->initialized || !isScreenRequested (o->name)) {
- continue;
- }
- sLog.out ("Output ", o->name, " appeared, attaching the wallpaper");
- if (m_waylandContext.xdgOutputManager && !o->xdgOutput) {
- o->setupXdgOutput (m_waylandContext.xdgOutputManager);
- }
- o->setupLS ();
- }
- }
- void WaylandOpenGLDriver::initGLEW () {
- glewExperimental = GL_TRUE;
- if (const GLenum result = glewInit (); result != GLEW_OK) {
- const char* error = reinterpret_cast<const char*> (glewGetErrorString (result));
- // on Wayland+EGL, GLEW may report GLEW_ERROR_NO_GLX_DISPLAY or a null string when the build
- // also includes X11 but no GLX display is present; non-fatal, the EGL context is already current
- if (result == GLEW_ERROR_NO_GLX_DISPLAY || error == nullptr) {
- sLog.out ("Failed to initialize GLEW, but continuing with EGL context: ", error ? error : "No GLX display");
- } else {
- sLog.error ("Failed to initialize GLEW: ", error);
- sLog.exception ("Cannot continue...");
- }
- }
- }
- WaylandOpenGLDriver::~WaylandOpenGLDriver () {
- for (const auto& screen : this->m_screens) {
- if (screen->xdgOutput) {
- zxdg_output_v1_destroy (screen->xdgOutput);
- screen->xdgOutput = nullptr;
- }
- }
- eglMakeCurrent (EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- if (m_eglContext.context != EGL_NO_CONTEXT) {
- eglDestroyContext (m_eglContext.display, m_eglContext.context);
- }
- eglTerminate (m_eglContext.display);
- eglReleaseThread ();
- if (this->m_waylandContext.display) {
- wl_display_disconnect (this->m_waylandContext.display);
- }
- }
- void WaylandOpenGLDriver::dispatchEventQueue () {
- if (this->m_layerSurfacesReady) {
- this->setupLateOutputs ();
- }
- // rendering only continues from frame callbacks, so a surface that never got its first frame or whose
- // callback the compositor dropped would stay blank forever; this also draws the very first frame
- const auto now = std::chrono::steady_clock::now ();
- for (const auto& o : this->m_screens) {
- if (o->layerSurface && now - o->lastSwap > MISSED_FRAME_TIMEOUT) {
- o->renderFrame ();
- }
- }
- // the compositor stops sending frame callbacks to hidden surfaces, a blocking dispatch would stall the main loop
- wl_display* display = m_waylandContext.display;
- while (wl_display_prepare_read (display) != 0) {
- if (wl_display_dispatch_pending (display) == -1) {
- m_requestedExit = true;
- return;
- }
- }
- wl_display_flush (display);
- pollfd fd = { .fd = wl_display_get_fd (display), .events = POLLIN, .revents = 0 };
- const int ready = poll (&fd, 1, EVENT_WAIT_TIMEOUT_MS);
- if (ready > 0) {
- if (wl_display_read_events (display) == -1) {
- m_requestedExit = true;
- return;
- }
- } else {
- wl_display_cancel_read (display);
- if (ready == -1 && errno != EINTR) {
- m_requestedExit = true;
- return;
- }
- }
- if (wl_display_dispatch_pending (display) == -1) {
- m_requestedExit = true;
- }
- m_frameCounter++;
- }
- Output::Output& WaylandOpenGLDriver::getOutput () { return this->m_output; }
- float WaylandOpenGLDriver::getRenderTime () const {
- return static_cast<float> (std::chrono::duration_cast<std::chrono::microseconds> (
- std::chrono::high_resolution_clock::now () - renderStart
- )
- .count ())
- / 1000000.0;
- }
- bool WaylandOpenGLDriver::closeRequested () { return this->m_requestedExit; }
- void WaylandOpenGLDriver::resizeWindow (glm::ivec2 size) { }
- void WaylandOpenGLDriver::resizeWindow (glm::ivec4 sizeandpos) { }
- void WaylandOpenGLDriver::showWindow () { }
- void WaylandOpenGLDriver::hideWindow () { }
- glm::ivec2 WaylandOpenGLDriver::getFramebufferSize () const { return glm::ivec2 { 0, 0 }; }
- uint32_t WaylandOpenGLDriver::getFrameCounter () const { return m_frameCounter; }
- WaylandOpenGLDriver::SEGLContext* WaylandOpenGLDriver::getEGLContext () { return &this->m_eglContext; }
- void* WaylandOpenGLDriver::getProcAddress (const char* name) const {
- return reinterpret_cast<void*> (eglGetProcAddress (name));
- }
- void* WaylandOpenGLDriver::getWaylandDisplay () const { return this->m_waylandContext.display; }
- WaylandOpenGLDriver::WaylandContext* WaylandOpenGLDriver::getWaylandContext () { return &this->m_waylandContext; }
- Output::WaylandOutputViewport* WaylandOpenGLDriver::surfaceToViewport (const wl_surface* surface) const {
- for (const auto& o : m_screens) {
- if (o->surface == surface) {
- return o;
- }
- }
- return nullptr;
- }
- __attribute__ ((constructor)) void registerWaylandOpenGL () {
- sVideoFactories.registerDriver (
- ApplicationContext::DESKTOP_BACKGROUND, "wayland",
- [] (ApplicationContext& context, WallpaperApplication& application) -> std::unique_ptr<VideoDriver> {
- return std::make_unique<WaylandOpenGLDriver> (context, application);
- }
- );
- }
|