From bdaf1ec6e42b43db1d98276fe65bba281c27f50a Mon Sep 17 00:00:00 2001 From: moetayuko Date: Fri, 8 Nov 2024 08:54:26 +0800 Subject: [PATCH] implement wayland fullscreen detector (#261) * feat: wayland fullscreen detector * feat: pause mpv when fullscreen --- CMakeLists.txt | 72 ++--- ...oreign-toplevel-management-unstable-v1.xml | 270 ++++++++++++++++++ .../Application/CWallpaperApplication.cpp | 52 ++-- src/WallpaperEngine/Render/CRenderContext.cpp | 7 +- src/WallpaperEngine/Render/CRenderContext.h | 3 +- src/WallpaperEngine/Render/CWallpaper.cpp | 4 +- src/WallpaperEngine/Render/CWallpaper.h | 8 +- .../Detectors/CWaylandFullScreenDetector.cpp | 146 +++++++++- .../Detectors/CWaylandFullScreenDetector.h | 29 +- .../Render/Wallpapers/CVideo.cpp | 11 +- .../Render/Wallpapers/CVideo.h | 2 + 11 files changed, 528 insertions(+), 76 deletions(-) create mode 100644 protocols/wlr-foreign-toplevel-management-unstable-v1.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index ab442e10..95e6ae67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,35 +40,35 @@ find_package(PulseAudio REQUIRED) set(CEF_VERSION "120.1.10+g3ce3184+chromium-120.0.6099.129") # Determine the platform. if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") - if("${PROJECT_ARCH}" STREQUAL "arm64") - set(CEF_PLATFORM "macosarm64") - elseif("${PROJECT_ARCH}" STREQUAL "x86_64") - set(CEF_PLATFORM "macosx64") - elseif("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") - set(PROJECT_ARCH "arm64") - set(CEF_PLATFORM "macosarm64") - else() - set(PROJECT_ARCH "x86_64") - set(CEF_PLATFORM "macosx64") - endif() + if("${PROJECT_ARCH}" STREQUAL "arm64") + set(CEF_PLATFORM "macosarm64") + elseif("${PROJECT_ARCH}" STREQUAL "x86_64") + set(CEF_PLATFORM "macosx64") + elseif("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") + set(PROJECT_ARCH "arm64") + set(CEF_PLATFORM "macosarm64") + else() + set(PROJECT_ARCH "x86_64") + set(CEF_PLATFORM "macosx64") + endif() elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") - if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm") - set(CEF_PLATFORM "linuxarm") - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64") - set(CEF_PLATFORM "linuxarm64") - elseif(CMAKE_SIZEOF_VOID_P MATCHES 8) - set(CEF_PLATFORM "linux64") - else() - message(FATAL_ERROR "Linux x86 32-bit builds are discontinued.") - endif() + if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm") + set(CEF_PLATFORM "linuxarm") + elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64") + set(CEF_PLATFORM "linuxarm64") + elseif(CMAKE_SIZEOF_VOID_P MATCHES 8) + set(CEF_PLATFORM "linux64") + else() + message(FATAL_ERROR "Linux x86 32-bit builds are discontinued.") + endif() elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") - if("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM64") - set(CEF_PLATFORM "windowsarm64") - elseif(CMAKE_SIZEOF_VOID_P MATCHES 8) - set(CEF_PLATFORM "windows64") - else() - set(CEF_PLATFORM "windows32") - endif() + if("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM64") + set(CEF_PLATFORM "windowsarm64") + elseif(CMAKE_SIZEOF_VOID_P MATCHES 8) + set(CEF_PLATFORM "windows64") + else() + set(CEF_PLATFORM "windows32") + endif() endif() include(DownloadCEF) DownloadCEF("${CEF_PLATFORM}" "${CEF_VERSION}" "${CMAKE_SOURCE_DIR}/third_party/cef") @@ -79,12 +79,12 @@ set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build ) - + set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib ) - + set( TARGET_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build @@ -106,7 +106,7 @@ include_directories( add_library(ceflib SHARED IMPORTED) -set_target_properties(ceflib +set_target_properties(ceflib PROPERTIES IMPORTED_LOCATION ${TARGET_OUTPUT_DIRECTORY}/libcef.so) ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}") @@ -124,9 +124,15 @@ if(WAYLAND_SUPPORT_FOUND) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") + message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") message(STATUS "Building protocols...") + execute_process( + COMMAND ${WaylandScanner} client-header ${CMAKE_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml wlr-foreign-toplevel-management-unstable-v1-protocol.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process( + COMMAND ${WaylandScanner} private-code ${CMAKE_SOURCE_DIR}/protocols/wlr-foreign-toplevel-management-unstable-v1.xml wlr-foreign-toplevel-management-unstable-v1-protocol.c + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) execute_process( COMMAND ${WaylandScanner} client-header ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml wlr-layer-shell-unstable-v1-protocol.h WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) @@ -153,6 +159,7 @@ if(WAYLAND_SUPPORT_FOUND) "src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.cpp" "src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.h" "xdg-shell-protocol.c" + "wlr-foreign-toplevel-management-unstable-v1-protocol.c" "wlr-layer-shell-unstable-v1-protocol.c") endif() @@ -429,7 +436,7 @@ add_dependencies(linux-wallpaperengine libcef_dll_wrapper) # Need to remove libvulkan, otherwise will get error on linking: # /usr/bin/ld: /usr/lib/libmpv.so: undefined reference to `vkCreateXlibSurfaceKHR' -file(REMOVE "${CEF_BINARY_DIR_DEBUG}/libvulkan.so.1" "${CEF_BINARY_DIR_RELEASE}/libvulkan.so.1") +file(REMOVE "${CEF_BINARY_DIR_DEBUG}/libvulkan.so.1" "${CEF_BINARY_DIR_RELEASE}/libvulkan.so.1") target_link_libraries (linux-wallpaperengine PUBLIC ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES} @@ -476,4 +483,3 @@ endif() install(DIRECTORY ${TARGET_OUTPUT_DIRECTORY}/ DESTINATION .) install(FILES ${TARGET_OUTPUT_DIRECTORY}/${PROJECT_NAME} PERMISSIONS OWNER_READ OWNER_WRITE WORLD_EXECUTE WORLD_READ GROUP_READ DESTINATION .) install(DIRECTORY share/ DESTINATION ./share) - diff --git a/protocols/wlr-foreign-toplevel-management-unstable-v1.xml b/protocols/wlr-foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 00000000..44505bbb --- /dev/null +++ b/protocols/wlr-foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,270 @@ + + + + Copyright © 2018 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The purpose of this protocol is to enable the creation of taskbars + and docks by providing them with a list of opened applications and + letting them request certain actions on them, like maximizing, etc. + + After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + toplevel window will be sent via the toplevel event + + + + + This event is emitted whenever a new toplevel window is created. It + is emitted for all toplevels, regardless of the app that has created + them. + + All initial details of the toplevel(title, app_id, states, etc.) will + be sent immediately after this event via the corresponding events in + zwlr_foreign_toplevel_handle_v1. + + + + + + + Indicates the client no longer wishes to receive events for new toplevels. + However the compositor may emit further toplevel_created events, until + the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending events to the + zwlr_foreign_toplevel_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + window. Each app may have multiple opened toplevels. + + Each toplevel has a list of outputs it is visible on, conveyed to the + client with the output_enter and output_leave events. + + + + + This event is emitted whenever the title of the toplevel changes. + + + + + + + This event is emitted whenever the app-id of the toplevel changes. + + + + + + + This event is emitted whenever the toplevel becomes visible on + the given output. A toplevel may be visible on multiple outputs. + + + + + + + This event is emitted whenever the toplevel stops being visible on + the given output. It is guaranteed that an entered-output event + with the same output has been emitted before this event. + + + + + + + Requests that the toplevel be maximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unmaximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be minimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unminimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Request that this toplevel be activated on the given seat. + There is no guarantee the toplevel will be actually activated. + + + + + + + The different states that a toplevel can have. These have the same meaning + as the states with the same names defined in xdg-toplevel + + + + + + + + + + + This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 + is created and each time the toplevel state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + + This event is sent after all changes in the toplevel state have been + sent. + + This allows changes to the zwlr_foreign_toplevel_handle_v1 properties + to be seen as atomic, even if they happen via multiple events. + + + + + + Send a request to the toplevel to close itself. The compositor would + typically use a shell-specific method to carry out this request, for + example by sending the xdg_toplevel.close event. However, this gives + no guarantees the toplevel will actually be destroyed. If and when + this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + be emitted. + + + + + + The rectangle of the surface specified in this request corresponds to + the place where the app using this protocol represents the given toplevel. + It can be used by the compositor as a hint for some operations, e.g + minimizing. The client is however not required to set this, in which + case the compositor is free to decide some default value. + + If the client specifies more than one rectangle, only the last one is + considered. + + The dimensions are given in surface-local coordinates. + Setting width=height=0 removes the already-set rectangle. + + + + + + + + + + + + + + + + This event means the toplevel has been destroyed. It is guaranteed there + won't be any more events for this zwlr_foreign_toplevel_handle_v1. The + toplevel itself becomes inert so any requests will be ignored except the + destroy request. + + + + + + Destroys the zwlr_foreign_toplevel_handle_v1 object. + + This request should be called either when the client does not want to + use the toplevel anymore or after the closed event to finalize the + destruction of the object. + + + + + + + + Requests that the toplevel be fullscreened on the given output. If the + fullscreen state and/or the outputs the toplevel is visible on actually + change, this will be indicated by the state and output_enter/leave + events. + + The output parameter is only a hint to the compositor. Also, if output + is NULL, the compositor should decide which output the toplevel will be + fullscreened on, if at all. + + + + + + + Requests that the toplevel be unfullscreened. If the fullscreen state + actually changes, this will be indicated by the state event. + + + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + + + diff --git a/src/WallpaperEngine/Application/CWallpaperApplication.cpp b/src/WallpaperEngine/Application/CWallpaperApplication.cpp index 2fd715ee..3379a387 100644 --- a/src/WallpaperEngine/Application/CWallpaperApplication.cpp +++ b/src/WallpaperEngine/Application/CWallpaperApplication.cpp @@ -56,7 +56,7 @@ void CWallpaperApplication::setupContainer (CCombinedContainer& container, const try { container.add (new CDirectory (this->m_context.settings.general.assets)); } catch (CAssetLoadException&) { - sLog.exception("Cannot find a valid assets folder, resolved to ", this->m_context.settings.general.assets); + sLog.exception ("Cannot find a valid assets folder, resolved to ", this->m_context.settings.general.assets); } // add two possible patches directories to the container @@ -173,8 +173,9 @@ void CWallpaperApplication::setupContainer (CCombinedContainer& container, const } void CWallpaperApplication::loadBackgrounds () { - if (this->m_context.settings.render.mode == CApplicationContext::NORMAL_WINDOW || this->m_context.settings.render.mode == CApplicationContext::EXPLICIT_WINDOW) { - this->m_backgrounds ["default"] = this->loadBackground(this->m_context.settings.general.defaultBackground); + if (this->m_context.settings.render.mode == CApplicationContext::NORMAL_WINDOW || + this->m_context.settings.render.mode == CApplicationContext::EXPLICIT_WINDOW) { + this->m_backgrounds ["default"] = this->loadBackground (this->m_context.settings.general.defaultBackground); return; } @@ -244,9 +245,9 @@ void CWallpaperApplication::takeScreenshot (const std::filesystem::path& filenam int xfinal = x + xoffset; int yfinal = this->m_renderContext->getOutput ().renderVFlip () ? (viewport->viewport.w - y) : y; - bitmap[yfinal * width * 3 + xfinal * 3] = *pixel++; - bitmap[yfinal * width * 3 + xfinal * 3 + 1] = *pixel++; - bitmap[yfinal * width * 3 + xfinal * 3 + 2] = *pixel++; + bitmap [yfinal * width * 3 + xfinal * 3] = *pixel++; + bitmap [yfinal * width * 3 + xfinal * 3 + 1] = *pixel++; + bitmap [yfinal * width * 3 + xfinal * 3 + 2] = *pixel++; } } @@ -257,14 +258,14 @@ void CWallpaperApplication::takeScreenshot (const std::filesystem::path& filenam delete [] buffer; } - auto extension = filename.extension(); + auto extension = filename.extension (); if (extension == ".bmp") { - stbi_write_bmp (filename.c_str(), width, height, 3, bitmap); + stbi_write_bmp (filename.c_str (), width, height, 3, bitmap); } else if (extension == ".png") { - stbi_write_png (filename.c_str(), width, height, 3, bitmap, width * 3); + stbi_write_png (filename.c_str (), width, height, 3, bitmap, width * 3); } else if (extension == ".jpg" || extension == ".jpeg") { - stbi_write_jpg (filename.c_str(), width, height, 3, bitmap, 100); + stbi_write_jpg (filename.c_str (), width, height, 3, bitmap, 100); } } @@ -276,14 +277,14 @@ void CWallpaperApplication::show () { "Cannot read environment variable XDG_SESSION_TYPE, window server detection failed. Please ensure proper values are set"); } - sLog.debug("Checking for window servers: "); + sLog.debug ("Checking for window servers: "); #ifdef ENABLE_WAYLAND - sLog.debug("\twayland"); + sLog.debug ("\twayland"); #endif // ENABLE_WAYLAND #ifdef ENABLE_X11 - sLog.debug("\tx11"); + sLog.debug ("\tx11"); #endif // ENABLE_X11 #ifdef ENABLE_WAYLAND @@ -349,8 +350,9 @@ void CWallpaperApplication::show () { new WallpaperEngine::Render::Drivers::Detectors::CFullScreenDetector (this->m_context); } - m_inputContext = new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CGLFWMouseInput ( - reinterpret_cast (m_videoDriver))); + m_inputContext = + new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CGLFWMouseInput ( + reinterpret_cast (m_videoDriver))); } if (this->m_context.settings.audio.audioprocessing) { @@ -374,9 +376,10 @@ void CWallpaperApplication::show () { // set all the specific wallpapers required for (const auto& [background, info] : this->m_backgrounds) { - m_renderContext->setWallpaper (background, WallpaperEngine::Render::CWallpaper::fromWallpaper ( - info->getWallpaper (), *m_renderContext, *m_audioContext, m_browserContext, - this->m_context.settings.general.screenScalings [background])); + m_renderContext->setWallpaper (background, + WallpaperEngine::Render::CWallpaper::fromWallpaper ( + info->getWallpaper (), *m_renderContext, *m_audioContext, m_browserContext, + this->m_context.settings.general.screenScalings [background])); } // wallpapers are setup, free browsesr context if possible @@ -401,6 +404,13 @@ void CWallpaperApplication::show () { m_audioDriver->update (); // update input information m_inputContext->update (); + // check for fullscreen windows and wait until there's none fullscreen + if (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) { + m_renderContext->setPause (true); + while (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) + usleep (FULLSCREEN_CHECK_WAIT_TIME); + m_renderContext->setPause (false); + } // process driver events m_videoDriver->dispatchEventQueue (); @@ -420,10 +430,6 @@ void CWallpaperApplication::show () { } void CWallpaperApplication::update (Render::Drivers::Output::COutputViewport* viewport) { - // check for fullscreen windows and wait until there's none fullscreen - while (this->m_fullScreenDetector->anythingFullscreen () && this->m_context.state.general.keepRunning) - usleep (FULLSCREEN_CHECK_WAIT_TIME); - // render the scene m_renderContext->render (viewport); } @@ -443,4 +449,4 @@ CApplicationContext& CWallpaperApplication::getContext () const { const WallpaperEngine::Render::Drivers::Output::COutput& CWallpaperApplication::getOutput () const { return this->m_renderContext->getOutput (); } -} // namespace WallpaperEngine::Application \ No newline at end of file +} // namespace WallpaperEngine::Application diff --git a/src/WallpaperEngine/Render/CRenderContext.cpp b/src/WallpaperEngine/Render/CRenderContext.cpp index ae4c0551..87c8b6d6 100644 --- a/src/WallpaperEngine/Render/CRenderContext.cpp +++ b/src/WallpaperEngine/Render/CRenderContext.cpp @@ -42,6 +42,11 @@ void CRenderContext::setWallpaper (const std::string& display, CWallpaper* wallp this->m_wallpapers.insert_or_assign (display, wallpaper); } +void CRenderContext::setPause (bool newState) { + for (auto&& wallpaper : this->m_wallpapers) + wallpaper.second->setPause (newState); +} + Input::CInputContext& CRenderContext::getInputContext () const { return this->m_input; } @@ -61,4 +66,4 @@ const Drivers::Output::COutput& CRenderContext::getOutput () const { const ITexture* CRenderContext::resolveTexture (const std::string& name) { return this->m_textureCache->resolve (name); } -} // namespace WallpaperEngine::Render \ No newline at end of file +} // namespace WallpaperEngine::Render diff --git a/src/WallpaperEngine/Render/CRenderContext.h b/src/WallpaperEngine/Render/CRenderContext.h index 6017f07f..5e51a296 100644 --- a/src/WallpaperEngine/Render/CRenderContext.h +++ b/src/WallpaperEngine/Render/CRenderContext.h @@ -35,6 +35,7 @@ class CRenderContext { void render (Drivers::Output::COutputViewport* viewport); void setWallpaper (const std::string& display, CWallpaper* wallpaper); + void setPause (bool newState); [[nodiscard]] Input::CInputContext& getInputContext () const; [[nodiscard]] const CWallpaperApplication& getApp () const; [[nodiscard]] const Drivers::CVideoDriver& getDriver () const; @@ -54,4 +55,4 @@ class CRenderContext { CTextureCache* m_textureCache; }; } // namespace Render -} // namespace WallpaperEngine \ No newline at end of file +} // namespace WallpaperEngine diff --git a/src/WallpaperEngine/Render/CWallpaper.cpp b/src/WallpaperEngine/Render/CWallpaper.cpp index 321c74c8..801bf410 100644 --- a/src/WallpaperEngine/Render/CWallpaper.cpp +++ b/src/WallpaperEngine/Render/CWallpaper.cpp @@ -232,6 +232,8 @@ void CWallpaper::render (glm::ivec4 viewport, bool vflip) { glDrawArrays (GL_TRIANGLES, 0, 6); } +void CWallpaper::setPause (bool newState) {} + void CWallpaper::setupFramebuffers () { const uint32_t width = this->getWidth (); const uint32_t height = this->getHeight (); @@ -291,4 +293,4 @@ CWallpaper* CWallpaper::fromWallpaper (Core::CWallpaper* wallpaper, CRenderConte scalingMode); else sLog.exception ("Unsupported wallpaper type"); -} \ No newline at end of file +} diff --git a/src/WallpaperEngine/Render/CWallpaper.h b/src/WallpaperEngine/Render/CWallpaper.h index 50110ebe..57e67361 100644 --- a/src/WallpaperEngine/Render/CWallpaper.h +++ b/src/WallpaperEngine/Render/CWallpaper.h @@ -50,6 +50,11 @@ class CWallpaper : public Helpers::CContextAware { */ void render (glm::ivec4 viewport, bool vflip); + /** + * Pause the renderer + */ + virtual void setPause (bool newState); + /** * @return The container to resolve files for this wallpaper */ @@ -141,7 +146,8 @@ class CWallpaper : public Helpers::CContextAware { * @return */ static CWallpaper* fromWallpaper (Core::CWallpaper* wallpaper, CRenderContext& context, CAudioContext& audioContext, - CWebBrowserContext& browserContext, const CWallpaperState::TextureUVsScaling& scalingMode); + CWebBrowserContext& browserContext, + const CWallpaperState::TextureUVsScaling& scalingMode); protected: CWallpaper (Core::CWallpaper* wallpaperData, std::string type, CRenderContext& context, CAudioContext& audioContext, diff --git a/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp b/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp index 21b49ffd..7e58e915 100644 --- a/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp +++ b/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp @@ -1,12 +1,150 @@ #include "CWaylandFullScreenDetector.h" -using namespace WallpaperEngine::Render::Drivers::Detectors; +#include "WallpaperEngine/Logging/CLog.h" +#include "wlr-foreign-toplevel-management-unstable-v1-protocol.h" +#include + +namespace WallpaperEngine::Render::Drivers::Detectors { + +namespace { + +struct FullscreenState { + bool pending; + bool current; + uint32_t* const count; +}; + +void toplevelHandleTitle (void*, struct zwlr_foreign_toplevel_handle_v1*, const char*) {} + +void toplevelHandleAppId (void*, struct zwlr_foreign_toplevel_handle_v1*, const char*) {} + +void toplevelHandleOutputEnter (void*, struct zwlr_foreign_toplevel_handle_v1*, struct wl_output*) {} + +void toplevelHandleOutputLeave (void*, struct zwlr_foreign_toplevel_handle_v1*, struct wl_output*) {} + +void toplevelHandleParent (void*, struct zwlr_foreign_toplevel_handle_v1*, struct zwlr_foreign_toplevel_handle_v1*) {} + +void toplevelHandleState (void* data, struct zwlr_foreign_toplevel_handle_v1* handle, struct wl_array* state) { + auto fullscreen = static_cast (data); + const auto begin = static_cast (state->data); + + fullscreen->pending = false; + for (auto it = begin; it < begin + state->size / sizeof (uint32_t); ++it) + if (*it == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) + fullscreen->pending = true; +} + +void toplevelHandleDone (void* data, struct zwlr_foreign_toplevel_handle_v1* handle) { + auto fullscreen = static_cast (data); + if (fullscreen->current != fullscreen->pending) { + fullscreen->current = fullscreen->pending; + if (fullscreen->current) { + ++(*fullscreen->count); + } else { + // sanity check + if (*fullscreen->count == 0) { + sLog.error ("Fullscreen count underflow!!!"); + } else { + --(*fullscreen->count); + } + } + } +} + +void toplevelHandleClosed (void* data, struct zwlr_foreign_toplevel_handle_v1* handle) { + auto fullscreen = static_cast (data); + + if (fullscreen->current) { + // sanity check + if (*fullscreen->count == 0) { + sLog.error ("Fullscreen count underflow!!!"); + } else { + --(*fullscreen->count); + } + } + + zwlr_foreign_toplevel_handle_v1_destroy (handle); + delete fullscreen; +} + +constexpr struct zwlr_foreign_toplevel_handle_v1_listener toplevelHandleListener = { + .title = toplevelHandleTitle, + .app_id = toplevelHandleAppId, + .output_enter = toplevelHandleOutputEnter, + .output_leave = toplevelHandleOutputLeave, + .state = toplevelHandleState, + .done = toplevelHandleDone, + .closed = toplevelHandleClosed, + .parent = toplevelHandleParent, +}; + +void handleToplevel (void* data, struct zwlr_foreign_toplevel_manager_v1* manager, + struct zwlr_foreign_toplevel_handle_v1* handle) { + auto fullscreen = new FullscreenState {.count = static_cast (data)}; + zwlr_foreign_toplevel_handle_v1_add_listener (handle, &toplevelHandleListener, fullscreen); +} + +void handleFinished (void* data, struct zwlr_foreign_toplevel_manager_v1* manager) { + zwlr_foreign_toplevel_manager_v1_destroy (manager); +} + +zwlr_foreign_toplevel_manager_v1_listener toplevelManagerListener = { + .toplevel = handleToplevel, + .finished = handleFinished, +}; + +}; // anonymous namespace + +void handleGlobal (void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { + const auto detector = static_cast (data); + if (strcmp (interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) { + detector->m_toplevelManager = static_cast ( + wl_registry_bind (registry, name, &zwlr_foreign_toplevel_manager_v1_interface, 3)); + if (detector->m_toplevelManager) { + zwlr_foreign_toplevel_manager_v1_add_listener (detector->m_toplevelManager, &toplevelManagerListener, + &detector->m_fullscreenCount); + } + } +} + +void handleGlobalRemoved (void* data, struct wl_registry* registry, uint32_t id) { + // todo: outputs +} + +constexpr struct wl_registry_listener registryListener = { + .global = handleGlobal, + .global_remove = handleGlobalRemoved, +}; CWaylandFullScreenDetector::CWaylandFullScreenDetector (Application::CApplicationContext& appContext) : - CFullScreenDetector (appContext) {} + CFullScreenDetector (appContext) { + m_display = wl_display_connect (nullptr); + if (!m_display) + sLog.exception ("Failed to query wayland display"); + + auto registry = wl_display_get_registry (m_display); + wl_registry_add_listener (registry, ®istryListener, this); + wl_display_roundtrip (m_display); // load list of toplevels + if (!m_toplevelManager) { + sLog.out ("Fullscreen detection not supported by your Wayland compositor"); + } else { + wl_display_roundtrip (m_display); // load toplevel details + } +} + +CWaylandFullScreenDetector::~CWaylandFullScreenDetector () { + if (m_display) + wl_display_disconnect (m_display); +} bool CWaylandFullScreenDetector::anythingFullscreen () const { - return false; // todo + if (!m_toplevelManager) { + return false; + } + wl_display_roundtrip (m_display); + return m_fullscreenCount > 0; } -void CWaylandFullScreenDetector::reset () {} \ No newline at end of file +void CWaylandFullScreenDetector::reset () {} + +} // namespace WallpaperEngine::Render::Drivers::Detectors diff --git a/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h b/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h index 23e94ff9..423e14f0 100644 --- a/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h +++ b/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h @@ -3,24 +3,31 @@ #ifdef ENABLE_WAYLAND #include -#include -#include #include "CFullScreenDetector.h" -#include "WallpaperEngine/Render/Drivers/CVideoDriver.h" -namespace WallpaperEngine::Render::Drivers { -class CWaylandOpenGLDriver; +struct wl_display; +struct wl_registry; +struct zwlr_foreign_toplevel_manager_v1; -namespace Detectors { +namespace WallpaperEngine::Render::Drivers::Detectors { class CWaylandFullScreenDetector final : public CFullScreenDetector { public: - CWaylandFullScreenDetector (Application::CApplicationContext& appContext); - ~CWaylandFullScreenDetector () override = default; + explicit CWaylandFullScreenDetector (Application::CApplicationContext& appContext); + ~CWaylandFullScreenDetector () override; [[nodiscard]] bool anythingFullscreen () const override; void reset () override; + + private: + wl_display* m_display = nullptr; + zwlr_foreign_toplevel_manager_v1* m_toplevelManager = nullptr; + + uint32_t m_fullscreenCount = 0; + + friend void handleGlobal (void* data, struct wl_registry* registry, uint32_t name, const char* interface, + uint32_t version); }; -} // namespace Detectors -} // namespace WallpaperEngine::Render::Drivers -#endif /* ENABLE_WAYLAND */ \ No newline at end of file +} // namespace WallpaperEngine::Render::Drivers::Detectors + +#endif /* ENABLE_WAYLAND */ diff --git a/src/WallpaperEngine/Render/Wallpapers/CVideo.cpp b/src/WallpaperEngine/Render/Wallpapers/CVideo.cpp index 84adf8f6..9e72f64d 100644 --- a/src/WallpaperEngine/Render/Wallpapers/CVideo.cpp +++ b/src/WallpaperEngine/Render/Wallpapers/CVideo.cpp @@ -15,6 +15,7 @@ CVideo::CVideo (Core::CVideo* video, CRenderContext& context, CAudioContext& aud CWallpaper (video, Type, context, audioContext, scalingMode), m_width (16), m_height (16), + m_paused (false), m_mpvGl (nullptr) { double volume = this->getContext ().getApp ().getContext ().settings.audio.volume * 100.0 / 128.0; @@ -40,7 +41,7 @@ CVideo::CVideo (Core::CVideo* video, CRenderContext& context, CAudioContext& aud mpv_set_option (this->m_mpv, "volume", MPV_FORMAT_DOUBLE, &volume); if (!this->getContext ().getApp ().getContext ().settings.audio.enabled) { - mpv_set_option_string(this->m_mpv, "mute", "yes"); + mpv_set_option_string (this->m_mpv, "mute", "yes"); } // initialize gl context for mpv @@ -121,6 +122,14 @@ Core::CVideo* CVideo::getVideo () { return this->getWallpaperData ()->as (); } +void CVideo::setPause (bool newState) { + if (m_paused == newState) + return; + m_paused = newState; + int pause = newState; + mpv_set_property (m_mpv, "pause", MPV_FORMAT_FLAG, &pause); +} + int CVideo::getWidth () const { return this->m_width; } diff --git a/src/WallpaperEngine/Render/Wallpapers/CVideo.h b/src/WallpaperEngine/Render/Wallpapers/CVideo.h index 5890b745..a6c98300 100644 --- a/src/WallpaperEngine/Render/Wallpapers/CVideo.h +++ b/src/WallpaperEngine/Render/Wallpapers/CVideo.h @@ -18,6 +18,7 @@ class CVideo final : public CWallpaper { [[nodiscard]] int getWidth () const override; [[nodiscard]] int getHeight () const override; + void setPause (bool newState) override; void setSize (int width, int height); protected: @@ -31,6 +32,7 @@ class CVideo final : public CWallpaper { mpv_handle* m_mpv; mpv_render_context* m_mpvGl; + bool m_paused; int64_t m_width; int64_t m_height; };