From 37b0896d6dfca8caf9148a09c118ba4dd9ccaa4a Mon Sep 17 00:00:00 2001 From: scorpion-26 Date: Sun, 3 Dec 2023 22:48:00 +0100 Subject: [PATCH] Allow overriding of SNI icons This adds a config option to override the name of the icons displayed. This also removes the frankenstein monster of the icon location querying, which was a giant mess and not even remotely close to being compliant with the XDG spec. Instead we use the much simpler Gtk API which has the added benefit of properly supporting themes. The SNI filter also now work for items without a set tooltip (e.g. steam). For these items the object name is matched instead. Implements https://github.com/scorpion-26/gBar/issues/63 --- data/config | 5 +- src/Config.cpp | 1 + src/Config.h | 1 + src/SNI.cpp | 279 +++++++++++++++++-------------------------------- 4 files changed, 99 insertions(+), 187 deletions(-) diff --git a/data/config b/data/config index 0446868..7c4b959 100644 --- a/data/config +++ b/data/config @@ -164,7 +164,8 @@ EnableSNI: true # SNIIconSize sets the icon size for a SNI icon. # SNIPaddingTop Can be used to push the Icon down. Negative values are allowed -# For both: The first parameter is a filter of the tooltip(The text that pops up, when the icon is hovered) of the icon +# SNIIconName overrides what icon from an icon theme to display. +# For all SNI properties: The first parameter is a filter of the tooltip(The text that pops up, when the icon is hovered) of the icon. # Scale everything down to 25 pixels ('*' as filter means everything) #SNIIconSize: *, 25 @@ -172,6 +173,8 @@ EnableSNI: true #SNIIconSize: OBS, 23 # Nudges the Discord icon a bit down #SNIPaddingTop: Discord, 5 +# Override the default icon given to gBar by discord to an icon theme supplied one (Example is from papirus theme) +#SNIIconName: Discord, discord-tray # These set the range for the network widget. The widget changes colors at six intervals: # - Below Min...Bytes ("under") diff --git a/src/Config.cpp b/src/Config.cpp index 6b580f1..33001c0 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -298,6 +298,7 @@ void Config::Load() AddConfigVar("SNIIconSize", config.sniIconSizes, lineView, foundProperty); AddConfigVar("SNIPaddingTop", config.sniPaddingTop, lineView, foundProperty); + AddConfigVar("SNIIconName", config.sniIconNames, lineView, foundProperty); // Modern map syntax AddConfigVar("WorkspaceSymbol", config.workspaceSymbols, lineView, foundProperty); // Legacy syntax diff --git a/src/Config.h b/src/Config.h index 9aba3c6..95aa92b 100644 --- a/src/Config.h +++ b/src/Config.h @@ -84,6 +84,7 @@ public: // SNIIconSize: ["Title String"], ["Size"] std::unordered_map sniIconSizes; std::unordered_map sniPaddingTop; + std::unordered_map sniIconNames; // Only affects outputs (i.e.: speakers, not microphones). This remaps the range of the volume; In percent double audioMinVolume = 0.f; // Map the minimum volume to this value diff --git a/src/SNI.cpp b/src/SNI.cpp index f32bcab..8725aa1 100644 --- a/src/SNI.cpp +++ b/src/SNI.cpp @@ -79,50 +79,19 @@ namespace SNI nullptr); } - // Allocates a pixbuf that contains a bitmap of the icon - static void ToPixbuf(const std::string& location, GdkPixbuf*& outPixbuf, size_t& outWidth, size_t& outHeight) + static bool ItemMatchesFilter(const Item& item, const std::string& filter, bool& wasExplicitOverride) { - std::string ext = location.substr(location.find_last_of('.') + 1); - if (ext == "png" || ext == "jpg") + auto& filterString = item.tooltip == "" ? item.object : item.tooltip; + if (filterString.find(filter) != std::string::npos) { - // png, load via svg - int width, height, channels; - stbi_uc* pixels = stbi_load(location.c_str(), &width, &height, &channels, STBI_rgb_alpha); - if (!pixels) - { - LOG("SNI: Cannot open " << location); - return; - } - outWidth = width; - outHeight = height; - uint8_t* iconData = new uint8_t[width * height * 4]; - // Already rgba32 - memcpy(iconData, pixels, width * height * 4); - stbi_image_free(pixels); - - outPixbuf = gdk_pixbuf_new_from_data( - iconData, GDK_COLORSPACE_RGB, true, 8, width, height, width * 4, - +[](uint8_t* data, void*) - { - delete[] data; - }, - nullptr); + wasExplicitOverride = true; + return true; } - else if (ext == "svg") + else if (filter == "*" && !wasExplicitOverride) { - // Just a random size, this should be plenty enough wiggle room - outWidth = 64; - outHeight = 64; - - // Use glib functions - GError* err = nullptr; - outPixbuf = gdk_pixbuf_new_from_file_at_scale(location.c_str(), outWidth, outHeight, true, &err); - if (err) - { - LOG("SNI: Error loading svg " << location << ": " << err->message); - return; - } + return true; } + return false; } static Item CreateItem(std::string&& name, std::string&& object) @@ -148,141 +117,6 @@ namespace SNI return res; }; - bool hasPixmap = false; - GVariant* iconPixmap = getProperty("IconPixmap"); - if (iconPixmap) - { - // Only get first item - GVariant* arr = nullptr; - g_variant_get(iconPixmap, "(v)", &arr); - - GVariantIter* arrIter = nullptr; - g_variant_get(arr, "a(iiay)", &arrIter); - - if (g_variant_iter_n_children(arrIter) != 0) - { - int width; - int height; - GVariantIter* data = nullptr; - g_variant_iter_next(arrIter, "(iiay)", &width, &height, &data); - - LOG("SNI: Width: " << width); - LOG("SNI: Height: " << height); - item.w = width; - item.h = height; - uint8_t* iconData = new uint8_t[width * height * 4]; - - uint8_t px = 0; - int i = 0; - while (g_variant_iter_next(data, "y", &px)) - { - iconData[i] = px; - i++; - } - item.pixbuf = ToPixbuf(iconData, width, height); - - g_variant_iter_free(data); - - hasPixmap = true; - } - g_variant_iter_free(arrIter); - g_variant_unref(arr); - g_variant_unref(iconPixmap); - } - - // Pixmap querying has failed, try IconName - if (!hasPixmap) - { - auto findIconWithoutPath = [](const char* iconName) -> std::string - { - std::string iconPath; - const char* dataDirs = getenv("XDG_DATA_DIRS"); - // Nothing defined, look in $XDG_DATA_DIRS/icons - // network-manager-applet does this e.g. - if (dataDirs) - { - for (auto& dataDir : Utils::Split(dataDirs, ':')) - { - LOG("SNI: Searching icon " << iconName << " in " << dataDir << "/icons"); - std::string path = Utils::FindFileWithName(dataDir + "/icons", iconName); - if (path != "") - { - iconPath = path; - break; - } - } - } - if (iconPath == "") - { - // Fallback to /usr/share/icons - LOG("SNI: Searching icon " << iconName << " in " - << "/usr/share/icons"); - iconPath = Utils::FindFileWithName("/usr/share/icons", iconName); - } - return iconPath; - }; - - // Get icon theme path - GVariant* themePathVariant = getProperty("IconThemePath"); // Not defined by freedesktop, I think ayatana does this... - GVariant* iconNameVariant = getProperty("IconName"); - - std::string iconPath; - if (themePathVariant && iconNameVariant) - { - // Why GLib? - GVariant* themePathStr = nullptr; - g_variant_get(themePathVariant, "(v)", &themePathStr); - GVariant* iconNameStr = nullptr; - g_variant_get(iconNameVariant, "(v)", &iconNameStr); - - const char* themePath = g_variant_get_string(themePathStr, nullptr); - const char* iconName = g_variant_get_string(iconNameStr, nullptr); - if (strlen(themePath) == 0) - { - iconPath = findIconWithoutPath(iconName); - } - else - { - LOG("SNI: Searching icon " << iconName << " in " << themePath); - iconPath = Utils::FindFileWithName(themePath, iconName); - } - - g_variant_unref(themePathVariant); - g_variant_unref(themePathStr); - g_variant_unref(iconNameVariant); - g_variant_unref(iconNameStr); - } - else if (iconNameVariant) - { - GVariant* iconNameStr = nullptr; - g_variant_get(iconNameVariant, "(v)", &iconNameStr); - - const char* iconName = g_variant_get_string(iconNameStr, nullptr); - iconPath = findIconWithoutPath(iconName); - if (iconPath == "") - { - // Try our luck with just using iconName, maybe its just an absolute path - iconPath = iconName; - } - - g_variant_unref(iconNameVariant); - g_variant_unref(iconNameStr); - } - else - { - LOG("SNI: Unknown path!"); - return item; - } - - if (iconPath == "") - { - LOG("SNI: Cannot find icon path for " << name); - return item; - } - LOG("SNI: Creating icon from \"" << iconPath << "\""); - ToPixbuf(iconPath, item.pixbuf, item.w, item.h); - } - // Query tooltip(Steam e.g. doesn't have one) GVariant* tooltip = getProperty("ToolTip"); if (tooltip) @@ -331,6 +165,89 @@ namespace SNI g_variant_unref(menuPath); } + bool wasExplicitOverride = false; + // First try icon theme querying + std::string iconName; + for (auto& [filter, name] : Config::Get().sniIconNames) + { + if (ItemMatchesFilter(item, filter, wasExplicitOverride)) + { + iconName = name; + } + } + if (iconName == "") + { + GVariant* iconNameVariant = getProperty("IconName"); + if (iconNameVariant) + { + GVariant* iconNameStr; + g_variant_get(iconNameVariant, "(v)", &iconNameStr); + + iconName = g_variant_get_string(iconNameStr, nullptr); + + g_variant_unref(iconNameVariant); + g_variant_unref(iconNameStr); + } + } + if (iconName != "") + { + GError* err = nullptr; + GtkIconTheme* defaultTheme = gtk_icon_theme_get_default(); + GdkPixbuf* pixbuf = gtk_icon_theme_load_icon(defaultTheme, iconName.c_str(), 64, GTK_ICON_LOOKUP_FORCE_SVG, &err); + if (err) + { + LOG("SNI: gtk_icon_theme_load_icon failed: " << err->message); + g_error_free(err); + } + else if (pixbuf) + { + LOG("SNI: Creating icon from \"" << iconName << "\""); + item.pixbuf = pixbuf; + item.w = gdk_pixbuf_get_width(pixbuf); + item.h = gdk_pixbuf_get_height(pixbuf); + } + } + + if (item.pixbuf == nullptr) + { + GVariant* iconPixmap = getProperty("IconPixmap"); + // Only get first item + GVariant* arr = nullptr; + g_variant_get(iconPixmap, "(v)", &arr); + + GVariantIter* arrIter = nullptr; + g_variant_get(arr, "a(iiay)", &arrIter); + + if (g_variant_iter_n_children(arrIter) != 0) + { + int width; + int height; + GVariantIter* data = nullptr; + g_variant_iter_next(arrIter, "(iiay)", &width, &height, &data); + + LOG("SNI: Width: " << width); + LOG("SNI: Height: " << height); + item.w = width; + item.h = height; + uint8_t* iconData = new uint8_t[width * height * 4]; + + uint8_t px = 0; + int i = 0; + while (g_variant_iter_next(data, "y", &px)) + { + iconData[i] = px; + i++; + } + LOG("SNI: Creating icon from pixmap"); + item.pixbuf = ToPixbuf(iconData, width, height); + + g_variant_iter_free(data); + } + g_variant_iter_free(arrIter); + g_variant_unref(arr); + g_variant_unref(iconPixmap); + } + return item; } @@ -519,12 +436,7 @@ namespace SNI int size = 24; for (auto& [filter, iconSize] : Config::Get().sniIconSizes) { - if (item.tooltip.find(filter) != std::string::npos) - { - wasExplicitOverride = true; - size = iconSize; - } - else if (filter == "*" && !wasExplicitOverride) + if (ItemMatchesFilter(item, filter, wasExplicitOverride)) { size = iconSize; } @@ -532,12 +444,7 @@ namespace SNI wasExplicitOverride = false; for (auto& [filter, padding] : Config::Get().sniPaddingTop) { - if (item.tooltip.find(filter) != std::string::npos) - { - wasExplicitOverride = true; - texture->AddPaddingTop(padding); - } - else if (filter == "*" && !wasExplicitOverride) + if (ItemMatchesFilter(item, filter, wasExplicitOverride)) { texture->AddPaddingTop(padding); }