diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d8feb60..365b08d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -377,6 +377,7 @@ protocolnew("staging/content-type" "content-type-v1" false) protocolnew("staging/color-management" "color-management-v1" false) protocolnew("staging/xdg-toplevel-tag" "xdg-toplevel-tag-v1" false) protocolnew("staging/xdg-system-bell" "xdg-system-bell-v1" false) +protocolnew("staging/ext-workspace" "ext-workspace-v1" false) protocolwayland() diff --git a/protocols/meson.build b/protocols/meson.build index 348ce8a3..a7f72888 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -75,6 +75,7 @@ protocols = [ wayland_protocol_dir / 'staging/color-management/color-management-v1.xml', wayland_protocol_dir / 'staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml', wayland_protocol_dir / 'staging/xdg-system-bell/xdg-system-bell-v1.xml', + wayland_protocol_dir / 'staging/ext-workspace/ext-workspace-v1.xml', ] wl_protocols = [] diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 0ed0b761..64583b1c 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -1972,7 +1972,7 @@ void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitor const auto PWORKSPACEB = pMonitorB->m_activeWorkspace; PWORKSPACEA->m_monitor = pMonitorB; - PWORKSPACEA->moveToMonitor(pMonitorB->m_id); + PWORKSPACEA->m_events.monitorChange.emit(); for (auto const& w : m_windows) { if (w->m_workspace == PWORKSPACEA) { @@ -1997,7 +1997,7 @@ void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitor } PWORKSPACEB->m_monitor = pMonitorA; - PWORKSPACEB->moveToMonitor(pMonitorA->m_id); + PWORKSPACEB->m_events.monitorChange.emit(); for (auto const& w : m_windows) { if (w->m_workspace == PWORKSPACEB) { @@ -2177,7 +2177,7 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo // move the workspace pWorkspace->m_monitor = pMonitor; - pWorkspace->moveToMonitor(pMonitor->m_id); + pWorkspace->m_events.monitorChange.emit(); for (auto const& w : m_windows) { if (w->m_workspace == pWorkspace) { @@ -2221,7 +2221,15 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo pMonitor->setSpecialWorkspace(nullptr); setActiveMonitor(pMonitor); + + auto oldWorkspace = pMonitor->m_activeWorkspace; pMonitor->m_activeWorkspace = pWorkspace; + + if (oldWorkspace) + oldWorkspace->m_events.activeChange.emit(); + + pWorkspace->m_events.activeChange.emit(); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->m_id); pWorkspace->startAnim(true, true, true); diff --git a/src/desktop/Workspace.cpp b/src/desktop/Workspace.cpp index 886c52fb..1e5fbfa6 100644 --- a/src/desktop/Workspace.cpp +++ b/src/desktop/Workspace.cpp @@ -70,6 +70,8 @@ CWorkspace::~CWorkspace() { g_pEventManager->postEvent({.event = "destroyworkspacev2", .data = std::format("{},{}", m_id, m_name)}); EMIT_HOOK_EVENT("destroyWorkspace", this); } + + m_events.destroy.emit(); } void CWorkspace::startAnim(bool in, bool left, bool instant) { @@ -185,14 +187,6 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) { } } -void CWorkspace::setActive(bool on) { - ; // empty until https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/40 -} - -void CWorkspace::moveToMonitor(const MONITORID& id) { - ; // empty until https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/40 -} - PHLWINDOW CWorkspace::getLastFocusedWindow() { if (!validMapped(m_lastFocusedWindow) || m_lastFocusedWindow->workspaceID() != m_id) return nullptr; @@ -650,6 +644,7 @@ void CWorkspace::rename(const std::string& name) { g_pCompositor->ensurePersistentWorkspacesPresent(std::vector{WORKSPACERULE}, m_self.lock()); g_pEventManager->postEvent({.event = "renameworkspace", .data = std::to_string(m_id) + "," + m_name}); + m_events.rename.emit(); } void CWorkspace::updateWindows() { diff --git a/src/desktop/Workspace.hpp b/src/desktop/Workspace.hpp index 4f109257..5bf38489 100644 --- a/src/desktop/Workspace.hpp +++ b/src/desktop/Workspace.hpp @@ -4,6 +4,7 @@ #include #include "DesktopTypes.hpp" #include "../helpers/MiscFunctions.hpp" +#include "../helpers/signal/Signal.hpp" enum eFullscreenMode : int8_t { FSMODE_NONE = 0, @@ -21,6 +22,8 @@ class CWorkspace { CWorkspace(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special = false, bool isEmpty = true); ~CWorkspace(); + WP m_self; + // Workspaces ID-based have IDs > 0 // and workspaces name-based have IDs starting with -1337 WORKSPACEID m_id = WORKSPACE_INVALID; @@ -60,8 +63,6 @@ class CWorkspace { // Inert: destroyed and invalid. If this is true, release the ptr you have. bool inert(); void startAnim(bool in, bool left, bool instant = false); - void setActive(bool on); - void moveToMonitor(const MONITORID&); MONITORID monitorID(); PHLWINDOW getLastFocusedWindow(); void rememberPrevWorkspace(const PHLWORKSPACE& prevWorkspace); @@ -83,6 +84,13 @@ class CWorkspace { void forceReportSizesToWindows(); void updateWindows(); + struct { + CSignal destroy; + CSignal rename; + CSignal monitorChange; + CSignal activeChange; + } m_events; + private: void init(PHLWORKSPACE self); // Previous workspace ID and name is stored during a workspace change, allowing travel @@ -91,7 +99,6 @@ class CWorkspace { SP m_focusedWindowHook; bool m_inert = true; - WP m_self; }; inline bool valid(const PHLWORKSPACE& ref) { diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index db06a49d..580e806a 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1026,7 +1026,7 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) { m_activeWorkspace = PNEWWORKSPACE; - PNEWWORKSPACE->setActive(true); + PNEWWORKSPACE->m_events.activeChange.emit(); PNEWWORKSPACE->m_visible = true; PNEWWORKSPACE->m_lastMonitor = ""; } @@ -1183,11 +1183,14 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo return; const auto POLDWORKSPACE = m_activeWorkspace; - if (POLDWORKSPACE) - POLDWORKSPACE->m_visible = false; - pWorkspace->m_visible = true; + m_activeWorkspace = pWorkspace; - m_activeWorkspace = pWorkspace; + if (POLDWORKSPACE) { + POLDWORKSPACE->m_visible = false; + POLDWORKSPACE->m_events.activeChange.emit(); + } + + pWorkspace->m_visible = true; if (!internal) { const auto ANIMTOLEFT = POLDWORKSPACE && (shouldWraparound(pWorkspace->m_id, POLDWORKSPACE->m_id) ^ (pWorkspace->m_id > POLDWORKSPACE->m_id)); @@ -1230,6 +1233,8 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo EMIT_HOOK_EVENT("workspace", pWorkspace); } + pWorkspace->m_events.activeChange.emit(); + g_pHyprRenderer->damageMonitor(m_self.lock()); g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace); @@ -1250,6 +1255,8 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { if (m_activeSpecialWorkspace == pWorkspace) return; + const auto POLDSPECIAL = m_activeSpecialWorkspace; + m_specialFade->setConfig(g_pConfigManager->getAnimationPropertyConfig(pWorkspace ? "specialWorkspaceIn" : "specialWorkspaceOut")); *m_specialFade = pWorkspace ? 1.F : 0.F; @@ -1265,6 +1272,9 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { } m_activeSpecialWorkspace.reset(); + if (POLDSPECIAL) + POLDSPECIAL->m_events.activeChange.emit(); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_id); if (!(g_pCompositor->m_lastWindow.lock() && g_pCompositor->m_lastWindow->m_pinned && g_pCompositor->m_lastWindow->m_monitor == m_self)) { @@ -1288,7 +1298,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { m_activeSpecialWorkspace->startAnim(false, false); } - bool animate = true; + bool wasActive = false; //close if open elsewhere const auto PMONITORWORKSPACEOWNER = pWorkspace->m_monitor.lock(); if (const auto PMWSOWNER = pWorkspace->m_monitor.lock(); PMWSOWNER && PMWSOWNER->m_activeSpecialWorkspace == pWorkspace) { @@ -1300,14 +1310,24 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { const auto PACTIVEWORKSPACE = PMWSOWNER->m_activeWorkspace; g_pCompositor->updateFullscreenFadeOnWorkspace(PACTIVEWORKSPACE); - animate = false; + wasActive = true; } // open special pWorkspace->m_monitor = m_self; m_activeSpecialWorkspace = pWorkspace; m_activeSpecialWorkspace->m_visible = true; - if (animate) + + if (POLDSPECIAL) + POLDSPECIAL->m_events.activeChange.emit(); + + if (PMONITORWORKSPACEOWNER != m_self) + pWorkspace->m_events.monitorChange.emit(); + + if (!wasActive) + pWorkspace->m_events.activeChange.emit(); + + if (!wasActive) pWorkspace->startAnim(true, true); for (auto const& w : g_pCompositor->m_windows) { diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index f6b94592..e39dcf4e 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -62,6 +62,7 @@ #include "../protocols/ContentType.hpp" #include "../protocols/XDGTag.hpp" #include "../protocols/XDGBell.hpp" +#include "../protocols/ExtWorkspace.hpp" #include "../helpers/Monitor.hpp" #include "../render/Renderer.hpp" @@ -187,6 +188,7 @@ CProtocolManager::CProtocolManager() { PROTO::contentType = makeUnique(&wp_content_type_manager_v1_interface, 1, "ContentType"); PROTO::xdgTag = makeUnique(&xdg_toplevel_tag_manager_v1_interface, 1, "XDGTag"); PROTO::xdgBell = makeUnique(&xdg_system_bell_v1_interface, 1, "XDGBell"); + PROTO::extWorkspace = makeUnique(&ext_workspace_manager_v1_interface, 1, "ExtWorkspace"); if (*PENABLECM) PROTO::colorManagement = makeUnique(&wp_color_manager_v1_interface, 1, "ColorManagement", *PDEBUGCM); @@ -283,6 +285,7 @@ CProtocolManager::~CProtocolManager() { PROTO::frogColorManagement.reset(); PROTO::xdgTag.reset(); PROTO::xdgBell.reset(); + PROTO::extWorkspace.reset(); for (auto& [_, lease] : PROTO::lease) { lease.reset(); diff --git a/src/protocols/ExtWorkspace.cpp b/src/protocols/ExtWorkspace.cpp new file mode 100644 index 00000000..6c55e76e --- /dev/null +++ b/src/protocols/ExtWorkspace.cpp @@ -0,0 +1,331 @@ +#include "ExtWorkspace.hpp" +#include "../Compositor.hpp" +#include "../managers/HookSystemManager.hpp" +#include "../managers/eventLoop/EventLoopManager.hpp" +#include +#include +#include +#include "core/Output.hpp" + +CExtWorkspaceGroupResource::CExtWorkspaceGroupResource(WP manager, UP resource, PHLMONITORREF monitor) : + m_monitor(std::move(monitor)), m_manager(std::move(manager)), m_resource(std::move(resource)) { + if (!good()) + return; + + m_resource->setData(this); + m_manager->m_resource->sendWorkspaceGroup(m_resource.get()); + + m_listeners.destroyed = m_monitor->m_events.destroy.registerListener([this](auto) { m_resource->sendRemoved(); }); + + m_resource->setOnDestroy([this](auto) { PROTO::extWorkspace->destroyGroup(m_self); }); + m_resource->setDestroy([this](auto) { PROTO::extWorkspace->destroyGroup(m_self); }); + + m_resource->sendCapabilities(static_cast(0)); + + const auto& output = PROTO::outputs.at(m_monitor->m_name); + + if (auto resource = output->outputResourceFrom(m_resource->client())) + m_resource->sendOutputEnter(resource->getResource()->resource()); + + m_listeners.outputBound = output->m_events.outputBound.registerListener([this](std::any data) { + auto resource = std::any_cast>(data); + + if (resource->client() == m_resource->client()) + m_resource->sendOutputEnter(resource->getResource()->resource()); + }); + + m_manager->sendGroupToWorkspaces(m_self); + m_manager->scheduleDone(); +} + +bool CExtWorkspaceGroupResource::good() const { + return m_resource; +} + +WP CExtWorkspaceGroupResource::fromResource(wl_resource* resource) { + auto handle = static_cast(wl_resource_get_user_data(resource))->data(); + auto data = static_cast(handle); + return data ? data->m_self : WP(); +} + +void CExtWorkspaceGroupResource::workspaceEnter(const WP& handle) { + m_resource->sendWorkspaceEnter(handle.get()); +} +void CExtWorkspaceGroupResource::workspaceLeave(const WP& handle) { + m_resource->sendWorkspaceLeave(handle.get()); +} + +CExtWorkspaceResource::CExtWorkspaceResource(WP manager, UP resource, PHLWORKSPACEREF workspace) : + m_manager(std::move(manager)), m_resource(std::move(resource)), m_workspace(std::move(workspace)) { + if (!good()) + return; + + m_resource->setData(this); + m_manager->m_resource->sendWorkspace(m_resource.get()); + + m_listeners.destroyed = m_workspace->m_events.destroy.registerListener([this](auto) { + m_resource->sendRemoved(); + + if (m_manager) + m_manager->scheduleDone(); + }); + + m_listeners.activeChanged = m_workspace->m_events.activeChange.registerListener([this](auto) { + sendState(); + sendCapabilities(); + }); + + m_listeners.monitorChanged = m_workspace->m_events.monitorChange.registerListener([this](auto) { this->sendGroup(); }); + + m_listeners.renamed = m_workspace->m_events.rename.registerListener([this](auto) { + m_resource->sendName(m_workspace->m_name.c_str()); + + if (m_manager) + m_manager->scheduleDone(); + }); + + m_resource->setOnDestroy([this](auto) { PROTO::extWorkspace->destroyWorkspace(m_self); }); + m_resource->setDestroy([this](auto) { PROTO::extWorkspace->destroyWorkspace(m_self); }); + + m_resource->setActivate([this](void*) { m_pendingState.activate = true; }); + m_resource->setDeactivate([this](void*) { m_pendingState.deactivate = true; }); + + m_resource->setAssign([this](void*, wl_resource* groupResource) { + auto group = CExtWorkspaceGroupResource::fromResource(groupResource); + + if (group) + m_pendingState.targetMonitor = group->m_monitor; + }); + + m_resource->sendName(m_workspace->m_name.c_str()); + + wl_array coordinates; + wl_array_init(&coordinates); + + auto id = m_workspace->m_id; + if (id < 0 && !m_workspace->m_name.empty()) + id += UINT32_MAX - 1337; + + if (id > 0) + *static_cast(wl_array_add(&coordinates, sizeof(uint32_t))) = id; + + m_resource->sendCoordinates(&coordinates); + wl_array_release(&coordinates); + + sendState(); + sendCapabilities(); + sendGroup(); + + m_manager->scheduleDone(); +} + +bool CExtWorkspaceResource::good() const { + return m_resource; +} + +bool CExtWorkspaceResource::isActive() const { + if (!m_workspace) + return false; + + auto const& monitor = m_workspace->m_monitor; + auto const& cmpWorkspace = m_workspace->m_isSpecialWorkspace ? monitor->m_activeSpecialWorkspace : monitor->m_activeWorkspace; + return m_workspace == cmpWorkspace; +} + +void CExtWorkspaceResource::sendState() { + uint32_t state = 0; + + if (isActive()) + state |= EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE; + + if (m_workspace->hasUrgentWindow()) + state |= EXT_WORKSPACE_HANDLE_V1_STATE_URGENT; + + if (m_workspace->m_isSpecialWorkspace) + state |= EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN; + + m_resource->sendState(static_cast(state)); + + if (m_manager) + m_manager->scheduleDone(); +} + +void CExtWorkspaceResource::sendCapabilities() { + uint32_t capabilities = EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN; + auto active = isActive(); + + if (!active) + capabilities |= EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE; + + if (active && m_workspace->m_isSpecialWorkspace) + capabilities |= EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE; + + m_resource->sendCapabilities(static_cast(capabilities)); + + if (m_manager) + m_manager->scheduleDone(); +} + +void CExtWorkspaceResource::sendGroup() { + if (m_group) + m_group->workspaceLeave(m_resource); + + if (m_manager) { + m_group = m_manager->findGroup(m_workspace->m_monitor); + + if (m_group) + m_group->workspaceEnter(m_resource); + + m_manager->scheduleDone(); + } +} + +void CExtWorkspaceResource::commit() { + // order is important + + if (m_pendingState.deactivate && isActive() && m_workspace->m_isSpecialWorkspace) + m_workspace->m_monitor->setSpecialWorkspace(nullptr); + + if (m_pendingState.targetMonitor && m_workspace && m_workspace->m_monitor != m_pendingState.targetMonitor) + g_pCompositor->moveWorkspaceToMonitor(m_workspace.lock(), m_pendingState.targetMonitor.lock(), true); + + if (m_pendingState.activate && !isActive() && m_workspace) + m_workspace->m_monitor->changeWorkspace(m_workspace.lock()); + + m_pendingState.activate = false; + m_pendingState.deactivate = false; + m_pendingState.targetMonitor.reset(); +} + +CExtWorkspaceManagerResource::CExtWorkspaceManagerResource(UP resource) : m_resource(std::move(resource)) { + if (!good()) + return; + + m_resource->setOnDestroy([this](auto) { PROTO::extWorkspace->destroyManager(m_self); }); + + m_resource->setStop([this](auto) { + m_resource->sendFinished(); + PROTO::extWorkspace->destroyManager(m_self); + }); + + m_resource->setCommit([this](auto) { + for (auto& workspace : PROTO::extWorkspace->m_workspaces) { + if (workspace->m_manager == m_self) + workspace->commit(); + } + }); +} + +void CExtWorkspaceManagerResource::init(WP self) { + if (!good()) + return; + + m_self = self; + + for (auto const& m : g_pCompositor->m_monitors) { + onMonitorCreated(m); + } + + for (auto const& w : g_pCompositor->m_workspaces) { + onWorkspaceCreated(w); + } +} + +bool CExtWorkspaceManagerResource::good() const { + return m_resource; +} + +void CExtWorkspaceManagerResource::scheduleDone() { + if (m_doneScheduled) + return; + + m_doneScheduled = true; + g_pEventLoopManager->doLater([self = m_self] { + if (!self || !self->m_resource) + return; + + self->m_doneScheduled = false; + self->m_resource->sendDone(); + }); +} + +WP CExtWorkspaceManagerResource::findGroup(const PHLMONITORREF& monitor) const { + auto iter = std::ranges::find_if(PROTO::extWorkspace->m_groups, + [&](const UP& resource) { return resource->m_manager.get() == this && resource->m_monitor == monitor; }); + + return iter != PROTO::extWorkspace->m_groups.end() ? *iter : WP(); +} + +void CExtWorkspaceManagerResource::sendGroupToWorkspaces(const WP& group) { + for (auto& workspace : PROTO::extWorkspace->m_workspaces) { + if (workspace->m_manager == m_self && workspace->m_workspace && workspace->m_workspace->m_monitor == group->m_monitor) + workspace->sendGroup(); + } +} + +void CExtWorkspaceManagerResource::onMonitorCreated(const PHLMONITOR& monitor) { + auto& group = PROTO::extWorkspace->m_groups.emplace_back( + makeUnique(m_self, makeUnique(m_resource->client(), m_resource->version(), 0), monitor)); + group->m_self = group; + + if UNLIKELY (!group->good()) { + LOGM(ERR, "Couldn't create a workspace group object"); + wl_client_post_no_memory(m_resource->client()); + return; + } + + scheduleDone(); +} + +void CExtWorkspaceManagerResource::onWorkspaceCreated(const PHLWORKSPACE& workspace) { + auto& ws = PROTO::extWorkspace->m_workspaces.emplace_back( + makeUnique(m_self, makeUnique(m_resource->client(), m_resource->version(), 0), workspace)); + ws->m_self = ws; + + if UNLIKELY (!ws->good()) { + LOGM(ERR, "Couldn't create a workspace object"); + wl_client_post_no_memory(m_resource->client()); + return; + } +} + +CExtWorkspaceProtocol::CExtWorkspaceProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + static auto P1 = g_pHookSystem->hookDynamic("createWorkspace", [this](void* self, SCallbackInfo& info, std::any data) { + auto workspace = std::any_cast(data)->m_self.lock(); + + for (auto const& m : m_managers) { + m->onWorkspaceCreated(workspace); + } + }); + + static auto P2 = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any data) { + auto monitor = std::any_cast(data); + + for (auto const& m : m_managers) { + m->onMonitorCreated(monitor); + } + }); +} + +void CExtWorkspaceProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + auto& manager = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))); + manager->init(manager); + + if UNLIKELY (!manager->good()) { + LOGM(ERR, "Couldn't create a workspace manager"); + wl_client_post_no_memory(client); + return; + } +} + +void CExtWorkspaceProtocol::destroyGroup(const WP& group) { + std::erase_if(m_groups, [&](const UP& resource) { return resource == group; }); +} + +void CExtWorkspaceProtocol::destroyWorkspace(const WP& workspace) { + std::erase_if(m_workspaces, [&](const UP& resource) { return resource == workspace; }); +} + +void CExtWorkspaceProtocol::destroyManager(const WP& manager) { + std::erase_if(PROTO::extWorkspace->m_managers, [&](const UP& resource) { return resource == manager; }); +} diff --git a/src/protocols/ExtWorkspace.hpp b/src/protocols/ExtWorkspace.hpp new file mode 100644 index 00000000..efaac3fe --- /dev/null +++ b/src/protocols/ExtWorkspace.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include "WaylandProtocol.hpp" +#include "../desktop/DesktopTypes.hpp" +#include "ext-workspace-v1.hpp" +#include +#include +#include "../helpers/signal/Signal.hpp" +#include "../helpers/Monitor.hpp" + +class CExtWorkspaceManagerResource; + +class CExtWorkspaceGroupResource { + public: + CExtWorkspaceGroupResource(WP manager, UP resource, PHLMONITORREF monitor); + + static WP fromResource(wl_resource*); + + [[nodiscard]] bool good() const; + + void workspaceEnter(const WP&); + void workspaceLeave(const WP&); + + PHLMONITORREF m_monitor; + + private: + WP m_self; + WP m_manager; + UP m_resource; + + struct { + CHyprSignalListener destroyed; + CHyprSignalListener outputBound; + } m_listeners; + + friend class CExtWorkspaceManagerResource; +}; + +class CExtWorkspaceResource { + public: + CExtWorkspaceResource(WP manager, UP resource, PHLWORKSPACEREF workspace); + + [[nodiscard]] bool good() const; + + void commit(); + + private: + WP m_self; + WP m_manager; + UP m_resource; + WP m_group; + PHLWORKSPACEREF m_workspace; + + [[nodiscard]] bool isActive() const; + + void sendState(); + void sendCapabilities(); + void sendGroup(); + + struct { + bool activate = false; + bool deactivate = false; + PHLMONITORREF targetMonitor; + } m_pendingState; + + struct { + CHyprSignalListener destroyed; + CHyprSignalListener activeChanged; + CHyprSignalListener monitorChanged; + CHyprSignalListener renamed; + } m_listeners; + + friend class CExtWorkspaceManagerResource; +}; + +class CExtWorkspaceManagerResource { + public: + CExtWorkspaceManagerResource(UP resource); + WP m_self; + + void init(WP self); + [[nodiscard]] bool good() const; + + void onMonitorCreated(const PHLMONITOR& monitor); + void onWorkspaceCreated(const PHLWORKSPACE& workspace); + + void scheduleDone(); + [[nodiscard]] WP findGroup(const PHLMONITORREF& monitor) const; + void sendGroupToWorkspaces(const WP& group); + + UP m_resource; + + private: + bool m_doneScheduled = false; +}; + +class CExtWorkspaceProtocol : public IWaylandProtocol { + public: + CExtWorkspaceProtocol(const wl_interface* iface, const int& var, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + void destroyManager(const WP& manager); + void destroyGroup(const WP& group); + void destroyWorkspace(const WP& workspace); + + private: + std::vector> m_managers; + std::vector> m_groups; + std::vector> m_workspaces; + + friend class CExtWorkspaceManagerResource; +}; + +namespace PROTO { + inline UP extWorkspace; +} diff --git a/src/protocols/core/Output.cpp b/src/protocols/core/Output.cpp index 5db2f678..a9d75972 100644 --- a/src/protocols/core/Output.cpp +++ b/src/protocols/core/Output.cpp @@ -107,6 +107,7 @@ void CWLOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver, RESOURCE->m_self = RESOURCE; RESOURCE->m_owner = m_self; + m_events.outputBound.emit(RESOURCE); } void CWLOutputProtocol::destroyResource(CWLOutputResource* resource) { diff --git a/src/protocols/core/Output.hpp b/src/protocols/core/Output.hpp index 9f14289e..50deccd3 100644 --- a/src/protocols/core/Output.hpp +++ b/src/protocols/core/Output.hpp @@ -46,6 +46,10 @@ class CWLOutputProtocol : public IWaylandProtocol { void remove(); bool isDefunct(); // true if above was called + struct { + CSignal outputBound; + } m_events; + private: void destroyResource(CWLOutputResource* resource);