Skip to content

Commit

Permalink
wip: reimplement old persistent workspaces, cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
zjeffer committed Feb 17, 2024
1 parent 57292d3 commit 93bcac2
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 42 deletions.
14 changes: 9 additions & 5 deletions include/modules/hyprland/workspaces.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ class Workspaces : public AModule, public EventHandler {
void onEvent(const std::string& e) override;
void updateWindowCount();
void sortWorkspaces();
void createWorkspace(Json::Value const& workspace_data,
Json::Value const& clients_data = Json::Value::nullRef);
void createWorkspace(Json::Value const& workspaceData,
Json::Value const& clientsData = Json::Value::nullRef);
void removeWorkspace(std::string const& name);
void setUrgentWorkspace(std::string const& windowaddress);
void parseConfig(const Json::Value& config);
Expand Down Expand Up @@ -167,11 +167,17 @@ class Workspaces : public AModule, public EventHandler {
void doUpdate();

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_showSpecial = false;
bool m_activeOnly = false;
Json::Value m_persistentWorkspaceConfig;

// Map for windows stored in workspaces not present in the current bar.
// This happens when the user has multiple monitors (hence, multiple bars)
Expand All @@ -186,8 +192,6 @@ class Workspaces : public AModule, public EventHandler {
{"NUMBER", SortMethod::NUMBER},
{"DEFAULT", SortMethod::DEFAULT}};

void initializeWorkspaces();

std::string m_format;

std::map<std::string, std::string> m_iconsMap;
Expand Down
174 changes: 137 additions & 37 deletions src/modules/hyprland/workspaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
gIPC = std::make_unique<IPC>();
}

setCurrentMonitorId();
init();
registerIpc();
}
Expand All @@ -64,7 +65,6 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void {
for (std::string &name : formatIcons.getMemberNames()) {
m_iconsMap.emplace(name, formatIcons[name].asString());
}

m_iconsMap.emplace("", "");
}

Expand Down Expand Up @@ -112,6 +112,17 @@ 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"];
m_formatWindowSeparator =
formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " ";
Expand All @@ -131,9 +142,9 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void {
[this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); });
}

void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_paylod) {
if (!create_window_paylod.isEmpty(*this)) {
m_orphanWindowMap[create_window_paylod.getAddress()] = create_window_paylod.repr(*this);
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
if (!create_window_payload.isEmpty(*this)) {
m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this);
}
}

