summaryrefslogtreecommitdiff
path: root/src/desktop/history/WorkspaceHistoryTracker.cpp
blob: 0b4ef2fdaad635fd9e44e86ea761ac551e55dc58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include "WorkspaceHistoryTracker.hpp"

#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;

SP<CWorkspaceHistoryTracker> History::workspaceTracker() {
    static SP<CWorkspaceHistoryTracker> tracker = makeShared<CWorkspaceHistoryTracker>();
    return tracker;
}

CWorkspaceHistoryTracker::CWorkspaceHistoryTracker() {
    static auto P = g_pHookSystem->hookDynamic("workspace", [this](void* self, SCallbackInfo& info, std::any data) {
        auto workspace = std::any_cast<PHLWORKSPACE>(data);
        track(workspace);
    });

    static auto P1 = g_pHookSystem->hookDynamic("focusedMon", [this](void* self, SCallbackInfo& info, std::any data) {
        auto mon = std::any_cast<PHLMONITOR>(data);

        // 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);
        });
    });
}

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)
        return;

    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)
        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;
    }
}

void CWorkspaceHistoryTracker::gc() {
    std::erase_if(m_datas, [](const auto& e) { return !e.workspace; });
}

const CWorkspaceHistoryTracker::SWorkspacePreviousData* CWorkspaceHistoryTracker::previousWorkspace(PHLWORKSPACE ws) {
    gc();

    for (const auto& d : m_datas) {
        if (d.workspace != ws)
            continue;
        return &d;
    }

    return &dataFor(ws);
}

SWorkspaceIDName CWorkspaceHistoryTracker::previousWorkspaceIDName(PHLWORKSPACE ws) {
    gc();

    for (const auto& d : m_datas) {
        if (d.workspace != ws)
            continue;
        return SWorkspaceIDName{.id = d.previousID, .name = d.previousName, .isAutoIDd = d.previousID <= 0};
    }

    auto& d = dataFor(ws);
    return SWorkspaceIDName{.id = d.previousID, .name = d.previousName, .isAutoIDd = d.previousID <= 0};
}

const CWorkspaceHistoryTracker::SWorkspacePreviousData* CWorkspaceHistoryTracker::previousWorkspace(PHLWORKSPACE ws, PHLMONITOR restrict) {
    if (!restrict)
        return previousWorkspace(ws);

    auto& data = dataFor(ws);
    while (true) {

        // case 1: previous exists
        if (data.previous) {
            if (data.previous->m_monitor != restrict) {
                data = dataFor(data.previous.lock());
                continue;
            }

            break;
        }

        // case 2: previous doesnt exist, but we have mon
        if (data.previousMon) {
            if (data.previousMon != restrict)
                return nullptr;

            break;
        }

        // case 3: no mon and no previous
        return nullptr;
    }

    return &data;
}

SWorkspaceIDName CWorkspaceHistoryTracker::previousWorkspaceIDName(PHLWORKSPACE ws, PHLMONITOR restrict) {
    const auto DATA = previousWorkspace(ws, restrict);
    if (!DATA)
        return SWorkspaceIDName{.id = WORKSPACE_INVALID};

    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;
}