From 6ba36e597d97cbd0f32f66c0d80f624c41b4d327 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Sun, 31 Jan 2021 19:37:14 -0400 Subject: [PATCH 01/11] [wf-dock] improved icon search. --- src/dock/toplevel-icon.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/dock/toplevel-icon.cpp b/src/dock/toplevel-icon.cpp index ca66a77e..b773ce82 100644 --- a/src/dock/toplevel-icon.cpp +++ b/src/dock/toplevel-icon.cpp @@ -256,8 +256,16 @@ namespace IconProvider * 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 first method: custom icon file provided by the user */ - if (set_custom_icon(image, app_id, size, scale)) + if ( + set_custom_icon(image, app_id, size, scale) + || + set_custom_icon(image, app_name, size, scale) + ) { found_icon = true; break; @@ -267,11 +275,16 @@ namespace IconProvider auto icon = get_from_desktop_app_info(app_id); std::string icon_name = "unknown"; + if (!icon) + icon = get_from_desktop_app_info(app_name); + 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 if (Gtk::IconTheme::get_default()->lookup_icon(app_name, 24)) + icon_name = app_name; } else { icon_name = icon->to_string(); From be286f9b006c6e996e8c80dfb7809022acb63095 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 4 Feb 2021 01:13:41 -0400 Subject: [PATCH 02/11] Added extra functions to util/gtk-utils. * Added improved set_image_from_icon() that also uses an app basename for icon lookup, eg: an app id 'org.name.appname' may fail but the basename 'appname' may work. * Added get_desktop_app_info() that takes into consideration a user ~/.local/share/applications directory for custom apps. Also searches the app basename and if everything fails performs a best match lookup using g_desktop_app_info_search() which works for applications like pamac that report an appid of pamac-manager but uses a desktop file named org.manjaro.pamac.manager.desktop * Improved set_image_icon() to also take into consideration image names that may be an absolute path to an icon file since some .desktop files declare the 'Icon' property as a path to image. --- src/util/gtk-utils.cpp | 158 +++++++++++++++++++++++++++++++++++++++-- src/util/gtk-utils.hpp | 7 ++ 2 files changed, 161 insertions(+), 4 deletions(-) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 0e8f1b5f..e0054216 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,165 @@ 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 = { + "", + "/usr/share/applications/", + "/usr/share/applications/kde/", + "/usr/share/applications/org.kde.", + "/usr/local/share/applications/", + "/usr/local/share/applications/org.kde.", + home_dir + }; + + 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::vector app_id_variations = { + app_id, + app_id_lowercase, + app_id_basename + }; + + 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); + } + } + } + } + + // As last resort perform a search and select best match + if (!app_info) + { + std::string desktop_file = ""; + + gchar*** desktop_list = g_desktop_app_info_search(app_id.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]; + + break; + } + g_strfreev(desktop_list[0]); + } + g_free(desktop_list); + + if(desktop_file != "") + app_info = Gio::DesktopAppInfo::create(desktop_file); + } + + 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) + 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 */ From f2a966764eab79c6cee93e706ae2716a0fb5be06 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 4 Feb 2021 01:32:24 -0400 Subject: [PATCH 03/11] [wf-dock] Use new gtk-utils icon loading function. * Removed desktop app info loading function from IconProvider. * Removed not used anymore tolower() from IconProvider. --- src/dock/toplevel-icon.cpp | 87 +------------------------------------- 1 file changed, 1 insertion(+), 86 deletions(-) diff --git a/src/dock/toplevel-icon.cpp b/src/dock/toplevel-icon.cpp index b773ce82..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) { @@ -270,39 +215,9 @@ namespace IconProvider 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) - icon = get_from_desktop_app_info(app_name); - - 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 if (Gtk::IconTheme::get_default()->lookup_icon(app_name, 24)) - icon_name = app_name; - } 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") - { - found_icon = true; - break; - } } 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); } }; From 7cfb188b17c762447046b94458bea31c34ecc5ef Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 4 Feb 2021 02:01:06 -0400 Subject: [PATCH 04/11] [wf-panel/window-list] replaced IconProvider with gtk-utils function. --- src/panel/widgets/window-list/toplevel.cpp | 110 +-------------------- 1 file changed, 1 insertion(+), 109 deletions(-) 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; - } - } -}; From f9a73f7034cb3ba6332c9f28d8a09e713b3c831a Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 4 Feb 2021 04:41:59 -0400 Subject: [PATCH 05/11] Improved gtk-utils get_desktop_app_info(). * Also search lower case appid basename. * If appid is divided by dashes search the first component which works for blueman-applet and others. * If appid is divided by dots and nothing was found take each component and try to find a matching desktop file, works for applications that report appid as appname.arch/ext and the desktop file is appname.desktop * Added workaround for visual studio code. * Check if DesktopAppInfo has the 'Icon' key to prevent crash. --- src/util/gtk-utils.cpp | 67 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index e0054216..f4288678 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -145,10 +145,15 @@ Glib::RefPtr get_desktop_app_info(std::string app_id) 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, + app_id_basename_lowercase }; std::vector suffixes = { @@ -171,27 +176,65 @@ Glib::RefPtr get_desktop_app_info(std::string app_id) } } - // As last resort perform a search and select best match + // Perform a search and select best match if (!app_info) { + std::vector app_id_variations; + + if (app_id == "Code") + app_id_variations.push_back("visual-studio-code"); + + 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 = ""; - gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str()); - if (desktop_list != nullptr && desktop_list[0] != nullptr) + for (auto token : app_id_variations) { - for (size_t i=0; desktop_list[0][i]; i++) + gchar*** desktop_list = g_desktop_app_info_search(token.c_str()); + if (desktop_list != nullptr && desktop_list[0] != nullptr) { - if(desktop_file == "") - desktop_file = desktop_list[0][i]; + for (size_t i=0; desktop_list[0][i]; i++) + { + if(desktop_file == "") + 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; } - g_strfreev(desktop_list[0]); } - g_free(desktop_list); + } - if(desktop_file != "") - app_info = Gio::DesktopAppInfo::create(desktop_file); + // 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) @@ -221,7 +264,7 @@ bool set_image_from_icon(Gtk::Image& image, // Try to load icon from the DesktopAppInfo auto app_info = get_desktop_app_info(app_id); - if (app_info) + if (app_info && app_info->has_key("Icon")) icon_name = app_info->get_icon()->to_string(); // Try directly looking up the icon, if it exists From 0d1756ec5f8efd750d9da640d2ae2cd2045faf50 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 4 Feb 2021 04:54:03 -0400 Subject: [PATCH 06/11] [gtk-utils] Check icon with get_icon() instead of has_key(). --- src/util/gtk-utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index f4288678..4d0a3206 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -264,7 +264,7 @@ bool set_image_from_icon(Gtk::Image& image, // Try to load icon from the DesktopAppInfo auto app_info = get_desktop_app_info(app_id); - if (app_info && app_info->has_key("Icon")) + if (app_info && app_info->get_icon()) icon_name = app_info->get_icon()->to_string(); // Try directly looking up the icon, if it exists From 12957e78fa3c0262a197fa56777b1d0a3c07df6b Mon Sep 17 00:00:00 2001 From: jgmdev Date: Fri, 5 Feb 2021 14:49:07 -0400 Subject: [PATCH 07/11] Removed vscode workaround from gtk-utils. --- src/util/gtk-utils.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 4d0a3206..1f002f06 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -181,9 +181,6 @@ Glib::RefPtr get_desktop_app_info(std::string app_id) { std::vector app_id_variations; - if (app_id == "Code") - app_id_variations.push_back("visual-studio-code"); - app_id_variations.push_back(app_id); // If appid has dashes add first component to search From 98eda249b7823921134bfaf11c15e52c71cda677 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Fri, 5 Feb 2021 15:10:08 -0400 Subject: [PATCH 08/11] [gtk-utils] Take into consideration the StartupWMClass when doing g_desktop_app_info_search(). --- src/util/gtk-utils.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 1f002f06..55f6165e 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -202,10 +202,22 @@ Glib::RefPtr get_desktop_app_info(std::string app_id) { for (size_t i=0; desktop_list[0][i]; i++) { - if(desktop_file == "") + if (desktop_file == "") + { desktop_file = desktop_list[0][i]; - - break; + } + else + { + auto tmp_info = Gio::DesktopAppInfo::create(desktop_file); + 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]; + } } g_strfreev(desktop_list[0]); } From d817ac9faabbf1228d39530e48f3a7074885ad5e Mon Sep 17 00:00:00 2001 From: jgmdev Date: Fri, 5 Feb 2021 18:07:25 -0400 Subject: [PATCH 09/11] Fix small mistake from previous commit. --- src/util/gtk-utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 55f6165e..0f05d913 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -208,7 +208,7 @@ Glib::RefPtr get_desktop_app_info(std::string app_id) } else { - auto tmp_info = Gio::DesktopAppInfo::create(desktop_file); + auto tmp_info = Gio::DesktopAppInfo::create(desktop_list[0][i]); auto startup_class = tmp_info->get_startup_wm_class(); if ( From 9dbe009b63478d3cfc5271164d0f05df3639a98c Mon Sep 17 00:00:00 2001 From: jgmdev Date: Sun, 7 Feb 2021 01:13:09 -0400 Subject: [PATCH 10/11] Added break when matching StartupWMClass is found. --- src/util/gtk-utils.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 0f05d913..0deced79 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -216,7 +216,10 @@ Glib::RefPtr get_desktop_app_info(std::string app_id) || startup_class == app_id_lowercase ) + { desktop_file = desktop_list[0][i]; + break; + } } } g_strfreev(desktop_list[0]); From 89417ee751aaf2ad63cbaaa02f682fb1957002f9 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Fri, 12 Feb 2021 02:06:58 -0400 Subject: [PATCH 11/11] [gtk-utils] Higher priority to ~/.local/share/applications on get_desktop_app_info() to properly use user overrides of .desktop files. --- src/util/gtk-utils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 0deced79..272abf9f 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -127,13 +127,13 @@ Glib::RefPtr get_desktop_app_info(std::string app_id) 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.", - home_dir + "/usr/local/share/applications/org.kde." }; std::string app_id_lowercase = app_id;