From 111d209bff5448c3a55e07f344cf89715536bba4 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Mon, 12 Dec 2022 14:56:42 +0000 Subject: [PATCH] Added toplevel handle sharing Rev2 (#1203) * Added toplevel handle sharing * nix: fix build * update hyprland-protocols Co-authored-by: Mihai Fufezan --- CMakeLists.txt | 1 + Makefile | 12 +- flake.lock | 12 +- protocols/meson.build | 1 + ...oreign-toplevel-management-unstable-v1.xml | 270 ++++++++++++++++++ src/Compositor.cpp | 17 ++ src/Compositor.hpp | 1 + src/protocols/ToplevelExport.cpp | 34 ++- src/protocols/ToplevelExport.hpp | 3 +- subprojects/hyprland-protocols | 2 +- 10 files changed, 331 insertions(+), 22 deletions(-) create mode 100644 protocols/wlr-foreign-toplevel-management-unstable-v1.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 91d2647b..a0eb2f10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,5 +107,6 @@ target_link_libraries(Hyprland pthread ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_SOURCE_DIR}/ext-workspace-unstable-v1-protocol.o + ${CMAKE_SOURCE_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.o ${CMAKE_SOURCE_DIR}/hyprland-toplevel-export-v1-protocol.o ) diff --git a/Makefile b/Makefile index 0c69b958..aa7357e1 100644 --- a/Makefile +++ b/Makefile @@ -111,6 +111,16 @@ linux-dmabuf-unstable-v1-protocol.c: linux-dmabuf-unstable-v1-protocol.o: linux-dmabuf-unstable-v1-protocol.h +wlr-foreign-toplevel-management-unstable-v1-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocols/wlr-foreign-toplevel-management-unstable-v1.xml $@ + +wlr-foreign-toplevel-management-unstable-v1-protocol.c: + $(WAYLAND_SCANNER) private-code \ + protocols/wlr-foreign-toplevel-management-unstable-v1.xml $@ + +wlr-foreign-toplevel-management-unstable-v1-protocol.o: wlr-foreign-toplevel-management-unstable-v1-protocol.h + legacyrenderer: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DLEGACY_RENDERER:STRING=true -H./ -B./build -G Ninja cmake --build ./build --config Release --target all -j$(shell nproc) @@ -189,7 +199,7 @@ uninstall: rm -f ${PREFIX}/share/man/man1/Hyprland.1 rm -f ${PREFIX}/share/man/man1/hyprctl.1 -protocols: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o wlr-screencopy-unstable-v1-protocol.o idle-protocol.o ext-workspace-unstable-v1-protocol.o pointer-constraints-unstable-v1-protocol.o tablet-unstable-v2-protocol.o wlr-output-power-management-unstable-v1-protocol.o linux-dmabuf-unstable-v1-protocol.o hyprland-toplevel-export-v1-protocol.o +protocols: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o wlr-screencopy-unstable-v1-protocol.o idle-protocol.o ext-workspace-unstable-v1-protocol.o pointer-constraints-unstable-v1-protocol.o tablet-unstable-v2-protocol.o wlr-output-power-management-unstable-v1-protocol.o linux-dmabuf-unstable-v1-protocol.o hyprland-toplevel-export-v1-protocol.o wlr-foreign-toplevel-management-unstable-v1-protocol.o fixwlr: sed -i -E 's/(soversion = 12)([^032]|$$)/soversion = 12032/g' subprojects/wlroots/meson.build diff --git a/flake.lock b/flake.lock index 2e759a79..3732b020 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "hyprland-protocols": { "flake": false, "locked": { - "lastModified": 1670258048, - "narHash": "sha256-Lm2sXnDVZNE+taHqsqVibvPmSdu65VHvXI507KVX4lg=", + "lastModified": 1670703428, + "narHash": "sha256-4KUW5SKR0Y9uaYGcYwy53YJ3B/sgiprCL4fRGO+mpOA=", "owner": "hyprwm", "repo": "hyprland-protocols", - "rev": "0dcff94fc10df2bbb66d3e1b5a1d6cfd3ada5515", + "rev": "d0d6db8cb5bef6d93ca3ad8fb2124964173396da", "type": "github" }, "original": { @@ -82,11 +82,11 @@ ] }, "locked": { - "lastModified": 1670593043, - "narHash": "sha256-tsDs6FB+7PlBOt46dMQFBMH5yPY/fduf4cYbQYhauhA=", + "lastModified": 1670797151, + "narHash": "sha256-ZFzJHqSXhGCjSeMgqTyJG1KJ2Nlwa+NEN9K4oGhWcjg=", "owner": "hyprwm", "repo": "xdg-desktop-portal-hyprland", - "rev": "5f7eecff553d82f21e049d79d8fb3f1f46568da0", + "rev": "36ffb6892e14b9c5be6e321b3c47fe286ac256e6", "type": "github" }, "original": { diff --git a/protocols/meson.build b/protocols/meson.build index d75acb3a..394ca1b8 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -21,6 +21,7 @@ wayland_scanner = find_program( protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], + ['wlr-foreign-toplevel-management-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['wlr-output-power-management-unstable-v1.xml'], ['ext-workspace-unstable-v1.xml'], 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/Compositor.cpp b/src/Compositor.cpp index 942da2ee..8b415bd2 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -931,6 +931,23 @@ CWindow* CCompositor::getWindowFromHandle(uint32_t handle) { return nullptr; } +CWindow* CCompositor::getWindowFromZWLRHandle(wl_resource* handle) { + for (auto& w : m_vWindows) { + if (!w->m_bIsMapped || w->isHidden() || !w->m_phForeignToplevel) + continue; + + wl_resource* current; + + wl_list_for_each(current, &w->m_phForeignToplevel->resources, link) { + if (current == handle) { + return w.get(); + } + } + } + + return nullptr; +} + CWindow* CCompositor::getFullscreenWindowOnWorkspace(const int& ID) { for (auto& w : m_vWindows) { if (w->m_iWorkspaceID == ID && w->m_bIsFullscreen) diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 125a61eb..c4c96fae 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -128,6 +128,7 @@ public: CWindow* getWindowForPopup(wlr_xdg_popup*); CWindow* getWindowFromSurface(wlr_surface*); CWindow* getWindowFromHandle(uint32_t); + CWindow* getWindowFromZWLRHandle(wl_resource*); bool isWorkspaceVisible(const int&); CWorkspace* getWorkspaceByID(const int&); CWorkspace* getWorkspaceByName(const std::string&); diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp index d7e49ab0..e579e1a7 100644 --- a/src/protocols/ToplevelExport.cpp +++ b/src/protocols/ToplevelExport.cpp @@ -6,7 +6,7 @@ #include "ToplevelExportWlrFuncs.hpp" -#define TOPLEVEL_EXPORT_VERSION 1 +#define TOPLEVEL_EXPORT_VERSION 2 static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) { g_pProtocolManager->m_pToplevelExportProtocolManager->bindManager(client, data, version, id); @@ -41,8 +41,17 @@ CToplevelExportProtocolManager::CToplevelExportProtocolManager() { Debug::log(LOG, "ToplevelExportManager started successfully!"); } +wlr_foreign_toplevel_handle_v1* zwlrHandleFromResource(wl_resource* resource) { + // we can't assert here, but it doesnt matter. + return (wlr_foreign_toplevel_handle_v1*)wl_resource_get_user_data(resource); +} + void handleCaptureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, uint32_t handle) { - g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, handle); + g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, g_pCompositor->getWindowFromHandle(handle)); +} + +void handleCaptureToplevelWithWlr(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* handle) { + g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, g_pCompositor->getWindowFromZWLRHandle(handle)); } void handleDestroy(wl_client* client, wl_resource* resource) { @@ -59,7 +68,8 @@ void handleDestroyFrame(wl_client* client, wl_resource* resource) { static const struct hyprland_toplevel_export_manager_v1_interface toplevelExportManagerImpl = { .capture_toplevel = handleCaptureToplevel, - .destroy = handleDestroy + .destroy = handleDestroy, + .capture_toplevel_with_wlr_toplevel_handle = handleCaptureToplevelWithWlr, }; static const struct hyprland_toplevel_export_frame_v1_interface toplevelFrameImpl = { @@ -133,26 +143,24 @@ void CToplevelExportProtocolManager::removeFrame(SToplevelFrame* frame, bool for m_lFrames.remove(*frame); } -void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, uint32_t handle) { +void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, CWindow* pWindow) { const auto PCLIENT = clientFromResource(resource); - const auto PWINDOW = g_pCompositor->getWindowFromHandle(handle); - // create a frame const auto PFRAME = &m_lFrames.emplace_back(); PFRAME->overlayCursor = !!overlay_cursor; PFRAME->resource = wl_resource_create(client, &hyprland_toplevel_export_frame_v1_interface, wl_resource_get_version(resource), frame); - PFRAME->pWindow = PWINDOW; + PFRAME->pWindow = pWindow; - if (!PWINDOW) { - Debug::log(ERR, "Client requested sharing of window handle %x which does not exist!", handle); + if (!PFRAME->pWindow) { + Debug::log(ERR, "Client requested sharing of window handle %x which does not exist!", PFRAME->pWindow); hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource); removeFrame(PFRAME); return; } - if (!PWINDOW->m_bIsMapped || PWINDOW->isHidden()) { - Debug::log(ERR, "Client requested sharing of window handle %x which is not shareable!", handle); + if (!PFRAME->pWindow->m_bIsMapped || PFRAME->pWindow->isHidden()) { + Debug::log(ERR, "Client requested sharing of window handle %x which is not shareable!", PFRAME->pWindow); hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource); removeFrame(PFRAME); return; @@ -170,7 +178,7 @@ void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resou PFRAME->client = PCLIENT; PCLIENT->ref++; - const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); + const auto PMONITOR = g_pCompositor->getMonitorFromID(PFRAME->pWindow->m_iMonitorID); PFRAME->shmFormat = wlr_output_preferred_read_format(PMONITOR->output); if (PFRAME->shmFormat == DRM_FORMAT_INVALID) { @@ -194,7 +202,7 @@ void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resou PFRAME->dmabufFormat = DRM_FORMAT_INVALID; } - PFRAME->box = { 0, 0, (int)(PWINDOW->m_vRealSize.vec().x * PMONITOR->scale), (int)(PWINDOW->m_vRealSize.vec().y * PMONITOR->scale) }; + PFRAME->box = { 0, 0, (int)(PFRAME->pWindow->m_vRealSize.vec().x * PMONITOR->scale), (int)(PFRAME->pWindow->m_vRealSize.vec().y * PMONITOR->scale) }; int ow, oh; wlr_output_effective_resolution(PMONITOR->output, &ow, &oh); wlr_box_transform(&PFRAME->box, &PFRAME->box, PMONITOR->transform, ow, oh); diff --git a/src/protocols/ToplevelExport.hpp b/src/protocols/ToplevelExport.hpp index bdf5cfd6..eee0e97d 100644 --- a/src/protocols/ToplevelExport.hpp +++ b/src/protocols/ToplevelExport.hpp @@ -1,6 +1,7 @@ #pragma once #include "../defines.hpp" +#include "wlr-foreign-toplevel-management-unstable-v1-protocol.h" #include "hyprland-toplevel-export-v1-protocol.h" #include @@ -45,7 +46,7 @@ public: CToplevelExportProtocolManager(); void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id); - void captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, uint32_t handle); + void captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, CWindow* handle); void removeClient(SToplevelClient* client, bool force = false); void removeFrame(SToplevelFrame* frame, bool force = false); void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage); diff --git a/subprojects/hyprland-protocols b/subprojects/hyprland-protocols index 0dcff94f..301733ae 160000 --- a/subprojects/hyprland-protocols +++ b/subprojects/hyprland-protocols @@ -1 +1 @@ -Subproject commit 0dcff94fc10df2bbb66d3e1b5a1d6cfd3ada5515 +Subproject commit 301733ae466b229066ba15a53e6d8b91c5dcef5b