mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-08-21 23:13:49 -07:00
core: fix workspace persistence tracking (#11239)
This commit is contained in:
@@ -399,6 +399,7 @@ if(NO_TESTS)
|
||||
message(STATUS "building tests is disabled")
|
||||
else()
|
||||
message(STATUS "building tests is enabled (NO_TESTS not defined)")
|
||||
add_subdirectory(hyprtester)
|
||||
endif()
|
||||
|
||||
# binary and symlink
|
||||
|
83
hyprtester/src/tests/main/persistent.cpp
Normal file
83
hyprtester/src/tests/main/persistent.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing persistent workspaces", Colors::GREEN);
|
||||
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
OK(getFromSocket("/keyword workspace 5, monitor:HEADLESS-2, persistent:1"));
|
||||
OK(getFromSocket("/keyword workspace 6, monitor:HEADLESS-PERSISTENT-TEST, persistent:1"));
|
||||
OK(getFromSocket("/keyword workspace name:PERSIST, monitor:HEADLESS-PERSISTENT-TEST, persistent:1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_CONTAINS(str, "ID 5 (5)");
|
||||
EXPECT_COUNT_STRING(str, "workspace ID ", 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/output create headless HEADLESS-PERSISTENT-TEST"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "HEADLESS-PERSISTENT-TEST");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-PERSISTENT-TEST"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_CONTAINS(str, "ID 2 (2)"); // this should be automatically generated by hl
|
||||
EXPECT_CONTAINS(str, "ID 5 (5)");
|
||||
EXPECT_CONTAINS(str, "ID 6 (6)");
|
||||
EXPECT_CONTAINS(str, "(PERSIST) on monitor");
|
||||
EXPECT_COUNT_STRING(str, "workspace ID ", 5);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_NOT_CONTAINS(str, "ID 5 (5)");
|
||||
EXPECT_NOT_CONTAINS(str, "ID 6 (6)");
|
||||
EXPECT_NOT_CONTAINS(str, "(PERSIST) on monitor");
|
||||
EXPECT_COUNT_STRING(str, "workspace ID ", 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/output remove HEADLESS-PERSISTENT-TEST"));
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
// reload cfg
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -22,6 +22,7 @@ monitor=HEADLESS-3,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-4,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-5,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-6,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-PERSISTENT-TEST,1920x1080@60,auto-right,1
|
||||
|
||||
monitor=,disabled
|
||||
|
||||
|
@@ -3109,6 +3109,8 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
|
||||
if (!m_lastMonitor)
|
||||
return;
|
||||
|
||||
std::vector<PHLWORKSPACE> persistentFound;
|
||||
|
||||
for (const auto& rule : rules) {
|
||||
if (!rule.isPersistent)
|
||||
continue;
|
||||
@@ -3142,17 +3144,20 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
|
||||
}
|
||||
PWORKSPACE = getWorkspaceByID(id);
|
||||
if (!PWORKSPACE)
|
||||
createNewWorkspace(id, PMONITOR ? PMONITOR->m_id : m_lastMonitor->m_id, wsname, false);
|
||||
PWORKSPACE = createNewWorkspace(id, PMONITOR ? PMONITOR->m_id : m_lastMonitor->m_id, wsname, false);
|
||||
}
|
||||
|
||||
if (PWORKSPACE)
|
||||
PWORKSPACE->m_persistent = true;
|
||||
|
||||
if (!PMONITOR) {
|
||||
Debug::log(ERR, "ensurePersistentWorkspacesPresent: couldn't resolve monitor for {}, skipping", rule.monitor);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PWORKSPACE)
|
||||
PWORKSPACE->setPersistent(true);
|
||||
|
||||
if (!pWorkspace)
|
||||
persistentFound.emplace_back(PWORKSPACE);
|
||||
|
||||
if (PWORKSPACE) {
|
||||
if (PWORKSPACE->m_monitor == PMONITOR) {
|
||||
Debug::log(LOG, "ensurePersistentWorkspacesPresent: workspace persistent {} already on {}", rule.workspaceString, PMONITOR->m_name);
|
||||
@@ -3165,4 +3170,22 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pWorkspace) {
|
||||
// check non-persistent and downgrade if workspace is no longer persistent
|
||||
std::vector<PHLWORKSPACEREF> toDowngrade;
|
||||
for (auto& w : getWorkspaces()) {
|
||||
if (!w->isPersistent())
|
||||
continue;
|
||||
|
||||
if (std::ranges::contains(persistentFound, w.lock()))
|
||||
continue;
|
||||
|
||||
toDowngrade.emplace_back(w);
|
||||
}
|
||||
|
||||
for (auto& ws : toDowngrade) {
|
||||
ws->setPersistent(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -355,12 +355,12 @@ std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat form
|
||||
}})#",
|
||||
w->m_id, escapeJSONStrings(w->m_name), escapeJSONStrings(PMONITOR ? PMONITOR->m_name : "?"),
|
||||
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->m_id) : "null"), w->getWindows(), w->m_hasFullscreenWindow ? "true" : "false",
|
||||
(uintptr_t)PLASTW.get(), PLASTW ? escapeJSONStrings(PLASTW->m_title) : "", w->m_persistent ? "true" : "false");
|
||||
(uintptr_t)PLASTW.get(), PLASTW ? escapeJSONStrings(PLASTW->m_title) : "", w->isPersistent() ? "true" : "false");
|
||||
} else {
|
||||
return std::format(
|
||||
"workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: {}\n\n",
|
||||
w->m_id, w->m_name, PMONITOR ? PMONITOR->m_name : "?", PMONITOR ? std::to_string(PMONITOR->m_id) : "null", w->getWindows(), (int)w->m_hasFullscreenWindow,
|
||||
(uintptr_t)PLASTW.get(), PLASTW ? PLASTW->m_title : "", (int)w->m_persistent);
|
||||
(uintptr_t)PLASTW.get(), PLASTW ? PLASTW->m_title : "", (int)w->isPersistent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1183,6 +1183,9 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
|
||||
}
|
||||
}
|
||||
|
||||
if (COMMAND.contains("workspace"))
|
||||
g_pConfigManager->ensurePersistentWorkspacesPresent();
|
||||
|
||||
Debug::log(LOG, "Hyprctl: keyword {} : {}", COMMAND, VALUE);
|
||||
|
||||
if (retval.empty())
|
||||
|
@@ -44,7 +44,7 @@ void CWorkspace::init(PHLWORKSPACE self) {
|
||||
m_inert = false;
|
||||
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self);
|
||||
m_persistent = WORKSPACERULE.isPersistent;
|
||||
setPersistent(WORKSPACERULE.isPersistent);
|
||||
|
||||
if (self->m_wasCreatedEmpty)
|
||||
if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd)
|
||||
@@ -639,7 +639,7 @@ void CWorkspace::rename(const std::string& name) {
|
||||
m_name = name;
|
||||
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_self.lock());
|
||||
m_persistent = WORKSPACERULE.isPersistent;
|
||||
setPersistent(WORKSPACERULE.isPersistent);
|
||||
|
||||
if (WORKSPACERULE.isPersistent)
|
||||
g_pCompositor->ensurePersistentWorkspacesPresent(std::vector<SWorkspaceRule>{WORKSPACERULE}, m_self.lock());
|
||||
@@ -658,3 +658,19 @@ void CWorkspace::updateWindows() {
|
||||
w->updateDynamicRules();
|
||||
}
|
||||
}
|
||||
|
||||
void CWorkspace::setPersistent(bool persistent) {
|
||||
if (m_persistent == persistent)
|
||||
return;
|
||||
|
||||
m_persistent = persistent;
|
||||
|
||||
if (persistent)
|
||||
m_selfPersistent = m_self.lock();
|
||||
else
|
||||
m_selfPersistent.reset();
|
||||
}
|
||||
|
||||
bool CWorkspace::isPersistent() {
|
||||
return m_persistent;
|
||||
}
|
||||
|
@@ -58,8 +58,6 @@ class CWorkspace {
|
||||
|
||||
bool m_wasCreatedEmpty = true;
|
||||
|
||||
bool m_persistent = false;
|
||||
|
||||
// Inert: destroyed and invalid. If this is true, release the ptr you have.
|
||||
bool inert();
|
||||
void startAnim(bool in, bool left, bool instant = false);
|
||||
@@ -83,6 +81,8 @@ class CWorkspace {
|
||||
void rename(const std::string& name = "");
|
||||
void forceReportSizesToWindows();
|
||||
void updateWindows();
|
||||
void setPersistent(bool persistent);
|
||||
bool isPersistent();
|
||||
|
||||
struct {
|
||||
CSignalT<> destroy;
|
||||
@@ -99,6 +99,9 @@ class CWorkspace {
|
||||
|
||||
SP<HOOK_CALLBACK_FN> m_focusedWindowHook;
|
||||
bool m_inert = true;
|
||||
|
||||
SP<CWorkspace> m_selfPersistent; // for persistent workspaces.
|
||||
bool m_persistent = false;
|
||||
};
|
||||
|
||||
inline bool valid(const PHLWORKSPACE& ref) {
|
||||
|
Reference in New Issue
Block a user