mirror of
https://github.com/scorpion-26/gBar.git
synced 2024-11-24 04:02:09 +00:00
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
This commit is contained in:
parent
cbc36239d1
commit
37b0896d6d
4 changed files with 99 additions and 187 deletions
|
@ -164,7 +164,8 @@ EnableSNI: true
|
||||||
|
|
||||||
# SNIIconSize sets the icon size for a SNI icon.
|
# SNIIconSize sets the icon size for a SNI icon.
|
||||||
# SNIPaddingTop Can be used to push the Icon down. Negative values are allowed
|
# 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)
|
# Scale everything down to 25 pixels ('*' as filter means everything)
|
||||||
#SNIIconSize: *, 25
|
#SNIIconSize: *, 25
|
||||||
|
@ -172,6 +173,8 @@ EnableSNI: true
|
||||||
#SNIIconSize: OBS, 23
|
#SNIIconSize: OBS, 23
|
||||||
# Nudges the Discord icon a bit down
|
# Nudges the Discord icon a bit down
|
||||||
#SNIPaddingTop: Discord, 5
|
#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:
|
# These set the range for the network widget. The widget changes colors at six intervals:
|
||||||
# - Below Min...Bytes ("under")
|
# - Below Min...Bytes ("under")
|
||||||
|
|
|
@ -298,6 +298,7 @@ void Config::Load()
|
||||||
|
|
||||||
AddConfigVar("SNIIconSize", config.sniIconSizes, lineView, foundProperty);
|
AddConfigVar("SNIIconSize", config.sniIconSizes, lineView, foundProperty);
|
||||||
AddConfigVar("SNIPaddingTop", config.sniPaddingTop, lineView, foundProperty);
|
AddConfigVar("SNIPaddingTop", config.sniPaddingTop, lineView, foundProperty);
|
||||||
|
AddConfigVar("SNIIconName", config.sniIconNames, lineView, foundProperty);
|
||||||
// Modern map syntax
|
// Modern map syntax
|
||||||
AddConfigVar("WorkspaceSymbol", config.workspaceSymbols, lineView, foundProperty);
|
AddConfigVar("WorkspaceSymbol", config.workspaceSymbols, lineView, foundProperty);
|
||||||
// Legacy syntax
|
// Legacy syntax
|
||||||
|
|
|
@ -84,6 +84,7 @@ public:
|
||||||
// SNIIconSize: ["Title String"], ["Size"]
|
// SNIIconSize: ["Title String"], ["Size"]
|
||||||
std::unordered_map<std::string, uint32_t> sniIconSizes;
|
std::unordered_map<std::string, uint32_t> sniIconSizes;
|
||||||
std::unordered_map<std::string, int32_t> sniPaddingTop;
|
std::unordered_map<std::string, int32_t> sniPaddingTop;
|
||||||
|
std::unordered_map<std::string, std::string> sniIconNames;
|
||||||
|
|
||||||
// Only affects outputs (i.e.: speakers, not microphones). This remaps the range of the volume; In percent
|
// 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
|
double audioMinVolume = 0.f; // Map the minimum volume to this value
|
||||||
|
|
279
src/SNI.cpp
279
src/SNI.cpp
|
@ -79,50 +79,19 @@ namespace SNI
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocates a pixbuf that contains a bitmap of the icon
|
static bool ItemMatchesFilter(const Item& item, const std::string& filter, bool& wasExplicitOverride)
|
||||||
static void ToPixbuf(const std::string& location, GdkPixbuf*& outPixbuf, size_t& outWidth, size_t& outHeight)
|
|
||||||
{
|
{
|
||||||
std::string ext = location.substr(location.find_last_of('.') + 1);
|
auto& filterString = item.tooltip == "" ? item.object : item.tooltip;
|
||||||
if (ext == "png" || ext == "jpg")
|
if (filterString.find(filter) != std::string::npos)
|
||||||
{
|
{
|
||||||
// png, load via svg
|
wasExplicitOverride = true;
|
||||||
int width, height, channels;
|
return true;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else if (ext == "svg")
|
else if (filter == "*" && !wasExplicitOverride)
|
||||||
{
|
{
|
||||||
// Just a random size, this should be plenty enough wiggle room
|
return true;
|
||||||
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Item CreateItem(std::string&& name, std::string&& object)
|
static Item CreateItem(std::string&& name, std::string&& object)
|
||||||
|
@ -148,141 +117,6 @@ namespace SNI
|
||||||
return res;
|
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)
|
// Query tooltip(Steam e.g. doesn't have one)
|
||||||
GVariant* tooltip = getProperty("ToolTip");
|
GVariant* tooltip = getProperty("ToolTip");
|
||||||
if (tooltip)
|
if (tooltip)
|
||||||
|
@ -331,6 +165,89 @@ namespace SNI
|
||||||
g_variant_unref(menuPath);
|
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;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,12 +436,7 @@ namespace SNI
|
||||||
int size = 24;
|
int size = 24;
|
||||||
for (auto& [filter, iconSize] : Config::Get().sniIconSizes)
|
for (auto& [filter, iconSize] : Config::Get().sniIconSizes)
|
||||||
{
|
{
|
||||||
if (item.tooltip.find(filter) != std::string::npos)
|
if (ItemMatchesFilter(item, filter, wasExplicitOverride))
|
||||||
{
|
|
||||||
wasExplicitOverride = true;
|
|
||||||
size = iconSize;
|
|
||||||
}
|
|
||||||
else if (filter == "*" && !wasExplicitOverride)
|
|
||||||
{
|
{
|
||||||
size = iconSize;
|
size = iconSize;
|
||||||
}
|
}
|
||||||
|
@ -532,12 +444,7 @@ namespace SNI
|
||||||
wasExplicitOverride = false;
|
wasExplicitOverride = false;
|
||||||
for (auto& [filter, padding] : Config::Get().sniPaddingTop)
|
for (auto& [filter, padding] : Config::Get().sniPaddingTop)
|
||||||
{
|
{
|
||||||
if (item.tooltip.find(filter) != std::string::npos)
|
if (ItemMatchesFilter(item, filter, wasExplicitOverride))
|
||||||
{
|
|
||||||
wasExplicitOverride = true;
|
|
||||||
texture->AddPaddingTop(padding);
|
|
||||||
}
|
|
||||||
else if (filter == "*" && !wasExplicitOverride)
|
|
||||||
{
|
{
|
||||||
texture->AddPaddingTop(padding);
|
texture->AddPaddingTop(padding);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue