summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2026-01-15 17:00:47 +0100
committerGitHub <noreply@github.com>2026-01-15 17:00:47 +0100
commitac9df44788492fd1d12da8ec0fbbf691386c45a4 (patch)
tree8b655b6e47f26bccc5d4b42e2aa8c53dcc0ce01e
parente0cf88809de12c39ad8a1ad1c0194967b0029ec8 (diff)
desktop/workspaceHistory: fix tracking for multiple monitors (#12979)
-rw-r--r--hyprtester/src/tests/main/workspaces.cpp70
-rw-r--r--src/desktop/history/WorkspaceHistoryTracker.cpp80
-rw-r--r--src/desktop/history/WorkspaceHistoryTracker.hpp8
3 files changed, 109 insertions, 49 deletions
diff --git a/hyprtester/src/tests/main/workspaces.cpp b/hyprtester/src/tests/main/workspaces.cpp
index 036ddaf5..a126d1b2 100644
--- a/hyprtester/src/tests/main/workspaces.cpp
+++ b/hyprtester/src/tests/main/workspaces.cpp
@@ -193,6 +193,68 @@ static bool testAsymmetricGaps() {
return true;
}
+static void testMultimonBAF() {
+ NLog::log("{}Testing multimon back and forth", Colors::YELLOW);
+
+ OK(getFromSocket("/keyword binds:workspace_back_and_forth 1"));
+
+ OK(getFromSocket("/dispatch focusmonitor HEADLESS-2"));
+ OK(getFromSocket("/dispatch workspace 1"));
+
+ Tests::spawnKitty();
+
+ OK(getFromSocket("/dispatch workspace 2"));
+
+ Tests::spawnKitty();
+
+ OK(getFromSocket("/dispatch focusmonitor HEADLESS-3"));
+ OK(getFromSocket("/dispatch workspace 3"));
+
+ Tests::spawnKitty();
+
+ OK(getFromSocket("/dispatch workspace 3"));
+
+ {
+ auto str = getFromSocket("/activeworkspace");
+ EXPECT_CONTAINS(str, "workspace ID 2 ");
+ }
+
+ OK(getFromSocket("/dispatch workspace 4"));
+ OK(getFromSocket("/dispatch workspace 4"));
+
+ {
+ auto str = getFromSocket("/activeworkspace");
+ EXPECT_CONTAINS(str, "workspace ID 2 ");
+ }
+
+ OK(getFromSocket("/dispatch workspace 2"));
+
+ {
+ auto str = getFromSocket("/activeworkspace");
+ EXPECT_CONTAINS(str, "workspace ID 4 ");
+ }
+
+ OK(getFromSocket("/dispatch workspace 3"));
+ OK(getFromSocket("/dispatch workspace 3"));
+
+ {
+ auto str = getFromSocket("/activeworkspace");
+ EXPECT_CONTAINS(str, "workspace ID 4 ");
+ }
+
+ OK(getFromSocket("/dispatch workspace 2"));
+ OK(getFromSocket("/dispatch workspace 3"));
+ OK(getFromSocket("/dispatch workspace 1"));
+ OK(getFromSocket("/dispatch workspace 1"));
+
+ {
+ auto str = getFromSocket("/activeworkspace");
+ EXPECT_CONTAINS(str, "workspace ID 3 ");
+ }
+
+ Tests::killAllWindows();
+}
+
static bool test() {
NLog::log("{}Testing workspaces", Colors::GREEN);
@@ -527,13 +589,15 @@ static bool test() {
EXPECT_CONTAINS(str, "class: kitty_B");
}
- // destroy the headless output
- OK(getFromSocket("/output remove HEADLESS-3"));
-
// kill all
NLog::log("{}Killing all windows", Colors::YELLOW);
Tests::killAllWindows();
+ testMultimonBAF();
+
+ // destroy the headless output
+ OK(getFromSocket("/output remove HEADLESS-3"));
+
testSpecialWorkspaceFullscreen();
testAsymmetricGaps();
diff --git a/src/desktop/history/WorkspaceHistoryTracker.cpp b/src/desktop/history/WorkspaceHistoryTracker.cpp
index bfedda13..0b4ef2fd 100644
--- a/src/desktop/history/WorkspaceHistoryTracker.cpp
+++ b/src/desktop/history/WorkspaceHistoryTracker.cpp
@@ -2,9 +2,13 @@
#include "../../helpers/Monitor.hpp"
#include "../Workspace.hpp"
+#include "../state/FocusState.hpp"
#include "../../managers/HookSystemManager.hpp"
+#include "../../managers/eventLoop/EventLoopManager.hpp"
#include "../../config/ConfigValue.hpp"
+#include <hyprutils/utils/ScopeGuard.hpp>
+
using namespace Desktop;
using namespace Desktop::History;
@@ -19,22 +23,16 @@ CWorkspaceHistoryTracker::CWorkspaceHistoryTracker() {
track(workspace);
});
- static auto P1 = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any data) {
+ static auto P1 = g_pHookSystem->hookDynamic("focusedMon", [this](void* self, SCallbackInfo& info, std::any data) {
auto mon = std::any_cast<PHLMONITOR>(data);
- track(mon);
- });
-}
-
-CWorkspaceHistoryTracker::SMonitorData& CWorkspaceHistoryTracker::dataFor(PHLMONITOR mon) {
- for (auto& ref : m_monitorDatas) {
- if (ref.monitor != mon)
- continue;
- return ref;
- }
-
- return m_monitorDatas.emplace_back(SMonitorData{
- .monitor = mon,
+ // This sucks ASS, but we have to do this because switching to a workspace on another mon will trigger a workspace event right afterwards and we don't
+ // want to remember the workspace that was not visible there
+ // TODO: do something about this
+ g_pEventLoopManager->doLater([this, mon = PHLMONITORREF{mon}] {
+ if (mon)
+ track(mon->m_activeWorkspace);
+ });
});
}
@@ -52,44 +50,32 @@ CWorkspaceHistoryTracker::SWorkspacePreviousData& CWorkspaceHistoryTracker::data
}
void CWorkspaceHistoryTracker::track(PHLWORKSPACE w) {
- if (!w->m_monitor)
+ if (!w || !w->m_monitor || w == m_lastWorkspaceData.workspace)
return;
- static auto PALLOWWORKSPACECYCLES = CConfigValue<Hyprlang::INT>("binds:allow_workspace_cycles");
+ static auto PALLOWWORKSPACECYCLES = CConfigValue<Hyprlang::INT>("binds:allow_workspace_cycles");
- auto& data = dataFor(w);
- auto& monData = dataFor(w->m_monitor.lock());
+ auto& data = dataFor(w);
- if (!monData.workspace) {
- data.previous.reset();
- data.previousID = WORKSPACE_INVALID;
- data.previousName = "";
- return;
- }
+ Hyprutils::Utils::CScopeGuard x([&] { setLastWorkspaceData(w); });
- if (monData.workspace == w && !*PALLOWWORKSPACECYCLES) {
- track(w->m_monitor.lock());
+ if (m_lastWorkspaceData.workspace == w && !*PALLOWWORKSPACECYCLES)
return;
- }
-
- data.previous = monData.workspace;
- data.previousName = monData.workspace->m_name;
- data.previousID = monData.workspace->m_id;
- data.previousMon = monData.workspace->m_monitor;
- track(w->m_monitor.lock());
-}
-
-void CWorkspaceHistoryTracker::track(PHLMONITOR mon) {
- auto& data = dataFor(mon);
- data.workspace = mon->m_activeWorkspace;
- data.workspaceName = mon->m_activeWorkspace ? mon->m_activeWorkspace->m_name : "";
- data.workspaceID = mon->activeWorkspaceID();
+ data.previous = m_lastWorkspaceData.workspace;
+ if (m_lastWorkspaceData.workspace) {
+ data.previousName = m_lastWorkspaceData.workspace->m_name;
+ data.previousID = m_lastWorkspaceData.workspace->m_id;
+ data.previousMon = m_lastWorkspaceData.workspace->m_monitor;
+ } else {
+ data.previousName = m_lastWorkspaceData.workspaceName;
+ data.previousID = m_lastWorkspaceData.workspaceID;
+ data.previousMon = m_lastWorkspaceData.monitor;
+ }
}
void CWorkspaceHistoryTracker::gc() {
std::erase_if(m_datas, [](const auto& e) { return !e.workspace; });
- std::erase_if(m_monitorDatas, [](const auto& e) { return !e.monitor; });
}
const CWorkspaceHistoryTracker::SWorkspacePreviousData* CWorkspaceHistoryTracker::previousWorkspace(PHLWORKSPACE ws) {
@@ -156,3 +142,15 @@ SWorkspaceIDName CWorkspaceHistoryTracker::previousWorkspaceIDName(PHLWORKSPACE
return SWorkspaceIDName{.id = DATA->previousID, .name = DATA->previousName, .isAutoIDd = DATA->previousID <= 0};
}
+
+void CWorkspaceHistoryTracker::setLastWorkspaceData(PHLWORKSPACE w) {
+ if (!w) {
+ m_lastWorkspaceData = {};
+ return;
+ }
+
+ m_lastWorkspaceData.workspace = w;
+ m_lastWorkspaceData.workspaceID = w->m_id;
+ m_lastWorkspaceData.workspaceName = w->m_name;
+ m_lastWorkspaceData.monitor = w->m_monitor;
+}
diff --git a/src/desktop/history/WorkspaceHistoryTracker.hpp b/src/desktop/history/WorkspaceHistoryTracker.hpp
index 4a3c109a..baecb363 100644
--- a/src/desktop/history/WorkspaceHistoryTracker.hpp
+++ b/src/desktop/history/WorkspaceHistoryTracker.hpp
@@ -32,21 +32,19 @@ namespace Desktop::History {
SWorkspaceIDName previousWorkspaceIDName(PHLWORKSPACE ws, PHLMONITOR restrict);
private:
- struct SMonitorData {
+ struct SLastWorkspaceData {
PHLMONITORREF monitor;
PHLWORKSPACEREF workspace;
std::string workspaceName = "";
WORKSPACEID workspaceID = WORKSPACE_INVALID;
- };
+ } m_lastWorkspaceData;
std::vector<SWorkspacePreviousData> m_datas;
- std::vector<SMonitorData> m_monitorDatas;
void track(PHLWORKSPACE w);
- void track(PHLMONITOR mon);
void gc();
+ void setLastWorkspaceData(PHLWORKSPACE w);
- SMonitorData& dataFor(PHLMONITOR mon);
SWorkspacePreviousData& dataFor(PHLWORKSPACE ws);
};