diff options
| author | outfoxxed <outfoxxed@outfoxxed.me> | 2025-06-26 09:32:44 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-26 18:32:44 +0200 |
| commit | 3bbdf9dc5ad2851f5d9f317362fc16705e4fd990 (patch) | |
| tree | 025d7a25435925b235f2e94d18e31931b622c0c0 /src/protocols/ExtWorkspace.cpp | |
| parent | 1f337a7a5e22ec45a77e275ce2e6ca165b298358 (diff) | |
protocols: add ext-workspace implementation (#10818)
Diffstat (limited to 'src/protocols/ExtWorkspace.cpp')
| -rw-r--r-- | src/protocols/ExtWorkspace.cpp | 331 |
1 files changed, 331 insertions, 0 deletions
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 <algorithm> +#include <any> +#include <utility> +#include "core/Output.hpp" + +CExtWorkspaceGroupResource::CExtWorkspaceGroupResource(WP<CExtWorkspaceManagerResource> manager, UP<CExtWorkspaceGroupHandleV1> 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<extWorkspaceGroupHandleV1GroupCapabilities>(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<SP<CWLOutputResource>>(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> CExtWorkspaceGroupResource::fromResource(wl_resource* resource) { + auto handle = static_cast<CExtWorkspaceGroupHandleV1*>(wl_resource_get_user_data(resource))->data(); + auto data = static_cast<CExtWorkspaceGroupResource*>(handle); + return data ? data->m_self : WP<CExtWorkspaceGroupResource>(); +} + +void CExtWorkspaceGroupResource::workspaceEnter(const WP<CExtWorkspaceHandleV1>& handle) { + m_resource->sendWorkspaceEnter(handle.get()); +} +void CExtWorkspaceGroupResource::workspaceLeave(const WP<CExtWorkspaceHandleV1>& handle) { + m_resource->sendWorkspaceLeave(handle.get()); +} + +CExtWorkspaceResource::CExtWorkspaceResource(WP<CExtWorkspaceManagerResource> manager, UP<CExtWorkspaceHandleV1> 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<uint32_t*>(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<extWorkspaceHandleV1State>(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<extWorkspaceHandleV1WorkspaceCapabilities>(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<CExtWorkspaceManagerV1> 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<CExtWorkspaceManagerResource> 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<CExtWorkspaceGroupResource> CExtWorkspaceManagerResource::findGroup(const PHLMONITORREF& monitor) const { + auto iter = std::ranges::find_if(PROTO::extWorkspace->m_groups, + [&](const UP<CExtWorkspaceGroupResource>& resource) { return resource->m_manager.get() == this && resource->m_monitor == monitor; }); + + return iter != PROTO::extWorkspace->m_groups.end() ? *iter : WP<CExtWorkspaceGroupResource>(); +} + +void CExtWorkspaceManagerResource::sendGroupToWorkspaces(const WP<CExtWorkspaceGroupResource>& 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<CExtWorkspaceGroupResource>(m_self, makeUnique<CExtWorkspaceGroupHandleV1>(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<CExtWorkspaceResource>(m_self, makeUnique<CExtWorkspaceHandleV1>(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<CWorkspace*>(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<PHLMONITOR>(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<CExtWorkspaceManagerResource>(makeUnique<CExtWorkspaceManagerV1>(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<CExtWorkspaceGroupResource>& group) { + std::erase_if(m_groups, [&](const UP<CExtWorkspaceGroupResource>& resource) { return resource == group; }); +} + +void CExtWorkspaceProtocol::destroyWorkspace(const WP<CExtWorkspaceResource>& workspace) { + std::erase_if(m_workspaces, [&](const UP<CExtWorkspaceResource>& resource) { return resource == workspace; }); +} + +void CExtWorkspaceProtocol::destroyManager(const WP<CExtWorkspaceManagerResource>& manager) { + std::erase_if(PROTO::extWorkspace->m_managers, [&](const UP<CExtWorkspaceManagerResource>& resource) { return resource == manager; }); +} |
