desktop/workspaceHistory: fix tracking for multiple monitors (#12979)

This commit is contained in:
Vaxry 2026-01-15 17:00:47 +01:00 committed by GitHub
parent e0cf88809d
commit ac9df44788
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 110 additions and 50 deletions

View file

@ -193,6 +193,68 @@ static bool testAsymmetricGaps() {
return true; 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() { static bool test() {
NLog::log("{}Testing workspaces", Colors::GREEN); NLog::log("{}Testing workspaces", Colors::GREEN);
@ -527,13 +589,15 @@ static bool test() {
EXPECT_CONTAINS(str, "class: kitty_B"); EXPECT_CONTAINS(str, "class: kitty_B");
} }
// destroy the headless output
OK(getFromSocket("/output remove HEADLESS-3"));
// kill all // kill all
NLog::log("{}Killing all windows", Colors::YELLOW); NLog::log("{}Killing all windows", Colors::YELLOW);
Tests::killAllWindows(); Tests::killAllWindows();
testMultimonBAF();
// destroy the headless output
OK(getFromSocket("/output remove HEADLESS-3"));
testSpecialWorkspaceFullscreen(); testSpecialWorkspaceFullscreen();
testAsymmetricGaps(); testAsymmetricGaps();

View file

@ -2,9 +2,13 @@
#include "../../helpers/Monitor.hpp" #include "../../helpers/Monitor.hpp"
#include "../Workspace.hpp" #include "../Workspace.hpp"
#include "../state/FocusState.hpp"
#include "../../managers/HookSystemManager.hpp" #include "../../managers/HookSystemManager.hpp"
#include "../../managers/eventLoop/EventLoopManager.hpp"
#include "../../config/ConfigValue.hpp" #include "../../config/ConfigValue.hpp"
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Desktop; using namespace Desktop;
using namespace Desktop::History; using namespace Desktop::History;
@ -19,22 +23,16 @@ CWorkspaceHistoryTracker::CWorkspaceHistoryTracker() {
track(workspace); 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); auto mon = std::any_cast<PHLMONITOR>(data);
track(mon);
});
}
CWorkspaceHistoryTracker::SMonitorData& CWorkspaceHistoryTracker::dataFor(PHLMONITOR 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
for (auto& ref : m_monitorDatas) { // want to remember the workspace that was not visible there
if (ref.monitor != mon) // TODO: do something about this
continue; g_pEventLoopManager->doLater([this, mon = PHLMONITORREF{mon}] {
if (mon)
return ref; track(mon->m_activeWorkspace);
} });
return m_monitorDatas.emplace_back(SMonitorData{
.monitor = mon,
}); });
} }
@ -52,44 +50,32 @@ CWorkspaceHistoryTracker::SWorkspacePreviousData& CWorkspaceHistoryTracker::data
} }
void CWorkspaceHistoryTracker::track(PHLWORKSPACE w) { void CWorkspaceHistoryTracker::track(PHLWORKSPACE w) {
if (!w->m_monitor) if (!w || !w->m_monitor || w == m_lastWorkspaceData.workspace)
return; 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& data = dataFor(w);
auto& monData = dataFor(w->m_monitor.lock());
if (!monData.workspace) { Hyprutils::Utils::CScopeGuard x([&] { setLastWorkspaceData(w); });
data.previous.reset();
data.previousID = WORKSPACE_INVALID; if (m_lastWorkspaceData.workspace == w && !*PALLOWWORKSPACECYCLES)
data.previousName = "";
return; return;
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;
} }
if (monData.workspace == w && !*PALLOWWORKSPACECYCLES) {
track(w->m_monitor.lock());
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();
} }
void CWorkspaceHistoryTracker::gc() { void CWorkspaceHistoryTracker::gc() {
std::erase_if(m_datas, [](const auto& e) { return !e.workspace; }); 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) { 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}; 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;
}

View file

@ -32,21 +32,19 @@ namespace Desktop::History {
SWorkspaceIDName previousWorkspaceIDName(PHLWORKSPACE ws, PHLMONITOR restrict); SWorkspaceIDName previousWorkspaceIDName(PHLWORKSPACE ws, PHLMONITOR restrict);
private: private:
struct SMonitorData { struct SLastWorkspaceData {
PHLMONITORREF monitor; PHLMONITORREF monitor;
PHLWORKSPACEREF workspace; PHLWORKSPACEREF workspace;
std::string workspaceName = ""; std::string workspaceName = "";
WORKSPACEID workspaceID = WORKSPACE_INVALID; WORKSPACEID workspaceID = WORKSPACE_INVALID;
}; } m_lastWorkspaceData;
std::vector<SWorkspacePreviousData> m_datas; std::vector<SWorkspacePreviousData> m_datas;
std::vector<SMonitorData> m_monitorDatas;
void track(PHLWORKSPACE w); void track(PHLWORKSPACE w);
void track(PHLMONITOR mon);
void gc(); void gc();
void setLastWorkspaceData(PHLWORKSPACE w);
SMonitorData& dataFor(PHLMONITOR mon);
SWorkspacePreviousData& dataFor(PHLWORKSPACE ws); SWorkspacePreviousData& dataFor(PHLWORKSPACE ws);
}; };