From e233022d1a4472a3af146a91dfa4f19f5123fb41 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Mon, 19 Jun 2023 23:08:03 +0300 Subject: [PATCH 1/4] hyprland/window: Rework, add .empty, .solo and . CSS classes --- include/modules/hyprland/window.hpp | 22 ++++- src/modules/hyprland/window.cpp | 144 ++++++++++++++++++++-------- 2 files changed, 119 insertions(+), 47 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index b5ab6fbb..a0681d4b 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -1,7 +1,5 @@ #include -#include - #include "ALabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" @@ -17,15 +15,29 @@ class Window : public waybar::ALabel, public EventHandler { auto update() -> void override; private: - int getActiveWorkspaceID(std::string); - std::string getLastWindowTitle(int); + struct Workspace { + int windows; + std::string last_window; + std::string last_window_title; + + static auto parse(const Json::Value&) -> Workspace; + }; + + auto getActiveWorkspace(const std::string&) -> Workspace; + auto getActiveWorkspace() -> Workspace; + auto getWindowClass(const std::string&) -> std::string; void onEvent(const std::string&) override; + void queryActiveWorkspace(); + void setClass(const std::string&, bool enable); bool separate_outputs; std::mutex mutex_; const Bar& bar_; util::JsonParser parser_; - std::string lastView; + std::string last_title_; + Workspace workspace_; + std::string solo_class_; + std::string last_solo_class_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 714d0a72..2025b616 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -6,7 +6,6 @@ #include #include "modules/hyprland/backend.hpp" -#include "util/command.hpp" #include "util/json.hpp" #include "util/rewrite_string.hpp" @@ -21,11 +20,13 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) gIPC = std::make_unique(); } - label_.hide(); - ALabel::update(); + queryActiveWorkspace(); + update(); // register for hyprland ipc gIPC->registerForIPC("activewindow", this); + gIPC->registerForIPC("closewindow", this); + gIPC->registerForIPC("movewindow", this); } Window::~Window() { @@ -38,69 +39,128 @@ auto Window::update() -> void { // fix ampersands std::lock_guard lg(mutex_); + std::string window_name = waybar::util::sanitize_string(workspace_.last_window_title); + + if (window_name != last_title_) { + if (window_name.empty()) { + label_.get_style_context()->add_class("empty"); + } else { + label_.get_style_context()->remove_class("empty"); + } + last_title_ = window_name; + } + + if (!format_.empty()) { label_.show(); label_.set_markup(fmt::format(fmt::runtime(format_), - waybar::util::rewriteString(lastView, config_["rewrite"]))); + waybar::util::rewriteString(window_name, config_["rewrite"]))); } else { label_.hide(); } + + setClass("empty", workspace_.windows == 0); + setClass("solo", workspace_.windows == 1); + + if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) { + if (bar_.window.get_style_context()->has_class(last_solo_class_)) { + bar_.window.get_style_context()->remove_class(last_solo_class_); + spdlog::trace("Removing solo class: {}", last_solo_class_); + } + } + + if (!solo_class_.empty() && solo_class_ != last_solo_class_) { + last_solo_class_ = solo_class_; + bar_.window.get_style_context()->add_class(solo_class_); + spdlog::trace("Adding solo class: {}", solo_class_); + } + ALabel::update(); } -int Window::getActiveWorkspaceID(std::string monitorName) { - auto cmd = waybar::util::command::exec("hyprctl monitors -j"); - assert(cmd.exit_code == 0); - Json::Value json = parser_.parse(cmd.out); +auto Window::getActiveWorkspace() -> Workspace { + const auto workspace = gIPC->getSocket1Reply("j/activeworkspace"); + Json::Value json = parser_.parse(workspace); + assert(json.isObject()); + return Workspace::parse(json); +} + +auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { + const auto monitors = gIPC->getSocket1Reply("j/monitors"); + Json::Value json = parser_.parse(monitors); assert(json.isArray()); auto monitor = std::find_if(json.begin(), json.end(), [&](Json::Value monitor) { return monitor["name"] == monitorName; }); if (monitor == std::end(json)) { - return 0; + spdlog::warn("Monitor not found: {}", monitorName); + return Workspace{0, "", ""}; } - return (*monitor)["activeWorkspace"]["id"].as(); + const int id = (*monitor)["activeWorkspace"]["id"].as(); + + const auto workspaces = gIPC->getSocket1Reply("j/workspaces"); + json = parser_.parse(workspaces); + assert(json.isArray()); + auto workspace = std::find_if(json.begin(), json.end(), + [&](Json::Value workspace) { return workspace["id"] == id; }); + if (workspace == std::end(json)) { + spdlog::warn("No workspace with id {}", id); + return Workspace{0, "", ""}; + } + return Workspace::parse(*workspace); } -std::string Window::getLastWindowTitle(int workspaceID) { - auto cmd = waybar::util::command::exec("hyprctl workspaces -j"); - assert(cmd.exit_code == 0); - Json::Value json = parser_.parse(cmd.out); - assert(json.isArray()); - auto workspace = std::find_if(json.begin(), json.end(), [&](Json::Value workspace) { - return workspace["id"].as() == workspaceID; - }); +auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { + return Workspace{ + value["windows"].as(), + value["lastwindow"].as(), + value["lastwindowtitle"].as() + }; +} - if (workspace == std::end(json)) { +auto Window::getWindowClass(const std::string& address) -> std::string { + const auto clients = gIPC->getSocket1Reply("j/clients"); + Json::Value json = parser_.parse(clients); + assert(json.isArray()); + auto client = std::find_if(json.begin(), json.end(), + [&](Json::Value window) { return window["address"] == address; }); + if (client == std::end(json)) { return ""; } - return (*workspace)["lastwindowtitle"].as(); + return (*client)["class"].as(); +} + +void Window::queryActiveWorkspace() { + std::lock_guard lg(mutex_); + + if (separate_outputs) { + workspace_ = getActiveWorkspace(this->bar_.output->name); + } else { + workspace_ = getActiveWorkspace(); + } + + if (workspace_.windows == 1) { + solo_class_ = getWindowClass(workspace_.last_window); + } else { + solo_class_ = ""; + } } void Window::onEvent(const std::string& ev) { - std::lock_guard lg(mutex_); - - std::string windowName; - if (separate_outputs) { - windowName = getLastWindowTitle(getActiveWorkspaceID(this->bar_.output->name)); - } else { - windowName = ev.substr(ev.find_first_of(',') + 1).substr(0, 256); - } - - windowName = waybar::util::sanitize_string(windowName); - - if (windowName == lastView) return; - - lastView = windowName; - - if (windowName.empty()) { - label_.get_style_context()->add_class("empty"); - } else { - label_.get_style_context()->remove_class("empty"); - } - - spdlog::debug("hyprland window onevent with {}", windowName); + queryActiveWorkspace(); dp.emit(); } + + +void Window::setClass(const std::string& classname, bool enable) { + if (enable) { + if (!bar_.window.get_style_context()->has_class(classname)) { + bar_.window.get_style_context()->add_class(classname); + } + } else { + bar_.window.get_style_context()->remove_class(classname); + } +} + } // namespace waybar::modules::hyprland From 4f14ce3285c8dab4243d68bbfc2324f27b414f1b Mon Sep 17 00:00:00 2001 From: gardenapple Date: Tue, 20 Jun 2023 00:42:19 +0300 Subject: [PATCH 2/4] hyprland/window: add .floating and .fullscreen CSS classes --- include/modules/hyprland/window.hpp | 4 +- src/modules/hyprland/window.cpp | 62 ++++++++++++++++++----------- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index a0681d4b..19aff50a 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -16,6 +16,7 @@ class Window : public waybar::ALabel, public EventHandler { private: struct Workspace { + int id; int windows; std::string last_window; std::string last_window_title; @@ -25,7 +26,6 @@ class Window : public waybar::ALabel, public EventHandler { auto getActiveWorkspace(const std::string&) -> Workspace; auto getActiveWorkspace() -> Workspace; - auto getWindowClass(const std::string&) -> std::string; void onEvent(const std::string&) override; void queryActiveWorkspace(); void setClass(const std::string&, bool enable); @@ -38,6 +38,8 @@ class Window : public waybar::ALabel, public EventHandler { Workspace workspace_; std::string solo_class_; std::string last_solo_class_; + bool fullscreen_; + bool all_floating_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 2025b616..47c68bb5 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -2,19 +2,20 @@ #include +#include #include -#include #include "modules/hyprland/backend.hpp" #include "util/json.hpp" #include "util/rewrite_string.hpp" +#include namespace waybar::modules::hyprland { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "window", id, "{}", 0, true), bar_(bar) { modulesReady = true; - separate_outputs = config["separate-outputs"].as(); + separate_outputs = config["separate-outputs"].asBool(); if (!gIPC.get()) { gIPC = std::make_unique(); @@ -27,6 +28,8 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) gIPC->registerForIPC("activewindow", this); gIPC->registerForIPC("closewindow", this); gIPC->registerForIPC("movewindow", this); + gIPC->registerForIPC("changefloatingmode", this); + gIPC->registerForIPC("fullscreen", this); } Window::~Window() { @@ -62,6 +65,8 @@ auto Window::update() -> void { setClass("empty", workspace_.windows == 0); setClass("solo", workspace_.windows == 1); + setClass("fullscreen", fullscreen_); + setClass("floating", all_floating_); if (!last_solo_class_.empty() && solo_class_ != last_solo_class_) { if (bar_.window.get_style_context()->has_class(last_solo_class_)) { @@ -71,10 +76,10 @@ auto Window::update() -> void { } if (!solo_class_.empty() && solo_class_ != last_solo_class_) { - last_solo_class_ = solo_class_; bar_.window.get_style_context()->add_class(solo_class_); spdlog::trace("Adding solo class: {}", solo_class_); } + last_solo_class_ = solo_class_; ALabel::update(); } @@ -94,42 +99,31 @@ auto Window::getActiveWorkspace(const std::string& monitorName) -> Workspace { [&](Json::Value monitor) { return monitor["name"] == monitorName; }); if (monitor == std::end(json)) { spdlog::warn("Monitor not found: {}", monitorName); - return Workspace{0, "", ""}; + return Workspace{-1, 0, "", ""}; } - const int id = (*monitor)["activeWorkspace"]["id"].as(); + const int id = (*monitor)["activeWorkspace"]["id"].asInt(); const auto workspaces = gIPC->getSocket1Reply("j/workspaces"); json = parser_.parse(workspaces); assert(json.isArray()); auto workspace = std::find_if(json.begin(), json.end(), - [&](Json::Value workspace) { return workspace["id"] == id; }); + [&](Json::Value workspace) { return workspace["id"] == id; }); if (workspace == std::end(json)) { spdlog::warn("No workspace with id {}", id); - return Workspace{0, "", ""}; + return Workspace{-1, 0, "", ""}; } return Workspace::parse(*workspace); } auto Window::Workspace::parse(const Json::Value& value) -> Window::Workspace { return Workspace{ - value["windows"].as(), - value["lastwindow"].as(), - value["lastwindowtitle"].as() + value["id"].asInt(), + value["windows"].asInt(), + value["lastwindow"].asString(), + value["lastwindowtitle"].asString() }; } -auto Window::getWindowClass(const std::string& address) -> std::string { - const auto clients = gIPC->getSocket1Reply("j/clients"); - Json::Value json = parser_.parse(clients); - assert(json.isArray()); - auto client = std::find_if(json.begin(), json.end(), - [&](Json::Value window) { return window["address"] == address; }); - if (client == std::end(json)) { - return ""; - } - return (*client)["class"].as(); -} - void Window::queryActiveWorkspace() { std::lock_guard lg(mutex_); @@ -139,10 +133,30 @@ void Window::queryActiveWorkspace() { workspace_ = getActiveWorkspace(); } - if (workspace_.windows == 1) { - solo_class_ = getWindowClass(workspace_.last_window); + + if (workspace_.windows > 0) { + const auto clients = gIPC->getSocket1Reply("j/clients"); + Json::Value json = parser_.parse(clients); + assert(json.isArray()); + auto active_window = std::find_if(json.begin(), json.end(), + [&](Json::Value window) { return window["address"] == workspace_.last_window; }); + if (active_window == std::end(json)) { + return; + } + + if (workspace_.windows == 1 && !(*active_window)["floating"].asBool()) { + solo_class_ = (*active_window)["class"].asString(); + } else { + solo_class_ = ""; + } + all_floating_ = std::all_of(json.begin(), json.end(), + [&](Json::Value window) { return window["floating"].asBool() || + window["workspace"]["id"] != workspace_.id; }); + fullscreen_ = (*active_window)["fullscreen"].asBool(); } else { solo_class_ = ""; + all_floating_ = false; + fullscreen_ = false; } } From fd7c2a2012e492d3db51ace23d9910e6a5a91d48 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Tue, 20 Jun 2023 00:43:33 +0300 Subject: [PATCH 3/4] hyprland/language: Show language on startup --- src/modules/hyprland/language.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 3dfdb83e..aa22a482 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -22,7 +22,7 @@ Language::Language(const std::string& id, const Bar& bar, const Json::Value& con initLanguage(); label_.hide(); - ALabel::update(); + update(); // register for hyprland ipc gIPC->registerForIPC("activelayout", this); From 30c4f08773e5608ac886251024fadace3dcdcea9 Mon Sep 17 00:00:00 2001 From: gardenapple Date: Tue, 20 Jun 2023 03:23:03 +0300 Subject: [PATCH 4/4] hyprland/window: Correct application of .solo class --- include/modules/hyprland/window.hpp | 3 ++- src/modules/hyprland/window.cpp | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 19aff50a..950be05f 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -38,8 +38,9 @@ class Window : public waybar::ALabel, public EventHandler { Workspace workspace_; std::string solo_class_; std::string last_solo_class_; - bool fullscreen_; + bool solo_; bool all_floating_; + bool fullscreen_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 47c68bb5..ba476e93 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "modules/hyprland/backend.hpp" #include "util/json.hpp" @@ -64,7 +65,7 @@ auto Window::update() -> void { setClass("empty", workspace_.windows == 0); - setClass("solo", workspace_.windows == 1); + setClass("solo", solo_); setClass("fullscreen", fullscreen_); setClass("floating", all_floating_); @@ -149,12 +150,18 @@ void Window::queryActiveWorkspace() { } else { solo_class_ = ""; } - all_floating_ = std::all_of(json.begin(), json.end(), - [&](Json::Value window) { return window["floating"].asBool() || - window["workspace"]["id"] != workspace_.id; }); + std::vector workspace_windows; + std::copy_if(json.begin(), json.end(), std::back_inserter(workspace_windows), + [&](Json::Value window) { return window["workspace"]["id"] == workspace_.id && + window["mapped"].asBool(); }); + solo_ = 1 == std::count_if(workspace_windows.begin(), workspace_windows.end(), + [&](Json::Value window) { return !window["floating"].asBool(); }); + all_floating_ = std::all_of(workspace_windows.begin(), workspace_windows.end(), + [&](Json::Value window) { return window["floating"].asBool(); }); fullscreen_ = (*active_window)["fullscreen"].asBool(); } else { solo_class_ = ""; + solo_ = false; all_floating_ = false; fullscreen_ = false; }