diff --git a/include/util/gtk_icon.hpp b/include/util/gtk_icon.hpp new file mode 100644 index 00000000..67261c1e --- /dev/null +++ b/include/util/gtk_icon.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include +#include + +class DefaultGtkIconThemeWrapper { + private: + static std::mutex default_theme_mutex; + public: + static bool has_icon(const std::string&); + static Glib::RefPtr load_icon(const char*, int, Gtk::IconLookupFlags); +}; diff --git a/meson.build b/meson.build index 87c03bff..aa250b7f 100644 --- a/meson.build +++ b/meson.build @@ -173,7 +173,8 @@ src_files = files( 'src/util/prepare_for_sleep.cpp', 'src/util/ustring_clen.cpp', 'src/util/sanitize_str.cpp', - 'src/util/rewrite_string.cpp' + 'src/util/rewrite_string.cpp', + 'src/util/gtk_icon.cpp' ) inc_dirs = ['include'] diff --git a/src/modules/gamemode.cpp b/src/modules/gamemode.cpp index 3b2213e9..10a7ec77 100644 --- a/src/modules/gamemode.cpp +++ b/src/modules/gamemode.cpp @@ -16,10 +16,11 @@ #include "glibmm/ustring.h" #include "glibmm/variant.h" #include "glibmm/varianttype.h" -#include "gtkmm/icontheme.h" #include "gtkmm/label.h" #include "gtkmm/tooltip.h" +#include "util/gtk_icon.hpp" + namespace waybar::modules { Gamemode::Gamemode(const std::string& id, const Json::Value& config) : AModule(config, "gamemode", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0), icon_(), label_() { @@ -224,7 +225,7 @@ auto Gamemode::update() -> void { label_.set_markup(str); if (useIcon) { - if (!Gtk::IconTheme::get_default()->has_icon(iconName)) { + if (!DefaultGtkIconThemeWrapper::has_icon(iconName)) { iconName = DEFAULT_ICON_NAME; } icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID); diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index e5bd6abb..9d3fc4bd 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -9,6 +9,7 @@ #include #include "util/format.hpp" +#include "util/gtk_icon.hpp" template <> struct fmt::formatter : formatter { @@ -379,10 +380,8 @@ Glib::RefPtr Item::getIconByName(const std::string& name, int reque return icon_theme->load_icon(name.c_str(), tmp_size, Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } - Glib::RefPtr default_theme = Gtk::IconTheme::get_default(); - default_theme->rescan_if_needed(); - return default_theme->load_icon(name.c_str(), tmp_size, - Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); + return DefaultGtkIconThemeWrapper::load_icon(name.c_str(), tmp_size, + Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } double Item::getScaledIconSize() { diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 030f93bf..6f97b2e5 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -13,6 +12,7 @@ #include #include "util/rewrite_string.hpp" +#include "util/gtk_icon.hpp" namespace waybar::modules::sway { @@ -81,13 +81,12 @@ std::optional getIconName(const std::string& app_id, const std::s if (!desktop_file_path.has_value()) { // Try some heuristics to find a matching icon - const auto default_icon_theme = Gtk::IconTheme::get_default(); - if (default_icon_theme->has_icon(app_id)) { + if (DefaultGtkIconThemeWrapper::has_icon(app_id)) { return app_id; } const auto app_id_desktop = app_id + "-desktop"; - if (default_icon_theme->has_icon(app_id_desktop)) { + if (DefaultGtkIconThemeWrapper::has_icon(app_id_desktop)) { return app_id_desktop; } @@ -101,7 +100,7 @@ std::optional getIconName(const std::string& app_id, const std::s const auto first_space = app_id.find_first_of(' '); if (first_space != std::string::npos) { const auto first_word = to_lower(app_id.substr(0, first_space)); - if (default_icon_theme->has_icon(first_word)) { + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } } @@ -109,7 +108,7 @@ std::optional getIconName(const std::string& app_id, const std::s const auto first_dash = app_id.find_first_of('-'); if (first_dash != std::string::npos) { const auto first_word = to_lower(app_id.substr(0, first_dash)); - if (default_icon_theme->has_icon(first_word)) { + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { return first_word; } } diff --git a/src/modules/upower/upower.cpp b/src/modules/upower/upower.cpp index 7aafdc63..ea039345 100644 --- a/src/modules/upower/upower.cpp +++ b/src/modules/upower/upower.cpp @@ -5,9 +5,10 @@ #include #include -#include "gtkmm/icontheme.h" #include "gtkmm/tooltip.h" +#include "util/gtk_icon.hpp" + namespace waybar::modules::upower { UPower::UPower(const std::string& id, const Json::Value& config) : AModule(config, "upower", id), @@ -372,7 +373,7 @@ auto UPower::update() -> void { label_.set_markup(onlySpaces ? "" : label_format); // Set icon - if (icon_name == NULL || !Gtk::IconTheme::get_default()->has_icon(icon_name)) { + if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) { icon_name = (char*)"battery-missing-symbolic"; } icon_.set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID); diff --git a/src/modules/upower/upower_tooltip.cpp b/src/modules/upower/upower_tooltip.cpp index 7dd5d10e..fddb6884 100644 --- a/src/modules/upower/upower_tooltip.cpp +++ b/src/modules/upower/upower_tooltip.cpp @@ -2,10 +2,11 @@ #include "gtkmm/box.h" #include "gtkmm/enums.h" -#include "gtkmm/icontheme.h" #include "gtkmm/image.h" #include "gtkmm/label.h" +#include "util/gtk_icon.hpp" + namespace waybar::modules::upower { UPowerTooltip::UPowerTooltip(uint iconSize_, uint tooltipSpacing_, uint tooltipPadding_) : Gtk::Window(), @@ -62,7 +63,7 @@ uint UPowerTooltip::updateTooltip(Devices& devices) { std::string deviceIconName = getDeviceIcon(kind); Gtk::Image* deviceIcon = new Gtk::Image(); deviceIcon->set_pixel_size(iconSize); - if (!Gtk::IconTheme::get_default()->has_icon(deviceIconName)) { + if (!DefaultGtkIconThemeWrapper::has_icon(deviceIconName)) { deviceIconName = "battery-missing-symbolic"; } deviceIcon->set_from_icon_name(deviceIconName, Gtk::ICON_SIZE_INVALID); @@ -79,7 +80,7 @@ uint UPowerTooltip::updateTooltip(Devices& devices) { // Set icon Gtk::Image* icon = new Gtk::Image(); icon->set_pixel_size(iconSize); - if (icon_name == NULL || !Gtk::IconTheme::get_default()->has_icon(icon_name)) { + if (icon_name == NULL || !DefaultGtkIconThemeWrapper::has_icon(icon_name)) { icon_name = (char*)"battery-missing-symbolic"; } icon->set_from_icon_name(icon_name, Gtk::ICON_SIZE_INVALID); diff --git a/src/util/gtk_icon.cpp b/src/util/gtk_icon.cpp new file mode 100644 index 00000000..70aeec27 --- /dev/null +++ b/src/util/gtk_icon.cpp @@ -0,0 +1,26 @@ +#include "util/gtk_icon.hpp" + +/* We need a global mutex for accessing the object returned by Gtk::IconTheme::get_default() + * because it always returns the same object across different threads, and concurrent + * access can cause data corruption and lead to invalid memory access and crashes. + * Even concurrent calls that seem read only such as has_icon can cause issues because + * the GTK lib may update the internal icon cache on this calls. +*/ + +std::mutex DefaultGtkIconThemeWrapper::default_theme_mutex; + +bool DefaultGtkIconThemeWrapper::has_icon(const std::string& value) { + + const std::lock_guard lock(default_theme_mutex); + + return Gtk::IconTheme::get_default()->has_icon(value); +} + +Glib::RefPtr DefaultGtkIconThemeWrapper::load_icon(const char *name, int tmp_size, Gtk::IconLookupFlags flags) { + + const std::lock_guard lock(default_theme_mutex); + + auto default_theme = Gtk::IconTheme::get_default(); + default_theme->rescan_if_needed(); + return default_theme->load_icon(name, tmp_size, flags); +}