hyprland/workspaces: sort methods
parent
9ba9d57c8c
commit
f5bb086460
|
@ -54,6 +54,8 @@ class Workspaces : public AModule, public EventHandler {
|
||||||
void sortWorkspaces();
|
void sortWorkspaces();
|
||||||
void createWorkspace(Json::Value const& workspaceData,
|
void createWorkspace(Json::Value const& workspaceData,
|
||||||
Json::Value const& clientsData = Json::Value::nullRef);
|
Json::Value const& clientsData = Json::Value::nullRef);
|
||||||
|
|
||||||
|
Json::Value createMonitorWorkspaceData(std::string const& name, std::string const& monitor);
|
||||||
void removeWorkspace(std::string const& name);
|
void removeWorkspace(std::string const& name);
|
||||||
void setUrgentWorkspace(std::string const& windowaddress);
|
void setUrgentWorkspace(std::string const& windowaddress);
|
||||||
|
|
||||||
|
|
|
@ -13,26 +13,6 @@
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) {
|
|
||||||
// Rules that match against title are prioritized
|
|
||||||
// Rules that don't specify if they're matching against either title or class are deprioritized
|
|
||||||
bool const hasTitle = window_rule.find("title") != std::string::npos;
|
|
||||||
bool const hasClass = window_rule.find("class") != std::string::npos;
|
|
||||||
|
|
||||||
if (hasTitle && hasClass) {
|
|
||||||
m_anyWindowRewriteRuleUsesTitle = true;
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
if (hasTitle) {
|
|
||||||
m_anyWindowRewriteRuleUsesTitle = true;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if (hasClass) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
|
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
|
||||||
: AModule(config, "workspaces", id, false, false), m_bar(bar), m_box(bar.orientation, 0) {
|
: AModule(config, "workspaces", id, false, false), m_bar(bar), m_box(bar.orientation, 0) {
|
||||||
modulesReady = true;
|
modulesReady = true;
|
||||||
|
@ -54,132 +34,87 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||||
registerIpc();
|
registerIpc();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Workspaces::parseConfig(const Json::Value &config) -> void {
|
Workspaces::~Workspaces() {
|
||||||
const auto &configFormat = config["format"];
|
gIPC->unregisterForIPC(this);
|
||||||
m_format = configFormat.isString() ? configFormat.asString() : "{name}";
|
// wait for possible event handler to finish
|
||||||
m_withIcon = m_format.find("{icon}") != std::string::npos;
|
std::lock_guard<std::mutex> lg(m_mutex);
|
||||||
|
|
||||||
if (m_withIcon && m_iconsMap.empty()) {
|
|
||||||
populateIconsMap(config["format-icons"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
populateBoolConfig(config, "all-outputs", m_allOutputs);
|
|
||||||
populateBoolConfig(config, "show-special", m_showSpecial);
|
|
||||||
populateBoolConfig(config, "active-only", m_activeOnly);
|
|
||||||
populateBoolConfig(config, "move-to-monitor", m_moveToMonitor);
|
|
||||||
|
|
||||||
populateSortByConfig(config);
|
|
||||||
populateIgnoreWorkspacesConfig(config);
|
|
||||||
populatePersistentWorkspacesConfig(config);
|
|
||||||
populateFormatWindowSeparatorConfig(config);
|
|
||||||
populateWindowRewriteConfig(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void {
|
void Workspaces::init() {
|
||||||
for (const auto &name : formatIcons.getMemberNames()) {
|
m_activeWorkspaceName = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString();
|
||||||
m_iconsMap.emplace(name, formatIcons[name].asString());
|
|
||||||
}
|
initializeWorkspaces();
|
||||||
m_iconsMap.emplace("", "");
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member)
|
Json::Value Workspaces::createMonitorWorkspaceData(std::string const &name,
|
||||||
-> void {
|
std::string const &monitor) {
|
||||||
auto configValue = config[key];
|
spdlog::trace("Creating persistent workspace: {} on monitor {}", name, monitor);
|
||||||
if (configValue.isBool()) {
|
Json::Value workspaceData;
|
||||||
member = configValue.asBool();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Workspaces::populateSortByConfig(const Json::Value &config) -> void {
|
|
||||||
auto configSortBy = config["sort-by"];
|
|
||||||
if (configSortBy.isString()) {
|
|
||||||
auto sortByStr = configSortBy.asString();
|
|
||||||
try {
|
try {
|
||||||
m_sortBy = m_enumParser.parseStringToEnum(sortByStr, m_sortMap);
|
// numbered persistent workspaces get the name as ID
|
||||||
} catch (const std::invalid_argument &e) {
|
workspaceData["id"] = name == "special" ? -99 : std::stoi(name);
|
||||||
m_sortBy = SortMethod::DEFAULT;
|
} catch (const std::exception &e) {
|
||||||
spdlog::warn(
|
// named persistent workspaces start with ID=0
|
||||||
"Invalid string representation for sort-by. Falling back to default sort method.");
|
workspaceData["id"] = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
workspaceData["name"] = name;
|
||||||
|
workspaceData["monitor"] = monitor;
|
||||||
|
workspaceData["windows"] = 0;
|
||||||
|
return workspaceData;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> void {
|
void Workspaces::createWorkspace(Json::Value const &workspace_data,
|
||||||
auto ignoreWorkspaces = config["ignore-workspaces"];
|
Json::Value const &clients_data) {
|
||||||
if (ignoreWorkspaces.isArray()) {
|
auto workspaceName = workspace_data["name"].asString();
|
||||||
for (const auto &workspaceRegex : ignoreWorkspaces) {
|
spdlog::debug("Creating workspace {}", workspaceName);
|
||||||
if (workspaceRegex.isString()) {
|
|
||||||
std::string ruleString = workspaceRegex.asString();
|
|
||||||
try {
|
|
||||||
const std::regex rule{ruleString, std::regex_constants::icase};
|
|
||||||
m_ignoreWorkspaces.emplace_back(rule);
|
|
||||||
} catch (const std::regex_error &e) {
|
|
||||||
spdlog::error("Invalid rule {}: {}", ruleString, e.what());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spdlog::error("Not a string: '{}'", workspaceRegex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void {
|
// avoid recreating existing workspaces
|
||||||
if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) {
|
auto workspace = std::find_if(
|
||||||
spdlog::warn(
|
m_workspaces.begin(), m_workspaces.end(),
|
||||||
"persistent_workspaces is deprecated. Please change config to use persistent-workspaces.");
|
[workspaceName](std::unique_ptr<Workspace> const &w) {
|
||||||
m_persistentWorkspaceConfig =
|
return (workspaceName.starts_with("special:") && workspaceName.substr(8) == w->name()) ||
|
||||||
config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value()));
|
workspaceName == w->name();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (workspace != m_workspaces.end()) {
|
||||||
|
// don't recreate workspace, but update persistency if necessary
|
||||||
|
const auto keys = workspace_data.getMemberNames();
|
||||||
|
|
||||||
|
const auto *k = "persistent-rule";
|
||||||
|
if (std::find(keys.begin(), keys.end(), k) != keys.end()) {
|
||||||
|
spdlog::debug("Set dynamic persistency of workspace {} to: {}", workspaceName,
|
||||||
|
workspace_data[k].asBool() ? "true" : "false");
|
||||||
|
(*workspace)->setPersistentRule(workspace_data[k].asBool());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void {
|
k = "persistent-config";
|
||||||
auto formatWindowSeparator = config["format-window-separator"];
|
if (std::find(keys.begin(), keys.end(), k) != keys.end()) {
|
||||||
m_formatWindowSeparator =
|
spdlog::debug("Set config persistency of workspace {} to: {}", workspaceName,
|
||||||
formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " ";
|
workspace_data[k].asBool() ? "true" : "false");
|
||||||
}
|
(*workspace)->setPersistentConfig(workspace_data[k].asBool());
|
||||||
|
}
|
||||||
|
|
||||||
auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void {
|
|
||||||
const auto &windowRewrite = config["window-rewrite"];
|
|
||||||
if (!windowRewrite.isObject()) {
|
|
||||||
spdlog::debug("window-rewrite is not defined or is not an object, using default rules.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &windowRewriteDefaultConfig = config["window-rewrite-default"];
|
// create new workspace
|
||||||
std::string windowRewriteDefault =
|
m_workspaces.emplace_back(std::make_unique<Workspace>(workspace_data, *this, clients_data));
|
||||||
windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?";
|
Gtk::Button &newWorkspaceButton = m_workspaces.back()->button();
|
||||||
|
m_box.pack_start(newWorkspaceButton, false, false);
|
||||||
m_windowRewriteRules = util::RegexCollection(
|
sortWorkspaces();
|
||||||
windowRewrite, windowRewriteDefault,
|
newWorkspaceButton.show_all();
|
||||||
[this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
|
void Workspaces::createWorkspacesToCreate() {
|
||||||
if (!create_window_payload.isEmpty(*this)) {
|
for (const auto &[workspaceData, clientsData] : m_workspacesToCreate) {
|
||||||
m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this);
|
createWorkspace(workspaceData, clientsData);
|
||||||
}
|
}
|
||||||
}
|
if (!m_workspacesToCreate.empty()) {
|
||||||
|
updateWindowCount();
|
||||||
auto Workspaces::registerIpc() -> void {
|
sortWorkspaces();
|
||||||
gIPC->registerForIPC("workspace", this);
|
|
||||||
gIPC->registerForIPC("activespecial", this);
|
|
||||||
gIPC->registerForIPC("createworkspace", this);
|
|
||||||
gIPC->registerForIPC("destroyworkspace", this);
|
|
||||||
gIPC->registerForIPC("focusedmon", this);
|
|
||||||
gIPC->registerForIPC("moveworkspace", this);
|
|
||||||
gIPC->registerForIPC("renameworkspace", this);
|
|
||||||
gIPC->registerForIPC("openwindow", this);
|
|
||||||
gIPC->registerForIPC("closewindow", this);
|
|
||||||
gIPC->registerForIPC("movewindow", this);
|
|
||||||
gIPC->registerForIPC("urgent", this);
|
|
||||||
gIPC->registerForIPC("configreloaded", this);
|
|
||||||
|
|
||||||
if (windowRewriteConfigUsesTitle()) {
|
|
||||||
spdlog::info(
|
|
||||||
"Registering for Hyprland's 'windowtitle' events because a user-defined window "
|
|
||||||
"rewrite rule uses the 'title' field.");
|
|
||||||
gIPC->registerForIPC("windowtitle", this);
|
|
||||||
}
|
}
|
||||||
|
m_workspacesToCreate.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -207,22 +142,25 @@ void Workspaces::doUpdate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::removeWorkspacesToRemove() {
|
void Workspaces::extendOrphans(int workspaceId, Json::Value const &clientsJson) {
|
||||||
for (const auto &workspaceName : m_workspacesToRemove) {
|
spdlog::trace("Extending orphans with workspace {}", workspaceId);
|
||||||
removeWorkspace(workspaceName);
|
for (const auto &client : clientsJson) {
|
||||||
|
if (client["workspace"]["id"].asInt() == workspaceId) {
|
||||||
|
registerOrphanWindow({client});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_workspacesToRemove.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::createWorkspacesToCreate() {
|
std::string Workspaces::getRewrite(std::string window_class, std::string window_title) {
|
||||||
for (const auto &[workspaceData, clientsData] : m_workspacesToCreate) {
|
std::string windowReprKey;
|
||||||
createWorkspace(workspaceData, clientsData);
|
if (windowRewriteConfigUsesTitle()) {
|
||||||
|
windowReprKey = fmt::format("class<{}> title<{}>", window_class, window_title);
|
||||||
|
} else {
|
||||||
|
windowReprKey = fmt::format("class<{}>", window_class);
|
||||||
}
|
}
|
||||||
if (!m_workspacesToCreate.empty()) {
|
auto const rewriteRule = m_windowRewriteRules.get(windowReprKey);
|
||||||
updateWindowCount();
|
return fmt::format(fmt::runtime(rewriteRule), fmt::arg("class", window_class),
|
||||||
sortWorkspaces();
|
fmt::arg("title", window_title));
|
||||||
}
|
|
||||||
m_workspacesToCreate.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Workspaces::getVisibleWorkspaces() {
|
std::vector<std::string> Workspaces::getVisibleWorkspaces() {
|
||||||
|
@ -242,62 +180,36 @@ std::vector<std::string> Workspaces::getVisibleWorkspaces() {
|
||||||
return visibleWorkspaces;
|
return visibleWorkspaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::updateWorkspaceStates(const std::vector<std::string> &visibleWorkspaces) {
|
void Workspaces::initializeWorkspaces() {
|
||||||
auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces");
|
spdlog::debug("Initializing workspaces");
|
||||||
for (auto &workspace : m_workspaces) {
|
|
||||||
workspace->setActive(workspace->name() == m_activeWorkspaceName ||
|
|
||||||
workspace->name() == m_activeSpecialWorkspaceName);
|
|
||||||
if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) {
|
|
||||||
workspace->setUrgent(false);
|
|
||||||
}
|
|
||||||
workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(),
|
|
||||||
workspace->name()) != visibleWorkspaces.end());
|
|
||||||
std::string &workspaceIcon = m_iconsMap[""];
|
|
||||||
if (m_withIcon) {
|
|
||||||
workspaceIcon = workspace->selectIcon(m_iconsMap);
|
|
||||||
}
|
|
||||||
auto updatedWorkspace = std::find_if(
|
|
||||||
updatedWorkspaces.begin(), updatedWorkspaces.end(), [&workspace](const auto &w) {
|
|
||||||
auto wNameRaw = w["name"].asString();
|
|
||||||
auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw;
|
|
||||||
return wName == workspace->name();
|
|
||||||
});
|
|
||||||
if (updatedWorkspace != updatedWorkspaces.end()) {
|
|
||||||
workspace->setOutput((*updatedWorkspace)["monitor"].asString());
|
|
||||||
}
|
|
||||||
workspace->update(m_format, workspaceIcon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Workspaces::updateWindowsToCreate() {
|
// if the workspace rules changed since last initialization, make sure we reset everything:
|
||||||
bool anyWindowCreated = false;
|
|
||||||
std::vector<WindowCreationPayload> notCreated;
|
|
||||||
for (auto &windowPayload : m_windowsToCreate) {
|
|
||||||
bool created = false;
|
|
||||||
for (auto &workspace : m_workspaces) {
|
for (auto &workspace : m_workspaces) {
|
||||||
if (workspace->onWindowOpened(windowPayload)) {
|
m_workspacesToRemove.push_back(workspace->name());
|
||||||
created = true;
|
|
||||||
anyWindowCreated = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!created) {
|
// get all current workspaces
|
||||||
static auto const WINDOW_CREATION_TIMEOUT = 2;
|
auto const workspacesJson = gIPC->getSocket1JsonReply("workspaces");
|
||||||
if (windowPayload.incrementTimeSpentUncreated() < WINDOW_CREATION_TIMEOUT) {
|
auto const clientsJson = gIPC->getSocket1JsonReply("clients");
|
||||||
notCreated.push_back(windowPayload);
|
|
||||||
|
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);
|
||||||
} else {
|
} else {
|
||||||
registerOrphanWindow(windowPayload);
|
extendOrphans(workspaceJson["id"].asInt(), clientsJson);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
m_windowsToCreate.clear();
|
|
||||||
m_windowsToCreate = notCreated;
|
|
||||||
return anyWindowCreated;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Workspaces::update() -> void {
|
spdlog::debug("Initializing persistent workspaces");
|
||||||
doUpdate();
|
if (m_persistentWorkspaceConfig.isObject()) {
|
||||||
AModule::update();
|
// a persistent workspace config is defined, so use that instead of workspace rules
|
||||||
|
loadPersistentWorkspacesFromConfig(clientsJson);
|
||||||
|
}
|
||||||
|
// load Hyprland's workspace rules
|
||||||
|
loadPersistentWorkspacesFromWorkspaceRules(clientsJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDoubleSpecial(std::string const &workspace_name) {
|
bool isDoubleSpecial(std::string const &workspace_name) {
|
||||||
|
@ -319,6 +231,90 @@ bool Workspaces::isWorkspaceIgnored(std::string const &name) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const &workspace : persistentWorkspacesToCreate) {
|
||||||
|
auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name);
|
||||||
|
workspaceData["persistent-config"] = true;
|
||||||
|
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 = createMonitorWorkspaceData(workspace, m_bar.output->name);
|
||||||
|
workspaceData["persistent-rule"] = true;
|
||||||
|
m_workspacesToCreate.emplace_back(workspaceData, clientsJson);
|
||||||
|
} else {
|
||||||
|
m_workspacesToRemove.emplace_back(workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Workspaces::onEvent(const std::string &ev) {
|
void Workspaces::onEvent(const std::string &ev) {
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
|
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
|
||||||
|
@ -569,66 +565,139 @@ void Workspaces::onConfigReloaded() {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::updateWindowCount() {
|
auto Workspaces::parseConfig(const Json::Value &config) -> void {
|
||||||
const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces");
|
const auto &configFormat = config["format"];
|
||||||
for (auto &workspace : m_workspaces) {
|
m_format = configFormat.isString() ? configFormat.asString() : "{name}";
|
||||||
auto workspaceJson =
|
m_withIcon = m_format.find("{icon}") != std::string::npos;
|
||||||
std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) {
|
|
||||||
return x["name"].asString() == workspace->name() ||
|
if (m_withIcon && m_iconsMap.empty()) {
|
||||||
(workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name());
|
populateIconsMap(config["format-icons"]);
|
||||||
});
|
|
||||||
uint32_t count = 0;
|
|
||||||
if (workspaceJson != workspacesJson.end()) {
|
|
||||||
try {
|
|
||||||
count = (*workspaceJson)["windows"].asUInt();
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
spdlog::error("Failed to update window count: {}", e.what());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
populateBoolConfig(config, "all-outputs", m_allOutputs);
|
||||||
|
populateBoolConfig(config, "show-special", m_showSpecial);
|
||||||
|
populateBoolConfig(config, "active-only", m_activeOnly);
|
||||||
|
populateBoolConfig(config, "move-to-monitor", m_moveToMonitor);
|
||||||
|
|
||||||
|
populateSortByConfig(config);
|
||||||
|
populateIgnoreWorkspacesConfig(config);
|
||||||
|
populatePersistentWorkspacesConfig(config);
|
||||||
|
populateFormatWindowSeparatorConfig(config);
|
||||||
|
populateWindowRewriteConfig(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void {
|
||||||
|
for (const auto &name : formatIcons.getMemberNames()) {
|
||||||
|
m_iconsMap.emplace(name, formatIcons[name].asString());
|
||||||
}
|
}
|
||||||
workspace->setWindows(count);
|
m_iconsMap.emplace("", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Workspaces::populateBoolConfig(const Json::Value &config, const std::string &key, bool &member)
|
||||||
|
-> void {
|
||||||
|
auto configValue = config[key];
|
||||||
|
if (configValue.isBool()) {
|
||||||
|
member = configValue.asBool();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::createWorkspace(Json::Value const &workspace_data,
|
auto Workspaces::populateSortByConfig(const Json::Value &config) -> void {
|
||||||
Json::Value const &clients_data) {
|
auto configSortBy = config["sort-by"];
|
||||||
auto workspaceName = workspace_data["name"].asString();
|
if (configSortBy.isString()) {
|
||||||
spdlog::debug("Creating workspace {}", workspaceName);
|
auto sortByStr = configSortBy.asString();
|
||||||
|
try {
|
||||||
// avoid recreating existing workspaces
|
m_sortBy = m_enumParser.parseStringToEnum(sortByStr, m_sortMap);
|
||||||
auto workspace = std::find_if(
|
} catch (const std::invalid_argument &e) {
|
||||||
m_workspaces.begin(), m_workspaces.end(),
|
m_sortBy = SortMethod::DEFAULT;
|
||||||
[workspaceName](std::unique_ptr<Workspace> const &w) {
|
spdlog::warn(
|
||||||
return (workspaceName.starts_with("special:") && workspaceName.substr(8) == w->name()) ||
|
"Invalid string representation for sort-by. Falling back to default sort method.");
|
||||||
workspaceName == w->name();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (workspace != m_workspaces.end()) {
|
|
||||||
// don't recreate workspace, but update persistency if necessary
|
|
||||||
const auto keys = workspace_data.getMemberNames();
|
|
||||||
|
|
||||||
const auto *k = "persistent-rule";
|
|
||||||
if (std::find(keys.begin(), keys.end(), k) != keys.end()) {
|
|
||||||
spdlog::debug("Set dynamic persistency of workspace {} to: {}", workspaceName,
|
|
||||||
workspace_data[k].asBool() ? "true" : "false");
|
|
||||||
(*workspace)->setPersistentRule(workspace_data[k].asBool());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
k = "persistent-config";
|
|
||||||
if (std::find(keys.begin(), keys.end(), k) != keys.end()) {
|
|
||||||
spdlog::debug("Set config persistency of workspace {} to: {}", workspaceName,
|
|
||||||
workspace_data[k].asBool() ? "true" : "false");
|
|
||||||
(*workspace)->setPersistentConfig(workspace_data[k].asBool());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Workspaces::populateIgnoreWorkspacesConfig(const Json::Value &config) -> void {
|
||||||
|
auto ignoreWorkspaces = config["ignore-workspaces"];
|
||||||
|
if (ignoreWorkspaces.isArray()) {
|
||||||
|
for (const auto &workspaceRegex : ignoreWorkspaces) {
|
||||||
|
if (workspaceRegex.isString()) {
|
||||||
|
std::string ruleString = workspaceRegex.asString();
|
||||||
|
try {
|
||||||
|
const std::regex rule{ruleString, std::regex_constants::icase};
|
||||||
|
m_ignoreWorkspaces.emplace_back(rule);
|
||||||
|
} catch (const std::regex_error &e) {
|
||||||
|
spdlog::error("Invalid rule {}: {}", ruleString, e.what());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spdlog::error("Not a string: '{}'", workspaceRegex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Workspaces::populatePersistentWorkspacesConfig(const Json::Value &config) -> void {
|
||||||
|
if (config.isMember("persistent-workspaces") || config.isMember("persistent_workspaces")) {
|
||||||
|
spdlog::warn(
|
||||||
|
"persistent_workspaces is deprecated. Please change config to use persistent-workspaces.");
|
||||||
|
m_persistentWorkspaceConfig =
|
||||||
|
config.get("persistent-workspaces", config.get("persistent_workspaces", Json::Value()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Workspaces::populateFormatWindowSeparatorConfig(const Json::Value &config) -> void {
|
||||||
|
auto formatWindowSeparator = config["format-window-separator"];
|
||||||
|
m_formatWindowSeparator =
|
||||||
|
formatWindowSeparator.isString() ? formatWindowSeparator.asString() : " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void {
|
||||||
|
const auto &windowRewrite = config["window-rewrite"];
|
||||||
|
if (!windowRewrite.isObject()) {
|
||||||
|
spdlog::debug("window-rewrite is not defined or is not an object, using default rules.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create new workspace
|
const auto &windowRewriteDefaultConfig = config["window-rewrite-default"];
|
||||||
m_workspaces.emplace_back(std::make_unique<Workspace>(workspace_data, *this, clients_data));
|
std::string windowRewriteDefault =
|
||||||
Gtk::Button &newWorkspaceButton = m_workspaces.back()->button();
|
windowRewriteDefaultConfig.isString() ? windowRewriteDefaultConfig.asString() : "?";
|
||||||
m_box.pack_start(newWorkspaceButton, false, false);
|
|
||||||
sortWorkspaces();
|
m_windowRewriteRules = util::RegexCollection(
|
||||||
newWorkspaceButton.show_all();
|
windowRewrite, windowRewriteDefault,
|
||||||
|
[this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
|
||||||
|
if (!create_window_payload.isEmpty(*this)) {
|
||||||
|
m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Workspaces::registerIpc() -> void {
|
||||||
|
gIPC->registerForIPC("workspace", this);
|
||||||
|
gIPC->registerForIPC("activespecial", this);
|
||||||
|
gIPC->registerForIPC("createworkspace", this);
|
||||||
|
gIPC->registerForIPC("destroyworkspace", this);
|
||||||
|
gIPC->registerForIPC("focusedmon", this);
|
||||||
|
gIPC->registerForIPC("moveworkspace", this);
|
||||||
|
gIPC->registerForIPC("renameworkspace", this);
|
||||||
|
gIPC->registerForIPC("openwindow", this);
|
||||||
|
gIPC->registerForIPC("closewindow", this);
|
||||||
|
gIPC->registerForIPC("movewindow", this);
|
||||||
|
gIPC->registerForIPC("urgent", this);
|
||||||
|
gIPC->registerForIPC("configreloaded", this);
|
||||||
|
|
||||||
|
if (windowRewriteConfigUsesTitle()) {
|
||||||
|
spdlog::info(
|
||||||
|
"Registering for Hyprland's 'windowtitle' events because a user-defined window "
|
||||||
|
"rewrite rule uses the 'title' field.");
|
||||||
|
gIPC->registerForIPC("windowtitle", this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::removeWorkspacesToRemove() {
|
||||||
|
for (const auto &workspaceName : m_workspacesToRemove) {
|
||||||
|
removeWorkspace(workspaceName);
|
||||||
|
}
|
||||||
|
m_workspacesToRemove.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::removeWorkspace(std::string const &name) {
|
void Workspaces::removeWorkspace(std::string const &name) {
|
||||||
|
@ -652,106 +721,6 @@ void Workspaces::removeWorkspace(std::string const &name) {
|
||||||
m_workspaces.erase(workspace);
|
m_workspaces.erase(workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value createMonitorWorkspaceData(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;
|
|
||||||
return workspaceData;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const &workspace : persistentWorkspacesToCreate) {
|
|
||||||
auto workspaceData = createMonitorWorkspaceData(workspace, m_bar.output->name);
|
|
||||||
workspaceData["persistent-config"] = true;
|
|
||||||
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 = createMonitorWorkspaceData(workspace, m_bar.output->name);
|
|
||||||
workspaceData["persistent-rule"] = true;
|
|
||||||
m_workspacesToCreate.emplace_back(workspaceData, clientsJson);
|
|
||||||
} else {
|
|
||||||
m_workspacesToRemove.emplace_back(workspace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Workspaces::setCurrentMonitorId() {
|
void Workspaces::setCurrentMonitorId() {
|
||||||
// get monitor ID from name (used by persistent workspaces)
|
// get monitor ID from name (used by persistent workspaces)
|
||||||
m_monitorId = 0;
|
m_monitorId = 0;
|
||||||
|
@ -767,60 +736,6 @@ void Workspaces::setCurrentMonitorId() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
extendOrphans(workspaceJson["id"].asInt(), 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);
|
|
||||||
}
|
|
||||||
// load 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();
|
|
||||||
dp.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
Workspaces::~Workspaces() {
|
|
||||||
gIPC->unregisterForIPC(this);
|
|
||||||
// wait for possible event handler to finish
|
|
||||||
std::lock_guard<std::mutex> lg(m_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Workspaces::sortWorkspaces() {
|
void Workspaces::sortWorkspaces() {
|
||||||
std::sort(m_workspaces.begin(), m_workspaces.end(),
|
std::sort(m_workspaces.begin(), m_workspaces.end(),
|
||||||
[&](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
|
[&](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
|
||||||
|
@ -903,15 +818,102 @@ void Workspaces::setUrgentWorkspace(std::string const &windowaddress) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Workspaces::getRewrite(std::string window_class, std::string window_title) {
|
auto Workspaces::update() -> void {
|
||||||
std::string windowReprKey;
|
doUpdate();
|
||||||
if (windowRewriteConfigUsesTitle()) {
|
AModule::update();
|
||||||
windowReprKey = fmt::format("class<{}> title<{}>", window_class, window_title);
|
|
||||||
} else {
|
|
||||||
windowReprKey = fmt::format("class<{}>", window_class);
|
|
||||||
}
|
|
||||||
auto const rewriteRule = m_windowRewriteRules.get(windowReprKey);
|
|
||||||
return fmt::format(fmt::runtime(rewriteRule), fmt::arg("class", window_class),
|
|
||||||
fmt::arg("title", window_title));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Workspaces::updateWindowCount() {
|
||||||
|
const Json::Value workspacesJson = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
for (auto &workspace : m_workspaces) {
|
||||||
|
auto workspaceJson =
|
||||||
|
std::find_if(workspacesJson.begin(), workspacesJson.end(), [&](Json::Value const &x) {
|
||||||
|
return x["name"].asString() == workspace->name() ||
|
||||||
|
(workspace->isSpecial() && x["name"].asString() == "special:" + workspace->name());
|
||||||
|
});
|
||||||
|
uint32_t count = 0;
|
||||||
|
if (workspaceJson != workspacesJson.end()) {
|
||||||
|
try {
|
||||||
|
count = (*workspaceJson)["windows"].asUInt();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
spdlog::error("Failed to update window count: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
workspace->setWindows(count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Workspaces::updateWindowsToCreate() {
|
||||||
|
bool anyWindowCreated = false;
|
||||||
|
std::vector<WindowCreationPayload> notCreated;
|
||||||
|
for (auto &windowPayload : m_windowsToCreate) {
|
||||||
|
bool created = false;
|
||||||
|
for (auto &workspace : m_workspaces) {
|
||||||
|
if (workspace->onWindowOpened(windowPayload)) {
|
||||||
|
created = true;
|
||||||
|
anyWindowCreated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!created) {
|
||||||
|
static auto const WINDOW_CREATION_TIMEOUT = 2;
|
||||||
|
if (windowPayload.incrementTimeSpentUncreated() < WINDOW_CREATION_TIMEOUT) {
|
||||||
|
notCreated.push_back(windowPayload);
|
||||||
|
} else {
|
||||||
|
registerOrphanWindow(windowPayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_windowsToCreate.clear();
|
||||||
|
m_windowsToCreate = notCreated;
|
||||||
|
return anyWindowCreated;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Workspaces::updateWorkspaceStates(const std::vector<std::string> &visibleWorkspaces) {
|
||||||
|
auto updatedWorkspaces = gIPC->getSocket1JsonReply("workspaces");
|
||||||
|
for (auto &workspace : m_workspaces) {
|
||||||
|
workspace->setActive(workspace->name() == m_activeWorkspaceName ||
|
||||||
|
workspace->name() == m_activeSpecialWorkspaceName);
|
||||||
|
if (workspace->name() == m_activeWorkspaceName && workspace->isUrgent()) {
|
||||||
|
workspace->setUrgent(false);
|
||||||
|
}
|
||||||
|
workspace->setVisible(std::find(visibleWorkspaces.begin(), visibleWorkspaces.end(),
|
||||||
|
workspace->name()) != visibleWorkspaces.end());
|
||||||
|
std::string &workspaceIcon = m_iconsMap[""];
|
||||||
|
if (m_withIcon) {
|
||||||
|
workspaceIcon = workspace->selectIcon(m_iconsMap);
|
||||||
|
}
|
||||||
|
auto updatedWorkspace = std::find_if(
|
||||||
|
updatedWorkspaces.begin(), updatedWorkspaces.end(), [&workspace](const auto &w) {
|
||||||
|
auto wNameRaw = w["name"].asString();
|
||||||
|
auto wName = wNameRaw.starts_with("special:") ? wNameRaw.substr(8) : wNameRaw;
|
||||||
|
return wName == workspace->name();
|
||||||
|
});
|
||||||
|
if (updatedWorkspace != updatedWorkspaces.end()) {
|
||||||
|
workspace->setOutput((*updatedWorkspace)["monitor"].asString());
|
||||||
|
}
|
||||||
|
workspace->update(m_format, workspaceIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Workspaces::windowRewritePriorityFunction(std::string const &window_rule) {
|
||||||
|
// Rules that match against title are prioritized
|
||||||
|
// Rules that don't specify if they're matching against either title or class are deprioritized
|
||||||
|
bool const hasTitle = window_rule.find("title") != std::string::npos;
|
||||||
|
bool const hasClass = window_rule.find("class") != std::string::npos;
|
||||||
|
|
||||||
|
if (hasTitle && hasClass) {
|
||||||
|
m_anyWindowRewriteRuleUsesTitle = true;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if (hasTitle) {
|
||||||
|
m_anyWindowRewriteRuleUsesTitle = true;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (hasClass) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::hyprland
|
} // namespace waybar::modules::hyprland
|
||||||
|
|
Loading…
Reference in New Issue