From 512c6fb12717050e8cf58e6ea375595c849ec543 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 8 Jan 2024 17:10:13 -0300 Subject: [PATCH 1/5] feat: add orphan windows attribute to workspaces this attribute will keep every window that doesn't have an associated workspace in the current bar --- include/modules/hyprland/workspaces.hpp | 8 +++++ src/modules/hyprland/workspaces.cpp | 48 +++++++++++++++++++++---- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 0109149e..d05a52fa 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -163,10 +163,18 @@ class Workspaces : public AModule, public EventHandler { void doUpdate(); + void extendOrphans(int workspaceId, Json::Value const& clientsJson); + void registerOrphanWindow(WindowCreationPayload create_window_paylod); + bool m_allOutputs = false; bool m_showSpecial = false; bool m_activeOnly = false; + // Map for windows stored in workspaces not present in the current bar. + // This happens when the user has multiple monitors (hence, multiple bars) + // and doesn't share windows accross bars (a.k.a `all-outputs` = false) + std::map m_orphanWindowMap; + enum class SortMethod { ID, NAME, NUMBER, DEFAULT }; util::EnumParser m_enumParser; SortMethod m_sortBy = SortMethod::DEFAULT; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3d8a5932..eb624a10 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -128,6 +128,12 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); } +void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_paylod) { + if (!create_window_paylod.isEmpty(*this)) { + m_orphanWindowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(*this); + } +} + auto Workspaces::registerIpc() -> void { gIPC->registerForIPC("workspace", this); gIPC->registerForIPC("createworkspace", this); @@ -215,6 +221,8 @@ void Workspaces::doUpdate() { static auto const WINDOW_CREATION_TIMEOUT = 2; if (windowPayload.incrementTimeSpentUncreated() < WINDOW_CREATION_TIMEOUT) { notCreated.push_back(windowPayload); + } else { + registerOrphanWindow(windowPayload); } } } @@ -402,18 +410,35 @@ void Workspaces::onWindowMoved(std::string const &payload) { } } - // ...and add it to the new workspace + // ...if it was empty, check if the window is an orphan... + if (windowRepr.empty() && m_orphanWindowMap.contains(windowAddress)) { + windowRepr = m_orphanWindowMap[windowAddress]; + } + + // ...and then add it to the new workspace if (!windowRepr.empty()) { m_windowsToCreate.emplace_back(workspaceName, windowAddress, windowRepr); } } void Workspaces::onWindowTitleEvent(std::string const &payload) { - auto windowWorkspace = - std::find_if(m_workspaces.begin(), m_workspaces.end(), - [payload](auto &workspace) { return workspace->containsWindow(payload); }); + std::optional> inserter; - if (windowWorkspace != m_workspaces.end()) { + if (m_orphanWindowMap.contains(payload)) { + inserter = [this](WindowCreationPayload wcp) { this->registerOrphanWindow(std::move(wcp)); }; + } else { + auto windowWorkspace = + std::find_if(m_workspaces.begin(), m_workspaces.end(), + [payload](auto &workspace) { return workspace->containsWindow(payload); }); + + if (windowWorkspace != m_workspaces.end()) { + inserter = [windowWorkspace](WindowCreationPayload wcp) { + (*windowWorkspace)->insertWindow(std::move(wcp)); + }; + } + } + + if (inserter.has_value()) { Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); std::string jsonWindowAddress = fmt::format("0x{}", payload); @@ -423,7 +448,7 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { }); if (!client->empty()) { - (*windowWorkspace)->insertWindow({*client}); + (*inserter)({*client}); } } } @@ -605,6 +630,14 @@ void Workspaces::createPersistentWorkspaces() { } } +void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) { + for (const auto &client : clientsJson) { + if (client["workspace"]["id"].asInt() == workspaceId) { + registerOrphanWindow({client}); + } + } +} + void Workspaces::init() { m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString(); @@ -625,10 +658,13 @@ void Workspaces::init() { for (Json::Value workspaceJson : workspacesJson) { std::string workspaceName = workspaceJson["name"].asString(); + spdlog::info("initing workpsace {}:{}", workspaceJson["id"], workspaceJson["name"]); if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && (!workspaceName.starts_with("special") || showSpecial()) && !isWorkspaceIgnored(workspaceName)) { createWorkspace(workspaceJson, clientsJson); + } else { + extendOrphans(workspaceJson["id"].asInt(), clientsJson); } } From bc7acbde5c5f85382a7055eaf6b39a3d9d8af7a7 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 8 Jan 2024 18:18:11 -0300 Subject: [PATCH 2/5] fix: rename windows while queued for creation this avoids the window arriving with the wrong icon when its eventually able to be created --- src/modules/hyprland/workspaces.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index eb624a10..e7ed064b 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -424,6 +424,7 @@ void Workspaces::onWindowMoved(std::string const &payload) { void Workspaces::onWindowTitleEvent(std::string const &payload) { std::optional> inserter; + // If the window was an orphan, rename it at the orphan's vector if (m_orphanWindowMap.contains(payload)) { inserter = [this](WindowCreationPayload wcp) { this->registerOrphanWindow(std::move(wcp)); }; } else { @@ -431,10 +432,21 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) { std::find_if(m_workspaces.begin(), m_workspaces.end(), [payload](auto &workspace) { return workspace->containsWindow(payload); }); + // If the window exists on a workspace, rename it at the workspace's window + // map if (windowWorkspace != m_workspaces.end()) { inserter = [windowWorkspace](WindowCreationPayload wcp) { (*windowWorkspace)->insertWindow(std::move(wcp)); }; + } else { + auto queuedWindow = std::find_if( + m_windowsToCreate.begin(), m_windowsToCreate.end(), + [payload](auto &windowPayload) { return windowPayload.getAddress() == payload; }); + + // If the window was queued, rename it in the queue + if (queuedWindow != m_windowsToCreate.end()) { + inserter = [queuedWindow](WindowCreationPayload wcp) { *queuedWindow = std::move(wcp); }; + } } } From 4339030c9d31f78fe5f14eee832c8e80c9f4f967 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 8 Jan 2024 20:11:39 -0300 Subject: [PATCH 3/5] feat: fetch clients data when moving workspaces accross monitors --- include/modules/hyprland/workspaces.hpp | 5 ++-- src/modules/hyprland/workspaces.cpp | 36 +++++++++++-------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index d05a52fa..d2006fcc 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -145,7 +145,8 @@ class Workspaces : public AModule, public EventHandler { // workspace events void onWorkspaceActivated(std::string const& payload); void onWorkspaceDestroyed(std::string const& payload); - void onWorkspaceCreated(std::string const& payload); + void onWorkspaceCreated(std::string const& workspaceName, + Json::Value const& clientsData = Json::Value::nullRef); void onWorkspaceMoved(std::string const& payload); void onWorkspaceRenamed(std::string const& payload); @@ -199,7 +200,7 @@ class Workspaces : public AModule, public EventHandler { uint64_t m_monitorId; std::string m_activeWorkspaceName; std::vector> m_workspaces; - std::vector m_workspacesToCreate; + std::vector> m_workspacesToCreate; std::vector m_workspacesToRemove; std::vector m_windowsToCreate; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index e7ed064b..3f85b975 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -170,8 +170,8 @@ void Workspaces::doUpdate() { m_workspacesToRemove.clear(); // add workspaces that wait to be created - for (auto &elem : m_workspacesToCreate) { - createWorkspace(elem); + for (auto &[workspaceData, clientsData] : m_workspacesToCreate) { + createWorkspace(workspaceData, clientsData); } m_workspacesToCreate.clear(); @@ -301,16 +301,17 @@ void Workspaces::onWorkspaceDestroyed(std::string const &payload) { } } -void Workspaces::onWorkspaceCreated(std::string const &payload) { +void Workspaces::onWorkspaceCreated(std::string const &workspaceName, + Json::Value const &clientsData) { const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - if (!isWorkspaceIgnored(payload)) { + if (!isWorkspaceIgnored(workspaceName)) { for (Json::Value workspaceJson : workspacesJson) { std::string name = workspaceJson["name"].asString(); - if (name == payload && + if (name == workspaceName && (allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && - (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(payload)) { - m_workspacesToCreate.push_back(workspaceJson); + (showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) { + m_workspacesToCreate.emplace_back(workspaceJson, clientsData); break; } } @@ -318,20 +319,14 @@ void Workspaces::onWorkspaceCreated(std::string const &payload) { } void Workspaces::onWorkspaceMoved(std::string const &payload) { - std::string workspace = payload.substr(0, payload.find(',')); - std::string newOutput = payload.substr(payload.find(',') + 1); - bool shouldShow = showSpecial() || !workspace.starts_with("special"); - if (shouldShow && m_bar.output->name == newOutput) { // TODO: implement this better - const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces"); - for (Json::Value workspaceJson : workspacesJson) { - std::string name = workspaceJson["name"].asString(); - if (name == workspace && m_bar.output->name == workspaceJson["monitor"].asString()) { - m_workspacesToCreate.push_back(workspaceJson); - break; - } - } + std::string workspaceName = payload.substr(0, payload.find(',')); + std::string monitorName = payload.substr(payload.find(',') + 1); + + if (m_bar.output->name == monitorName) { + Json::Value clientsData = gIPC->getSocket1JsonReply("clients"); + onWorkspaceCreated(workspaceName, clientsData); } else { - m_workspacesToRemove.push_back(workspace); + onWorkspaceDestroyed(workspaceName); } } @@ -670,7 +665,6 @@ void Workspaces::init() { for (Json::Value workspaceJson : workspacesJson) { std::string workspaceName = workspaceJson["name"].asString(); - spdlog::info("initing workpsace {}:{}", workspaceJson["id"], workspaceJson["name"]); if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) && (!workspaceName.starts_with("special") || showSpecial()) && !isWorkspaceIgnored(workspaceName)) { From c69a6dde67758441ff0c25ff0efca57f22d2247a Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Mon, 8 Jan 2024 20:31:15 -0300 Subject: [PATCH 4/5] chore: update Hyprland's featureset in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3441ff8c..07b11152 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ #### Current features - Sway (Workspaces, Binding mode, Focused window name) - River (Mapping mode, Tags, Focused window name) -- Hyprland (Focused window name) +- Hyprland (Window Icons, Workspaces, Focused window name) - DWL (Tags) [requires dwl ipc patch](https://github.com/djpohly/dwl/wiki/ipc) - Tray [#21](https://github.com/Alexays/Waybar/issues/21) - Local time From 9e08512927d2de34a55281ee7fc3f13c36c6c9c5 Mon Sep 17 00:00:00 2001 From: Brenno Lemos Date: Wed, 10 Jan 2024 02:24:51 -0300 Subject: [PATCH 5/5] feat: strip workspace qualifiers when creating windows --- src/modules/hyprland/workspaces.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3f85b975..b05ce134 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -1023,6 +1023,11 @@ void WindowCreationPayload::clearWorkspaceName() { m_workspaceName = m_workspaceName.substr( SPECIAL_QUALIFIER_PREFIX_LEN, m_workspaceName.length() - SPECIAL_QUALIFIER_PREFIX_LEN); } + + std::size_t spaceFound = m_workspaceName.find(' '); + if (spaceFound != std::string::npos) { + m_workspaceName.erase(m_workspaceName.begin() + spaceFound, m_workspaceName.end()); + } } void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) {