diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index 44253e1f..a916610c 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -1,13 +1,13 @@ #include -#include "ALabel.hpp" +#include "AIconLabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" #include "util/json.hpp" namespace waybar::modules::hyprland { -class Window : public waybar::ALabel, public EventHandler { +class Window : public waybar::AIconLabel, public EventHandler { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); virtual ~Window(); @@ -40,6 +40,8 @@ class Window : public waybar::ALabel, public EventHandler { void onEvent(const std::string&) override; void queryActiveWorkspace(); void setClass(const std::string&, bool enable); + void updateAppIconName(); + void updateAppIcon(); bool separate_outputs; std::mutex mutex_; @@ -53,6 +55,9 @@ class Window : public waybar::ALabel, public EventHandler { bool all_floating_; bool hidden_; bool fullscreen_; + unsigned app_icon_size_{24}; + bool update_app_icon_{true}; + std::string app_icon_name_; }; } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 7fc7fe28..59303afa 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -1,23 +1,35 @@ #include "modules/hyprland/window.hpp" +#include +#include +#include #include #include +#include +#include #include #include #include #include "modules/hyprland/backend.hpp" +#include "util/gtk_icon.hpp" #include "util/json.hpp" #include "util/rewrite_string.hpp" namespace waybar::modules::hyprland { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : ALabel(config, "window", id, "{title}", 0, true), bar_(bar) { + : AIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { modulesReady = true; separate_outputs = config["separate-outputs"].asBool(); + // Icon size + if (config["icon-size"].isUInt()) { + app_icon_size_ = config["icon-size"].asUInt(); + } + image_.set_pixel_size(app_icon_size_); + if (!gIPC.get()) { gIPC = std::make_unique(); } @@ -39,6 +51,98 @@ Window::~Window() { std::lock_guard lg(mutex_); } +std::optional getDesktopFilePath(const std::string& app_class) { + const auto data_dirs = Glib::get_system_data_dirs(); + for (const auto& data_dir : data_dirs) { + const auto data_app_dir = data_dir + "applications/"; + auto desktop_file_path = data_app_dir + app_class + ".desktop"; + if (std::filesystem::exists(desktop_file_path)) { + return desktop_file_path; + } + } + return {}; +} + +std::optional getIconName(const std::string& app_class) { + const auto desktop_file_path = getDesktopFilePath(app_class); + if (!desktop_file_path.has_value()) { + // Try some heuristics to find a matching icon + + if (DefaultGtkIconThemeWrapper::has_icon(app_class)) { + return app_class; + } + + const auto app_identifier_desktop = app_class + "-desktop"; + if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) { + return app_identifier_desktop; + } + + const auto to_lower = [](const std::string& str) { + auto str_cpy = str; + std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(), + [](unsigned char c) { return std::tolower(c); }); + return str; + }; + + const auto first_space = app_class.find_first_of(' '); + if (first_space != std::string::npos) { + const auto first_word = to_lower(app_class.substr(0, first_space)); + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + return first_word; + } + } + + const auto first_dash = app_class.find_first_of('-'); + if (first_dash != std::string::npos) { + const auto first_word = to_lower(app_class.substr(0, first_dash)); + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + return first_word; + } + } + + return {}; + } + + try { + Glib::KeyFile desktop_file; + desktop_file.load_from_file(desktop_file_path.value()); + return desktop_file.get_string("Desktop Entry", "Icon"); + } catch (Glib::FileError& error) { + spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), + error.what().c_str()); + } catch (Glib::KeyFileError& error) { + spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), + error.what().c_str()); + } + return {}; +} + +void Window::updateAppIconName() { + if (!iconEnabled()) { + return; + } + + const auto icon_name = getIconName(window_data_.class_name); + if (icon_name.has_value()) { + app_icon_name_ = icon_name.value(); + } else { + app_icon_name_ = ""; + } + update_app_icon_ = true; +} + +void Window::updateAppIcon() { + if (update_app_icon_) { + update_app_icon_ = false; + if (app_icon_name_.empty()) { + image_.set_visible(false); + } else { + image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); + image_.set_visible(true); + } + } +} + auto Window::update() -> void { // fix ampersands std::lock_guard lg(mutex_); @@ -86,6 +190,8 @@ auto Window::update() -> void { } last_solo_class_ = solo_class_; + updateAppIcon(); + ALabel::update(); } @@ -148,6 +254,7 @@ void Window::queryActiveWorkspace() { } window_data_ = WindowData::parse(*active_window); + updateAppIconName(); std::vector workspace_windows; std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows), [&](Json::Value window) {