Merge pull request #2603 from zjeffer/hyprland/persistent-workspaces
Hyprland/workspaces: use Hyprland's workspace rules for persistencypull/1899/merge
commit
9abd0da1c7
|
@ -68,7 +68,7 @@ class Workspace {
|
||||||
int id() const { return m_id; };
|
int id() const { return m_id; };
|
||||||
std::string name() const { return m_name; };
|
std::string name() const { return m_name; };
|
||||||
std::string output() const { return m_output; };
|
std::string output() const { return m_output; };
|
||||||
bool isActive() const { return m_active; };
|
bool isActive() const { return m_isActive; };
|
||||||
bool isSpecial() const { return m_isSpecial; };
|
bool isSpecial() const { return m_isSpecial; };
|
||||||
bool isPersistent() const { return m_isPersistent; };
|
bool isPersistent() const { return m_isPersistent; };
|
||||||
bool isVisible() const { return m_isVisible; };
|
bool isVisible() const { return m_isVisible; };
|
||||||
|
@ -76,7 +76,7 @@ class Workspace {
|
||||||
bool isUrgent() const { return m_isUrgent; };
|
bool isUrgent() const { return m_isUrgent; };
|
||||||
|
|
||||||
bool handleClicked(GdkEventButton* bt) const;
|
bool handleClicked(GdkEventButton* bt) const;
|
||||||
void setActive(bool value = true) { m_active = value; };
|
void setActive(bool value = true) { m_isActive = value; };
|
||||||
void setPersistent(bool value = true) { m_isPersistent = value; };
|
void setPersistent(bool value = true) { m_isPersistent = value; };
|
||||||
void setUrgent(bool value = true) { m_isUrgent = value; };
|
void setUrgent(bool value = true) { m_isUrgent = value; };
|
||||||
void setVisible(bool value = true) { m_isVisible = value; };
|
void setVisible(bool value = true) { m_isVisible = value; };
|
||||||
|
@ -99,7 +99,7 @@ class Workspace {
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_output;
|
std::string m_output;
|
||||||
uint m_windows;
|
uint m_windows;
|
||||||
bool m_active = false;
|
bool m_isActive = false;
|
||||||
bool m_isSpecial = false;
|
bool m_isSpecial = false;
|
||||||
bool m_isPersistent = false;
|
bool m_isPersistent = false;
|
||||||
bool m_isUrgent = false;
|
bool m_isUrgent = false;
|
||||||
|
@ -135,8 +135,8 @@ class Workspaces : public AModule, public EventHandler {
|
||||||
void onEvent(const std::string& e) override;
|
void onEvent(const std::string& e) override;
|
||||||
void updateWindowCount();
|
void updateWindowCount();
|
||||||
void sortWorkspaces();
|
void sortWorkspaces();
|
||||||
void createWorkspace(Json::Value const& workspace_data,
|
void createWorkspace(Json::Value const& workspaceData,
|
||||||
Json::Value const& clients_data = Json::Value::nullRef);
|
Json::Value const& clientsData = Json::Value::nullRef);
|
||||||
void removeWorkspace(std::string const& name);
|
void removeWorkspace(std::string const& name);
|
||||||
void setUrgentWorkspace(std::string const& windowaddress);
|
void setUrgentWorkspace(std::string const& windowaddress);
|
||||||
void parseConfig(const Json::Value& config);
|
void parseConfig(const Json::Value& config);
|
||||||
|
@ -160,16 +160,24 @@ class Workspaces : public AModule, public EventHandler {
|
||||||
|
|
||||||
void onWindowTitleEvent(std::string const& payload);
|
void onWindowTitleEvent(std::string const& payload);
|
||||||
|
|
||||||
|
void onConfigReloaded();
|
||||||
|
|
||||||
int windowRewritePriorityFunction(std::string const& window_rule);
|
int windowRewritePriorityFunction(std::string const& window_rule);
|
||||||
|
|
||||||
void doUpdate();
|
void doUpdate();
|
||||||
|
|
||||||
void extendOrphans(int workspaceId, Json::Value const& clientsJson);
|
void extendOrphans(int workspaceId, Json::Value const& clientsJson);
|
||||||
void registerOrphanWindow(WindowCreationPayload create_window_paylod);
|
void registerOrphanWindow(WindowCreationPayload create_window_payload);
|
||||||
|
|
||||||
|
void initializeWorkspaces();
|
||||||
|
void setCurrentMonitorId();
|
||||||
|
void loadPersistentWorkspacesFromConfig(Json::Value const& clientsJson);
|
||||||
|
void loadPersistentWorkspacesFromWorkspaceRules(const Json::Value& clientsJson);
|
||||||
|
|
||||||
bool m_allOutputs = false;
|
bool m_allOutputs = false;
|
||||||
bool m_showSpecial = false;
|
bool m_showSpecial = false;
|
||||||
bool m_activeOnly = false;
|
bool m_activeOnly = false;
|
||||||
|
Json::Value m_persistentWorkspaceConfig;
|
||||||
|
|
||||||
// Map for windows stored in workspaces not present in the current bar.
|
// Map for windows stored in workspaces not present in the current bar.
|
||||||
// This happens when the user has multiple monitors (hence, multiple bars)
|
// This happens when the user has multiple monitors (hence, multiple bars)
|
||||||
|
@ -184,11 +192,6 @@ class Workspaces : public AModule, public EventHandler {
|
||||||
{"NUMBER", SortMethod::NUMBER},
|
{"NUMBER", SortMethod::NUMBER},
|
||||||
{"DEFAULT", SortMethod::DEFAULT}};
|
{"DEFAULT", SortMethod::DEFAULT}};
|
||||||
|
|
||||||
void fillPersistentWorkspaces();
|
|
||||||
void createPersistentWorkspaces();
|
|
||||||
std::vector<std::string> m_persistentWorkspacesToCreate;
|
|
||||||
bool m_persistentCreated = false;
|
|
||||||
|
|
||||||
std::string m_format;
|
std::string m_format;
|
||||||
|
|
||||||
std::map<std::string, std::string> m_iconsMap;
|
std::map<std::string, std::string> m_iconsMap;
|
||||||
|
|
|
@ -49,6 +49,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||||
gIPC = std::make_unique<IPC>();
|
gIPC = std::make_unique<IPC>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCurrentMonitorId();
|
||||||
init();
|
init();
|
||||||
registerIpc();
|
registerIpc();
|
||||||
}
|
}
|
||||||
|
@ -64,7 +65,6 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void {
|
||||||
for (std::string &name : formatIcons.getMemberNames()) {
|
for (std::string &name : formatIcons.getMemberNames()) {
|
||||||
m_iconsMap.emplace(name, formatIcons[name].asString());
|
m_iconsMap.emplace(name, formatIcons[name].asString());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_iconsMap.emplace("", "");
|
m_iconsMap.emplace("", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,11 +112,26 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_["persistent_workspaces"].isObject()) {
|
||||||
|
spdlog::warn(
|
||||||
|
"persistent_workspaces is deprecated. Please change config to use persistent-workspaces.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_["persistent-workspaces"].isObject() || config_["persistent_workspaces"].isObject()) {
|
||||||
|
m_persistentWorkspaceConfig = config_["persistent-workspaces"].isObject()
|
||||||
|
? config_["persistent-workspaces"]
|
||||||
|
: config_["persistent_workspaces"];
|
||||||
|
}
|
||||||
|
|
||||||
const Json::Value &formatWindowSeparator = config["format-window-separator"];
|
const Json::Value &formatWindowSeparator = config["format-window-separator"];
|
||||||
m_formatWindowSeparator =
|
m_formatWindowSeparator =
|
||||||
formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " ";
|
formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " ";
|
||||||
|
|
||||||
const Json::Value &windowRewrite = config["window-rewrite"];
|
const Json::Value &windowRewrite = config["window-rewrite"];
|
||||||
|
if (!windowRewrite.isObject()) {
|
||||||
|
spdlog::debug("window-rewrite is not defined or is not an object, using default rules.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"];
|
const Json::Value &windowRewriteDefaultConfig = config["window-rewrite-default"];
|
||||||
std::string windowRewriteDefault =
|
std::string windowRewriteDefault =
|
||||||
|
@ -127,9 +142,9 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void {
|
||||||
[this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); });
|
[this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_paylod) {
|
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
|
||||||
if (!create_window_paylod.isEmpty(*this)) {
|
if (!create_window_payload.isEmpty(*this)) {
|
||||||
m_orphanWindowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(*this);
|
m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +159,7 @@ auto Workspaces::registerIpc() -> void {
|
||||||
gIPC->registerForIPC("closewindow", this);
|
gIPC->registerForIPC("closewindow", this);
|
||||||
gIPC->registerForIPC("movewindow", this);
|
gIPC->registerForIPC("movewindow", this);
|
||||||
gIPC->registerForIPC("urgent", this);
|
gIPC->registerForIPC("urgent", this);
|
||||||
|
gIPC->registerForIPC("configreloaded", this);
|
||||||
|
|
||||||
if (windowRewriteConfigUsesTitle()) {
|
if (windowRewriteConfigUsesTitle()) {
|
||||||
spdlog::info(
|
spdlog::info(
|
||||||
|
@ -175,6 +191,7 @@ void Workspaces::doUpdate() {
|
||||||
m_workspacesToCreate.clear();
|
m_workspacesToCreate.clear();
|
||||||
|
|
||||||
// get all active workspaces
|
// get all active workspaces
|
||||||
|
spdlog::trace("Getting active workspaces");
|
||||||
auto monitors = gIPC->getSocket1JsonReply("monitors");
|
auto monitors = gIPC->getSocket1JsonReply("monitors");
|
||||||
std::vector<std::string> visibleWorkspaces;
|
std::vector<std::string> visibleWorkspaces;
|
||||||
for (Json::Value &monitor : monitors) {
|
for (Json::Value &monitor : monitors) {
|
||||||
|
@ -184,6 +201,7 @@ void Workspaces::doUpdate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spdlog::trace("Updating workspace states");
|
||||||
for (auto &workspace : m_workspaces) {
|
for (auto &workspace : m_workspaces) {
|
||||||
// active
|
// active
|
||||||
workspace->setActive(workspace->name() == m_activeWorkspaceName);
|
workspace->setActive(workspace->name() == m_activeWorkspaceName);
|
||||||
|
@ -204,6 +222,7 @@ void Workspaces::doUpdate() {
|
||||||
workspace->update(m_format, workspaceIcon);
|
workspace->update(m_format, workspaceIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spdlog::trace("Updating window count");
|
||||||
bool anyWindowCreated = false;
|
bool anyWindowCreated = false;
|
||||||
std::vector<WindowCreationPayload> notCreated;
|
std::vector<WindowCreationPayload> notCreated;
|
||||||
|
|
||||||
|
@ -285,6 +304,8 @@ void Workspaces::onEvent(const std::string &ev) {
|
||||||
onWorkspaceRenamed(payload);
|
onWorkspaceRenamed(payload);
|
||||||
} else if (eventName == "windowtitle") {
|
} else if (eventName == "windowtitle") {
|
||||||
onWindowTitleEvent(payload);
|
onWindowTitleEvent(payload);
|
||||||
|
} else if (eventName == "configreloaded") {
|
||||||
|
onConfigReloaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
dp.emit();
|
dp.emit();
|
||||||
|
@ -302,22 +323,37 @@ void Workspaces::onWorkspaceDestroyed(std::string const &payload) {
|
||||||
|
|
||||||
void Workspaces::onWorkspaceCreated(std::string const &workspaceName,
|
void Workspaces::onWorkspaceCreated(std::string const &workspaceName,
|
||||||
Json::Value const &clientsData) {
|
Json::Value const &clientsData) {
|
||||||
const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces");
|
spdlog::debug("Workspace created: {}", workspaceName);
|
||||||
|
auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
|
||||||
if (!isWorkspaceIgnored(workspaceName)) {
|
if (!isWorkspaceIgnored(workspaceName)) {
|
||||||
|
auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules");
|
||||||
for (Json::Value workspaceJson : workspacesJson) {
|
for (Json::Value workspaceJson : workspacesJson) {
|
||||||
std::string name = workspaceJson["name"].asString();
|
std::string name = workspaceJson["name"].asString();
|
||||||
if (name == workspaceName &&
|
if (name == workspaceName) {
|
||||||
(allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) &&
|
if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) &&
|
||||||
(showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) {
|
(showSpecial() || !name.starts_with("special")) && !isDoubleSpecial(workspaceName)) {
|
||||||
m_workspacesToCreate.emplace_back(workspaceJson, clientsData);
|
for (Json::Value const &rule : workspaceRules) {
|
||||||
break;
|
if (rule["workspaceString"].asString() == workspaceName) {
|
||||||
|
workspaceJson["persistent"] = rule["persistent"].asBool();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_workspacesToCreate.emplace_back(workspaceJson, clientsData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
extendOrphans(workspaceJson["id"].asInt(), clientsData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
spdlog::trace("Not creating workspace because it is ignored: {}", workspaceName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::onWorkspaceMoved(std::string const &payload) {
|
void Workspaces::onWorkspaceMoved(std::string const &payload) {
|
||||||
|
spdlog::debug("Workspace moved: {}", payload);
|
||||||
std::string workspaceName = payload.substr(0, payload.find(','));
|
std::string workspaceName = payload.substr(0, payload.find(','));
|
||||||
std::string monitorName = payload.substr(payload.find(',') + 1);
|
std::string monitorName = payload.substr(payload.find(',') + 1);
|
||||||
|
|
||||||
|
@ -325,11 +361,13 @@ void Workspaces::onWorkspaceMoved(std::string const &payload) {
|
||||||
Json::Value clientsData = gIPC->getSocket1JsonReply("clients");
|
Json::Value clientsData = gIPC->getSocket1JsonReply("clients");
|
||||||
onWorkspaceCreated(workspaceName, clientsData);
|
onWorkspaceCreated(workspaceName, clientsData);
|
||||||
} else {
|
} else {
|
||||||
|
spdlog::debug("Removing workspace because it was moved to another monitor: {}");
|
||||||
onWorkspaceDestroyed(workspaceName);
|
onWorkspaceDestroyed(workspaceName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::onWorkspaceRenamed(std::string const &payload) {
|
void Workspaces::onWorkspaceRenamed(std::string const &payload) {
|
||||||
|
spdlog::debug("Workspace renamed: {}", payload);
|
||||||
std::string workspaceIdStr = payload.substr(0, payload.find(','));
|
std::string workspaceIdStr = payload.substr(0, payload.find(','));
|
||||||
int workspaceId = workspaceIdStr == "special" ? -99 : std::stoi(workspaceIdStr);
|
int workspaceId = workspaceIdStr == "special" ? -99 : std::stoi(workspaceIdStr);
|
||||||
std::string newName = payload.substr(payload.find(',') + 1);
|
std::string newName = payload.substr(payload.find(',') + 1);
|
||||||
|
@ -346,10 +384,12 @@ void Workspaces::onWorkspaceRenamed(std::string const &payload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::onMonitorFocused(std::string const &payload) {
|
void Workspaces::onMonitorFocused(std::string const &payload) {
|
||||||
|
spdlog::trace("Monitor focused: {}", payload);
|
||||||
m_activeWorkspaceName = payload.substr(payload.find(',') + 1);
|
m_activeWorkspaceName = payload.substr(payload.find(',') + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::onWindowOpened(std::string const &payload) {
|
void Workspaces::onWindowOpened(std::string const &payload) {
|
||||||
|
spdlog::trace("Window opened: {}", payload);
|
||||||
updateWindowCount();
|
updateWindowCount();
|
||||||
size_t lastCommaIdx = 0;
|
size_t lastCommaIdx = 0;
|
||||||
size_t nextCommaIdx = payload.find(',');
|
size_t nextCommaIdx = payload.find(',');
|
||||||
|
@ -369,6 +409,7 @@ void Workspaces::onWindowOpened(std::string const &payload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::onWindowClosed(std::string const &addr) {
|
void Workspaces::onWindowClosed(std::string const &addr) {
|
||||||
|
spdlog::trace("Window closed: {}", addr);
|
||||||
updateWindowCount();
|
updateWindowCount();
|
||||||
for (auto &workspace : m_workspaces) {
|
for (auto &workspace : m_workspaces) {
|
||||||
if (workspace->closeWindow(addr)) {
|
if (workspace->closeWindow(addr)) {
|
||||||
|
@ -378,6 +419,7 @@ void Workspaces::onWindowClosed(std::string const &addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::onWindowMoved(std::string const &payload) {
|
void Workspaces::onWindowMoved(std::string const &payload) {
|
||||||
|
spdlog::trace("Window moved: {}", payload);
|
||||||
updateWindowCount();
|
updateWindowCount();
|
||||||
size_t lastCommaIdx = 0;
|
size_t lastCommaIdx = 0;
|
||||||
size_t nextCommaIdx = payload.find(',');
|
size_t nextCommaIdx = payload.find(',');
|
||||||
|
@ -416,6 +458,7 @@ void Workspaces::onWindowMoved(std::string const &payload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::onWindowTitleEvent(std::string const &payload) {
|
void Workspaces::onWindowTitleEvent(std::string const &payload) {
|
||||||
|
spdlog::trace("Window title changed: {}", payload);
|
||||||
std::optional<std::function<void(WindowCreationPayload)>> inserter;
|
std::optional<std::function<void(WindowCreationPayload)>> inserter;
|
||||||
|
|
||||||
// If the window was an orphan, rename it at the orphan's vector
|
// If the window was an orphan, rename it at the orphan's vector
|
||||||
|
@ -459,6 +502,11 @@ void Workspaces::onWindowTitleEvent(std::string const &payload) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Workspaces::onConfigReloaded() {
|
||||||
|
spdlog::info("Hyprland config reloaded, reinitializing hyprland/workspaces module...");
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
void Workspaces::updateWindowCount() {
|
void Workspaces::updateWindowCount() {
|
||||||
const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces");
|
const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces");
|
||||||
for (auto &workspace : m_workspaces) {
|
for (auto &workspace : m_workspaces) {
|
||||||
|
@ -515,8 +563,11 @@ std::optional<std::string> Workspace::closeWindow(WindowAddress const &addr) {
|
||||||
|
|
||||||
void Workspaces::createWorkspace(Json::Value const &workspace_data,
|
void Workspaces::createWorkspace(Json::Value const &workspace_data,
|
||||||
Json::Value const &clients_data) {
|
Json::Value const &clients_data) {
|
||||||
// avoid recreating existing workspaces
|
|
||||||
auto workspaceName = workspace_data["name"].asString();
|
auto workspaceName = workspace_data["name"].asString();
|
||||||
|
spdlog::debug("Creating workspace {}, persistent: {}", workspaceName,
|
||||||
|
workspace_data["persistent"].asBool() ? "true" : "false");
|
||||||
|
|
||||||
|
// avoid recreating existing workspaces
|
||||||
auto workspace = std::find_if(
|
auto workspace = std::find_if(
|
||||||
m_workspaces.begin(), m_workspaces.end(),
|
m_workspaces.begin(), m_workspaces.end(),
|
||||||
[workspaceName](std::unique_ptr<Workspace> const &w) {
|
[workspaceName](std::unique_ptr<Workspace> const &w) {
|
||||||
|
@ -525,9 +576,8 @@ void Workspaces::createWorkspace(Json::Value const &workspace_data,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (workspace != m_workspaces.end()) {
|
if (workspace != m_workspaces.end()) {
|
||||||
if (workspace_data["persistent"].asBool() and !(*workspace)->isPersistent()) {
|
// don't recreate workspace, but update persistency if necessary
|
||||||
(*workspace)->setPersistent();
|
(*workspace)->setPersistent(workspace_data["persistent"].asBool());
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,6 +590,7 @@ void Workspaces::createWorkspace(Json::Value const &workspace_data,
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::removeWorkspace(std::string const &name) {
|
void Workspaces::removeWorkspace(std::string const &name) {
|
||||||
|
spdlog::debug("Removing workspace {}", name);
|
||||||
auto workspace =
|
auto workspace =
|
||||||
std::find_if(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr<Workspace> &x) {
|
std::find_if(m_workspaces.begin(), m_workspaces.end(), [&](std::unique_ptr<Workspace> &x) {
|
||||||
return (name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name();
|
return (name.starts_with("special:") && name.substr(8) == x->name()) || name == x->name();
|
||||||
|
@ -551,7 +602,7 @@ void Workspaces::removeWorkspace(std::string const &name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*workspace)->isPersistent()) {
|
if ((*workspace)->isPersistent()) {
|
||||||
// don't remove persistent workspaces, createWorkspace will take care of replacement
|
spdlog::trace("Not removing persistent workspace {}", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,94 +610,106 @@ void Workspaces::removeWorkspace(std::string const &name) {
|
||||||
m_workspaces.erase(workspace);
|
m_workspaces.erase(workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::fillPersistentWorkspaces() {
|
Json::Value createPersistentWorkspaceData(std::string const &name, std::string const &monitor) {
|
||||||
if (config_["persistent_workspaces"].isObject()) {
|
spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor);
|
||||||
spdlog::warn(
|
Json::Value workspaceData;
|
||||||
"persistent_workspaces is deprecated. Please change config to use persistent-workspaces.");
|
try {
|
||||||
|
// numbered persistent workspaces get the name as ID
|
||||||
|
workspaceData["id"] = name == "special" ? -99 : std::stoi(name);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
// named persistent workspaces start with ID=0
|
||||||
|
workspaceData["id"] = 0;
|
||||||
}
|
}
|
||||||
|
workspaceData["name"] = name;
|
||||||
|
workspaceData["monitor"] = monitor;
|
||||||
|
workspaceData["windows"] = 0;
|
||||||
|
workspaceData["persistent"] = true;
|
||||||
|
return workspaceData;
|
||||||
|
}
|
||||||
|
|
||||||
if (config_["persistent-workspaces"].isObject() || config_["persistent_workspaces"].isObject()) {
|
void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJson) {
|
||||||
const Json::Value persistentWorkspaces = config_["persistent-workspaces"].isObject()
|
spdlog::info("Loading persistent workspaces from Waybar config");
|
||||||
? config_["persistent-workspaces"]
|
const std::vector<std::string> keys = m_persistentWorkspaceConfig.getMemberNames();
|
||||||
: config_["persistent_workspaces"];
|
std::vector<std::string> persistentWorkspacesToCreate;
|
||||||
const std::vector<std::string> keys = persistentWorkspaces.getMemberNames();
|
|
||||||
|
|
||||||
for (const std::string &key : keys) {
|
const std::string currentMonitor = m_bar.output->name;
|
||||||
// only add if either:
|
const bool monitorInConfig = std::find(keys.begin(), keys.end(), currentMonitor) != keys.end();
|
||||||
// 1. key is "*" and this monitor is not already defined in the config
|
for (const std::string &key : keys) {
|
||||||
// 2. key is the current monitor name
|
// only add if either:
|
||||||
bool canCreate =
|
// 1. key is the current monitor name
|
||||||
(key == "*" && std::find(keys.begin(), keys.end(), m_bar.output->name) == keys.end()) ||
|
// 2. key is "*" and this monitor is not already defined in the config
|
||||||
key == m_bar.output->name;
|
bool canCreate = key == currentMonitor || (key == "*" && !monitorInConfig);
|
||||||
const Json::Value &value = persistentWorkspaces[key];
|
const Json::Value &value = m_persistentWorkspaceConfig[key];
|
||||||
|
spdlog::trace("Parsing persistent workspace config: {} => {}", key, value.toStyledString());
|
||||||
|
|
||||||
if (value.isInt()) {
|
if (value.isInt()) {
|
||||||
// value is a number => create that many workspaces for this monitor
|
// value is a number => create that many workspaces for this monitor
|
||||||
if (canCreate) {
|
if (canCreate) {
|
||||||
int amount = value.asInt();
|
int amount = value.asInt();
|
||||||
spdlog::debug("Creating {} persistent workspaces for monitor {}", amount,
|
spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, currentMonitor);
|
||||||
m_bar.output->name);
|
for (int i = 0; i < amount; i++) {
|
||||||
for (int i = 0; i < amount; i++) {
|
persistentWorkspacesToCreate.emplace_back(std::to_string(m_monitorId * amount + i + 1));
|
||||||
m_persistentWorkspacesToCreate.emplace_back(
|
|
||||||
std::to_string(m_monitorId * amount + i + 1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (value.isArray() && !value.empty()) {
|
}
|
||||||
// value is an array => create defined workspaces for this monitor
|
} else if (value.isArray() && !value.empty()) {
|
||||||
if (canCreate) {
|
// value is an array => create defined workspaces for this monitor
|
||||||
for (const Json::Value &workspace : value) {
|
if (canCreate) {
|
||||||
if (workspace.isInt()) {
|
for (const Json::Value &workspace : value) {
|
||||||
spdlog::debug("Creating workspace {} on monitor {}", workspace, m_bar.output->name);
|
if (workspace.isInt()) {
|
||||||
m_persistentWorkspacesToCreate.emplace_back(std::to_string(workspace.asInt()));
|
spdlog::debug("Creating workspace {} on monitor {}", workspace, currentMonitor);
|
||||||
}
|
persistentWorkspacesToCreate.emplace_back(std::to_string(workspace.asInt()));
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// key is the workspace and value is array of monitors to create on
|
|
||||||
for (const Json::Value &monitor : value) {
|
|
||||||
if (monitor.isString() && monitor.asString() == m_bar.output->name) {
|
|
||||||
m_persistentWorkspacesToCreate.emplace_back(key);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// this workspace should be displayed on all monitors
|
// key is the workspace and value is array of monitors to create on
|
||||||
m_persistentWorkspacesToCreate.emplace_back(key);
|
for (const Json::Value &monitor : value) {
|
||||||
|
if (monitor.isString() && monitor.asString() == currentMonitor) {
|
||||||
|
persistentWorkspacesToCreate.emplace_back(currentMonitor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// this workspace should be displayed on all monitors
|
||||||
|
persistentWorkspacesToCreate.emplace_back(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const &workspace : persistentWorkspacesToCreate) {
|
||||||
|
auto const workspaceData = createPersistentWorkspaceData(workspace, m_bar.output->name);
|
||||||
|
m_workspacesToCreate.emplace_back(workspaceData, clientsJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::loadPersistentWorkspacesFromWorkspaceRules(const Json::Value &clientsJson) {
|
||||||
|
spdlog::info("Loading persistent workspaces from Hyprland workspace rules");
|
||||||
|
|
||||||
|
auto const workspaceRules = gIPC->getSocket1JsonReply("workspacerules");
|
||||||
|
for (Json::Value const &rule : workspaceRules) {
|
||||||
|
if (!rule["workspaceString"].isString()) {
|
||||||
|
spdlog::warn("Workspace rules: invalid workspaceString, skipping: {}", rule);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!rule["persistent"].asBool()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto const &workspace = rule["workspaceString"].asString();
|
||||||
|
auto const &monitor = rule["monitor"].asString();
|
||||||
|
// create this workspace persistently if:
|
||||||
|
// 1. the allOutputs config option is enabled
|
||||||
|
// 2. the rule's monitor is the current monitor
|
||||||
|
// 3. no monitor is specified in the rule => assume it needs to be persistent on every monitor
|
||||||
|
if (allOutputs() || m_bar.output->name == monitor || monitor.empty()) {
|
||||||
|
// => persistent workspace should be shown on this monitor
|
||||||
|
auto workspaceData = createPersistentWorkspaceData(workspace, m_bar.output->name);
|
||||||
|
m_workspacesToCreate.emplace_back(workspaceData, clientsJson);
|
||||||
|
} else {
|
||||||
|
m_workspacesToRemove.emplace_back(workspace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::createPersistentWorkspaces() {
|
void Workspaces::setCurrentMonitorId() {
|
||||||
for (const std::string &workspaceName : m_persistentWorkspacesToCreate) {
|
|
||||||
Json::Value newWorkspace;
|
|
||||||
try {
|
|
||||||
// numbered persistent workspaces get the name as ID
|
|
||||||
newWorkspace["id"] = workspaceName == "special" ? -99 : std::stoi(workspaceName);
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
// named persistent workspaces start with ID=0
|
|
||||||
newWorkspace["id"] = 0;
|
|
||||||
}
|
|
||||||
newWorkspace["name"] = workspaceName;
|
|
||||||
newWorkspace["monitor"] = m_bar.output->name;
|
|
||||||
newWorkspace["windows"] = 0;
|
|
||||||
newWorkspace["persistent"] = true;
|
|
||||||
|
|
||||||
createWorkspace(newWorkspace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) {
|
|
||||||
for (const auto &client : clientsJson) {
|
|
||||||
if (client["workspace"]["id"].asInt() == workspaceId) {
|
|
||||||
registerOrphanWindow({client});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Workspaces::init() {
|
|
||||||
m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString();
|
|
||||||
|
|
||||||
// get monitor ID from name (used by persistent workspaces)
|
// get monitor ID from name (used by persistent workspaces)
|
||||||
m_monitorId = 0;
|
m_monitorId = 0;
|
||||||
auto monitors = gIPC->getSocket1JsonReply("monitors");
|
auto monitors = gIPC->getSocket1JsonReply("monitors");
|
||||||
|
@ -657,29 +720,58 @@ void Workspaces::init() {
|
||||||
spdlog::error("Monitor '{}' does not have an ID? Using 0", m_bar.output->name);
|
spdlog::error("Monitor '{}' does not have an ID? Using 0", m_bar.output->name);
|
||||||
} else {
|
} else {
|
||||||
m_monitorId = (*currentMonitor)["id"].asInt();
|
m_monitorId = (*currentMonitor)["id"].asInt();
|
||||||
|
spdlog::trace("Current monitor ID: {}", m_monitorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::initializeWorkspaces() {
|
||||||
|
spdlog::debug("Initializing workspaces");
|
||||||
|
|
||||||
|
// if the workspace rules changed since last initialization, make sure we reset everything:
|
||||||
|
for (auto &workspace : m_workspaces) {
|
||||||
|
m_workspacesToRemove.push_back(workspace->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces");
|
// get all current workspaces
|
||||||
const Json::Value clientsJson = gIPC->getSocket1JsonReply("clients");
|
auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
auto const clientsJson = gIPC->getSocket1JsonReply("clients");
|
||||||
|
|
||||||
for (Json::Value workspaceJson : workspacesJson) {
|
for (Json::Value workspaceJson : workspacesJson) {
|
||||||
std::string workspaceName = workspaceJson["name"].asString();
|
std::string workspaceName = workspaceJson["name"].asString();
|
||||||
if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) &&
|
if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) &&
|
||||||
(!workspaceName.starts_with("special") || showSpecial()) &&
|
(!workspaceName.starts_with("special") || showSpecial()) &&
|
||||||
!isWorkspaceIgnored(workspaceName)) {
|
!isWorkspaceIgnored(workspaceName)) {
|
||||||
createWorkspace(workspaceJson, clientsJson);
|
m_workspacesToCreate.emplace_back(workspaceJson, clientsJson);
|
||||||
} else {
|
} else {
|
||||||
extendOrphans(workspaceJson["id"].asInt(), clientsJson);
|
extendOrphans(workspaceJson["id"].asInt(), clientsJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fillPersistentWorkspaces();
|
spdlog::debug("Initializing persistent workspaces");
|
||||||
createPersistentWorkspaces();
|
if (m_persistentWorkspaceConfig.isObject()) {
|
||||||
|
// a persistent workspace config is defined, so use that instead of workspace rules
|
||||||
|
loadPersistentWorkspacesFromConfig(clientsJson);
|
||||||
|
} else {
|
||||||
|
// no persistent workspaces config defined, use Hyprland's workspace rules
|
||||||
|
loadPersistentWorkspacesFromWorkspaceRules(clientsJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) {
|
||||||
|
spdlog::trace("Extending orphans with workspace {}", workspaceId);
|
||||||
|
for (const auto &client : clientsJson) {
|
||||||
|
if (client["workspace"]["id"].asInt() == workspaceId) {
|
||||||
|
registerOrphanWindow({client});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::init() {
|
||||||
|
m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString();
|
||||||
|
|
||||||
|
initializeWorkspaces();
|
||||||
updateWindowCount();
|
updateWindowCount();
|
||||||
|
|
||||||
sortWorkspaces();
|
sortWorkspaces();
|
||||||
|
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,7 +788,8 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma
|
||||||
m_name(workspace_data["name"].asString()),
|
m_name(workspace_data["name"].asString()),
|
||||||
m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc
|
m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc
|
||||||
m_windows(workspace_data["windows"].asInt()),
|
m_windows(workspace_data["windows"].asInt()),
|
||||||
m_active(true) {
|
m_isActive(true),
|
||||||
|
m_isPersistent(workspace_data["persistent"].asBool()) {
|
||||||
if (m_name.starts_with("name:")) {
|
if (m_name.starts_with("name:")) {
|
||||||
m_name = m_name.substr(5);
|
m_name = m_name.substr(5);
|
||||||
} else if (m_name.starts_with("special")) {
|
} else if (m_name.starts_with("special")) {
|
||||||
|
@ -704,10 +797,6 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma
|
||||||
m_isSpecial = true;
|
m_isSpecial = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workspace_data.isMember("persistent")) {
|
|
||||||
m_isPersistent = workspace_data["persistent"].asBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_button.add_events(Gdk::BUTTON_PRESS_MASK);
|
m_button.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||||
m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked),
|
m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked),
|
||||||
false);
|
false);
|
||||||
|
@ -813,8 +902,7 @@ void Workspaces::sortWorkspaces() {
|
||||||
if (a->id() == -99 || b->id() == -99) {
|
if (a->id() == -99 || b->id() == -99) {
|
||||||
return b->id() == -99;
|
return b->id() == -99;
|
||||||
}
|
}
|
||||||
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID
|
// both are 0 (not yet named persistents) / named specials (-98 <= ID <= -1)
|
||||||
// <=-1)
|
|
||||||
return isNameLess;
|
return isNameLess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -833,6 +921,7 @@ void Workspaces::sortWorkspaces() {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map) {
|
std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map) {
|
||||||
|
spdlog::trace("Selecting icon for workspace {}", name());
|
||||||
if (isUrgent()) {
|
if (isUrgent()) {
|
||||||
auto urgentIconIt = icons_map.find("urgent");
|
auto urgentIconIt = icons_map.find("urgent");
|
||||||
if (urgentIconIt != icons_map.end()) {
|
if (urgentIconIt != icons_map.end()) {
|
||||||
|
@ -889,21 +978,19 @@ std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Workspace::handleClicked(GdkEventButton *bt) const {
|
bool Workspace::handleClicked(GdkEventButton *bt) const {
|
||||||
if (bt->type == GDK_BUTTON_PRESS) {
|
try {
|
||||||
try {
|
if (id() > 0) { // normal
|
||||||
if (id() > 0) { // normal or numbered persistent
|
gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id()));
|
||||||
gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id()));
|
} else if (!isSpecial()) { // named (this includes persistent)
|
||||||
} else if (!isSpecial()) { // named
|
gIPC->getSocket1Reply("dispatch workspace name:" + name());
|
||||||
gIPC->getSocket1Reply("dispatch workspace name:" + name());
|
} else if (id() != -99) { // named special
|
||||||
} else if (id() != -99) { // named special
|
gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name());
|
||||||
gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name());
|
} else { // special
|
||||||
} else { // special
|
gIPC->getSocket1Reply("dispatch togglespecialworkspace");
|
||||||
gIPC->getSocket1Reply("dispatch togglespecialworkspace");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
spdlog::error("Failed to dispatch workspace: {}", e.what());
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
spdlog::error("Failed to dispatch workspace: {}", e.what());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,4 +66,4 @@ std::string& RegexCollection::get(std::string& value) {
|
||||||
return get(value, matched_any);
|
return get(value, matched_any);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace waybar::util
|
} // namespace waybar::util
|
||||||
|
|
Loading…
Reference in New Issue