diff --git a/src/Common.h b/src/Common.h index e6a3531..3fd93b3 100644 --- a/src/Common.h +++ b/src/Common.h @@ -133,7 +133,7 @@ namespace Utils return result; } - inline std::string FindFileWithName(const std::string& directory, const std::string& name, const std::string& extension) + inline std::string FindFileWithName(const std::string& directory, const std::string& name) { if (!std::filesystem::exists(directory)) { @@ -143,7 +143,7 @@ namespace Utils { if (path.is_directory()) continue; - if (path.path().filename().string().find(name) != std::string::npos && path.path().extension().string() == extension) + if (path.path().filename().string().find(name) != std::string::npos) { return path.path().string(); } diff --git a/src/SNI.cpp b/src/SNI.cpp index 7c6a651..c5a3498 100644 --- a/src/SNI.cpp +++ b/src/SNI.cpp @@ -32,7 +32,7 @@ namespace SNI std::string object; size_t w = 0; size_t h = 0; - uint8_t* iconData = nullptr; + GdkPixbuf* pixbuf = nullptr; std::string tooltip = ""; @@ -54,6 +54,77 @@ namespace SNI Widget* parentBox; Widget* iconBox; + // Swap channels for the render format and create a pixbuf out of it + static GdkPixbuf* ToPixbuf(uint8_t* sniData, int32_t width, int32_t height) + { + for (int i = 0; i < width * height; i++) + { + struct Px + { + // This should be bgra... + // Since source is ARGB32 in network order(=big-endian) + // and x86 Linux is little-endian, we *should* swap b and r... + uint8_t a, r, g, b; + }; + Px& pixel = ((Px*)sniData)[i]; + // Swap to create rgba + pixel = {pixel.r, pixel.g, pixel.b, pixel.a}; + } + return gdk_pixbuf_new_from_data( + sniData, GDK_COLORSPACE_RGB, true, 8, width, height, width * 4, + +[](uint8_t* data, void*) + { + delete[] data; + }, + 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) + { + std::string ext = location.substr(location.find_last_of('.') + 1); + if (ext == "png" || ext == "jpg") + { + // 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); + } + else if (ext == "svg") + { + // 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; + } + } + } + static Item CreateItem(std::string&& name, std::string&& object) { Item item{}; @@ -102,28 +173,16 @@ namespace SNI LOG("SNI: Height: " << height); item.w = width; item.h = height; - item.iconData = new uint8_t[width * height * 4]; + uint8_t* iconData = new uint8_t[width * height * 4]; uint8_t px = 0; int i = 0; while (g_variant_iter_next(data, "y", &px)) { - item.iconData[i] = px; + iconData[i] = px; i++; } - for (int i = 0; i < width * height; i++) - { - struct Px - { - // This should be bgra... - // Since source is ARGB32 in network order(=big-endian) - // and x86 Linux is little-endian, we *should* swap b and r... - uint8_t a, r, g, b; - }; - Px& pixel = ((Px*)item.iconData)[i]; - // Swap to create rgba - pixel = {pixel.r, pixel.g, pixel.b, pixel.a}; - } + item.pixbuf = ToPixbuf(iconData, width, height); g_variant_iter_free(data); @@ -148,7 +207,7 @@ namespace SNI for (auto& dataDir : Utils::Split(dataDirs, ':')) { LOG("SNI: Searching icon " << iconName << " in " << dataDir << "/icons"); - std::string path = Utils::FindFileWithName(dataDir + "/icons", iconName, ".png"); + std::string path = Utils::FindFileWithName(dataDir + "/icons", iconName); if (path != "") { iconPath = path; @@ -161,7 +220,7 @@ namespace SNI // Fallback to /usr/share/icons LOG("SNI: Searching icon " << iconName << " in " << "/usr/share/icons"); - iconPath = Utils::FindFileWithName("/usr/share/icons", iconName, ".png"); + iconPath = Utils::FindFileWithName("/usr/share/icons", iconName); } return iconPath; }; @@ -187,7 +246,8 @@ namespace SNI } else { - iconPath = std::string(themePath) + "/" + iconName + ".png"; // TODO: Find out if this is always png + LOG("SNI: Searching icon " << iconName << " in " << themePath); + iconPath = Utils::FindFileWithName(themePath, iconName); } g_variant_unref(themePathVariant); @@ -222,20 +282,8 @@ namespace SNI LOG("SNI: Cannot find icon path for " << name); return item; } - - int width, height, channels; - stbi_uc* pixels = stbi_load(iconPath.c_str(), &width, &height, &channels, STBI_rgb_alpha); - if (!pixels) - { - LOG("SNI: Cannot open " << iconPath); - return item; - } - item.w = width; - item.h = height; - item.iconData = new uint8_t[width * height * 4]; - // Already rgba32 - memcpy(item.iconData, pixels, width * height * 4); - stbi_image_free(pixels); + LOG("SNI: Creating icon from \"" << iconPath << "\""); + ToPixbuf(iconPath, item.pixbuf, item.w, item.h); } // Query tooltip(Steam e.g. doesn't have one) @@ -303,6 +351,7 @@ namespace SNI LOG("SNI: " << name << " vanished!"); g_bus_unwatch_name(it->watcherID); g_dbus_connection_signal_unsubscribe(dbusConnection, it->propertyChangeWatcherID); + g_free(it->pixbuf); items.erase(it); InvalidateWidget(); return; @@ -360,6 +409,7 @@ namespace SNI { g_bus_unwatch_name(it->watcherID); g_dbus_connection_signal_unsubscribe(dbusConnection, it->propertyChangeWatcherID); + g_free(it->pixbuf); items.erase(it); } else @@ -441,7 +491,7 @@ namespace SNI bool rotatedIcons = (Config::Get().location == 'L' || Config::Get().location == 'R') && Config::Get().iconsAlwaysUp; for (auto& item : items) { - if (item.iconData) + if (item.pixbuf) { auto eventBox = Widget::Create(); item.gtkEvent = eventBox.get(); @@ -496,7 +546,7 @@ namespace SNI } } Utils::SetTransform(*texture, {size, true, Alignment::Fill}, {size, true, Alignment::Fill, 0, rotatedIcons ? 6 : 0}); - texture->SetBuf(item.w, item.h, item.iconData); + texture->SetBuf(item.pixbuf, item.w, item.h); texture->SetTooltip(item.tooltip); texture->SetAngle(Utils::GetAngle() == 270 ? 90 : 0); diff --git a/src/Widget.cpp b/src/Widget.cpp index 82e63fd..d79f560 100644 --- a/src/Widget.cpp +++ b/src/Widget.cpp @@ -525,16 +525,13 @@ Texture::~Texture() { if (m_Pixbuf) g_free(m_Pixbuf); - if (m_Bytes) - g_free(m_Bytes); } -void Texture::SetBuf(size_t width, size_t height, uint8_t* buf) +void Texture::SetBuf(GdkPixbuf* pixbuf, size_t width, size_t height) { m_Width = width; m_Height = height; - m_Bytes = g_bytes_new(buf, m_Width * m_Height * 4); - m_Pixbuf = gdk_pixbuf_new_from_bytes((GBytes*)m_Bytes, GDK_COLORSPACE_RGB, true, 8, m_Width, m_Height, m_Width * 4); + m_Pixbuf = gdk_pixbuf_copy(pixbuf); } void Texture::Draw(cairo_t* cr) diff --git a/src/Widget.h b/src/Widget.h index 05c4bdd..7e30315 100644 --- a/src/Widget.h +++ b/src/Widget.h @@ -285,8 +285,8 @@ public: Texture() = default; virtual ~Texture(); - // Non-Owning, ARGB32 - void SetBuf(size_t width, size_t height, uint8_t* buf); + // Non-Owning (Copies the pixbuf), ARGB32 + void SetBuf(GdkPixbuf* pixbuf, size_t width, size_t height); void ForceHeight(size_t height) { m_ForcedHeight = height; }; void AddPaddingTop(int32_t topPadding) { m_Padding = topPadding; }; @@ -300,7 +300,6 @@ private: size_t m_ForcedHeight = 0; double m_Angle; int32_t m_Padding = 0; - GBytes* m_Bytes; GdkPixbuf* m_Pixbuf; };