diff --git a/CMakeLists.txt b/CMakeLists.txt index ab442e1..95e6ae6 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 0000000..44505bb --- /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/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp b/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp index 21b49ff..7e58e91 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 -CWaylandFullScreenDetector::CWaylandFullScreenDetector (Application::CApplicationContext& appContext) : - CFullScreenDetector (appContext) {} +namespace WallpaperEngine::Render::Drivers::Detectors { -bool CWaylandFullScreenDetector::anythingFullscreen () const { - return false; // todo +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 CWaylandFullScreenDetector::reset () {} \ No newline at end of file +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) { + 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 { + if (!m_toplevelManager) { + return false; + } + wl_display_roundtrip (m_display); + return m_fullscreenCount > 0; +} + +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 23e94ff..423e14f 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 */