mirror of
https://github.com/hyprwm/Hyprland
synced 2026-05-07 11:38:01 +02:00
desktop/workspaceHistory: small refactor to work better with multi monitor setups (#13632)
This commit is contained in:
parent
4c29b9de4e
commit
b0f6ac23b2
3 changed files with 95 additions and 87 deletions
|
|
@ -193,6 +193,35 @@ static bool testAsymmetricGaps() {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void testWorkspaceHistoryMultiMon() {
|
||||
NLog::log("{}Testing multimon workspace history tracker", Colors::YELLOW);
|
||||
|
||||
// Initial state:
|
||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-2"));
|
||||
OK(getFromSocket("/dispatch workspace 10"));
|
||||
Tests::spawnKitty();
|
||||
OK(getFromSocket("/dispatch workspace 11"));
|
||||
Tests::spawnKitty();
|
||||
|
||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-3"));
|
||||
OK(getFromSocket("/dispatch workspace 12"));
|
||||
Tests::spawnKitty();
|
||||
|
||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-2"));
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_CONTAINS(str, "workspace ID 11");
|
||||
}
|
||||
OK(getFromSocket("/dispatch workspace previous_per_monitor"));
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_CONTAINS(str, "workspace ID 10");
|
||||
}
|
||||
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static void testMultimonBAF() {
|
||||
NLog::log("{}Testing multimon back and forth", Colors::YELLOW);
|
||||
|
||||
|
|
@ -733,6 +762,7 @@ static bool test() {
|
|||
|
||||
testMultimonBAF();
|
||||
testMultimonFocus();
|
||||
testWorkspaceHistoryMultiMon();
|
||||
|
||||
// destroy the headless output
|
||||
OK(getFromSocket("/output remove HEADLESS-3"));
|
||||
|
|
|
|||
|
|
@ -31,111 +31,92 @@ CWorkspaceHistoryTracker::CWorkspaceHistoryTracker() {
|
|||
});
|
||||
}
|
||||
|
||||
CWorkspaceHistoryTracker::SWorkspacePreviousData& CWorkspaceHistoryTracker::dataFor(PHLWORKSPACE ws) {
|
||||
for (auto& ref : m_datas) {
|
||||
if (ref.workspace != ws)
|
||||
continue;
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
return m_datas.emplace_back(SWorkspacePreviousData{
|
||||
.workspace = ws,
|
||||
});
|
||||
}
|
||||
|
||||
void CWorkspaceHistoryTracker::track(PHLWORKSPACE w) {
|
||||
if (!w || !w->m_monitor || w == m_lastWorkspaceData.workspace)
|
||||
void CWorkspaceHistoryTracker::track(PHLWORKSPACE ws) {
|
||||
if (!ws || !ws->m_monitor)
|
||||
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);
|
||||
|
||||
Hyprutils::Utils::CScopeGuard x([&] { setLastWorkspaceData(w); });
|
||||
|
||||
if (m_lastWorkspaceData.workspace == w && !*PALLOWWORKSPACECYCLES)
|
||||
if (!m_history.empty() && m_history.front().workspace == ws && !*PALLOWWORKSPACECYCLES)
|
||||
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;
|
||||
}
|
||||
// Erase from timeline if it exists so we can move it to the very front
|
||||
std::erase_if(m_history, [&](const auto& entry) { return entry.workspace == ws; });
|
||||
|
||||
// Push the newly focused workspace to the top of our MRU list
|
||||
m_history.push_front(SHistoryEntry{.workspace = ws, .monitor = ws->m_monitor, .name = ws->m_name, .id = ws->m_id});
|
||||
|
||||
Hyprutils::Utils::CScopeGuard x([&] { setLastWorkspaceData(ws); });
|
||||
}
|
||||
|
||||
void CWorkspaceHistoryTracker::gc() {
|
||||
std::erase_if(m_datas, [](const auto& e) { return !e.workspace; });
|
||||
std::vector<PHLMONITORREF> monitorCounts;
|
||||
std::erase_if(m_history, [&](const auto& entry) {
|
||||
// Search if the monitor has been seen already
|
||||
for (auto& mon : monitorCounts | std::views::drop(1)) {
|
||||
// Remove entry
|
||||
if (mon == entry.monitor)
|
||||
return !entry.workspace;
|
||||
}
|
||||
// Add monitor to seen monitors
|
||||
monitorCounts.emplace_back(entry.monitor);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
const CWorkspaceHistoryTracker::SWorkspacePreviousData* CWorkspaceHistoryTracker::previousWorkspace(PHLWORKSPACE ws) {
|
||||
const CWorkspaceHistoryTracker::SHistoryEntry CWorkspaceHistoryTracker::previousWorkspace(PHLWORKSPACE ws) {
|
||||
gc();
|
||||
auto it = std::ranges::find_if(m_history, [&](const auto& entry) { return entry.workspace == ws; });
|
||||
|
||||
for (const auto& d : m_datas) {
|
||||
if (d.workspace != ws)
|
||||
continue;
|
||||
return &d;
|
||||
}
|
||||
// If the workspace is found in history, the previous one is simply the next element down the timeline
|
||||
if (it != m_history.end() && std::next(it) != m_history.end())
|
||||
return *std::next(it);
|
||||
|
||||
return &dataFor(ws);
|
||||
// No prior history found
|
||||
return SHistoryEntry{.id = WORKSPACE_INVALID};
|
||||
}
|
||||
|
||||
SWorkspaceIDName CWorkspaceHistoryTracker::previousWorkspaceIDName(PHLWORKSPACE ws) {
|
||||
gc();
|
||||
const auto DATA = previousWorkspace(ws);
|
||||
|
||||
for (const auto& d : m_datas) {
|
||||
if (d.workspace != ws)
|
||||
continue;
|
||||
return SWorkspaceIDName{.id = d.previousID, .name = d.previousName, .isAutoIDd = d.previousID <= 0};
|
||||
}
|
||||
if (DATA.id == WORKSPACE_INVALID)
|
||||
return SWorkspaceIDName{.id = WORKSPACE_INVALID};
|
||||
|
||||
auto& d = dataFor(ws);
|
||||
return SWorkspaceIDName{.id = d.previousID, .name = d.previousName, .isAutoIDd = d.previousID <= 0};
|
||||
return SWorkspaceIDName{.id = DATA.id, .name = DATA.name, .isAutoIDd = DATA.id <= 0};
|
||||
}
|
||||
|
||||
const CWorkspaceHistoryTracker::SWorkspacePreviousData* CWorkspaceHistoryTracker::previousWorkspace(PHLWORKSPACE ws, PHLMONITOR restrict) {
|
||||
const CWorkspaceHistoryTracker::SHistoryEntry CWorkspaceHistoryTracker::previousWorkspace(PHLWORKSPACE ws, PHLMONITOR restrict) {
|
||||
if (!restrict)
|
||||
return previousWorkspace(ws);
|
||||
|
||||
auto& data = dataFor(ws);
|
||||
while (true) {
|
||||
gc();
|
||||
|
||||
// case 1: previous exists
|
||||
if (data.previous) {
|
||||
if (data.previous->m_monitor != restrict) {
|
||||
data = dataFor(data.previous.lock());
|
||||
continue;
|
||||
}
|
||||
auto it = std::ranges::find_if(m_history, [&](const auto& entry) { return entry.workspace == ws; });
|
||||
|
||||
break;
|
||||
}
|
||||
// Start looking from the element immediately following `ws` in the list
|
||||
if (it != m_history.end())
|
||||
it++;
|
||||
else
|
||||
it = m_history.begin();
|
||||
|
||||
// case 2: previous doesnt exist, but we have mon
|
||||
if (data.previousMon) {
|
||||
if (data.previousMon != restrict)
|
||||
return nullptr;
|
||||
// Scan down the timeline until we hit a workspace mapped to the restricted monitor
|
||||
while (it != m_history.end()) {
|
||||
if (it->monitor == restrict)
|
||||
return *it;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// case 3: no mon and no previous
|
||||
return nullptr;
|
||||
it++;
|
||||
}
|
||||
|
||||
return &data;
|
||||
// Entry not found
|
||||
return SHistoryEntry{.id = WORKSPACE_INVALID};
|
||||
}
|
||||
|
||||
SWorkspaceIDName CWorkspaceHistoryTracker::previousWorkspaceIDName(PHLWORKSPACE ws, PHLMONITOR restrict) {
|
||||
const auto DATA = previousWorkspace(ws, restrict);
|
||||
if (!DATA)
|
||||
if (DATA.id == WORKSPACE_INVALID)
|
||||
return SWorkspaceIDName{.id = WORKSPACE_INVALID};
|
||||
|
||||
return SWorkspaceIDName{.id = DATA->previousID, .name = DATA->previousName, .isAutoIDd = DATA->previousID <= 0};
|
||||
return SWorkspaceIDName{.id = DATA.id, .name = DATA.name, .isAutoIDd = DATA.id <= 0};
|
||||
}
|
||||
|
||||
void CWorkspaceHistoryTracker::setLastWorkspaceData(PHLWORKSPACE w) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include "../../macros.hpp"
|
||||
#include "../../helpers/MiscFunctions.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
namespace Desktop::History {
|
||||
class CWorkspaceHistoryTracker {
|
||||
|
|
@ -17,19 +17,18 @@ namespace Desktop::History {
|
|||
CWorkspaceHistoryTracker(CWorkspaceHistoryTracker&) = delete;
|
||||
CWorkspaceHistoryTracker(CWorkspaceHistoryTracker&&) = delete;
|
||||
|
||||
struct SWorkspacePreviousData {
|
||||
struct SHistoryEntry {
|
||||
PHLWORKSPACEREF workspace;
|
||||
PHLWORKSPACEREF previous;
|
||||
PHLMONITORREF previousMon;
|
||||
std::string previousName = "";
|
||||
WORKSPACEID previousID = WORKSPACE_INVALID;
|
||||
PHLMONITORREF monitor;
|
||||
std::string name = "";
|
||||
WORKSPACEID id = WORKSPACE_INVALID;
|
||||
};
|
||||
|
||||
const SWorkspacePreviousData* previousWorkspace(PHLWORKSPACE ws);
|
||||
SWorkspaceIDName previousWorkspaceIDName(PHLWORKSPACE ws);
|
||||
const SHistoryEntry previousWorkspace(PHLWORKSPACE ws);
|
||||
SWorkspaceIDName previousWorkspaceIDName(PHLWORKSPACE ws);
|
||||
|
||||
const SWorkspacePreviousData* previousWorkspace(PHLWORKSPACE ws, PHLMONITOR restrict);
|
||||
SWorkspaceIDName previousWorkspaceIDName(PHLWORKSPACE ws, PHLMONITOR restrict);
|
||||
const SHistoryEntry previousWorkspace(PHLWORKSPACE ws, PHLMONITOR restrict);
|
||||
SWorkspaceIDName previousWorkspaceIDName(PHLWORKSPACE ws, PHLMONITOR restrict);
|
||||
|
||||
private:
|
||||
struct SLastWorkspaceData {
|
||||
|
|
@ -39,14 +38,12 @@ namespace Desktop::History {
|
|||
WORKSPACEID workspaceID = WORKSPACE_INVALID;
|
||||
} m_lastWorkspaceData;
|
||||
|
||||
std::vector<SWorkspacePreviousData> m_datas;
|
||||
std::deque<SHistoryEntry> m_history;
|
||||
|
||||
void track(PHLWORKSPACE w);
|
||||
void gc();
|
||||
void setLastWorkspaceData(PHLWORKSPACE w);
|
||||
|
||||
SWorkspacePreviousData& dataFor(PHLWORKSPACE ws);
|
||||
void track(PHLWORKSPACE w);
|
||||
void gc();
|
||||
void setLastWorkspaceData(PHLWORKSPACE w);
|
||||
};
|
||||
|
||||
SP<CWorkspaceHistoryTracker> workspaceTracker();
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue