diff --git a/src/dock/toplevel-icon.cpp b/src/dock/toplevel-icon.cpp index ca66a77e..0ae0c4af 100644 --- a/src/dock/toplevel-icon.cpp +++ b/src/dock/toplevel-icon.cpp @@ -158,13 +158,6 @@ namespace IconProvider namespace { - std::string tolower(std::string str) - { - for (auto& c : str) - c = std::tolower(c); - return str; - } - std::map custom_icons; } @@ -196,54 +189,6 @@ namespace IconProvider return true; } - /* Gio::DesktopAppInfo - * - * Usually knowing the app_id, we can get a desktop app info from Gio - * The filename is either the app_id + ".desktop" or lower_app_id + ".desktop" */ - Icon get_from_desktop_app_info(std::string app_id) - { - Glib::RefPtr app_info; - - std::vector prefixes = { - "", - "/usr/share/applications/", - "/usr/share/applications/kde/", - "/usr/share/applications/org.kde.", - "/usr/local/share/applications/", - "/usr/local/share/applications/org.kde.", - }; - - std::vector app_id_variations = { - app_id, - tolower(app_id), - }; - - std::vector suffixes = { - "", - ".desktop" - }; - - for (auto& prefix : prefixes) - { - for (auto& id : app_id_variations) - { - for (auto& suffix : suffixes) - { - if (!app_info) - { - app_info = Gio::DesktopAppInfo - ::create_from_filename(prefix + id + suffix); - } - } - } - } - - if (app_info) // success - return app_info->get_icon(); - - return Icon{}; - } - void set_image_from_icon(Gtk::Image& image, std::string app_id_list, int size, int scale) { @@ -256,33 +201,16 @@ namespace IconProvider * send a single app-id, but in any case this works fine */ while (stream >> app_id) { - /* Try first method: custom icon file provided by the user */ - if (set_custom_icon(image, app_id, size, scale)) - { - found_icon = true; - break; - } - - /* Then try to load the DesktopAppInfo */ - auto icon = get_from_desktop_app_info(app_id); - std::string icon_name = "unknown"; - - if (!icon) - { - /* Finally try directly looking up the icon, if it exists */ - if (Gtk::IconTheme::get_default()->lookup_icon(app_id, 24)) - icon_name = app_id; - } else - { - icon_name = icon->to_string(); - } - - WfIconLoadOptions options; - options.user_scale = scale; - set_image_icon(image, icon_name, size, options); + std::string app_name = app_id.substr( + app_id.rfind(".")+1, app_id.size() + ); - /* finally found some icon */ - if (icon_name != "unknown") + /* Try first method: custom icon file provided by the user */ + if ( + set_custom_icon(image, app_id, size, scale) + || + set_custom_icon(image, app_name, size, scale) + ) { found_icon = true; break; @@ -290,6 +218,6 @@ namespace IconProvider } if (!found_icon) - std::cout << "Failed to load icon for any of " << app_id_list << std::endl; + ::set_image_from_icon(image, app_id_list, size, scale); } }; diff --git a/src/panel/widgets/window-list/toplevel.cpp b/src/panel/widgets/window-list/toplevel.cpp index 69e5f0e1..d8f4d5c0 100644 --- a/src/panel/widgets/window-list/toplevel.cpp +++ b/src/panel/widgets/window-list/toplevel.cpp @@ -22,12 +22,6 @@ namespace extern zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_v1_impl; } -namespace IconProvider -{ - void set_image_from_icon(Gtk::Image& image, - std::string app_id_list, int size, int scale); -} - class WayfireToplevel::impl { zwlr_foreign_toplevel_handle_v1 *handle, *parent; @@ -277,8 +271,7 @@ class WayfireToplevel::impl void set_app_id(std::string app_id) { this->app_id = app_id; - IconProvider::set_image_from_icon(image, app_id, - 24, button.get_scale_factor()); + set_image_from_icon(image, app_id, 24, button.get_scale_factor()); } void send_rectangle_hint() @@ -573,104 +566,3 @@ struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_v1_impl = { .parent = handle_toplevel_parent }; } - -/* Icon loading functions */ -namespace IconProvider -{ - using Icon = Glib::RefPtr; - - namespace - { - std::string tolower(std::string str) - { - for (auto& c : str) - c = std::tolower(c); - return str; - } - } - - /* Gio::DesktopAppInfo - * - * Usually knowing the app_id, we can get a desktop app info from Gio - * The filename is either the app_id + ".desktop" or lower_app_id + ".desktop" */ - Icon get_from_desktop_app_info(std::string app_id) - { - Glib::RefPtr app_info; - - std::vector prefixes = { - "", - "/usr/share/applications/", - "/usr/share/applications/kde/", - "/usr/share/applications/org.kde.", - "/usr/local/share/applications/", - "/usr/local/share/applications/org.kde.", - }; - - std::vector app_id_variations = { - app_id, - tolower(app_id), - }; - - std::vector suffixes = { - "", - ".desktop" - }; - - for (auto& prefix : prefixes) - { - for (auto& id : app_id_variations) - { - for (auto& suffix : suffixes) - { - if (!app_info) - { - app_info = Gio::DesktopAppInfo - ::create_from_filename(prefix + id + suffix); - } - } - } - } - - if (app_info) // success - return app_info->get_icon(); - - return Icon{}; - } - - /* Second method: Just look up the built-in icon theme, - * perhaps some icon can be found there */ - - void set_image_from_icon(Gtk::Image& image, - std::string app_id_list, int size, int scale) - { - std::string app_id; - std::istringstream stream(app_id_list); - - /* Wayfire sends a list of app-id's in space separated format, other compositors - * send a single app-id, but in any case this works fine */ - while (stream >> app_id) - { - auto icon = get_from_desktop_app_info(app_id); - std::string icon_name = "unknown"; - - if (!icon) - { - /* Perhaps no desktop app info, but we might still be able to - * get an icon directly from the icon theme */ - if (Gtk::IconTheme::get_default()->lookup_icon(app_id, 24)) - icon_name = app_id; - } else - { - icon_name = icon->to_string(); - } - - WfIconLoadOptions options; - options.user_scale = scale; - set_image_icon(image, icon_name, size, options); - - /* finally found some icon */ - if (icon_name != "unknown") - break; - } - } -}; diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 0e8f1b5f..272abf9f 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -1,6 +1,8 @@ -#include +#include "gtk-utils.hpp" + #include #include +#include #include #include @@ -86,17 +88,220 @@ void set_image_icon(Gtk::Image& image, std::string icon_name, int size, image.get_scale_factor() : options.user_scale); int scaled_size = size * scale; + Glib::RefPtr pbuff; + if (!icon_theme->lookup_icon(icon_name, scaled_size)) + { + if (Glib::file_test(icon_name, Glib::FILE_TEST_EXISTS)) + pbuff = load_icon_pixbuf_safe(icon_name, scaled_size); + } + else + { + pbuff = icon_theme->load_icon(icon_name, scaled_size) + ->scale_simple(scaled_size, scaled_size, Gdk::INTERP_BILINEAR); + } + + if (!pbuff) { std::cerr << "Failed to load icon \"" << icon_name << "\"" << std::endl; return; } - auto pbuff = icon_theme->load_icon(icon_name, scaled_size) - ->scale_simple(scaled_size, scaled_size, Gdk::INTERP_BILINEAR); - if (options.invert) invert_pixbuf(pbuff); set_image_pixbuf(image, pbuff, scale); } + +/* Gio::DesktopAppInfo + * + * Usually knowing the app_id, we can get a desktop app info from Gio + * The filename is either the app_id + ".desktop" or lower_app_id + ".desktop" + */ +Glib::RefPtr get_desktop_app_info(std::string app_id) +{ + Glib::RefPtr app_info; + + // search also on user defined .desktop files + std::string home_dir = std::getenv("HOME"); + home_dir += "/.local/share/applications/"; + + std::vector prefixes = { + home_dir, + "", + "/usr/share/applications/", + "/usr/share/applications/kde/", + "/usr/share/applications/org.kde.", + "/usr/local/share/applications/", + "/usr/local/share/applications/org.kde." + }; + + std::string app_id_lowercase = app_id; + for (auto& c : app_id_lowercase) + c = std::tolower(c); + + // if app id is org.name.appname take only the appname part + std::string app_id_basename = app_id.substr( + app_id.rfind(".")+1, app_id.size() + ); + + std::string app_id_basename_lowercase = app_id; + for (auto& c : app_id_basename_lowercase) + c = std::tolower(c); + + std::vector app_id_variations = { + app_id, + app_id_lowercase, + app_id_basename, + app_id_basename_lowercase + }; + + std::vector suffixes = { + "", + ".desktop" + }; + + for (auto& prefix : prefixes) + { + for (auto& id : app_id_variations) + { + for (auto& suffix : suffixes) + { + if (!app_info) + { + app_info = Gio::DesktopAppInfo + ::create_from_filename(prefix + id + suffix); + } + } + } + } + + // Perform a search and select best match + if (!app_info) + { + std::vector app_id_variations; + + app_id_variations.push_back(app_id); + + // If appid has dashes add first component to search + if (app_id.find('-') != std::string::npos) + { + std::istringstream stream(app_id); + std::string token; + std::getline(stream, token, '-'); + + app_id_variations.push_back(token); + } + + std::string desktop_file = ""; + + for (auto token : app_id_variations) + { + gchar*** desktop_list = g_desktop_app_info_search(token.c_str()); + if (desktop_list != nullptr && desktop_list[0] != nullptr) + { + for (size_t i=0; desktop_list[0][i]; i++) + { + if (desktop_file == "") + { + desktop_file = desktop_list[0][i]; + } + else + { + auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]); + auto startup_class = tmp_info->get_startup_wm_class(); + + if ( + startup_class == app_id + || + startup_class == app_id_lowercase + ) + { + desktop_file = desktop_list[0][i]; + break; + } + } + } + g_strfreev(desktop_list[0]); + } + g_free(desktop_list); + + if(desktop_file != "") + { + app_info = Gio::DesktopAppInfo::create(desktop_file); + break; + } + } + } + + // If app has dots try each component + if (!app_info && app_id.find('.') != std::string::npos) + { + std::istringstream stream(app_id); + std::string token; + + while (std::getline(stream, token, '.')) + { + app_info = Gio::DesktopAppInfo::create(token + ".desktop"); + + if (app_info) + break; + } + } + + if (app_info) + return app_info; + + return {}; +} + +bool set_image_from_icon(Gtk::Image& image, + std::string app_id_list, int size, int scale) +{ + std::string app_id; + std::istringstream stream(app_id_list); + + bool found_icon = false; + + std::string icon_name = "unknown"; + + // Wayfire sends a list of app-id's in space separated format, other + // compositors send a single app-id, but in any case this works fine + while (stream >> app_id) + { + std::string app_name = app_id.substr( + app_id.rfind(".")+1, app_id.size() + ); + + // Try to load icon from the DesktopAppInfo + auto app_info = get_desktop_app_info(app_id); + + if (app_info && app_info->get_icon()) + icon_name = app_info->get_icon()->to_string(); + + // Try directly looking up the icon, if it exists + if (icon_name == "unknown") + { + if (Gtk::IconTheme::get_default()->lookup_icon(app_id, 24)) + icon_name = app_id; + else if (Gtk::IconTheme::get_default()->lookup_icon(app_name, 24)) + icon_name = app_name; + } + + if (icon_name != "unknown") + { + found_icon = true; + break; + } + } + + WfIconLoadOptions options; + options.user_scale = scale; + set_image_icon(image, icon_name, size, options); + + if (found_icon) + return true; + + std::cout << "Failed to load icon for any of " << app_id_list << std::endl; + return false; +} diff --git a/src/util/gtk-utils.hpp b/src/util/gtk-utils.hpp index a27c4aee..a0368d3e 100644 --- a/src/util/gtk-utils.hpp +++ b/src/util/gtk-utils.hpp @@ -1,8 +1,10 @@ #ifndef WF_GTK_UTILS #define WF_GTK_UTILS +#include #include #include +#include #include /* Loads a pixbuf with the given size from the given file, returns null if unsuccessful */ @@ -27,4 +29,9 @@ void set_image_icon(Gtk::Image& image, std::string icon_name, int size, void invert_pixbuf(Glib::RefPtr& pbuff); +Glib::RefPtr get_desktop_app_info(std::string app_id); + +bool set_image_from_icon(Gtk::Image& image, + std::string app_id_list, int size, int scale); + #endif /* end of include guard: WF_GTK_UTILS */