From 00d0d219f990f2873edce121c229e9d8acf4ddf7 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 30 Jan 2018 10:30:21 +0000 Subject: Convert local icons found in metainfo files This allows us to support desktop-less components that specify a cached icon. The icon search code has been split into a new plugin that is run after the appdata and desktop plugins have added the AsbApp to the list of available apps. --- contrib/libappstream-glib.spec.in | 1 + data/tests/driver-1-1.fc27.noarch.rpm | Bin 0 -> 7404 bytes data/tests/driver.spec | 26 +++ data/tests/rpmbuild/build.sh | 2 + data/tests/rpmbuild/driver.metainfo.xml | 10 ++ libappstream-builder/asb-self-test.c | 22 ++- libappstream-builder/plugins/asb-plugin-desktop.c | 162 +---------------- libappstream-builder/plugins/asb-plugin-icon.c | 205 ++++++++++++++++++++++ libappstream-builder/plugins/meson.build | 17 ++ 9 files changed, 281 insertions(+), 164 deletions(-) create mode 100644 data/tests/driver-1-1.fc27.noarch.rpm create mode 100644 data/tests/driver.spec create mode 100644 data/tests/rpmbuild/driver.metainfo.xml create mode 100644 libappstream-builder/plugins/asb-plugin-icon.c diff --git a/contrib/libappstream-glib.spec.in b/contrib/libappstream-glib.spec.in index 335d73c..a3e837e 100644 --- a/contrib/libappstream-glib.spec.in +++ b/contrib/libappstream-glib.spec.in @@ -136,6 +136,7 @@ GLib headers and libraries for appstream-builder. %{_libdir}/asb-plugins-%{as_plugin_version}/libasb_plugin_font.so %{_libdir}/asb-plugins-%{as_plugin_version}/libasb_plugin_gettext.so %{_libdir}/asb-plugins-%{as_plugin_version}/libasb_plugin_hardcoded.so +%{_libdir}/asb-plugins-%{as_plugin_version}/libasb_plugin_icon.so %{_libdir}/asb-plugins-%{as_plugin_version}/libasb_plugin_shell_extension.so %{_libdir}/libappstream-builder.so.8* %{_mandir}/man1/appstream-builder.1.gz diff --git a/data/tests/driver-1-1.fc27.noarch.rpm b/data/tests/driver-1-1.fc27.noarch.rpm new file mode 100644 index 0000000..2d3dbf6 Binary files /dev/null and b/data/tests/driver-1-1.fc27.noarch.rpm differ diff --git a/data/tests/driver.spec b/data/tests/driver.spec new file mode 100644 index 0000000..d024314 --- /dev/null +++ b/data/tests/driver.spec @@ -0,0 +1,26 @@ +Summary: A hardware driver +Name: driver +Version: 1 +Release: 1%{?dist} +URL: http://people.freedesktop.org/ +License: GPLv2+ +BuildArch: noarch + +Source6: driver.metainfo.xml +Source17: app.png + +%description +This is a hardware driver. + +%install +install -Dp %{SOURCE6} $RPM_BUILD_ROOT/%{_datadir}/metainfo/driver.metainfo.xml +install -Dp %{SOURCE17} $RPM_BUILD_ROOT%{_datadir}/pixmaps/driver.png + +%files +%defattr(-,root,root) +%{_datadir}/metainfo/driver.metainfo.xml +%{_datadir}/pixmaps/driver.png + +%changelog +* Tue Jan 30 2018 Richard Hughes - 1-1 +- Initial version diff --git a/data/tests/rpmbuild/build.sh b/data/tests/rpmbuild/build.sh index 1b62734..6d1d4f2 100755 --- a/data/tests/rpmbuild/build.sh +++ b/data/tests/rpmbuild/build.sh @@ -3,6 +3,8 @@ msgfmt en_GB.po -o en_GB.mo gcc -o app.bin app.c `pkg-config --cflags --libs gtk+-3.0` cp * ~/rpmbuild/SOURCES/ rpmbuild -ba ../app.spec +rpmbuild -ba ../driver.spec rpmbuild -ba ../composite.spec cp ~/rpmbuild/RPMS/app*.rpm ../ +cp ~/rpmbuild/RPMS/driver*.rpm ../ cp ~/rpmbuild/RPMS/composite*.rpm ../ diff --git a/data/tests/rpmbuild/driver.metainfo.xml b/data/tests/rpmbuild/driver.metainfo.xml new file mode 100644 index 0000000..32467b0 --- /dev/null +++ b/data/tests/rpmbuild/driver.metainfo.xml @@ -0,0 +1,10 @@ + + + driver + Driver + A hardware driver + CC0-1.0 + GPL-2.0+ + richard_at_hughsie.com + /usr/share/pixmaps/driver.png + diff --git a/libappstream-builder/asb-self-test.c b/libappstream-builder/asb-self-test.c index 7fda2cc..9e42433 100644 --- a/libappstream-builder/asb-self-test.c +++ b/libappstream-builder/asb-self-test.c @@ -337,6 +337,7 @@ asb_test_context_func (void) "app-console-1-1.fc25.noarch.rpm", /* app with no icon */ "app-1-1.fc25.i686.rpm", /* GUI multiarch app */ "composite-1-1.fc21.x86_64.rpm", /* multiple GUI apps */ + "driver-1-1.fc27.noarch.rpm", /* a hardware driver */ #ifdef HAVE_FONTS "font-1-1.fc21.noarch.rpm", /* font */ "font-serif-1-1.fc21.noarch.rpm", /* font that extends */ @@ -388,9 +389,9 @@ asb_test_context_func (void) /* verify queue size */ #ifdef HAVE_FONTS - g_assert_cmpint (asb_context_get_packages(ctx)->len, ==, 9); + g_assert_cmpint (asb_context_get_packages(ctx)->len, ==, 10); #else - g_assert_cmpint (asb_context_get_packages(ctx)->len, ==, 7); + g_assert_cmpint (asb_context_get_packages(ctx)->len, ==, 8); #endif /* run the plugins */ @@ -414,9 +415,9 @@ asb_test_context_func (void) g_assert_no_error (error); g_assert (ret); #ifdef HAVE_FONTS -// g_assert_cmpint (as_store_get_size (store), ==, 5); +// g_assert_cmpint (as_store_get_size (store), ==, 6); #else - g_assert_cmpint (as_store_get_size (store), ==, 4); + g_assert_cmpint (as_store_get_size (store), ==, 5); #endif app = as_store_get_app_by_pkgname (store, "app"); g_assert (app != NULL); @@ -549,6 +550,18 @@ asb_test_context_func (void) "\n" "\n" #endif + "\n" + "driver\n" + "driver\n" + "Driver\n" + "A hardware driver\n" + "driver.png\n" + "GPL-2.0+\n" + "http://people.freedesktop.org/\n" + "\n" + "\n" + "\n" + "\n" "\n"; ret = asb_test_compare_lines (xml->str, expected_xml, &error); g_assert_no_error (error); @@ -792,6 +805,7 @@ main (int argc, char **argv) /* only critical and error are fatal */ g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); g_setenv ("ASB_IS_SELF_TEST", "", TRUE); + g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); /* tests go here */ g_test_add_func ("/AppStreamBuilder/package", asb_test_package_func); diff --git a/libappstream-builder/plugins/asb-plugin-desktop.c b/libappstream-builder/plugins/asb-plugin-desktop.c index a4662e9..02f5909 100644 --- a/libappstream-builder/plugins/asb-plugin-desktop.c +++ b/libappstream-builder/plugins/asb-plugin-desktop.c @@ -22,7 +22,6 @@ #include #include #include -#include #include @@ -41,128 +40,6 @@ asb_plugin_add_globs (AsbPlugin *plugin, GPtrArray *globs) { asb_plugin_add_glob (globs, "/usr/share/applications/*.desktop"); asb_plugin_add_glob (globs, "/usr/share/applications/kde4/*.desktop"); - asb_plugin_add_glob (globs, "/usr/share/pixmaps/*"); - asb_plugin_add_glob (globs, "/usr/share/icons/*"); - asb_plugin_add_glob (globs, "/usr/share/*/icons/*"); -} - -static GdkPixbuf * -asb_app_load_icon (AsbPlugin *plugin, - const gchar *filename, - const gchar *logfn, - guint icon_size, - guint min_icon_size, - GError **error) -{ - g_autoptr(AsImage) im = NULL; - g_autoptr(GError) error_local = NULL; - AsImageLoadFlags load_flags = AS_IMAGE_LOAD_FLAG_ALWAYS_RESIZE; - - /* is icon in a unsupported format */ - if (!asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_IGNORE_LEGACY_ICONS)) - load_flags |= AS_IMAGE_LOAD_FLAG_ONLY_SUPPORTED; - - im = as_image_new (); - if (!as_image_load_filename_full (im, - filename, - icon_size, - min_icon_size, - load_flags, - &error_local)) { - g_set_error (error, - ASB_PLUGIN_ERROR, - ASB_PLUGIN_ERROR_FAILED, - "%s: %s", - error_local->message, logfn); - return NULL; - } - return g_object_ref (as_image_get_pixbuf (im)); -} - -static gboolean -asb_plugin_desktop_add_icons (AsbPlugin *plugin, - AsbApp *app, - const gchar *tmpdir, - const gchar *key, - GError **error) -{ - guint min_icon_size; - g_autofree gchar *fn_hidpi = NULL; - g_autofree gchar *fn = NULL; - g_autofree gchar *name_hidpi = NULL; - g_autofree gchar *name = NULL; - g_autoptr(AsIcon) icon_hidpi = NULL; - g_autoptr(AsIcon) icon = NULL; - g_autoptr(GdkPixbuf) pixbuf_hidpi = NULL; - g_autoptr(GdkPixbuf) pixbuf = NULL; - - /* find 64x64 icon */ - fn = as_utils_find_icon_filename_full (tmpdir, key, - AS_UTILS_FIND_ICON_NONE, - error); - if (fn == NULL) { - g_prefix_error (error, "Failed to find icon: "); - return FALSE; - } - - /* load the icon */ - min_icon_size = asb_context_get_min_icon_size (plugin->ctx); - pixbuf = asb_app_load_icon (plugin, fn, fn + strlen (tmpdir), - 64, min_icon_size, error); - if (pixbuf == NULL) { - g_prefix_error (error, "Failed to load icon: "); - return FALSE; - } - - /* save in target directory */ - if (asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_HIDPI_ICONS)) { - name = g_strdup_printf ("%ix%i/%s.png", - 64, 64, - as_app_get_id_filename (AS_APP (app))); - } else { - name = g_strdup_printf ("%s.png", - as_app_get_id_filename (AS_APP (app))); - } - icon = as_icon_new (); - as_icon_set_pixbuf (icon, pixbuf); - as_icon_set_name (icon, name); - as_icon_set_kind (icon, AS_ICON_KIND_CACHED); - as_icon_set_prefix (icon, as_app_get_icon_path (AS_APP (app))); - as_app_add_icon (AS_APP (app), icon); - - /* is HiDPI disabled */ - if (!asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_HIDPI_ICONS)) - return TRUE; - - /* try to get a HiDPI icon */ - fn_hidpi = as_utils_find_icon_filename_full (tmpdir, key, - AS_UTILS_FIND_ICON_HI_DPI, - NULL); - if (fn_hidpi == NULL) - return TRUE; - - /* load the HiDPI icon */ - pixbuf_hidpi = asb_app_load_icon (plugin, fn_hidpi, - fn_hidpi + strlen (tmpdir), - 128, 128, NULL); - if (pixbuf_hidpi == NULL) - return TRUE; - if (gdk_pixbuf_get_width (pixbuf_hidpi) <= gdk_pixbuf_get_width (pixbuf) || - gdk_pixbuf_get_height (pixbuf_hidpi) <= gdk_pixbuf_get_height (pixbuf)) - return TRUE; - as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_HI_DPI_ICON); - - /* save icon */ - name_hidpi = g_strdup_printf ("%ix%i/%s.png", - 128, 128, - as_app_get_id_filename (AS_APP (app))); - icon_hidpi = as_icon_new (); - as_icon_set_pixbuf (icon_hidpi, pixbuf_hidpi); - as_icon_set_name (icon_hidpi, name_hidpi); - as_icon_set_kind (icon_hidpi, AS_ICON_KIND_CACHED); - as_icon_set_prefix (icon_hidpi, as_app_get_icon_path (AS_APP (app))); - as_app_add_icon (AS_APP (app), icon_hidpi); - return TRUE; } static gboolean @@ -173,12 +50,9 @@ asb_plugin_desktop_refine (AsbPlugin *plugin, const gchar *tmpdir, GError **error) { - AsIcon *icon; AsAppParseFlags parse_flags = AS_APP_PARSE_FLAG_USE_HEURISTICS | AS_APP_PARSE_FLAG_ALLOW_VETO; GPtrArray *icons; - gboolean ret; - guint i; g_autoptr(AsApp) desktop_app = NULL; g_autoptr(GdkPixbuf) pixbuf = NULL; @@ -193,8 +67,8 @@ asb_plugin_desktop_refine (AsbPlugin *plugin, /* convert any UNKNOWN icons to CACHED */ icons = as_app_get_icons (AS_APP (desktop_app)); - for (i = 0; i < icons->len; i++) { - icon = g_ptr_array_index (icons, i); + for (guint i = 0; i < icons->len; i++) { + AsIcon *icon = g_ptr_array_index (icons, i); if (as_icon_get_kind (icon) == AS_ICON_KIND_UNKNOWN) as_icon_set_kind (icon, AS_ICON_KIND_CACHED); } @@ -204,38 +78,6 @@ asb_plugin_desktop_refine (AsbPlugin *plugin, AS_APP_SUBSUME_FLAG_NO_OVERWRITE | AS_APP_SUBSUME_FLAG_MERGE); - /* is the icon a stock-icon-name? */ - icon = as_app_get_icon_default (AS_APP (app)); - if (icon != NULL) { - if (as_icon_get_kind (icon) == AS_ICON_KIND_STOCK) { - asb_package_log (pkg, - ASB_PACKAGE_LOG_LEVEL_DEBUG, - "using stock icon %s", - as_icon_get_name (icon)); - } else { - g_autofree gchar *key = NULL; - g_autoptr(GError) error_local = NULL; - switch (as_icon_get_kind (icon)) { - case AS_ICON_KIND_LOCAL: - key = g_strdup (as_icon_get_filename (icon)); - break; - default: - key = g_strdup (as_icon_get_name (icon)); - break; - } - g_ptr_array_set_size (as_app_get_icons (AS_APP (app)), 0); - ret = asb_plugin_desktop_add_icons (plugin, - app, - tmpdir, - key, - &error_local); - if (!ret) { - as_app_add_veto (AS_APP (app), "%s", - error_local->message); - } - } - } - return TRUE; } diff --git a/libappstream-builder/plugins/asb-plugin-icon.c b/libappstream-builder/plugins/asb-plugin-icon.c new file mode 100644 index 0000000..1cf9ad3 --- /dev/null +++ b/libappstream-builder/plugins/asb-plugin-icon.c @@ -0,0 +1,205 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2014-2018 Richard Hughes + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include + +#define __APPSTREAM_GLIB_PRIVATE_H +#include +#include + +const gchar * +asb_plugin_get_name (void) +{ + return "icon"; +} + +void +asb_plugin_add_globs (AsbPlugin *plugin, GPtrArray *globs) +{ + asb_plugin_add_glob (globs, "/usr/share/pixmaps/*"); + asb_plugin_add_glob (globs, "/usr/share/icons/*"); + asb_plugin_add_glob (globs, "/usr/share/*/icons/*"); +} + +static GdkPixbuf * +asb_app_load_icon (AsbPlugin *plugin, + const gchar *filename, + const gchar *logfn, + guint icon_size, + guint min_icon_size, + GError **error) +{ + g_autoptr(AsImage) im = NULL; + g_autoptr(GError) error_local = NULL; + AsImageLoadFlags load_flags = AS_IMAGE_LOAD_FLAG_ALWAYS_RESIZE; + + /* is icon in a unsupported format */ + if (!asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_IGNORE_LEGACY_ICONS)) + load_flags |= AS_IMAGE_LOAD_FLAG_ONLY_SUPPORTED; + + im = as_image_new (); + if (!as_image_load_filename_full (im, + filename, + icon_size, + min_icon_size, + load_flags, + &error_local)) { + g_set_error (error, + ASB_PLUGIN_ERROR, + ASB_PLUGIN_ERROR_FAILED, + "%s: %s", + error_local->message, logfn); + return NULL; + } + return g_object_ref (as_image_get_pixbuf (im)); +} + +static gboolean +asb_plugin_icon_convert_cached (AsbPlugin *plugin, + AsbApp *app, + const gchar *tmpdir, + const gchar *key, + GError **error) +{ + guint min_icon_size; + g_autofree gchar *fn_hidpi = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *name_hidpi = NULL; + g_autofree gchar *name = NULL; + g_autoptr(AsIcon) icon_hidpi = NULL; + g_autoptr(AsIcon) icon = NULL; + g_autoptr(GdkPixbuf) pixbuf_hidpi = NULL; + g_autoptr(GdkPixbuf) pixbuf = NULL; + + /* find 64x64 icon */ + fn = as_utils_find_icon_filename_full (tmpdir, key, + AS_UTILS_FIND_ICON_NONE, + error); + if (fn == NULL) { + g_prefix_error (error, "Failed to find icon: "); + return FALSE; + } + + /* load the icon */ + min_icon_size = asb_context_get_min_icon_size (plugin->ctx); + pixbuf = asb_app_load_icon (plugin, fn, fn + strlen (tmpdir), + 64, min_icon_size, error); + if (pixbuf == NULL) { + g_prefix_error (error, "Failed to load icon: "); + return FALSE; + } + + /* save in target directory */ + if (asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_HIDPI_ICONS)) { + name = g_strdup_printf ("%ix%i/%s.png", + 64, 64, + as_app_get_id_filename (AS_APP (app))); + } else { + name = g_strdup_printf ("%s.png", + as_app_get_id_filename (AS_APP (app))); + } + icon = as_icon_new (); + as_icon_set_pixbuf (icon, pixbuf); + as_icon_set_name (icon, name); + as_icon_set_kind (icon, AS_ICON_KIND_CACHED); + as_icon_set_prefix (icon, as_app_get_icon_path (AS_APP (app))); + as_app_add_icon (AS_APP (app), icon); + + /* is HiDPI disabled */ + if (!asb_context_get_flag (plugin->ctx, ASB_CONTEXT_FLAG_HIDPI_ICONS)) + return TRUE; + + /* try to get a HiDPI icon */ + fn_hidpi = as_utils_find_icon_filename_full (tmpdir, key, + AS_UTILS_FIND_ICON_HI_DPI, + NULL); + if (fn_hidpi == NULL) + return TRUE; + + /* load the HiDPI icon */ + pixbuf_hidpi = asb_app_load_icon (plugin, fn_hidpi, + fn_hidpi + strlen (tmpdir), + 128, 128, NULL); + if (pixbuf_hidpi == NULL) + return TRUE; + if (gdk_pixbuf_get_width (pixbuf_hidpi) <= gdk_pixbuf_get_width (pixbuf) || + gdk_pixbuf_get_height (pixbuf_hidpi) <= gdk_pixbuf_get_height (pixbuf)) + return TRUE; + as_app_add_kudo_kind (AS_APP (app), AS_KUDO_KIND_HI_DPI_ICON); + + /* save icon */ + name_hidpi = g_strdup_printf ("%ix%i/%s.png", + 128, 128, + as_app_get_id_filename (AS_APP (app))); + icon_hidpi = as_icon_new (); + as_icon_set_pixbuf (icon_hidpi, pixbuf_hidpi); + as_icon_set_name (icon_hidpi, name_hidpi); + as_icon_set_kind (icon_hidpi, AS_ICON_KIND_CACHED); + as_icon_set_prefix (icon_hidpi, as_app_get_icon_path (AS_APP (app))); + as_app_add_icon (AS_APP (app), icon_hidpi); + return TRUE; +} + +gboolean +asb_plugin_process_app (AsbPlugin *plugin, + AsbPackage *pkg, + AsbApp *app, + const gchar *tmpdir, + GError **error) +{ + AsIcon *icon; + g_autofree gchar *key = NULL; + g_autoptr(GError) error_local = NULL; + + /* no icon defined */ + icon = as_app_get_icon_default (AS_APP (app)); + if (icon == NULL) + return TRUE; + + /* is the icon a stock-icon-name? */ + if (as_icon_get_kind (icon) == AS_ICON_KIND_STOCK) { + asb_package_log (pkg, + ASB_PACKAGE_LOG_LEVEL_DEBUG, + "using stock icon %s", + as_icon_get_name (icon)); + return TRUE; + } + + /* convert to cached */ + switch (as_icon_get_kind (icon)) { + case AS_ICON_KIND_LOCAL: + key = g_strdup (as_icon_get_filename (icon)); + break; + default: + key = g_strdup (as_icon_get_name (icon)); + break; + } + g_ptr_array_set_size (as_app_get_icons (AS_APP (app)), 0); + if (!asb_plugin_icon_convert_cached (plugin, app, tmpdir, key, &error_local)) + as_app_add_veto (AS_APP (app), "%s", error_local->message); + + return TRUE; +} diff --git a/libappstream-builder/plugins/meson.build b/libappstream-builder/plugins/meson.build index 542f92f..e7d6b3c 100644 --- a/libappstream-builder/plugins/meson.build +++ b/libappstream-builder/plugins/meson.build @@ -1,5 +1,22 @@ asb_plugins_cargs = ['-DG_LOG_DOMAIN="Asb"'] +shared_module( + 'asb_plugin_icon', + sources : 'asb-plugin-icon.c', + include_directories: [ + top_build_incdir, + asbuilder_incdir, + asglib_incdir, + ], + dependencies : [ + gdkpixbuf, + ], + link_with : [asbuilder, asglib], + c_args : asb_plugins_cargs, + install : true, + install_dir : plugindir, +) + shared_module( 'asb_plugin_gettext', sources : 'asb-plugin-gettext.c', -- cgit v1.2.1