Expand Down Expand Up @@ -180,6 +191,7 @@ void Workspaces::doUpdate() {
m_workspacesToCreate.clear();

// get all active workspaces
spdlog::trace("Getting active workspaces");
auto monitors = gIPC->getSocket1JsonReply("monitors");
std::vector<std::string> visibleWorkspaces;
for (Json::Value &monitor : monitors) {
Expand All @@ -189,6 +201,7 @@ void Workspaces::doUpdate() {
}
}

spdlog::trace("Updating workspace states");
for (auto &workspace : m_workspaces) {
// active
workspace->setActive(workspace->name() == m_activeWorkspaceName);
Expand All @@ -209,6 +222,7 @@ void Workspaces::doUpdate() {
workspace->update(m_format, workspaceIcon);
}

spdlog::trace("Updating window count");
bool anyWindowCreated = false;
std::vector<WindowCreationPayload> notCreated;

Expand Down Expand Up @@ -588,65 +602,150 @@ void Workspaces::removeWorkspace(std::string const &name) {
m_workspaces.erase(workspace);
}

void Workspaces::initializeWorkspaces() {
spdlog::debug("Initializing workspaces");
Json::Value createPersistentWorkspaceData(std::string const &name, std::string const &monitor) {
spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor);
Json::Value workspaceData;
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 the workspace rules changed since last initialization, make sure we reset everything:
for (auto &workspace : m_workspaces) {
m_workspacesToRemove.push_back(workspace->name());
void Workspaces::loadPersistentWorkspacesFromConfig(Json::Value const &clientsJson) {
spdlog::info("Loading persistent workspaces from Waybar config");
const std::vector<std::string> keys = m_persistentWorkspaceConfig.getMemberNames();
std::vector<std::string> persistentWorkspacesToCreate;

const std::string currentMonitor = m_bar.output->name;
const bool monitorInConfig = std::find(keys.begin(), keys.end(), currentMonitor) != keys.end();
for (const std::string &key : keys) {
// only add if either:
// 1. key is the current monitor name
// 2. key is "*" and this monitor is not already defined in the config
bool canCreate = key == currentMonitor || (key == "*" && !monitorInConfig);
const Json::Value &value = m_persistentWorkspaceConfig[key];
spdlog::trace("Parsing persistent workspace config: {} => {}", key, value.toStyledString());

if (value.isInt()) {
// value is a number => create that many workspaces for this monitor
if (canCreate) {
int amount = value.asInt();
spdlog::debug("Creating {} persistent workspaces for monitor {}", amount, currentMonitor);
for (int i = 0; i < amount; i++) {
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
if (canCreate) {
for (const Json::Value &workspace : value) {
if (workspace.isInt()) {
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() == currentMonitor) {
persistentWorkspacesToCreate.emplace_back(currentMonitor);
break;
}
}
}
} else {
// this workspace should be displayed on all monitors
persistentWorkspacesToCreate.emplace_back(key);
}
}

// get all current workspaces
auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces");
auto const clientsJson = gIPC->getSocket1JsonReply("clients");

for (Json::Value workspaceJson : workspacesJson) {
std::string workspaceName = workspaceJson["name"].asString();
if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) &&
(!workspaceName.starts_with("special") || showSpecial()) &&
!isWorkspaceIgnored(workspaceName)) {
m_workspacesToCreate.emplace_back(workspaceJson, clientsJson);
}
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");

spdlog::debug("Initializing persistent workspaces");
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;
}
auto const &workspace = rule["workspaceString"].asString();
auto const &monitor = rule["monitor"].asString();
if (!rule["persistent"].asBool()) {
continue;
}
auto const &workspace = rule["workspaceString"].asString();
auto const &monitor = rule["monitor"].asString();
// create this workspace persistently if:
// 1. allOutputs is true
// 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
Json::Value workspaceData;
try {
// numbered persistent workspaces get the name as ID
workspaceData["id"] = workspace == "special" ? -99 : std::stoi(workspace);
} catch (const std::exception &e) {
// named persistent workspaces start with ID=0
workspaceData["id"] = 0;
}
workspaceData["name"] = workspace;
workspaceData["monitor"] = monitor;
workspaceData["windows"] = 0;
workspaceData["persistent"] = true;

auto workspaceData = createPersistentWorkspaceData(workspace, m_bar.output->name);
m_workspacesToCreate.emplace_back(workspaceData, clientsJson);
} else {
m_workspacesToRemove.emplace_back(workspace);
}
}
}

void Workspaces::setCurrentMonitorId() {
// get monitor ID from name (used by persistent workspaces)
m_monitorId = 0;
auto monitors = gIPC->getSocket1JsonReply("monitors");
auto currentMonitor = std::find_if(
monitors.begin(), monitors.end(),
[this](const Json::Value &m) { return m["name"].asString() == m_bar.output->name; });
if (currentMonitor == monitors.end()) {
spdlog::error("Monitor '{}' does not have an ID? Using 0", m_bar.output->name);
} else {
m_monitorId = (*currentMonitor)["id"].asInt();
}
}

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

// get all current workspaces
auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces");
auto const clientsJson = gIPC->getSocket1JsonReply("clients");

for (Json::Value workspaceJson : workspacesJson) {
std::string workspaceName = workspaceJson["name"].asString();
if ((allOutputs() || m_bar.output->name == workspaceJson["monitor"].asString()) &&
(!workspaceName.starts_with("special") || showSpecial()) &&
!isWorkspaceIgnored(workspaceName)) {
m_workspacesToCreate.emplace_back(workspaceJson, clientsJson);
}
}

spdlog::debug("Initializing persistent workspaces");
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::init() {
m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString();

Expand Down Expand Up @@ -802,6 +901,7 @@ void Workspaces::sortWorkspaces() {
}

std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map) {
spdlog::trace("Selecting icon for workspace {}", name());
if (isUrgent()) {
auto urgentIconIt = icons_map.find("urgent");
if (urgentIconIt != icons_map.end()) {
Expand Down

0 comments on commit 93bcac2

Please sign in to comment.