diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index b5ab6fb..950be05 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,32 @@ class Window : public waybar::ALabel, public EventHandler { auto update() -> void override; private: - int getActiveWorkspaceID(std::string); - std::string getLastWindowTitle(int); + struct Workspace { + int id; + 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; 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_; + bool solo_; + bool all_floating_; + bool fullscreen_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 3dfdb83..aa22a48 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); diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 714d0a7..ba476e9 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -2,30 +2,35 @@ #include +#include #include -#include +#include #include "modules/hyprland/backend.hpp" -#include "util/command.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(); } - label_.hide(); - ALabel::update(); + queryActiveWorkspace(); + update(); // register for hyprland ipc gIPC->registerForIPC("activewindow", this); + gIPC->registerForIPC("closewindow", this); + gIPC->registerForIPC("movewindow", this); + gIPC->registerForIPC("changefloatingmode", this); + gIPC->registerForIPC("fullscreen", this); } Window::~Window() { @@ -38,69 +43,145 @@ 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", solo_); + 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_)) { + 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_) { + bar_.window.get_style_context()->add_class(solo_class_); + spdlog::trace("Adding solo class: {}", solo_class_); + } + last_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{-1, 0, "", ""}; } - return (*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; }); + if (workspace == std::end(json)) { + spdlog::warn("No workspace with id {}", id); + return Workspace{-1, 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["id"].asInt(), + value["windows"].asInt(), + value["lastwindow"].asString(), + value["lastwindowtitle"].asString() + }; +} - if (workspace == std::end(json)) { - return ""; +void Window::queryActiveWorkspace() { + std::lock_guard lg(mutex_); + + if (separate_outputs) { + workspace_ = getActiveWorkspace(this->bar_.output->name); + } else { + workspace_ = getActiveWorkspace(); + } + + + 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_ = ""; + } + 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; } - return (*workspace)["lastwindowtitle"].as(); } 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