diff options
author | Dave Davenport <DaveDavenport@users.noreply.github.com> | 2018-06-09 19:13:57 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-09 19:13:57 +0200 |
commit | 441c511296dd4416b86d02ee8c0672cd08808f33 (patch) | |
tree | b5d3f7e13fddebe40f7bd7a6210b8e587ae290be | |
parent | ca1ae5dfcd8170ad3978f0846b523cc0ff8980a2 (diff) |
Add an generic icon fetcher that can be used by any widget and re-uses the main threadpool.
Add an generic icon fetcher that can be used by any widget and re-uses the main threadpool.
* Make threadpool more generic usable.
* Add generic icon fetcher, that caches icons.
* Make DRUN use this fetcher.
* Add icon widget.
#809
-rw-r--r-- | .travis.yml | 4 | ||||
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | include/rofi-icon-fetcher.h | 21 | ||||
-rw-r--r-- | include/rofi-types.h | 11 | ||||
-rw-r--r-- | include/view-internal.h | 4 | ||||
-rw-r--r-- | include/widgets/icon.h | 63 | ||||
-rw-r--r-- | include/widgets/widget-internal.h | 3 | ||||
-rw-r--r-- | include/widgets/widget.h | 7 | ||||
-rw-r--r-- | lexer/theme-lexer.l | 1 | ||||
-rw-r--r-- | meson.build | 4 | ||||
-rw-r--r-- | source/dialogs/drun.c | 106 | ||||
-rw-r--r-- | source/dialogs/run.c | 3 | ||||
-rw-r--r-- | source/dialogs/window.c | 10 | ||||
-rw-r--r-- | source/rofi-icon-fetcher.c | 219 | ||||
-rw-r--r-- | source/rofi.c | 3 | ||||
-rw-r--r-- | source/view.c | 54 | ||||
-rw-r--r-- | source/widgets/icon.c | 156 | ||||
-rw-r--r-- | source/widgets/widget.c | 25 |
18 files changed, 585 insertions, 113 deletions
diff --git a/.travis.yml b/.travis.yml index 0f04e028..d4bb3ab9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,8 +50,8 @@ compiler: - gcc before_install: - - sudo add-apt-repository -y 'deb http://archive.ubuntu.com/ubuntu trusty-backports main restricted universe multiverse' - - sudo add-apt-repository -y 'deb http://debian.jpleau.ca/ jessie-backports main contrib non-free' + - sudo add-apt-repository -y 'deb https://archive.ubuntu.com/ubuntu trusty-backports main restricted universe multiverse' + - sudo add-apt-repository -y 'deb https://debian.jpleau.ca/ jessie-backports main contrib non-free' - sudo apt-get update -qq - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- diff --git a/Makefile.am b/Makefile.am index d2d0db4e..e8d99e1e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,8 +69,10 @@ SOURCES=\ source/history.c\ source/theme.c\ source/rofi-types.c\ + source/rofi-icon-fetcher.c\ source/widgets/box.c\ source/widgets/container.c\ + source/widgets/icon.c\ source/widgets/widget.c\ source/widgets/textbox.c\ source/widgets/listview.c\ @@ -91,6 +93,7 @@ SOURCES=\ include/xcb-internal.h\ include/rofi.h\ include/rofi-types.h\ + include/rofi-icon-fetcher.h\ include/mode.h\ include/mode-private.h\ include/settings.h\ @@ -105,6 +108,7 @@ SOURCES=\ include/css-colors.h\ include/widgets/box.h\ include/widgets/container.h\ + include/widgets/icon.h\ include/widgets/widget.h\ include/widgets/widget-internal.h\ include/widgets/textbox.h\ diff --git a/include/rofi-icon-fetcher.h b/include/rofi-icon-fetcher.h new file mode 100644 index 00000000..3198658e --- /dev/null +++ b/include/rofi-icon-fetcher.h @@ -0,0 +1,21 @@ +#ifndef ROFI_ICON_FETCHER_H +#define ROFI_ICON_FETCHER_H + +#include <glib.h> +#include <stdint.h> +#include <cairo.h> +#include "nkutils-xdg-theme.h" + +void rofi_icon_fetcher_init ( void ); + + +void rofi_icon_fetcher_destroy ( void ); + + +uint32_t rofi_icon_fetcher_query ( const char *name, const int size ); + + +cairo_surface_t * rofi_icon_fetcher_get ( const uint32_t uid ); + + +#endif // ROFI_ICON_FETCHER_H diff --git a/include/rofi-types.h b/include/rofi-types.h index 93fc683c..46b01f1b 100644 --- a/include/rofi-types.h +++ b/include/rofi-types.h @@ -235,5 +235,16 @@ typedef struct rofi_int_matcher_t gboolean invert; } rofi_int_matcher; +/** + * Structure with data to process by each worker thread. + * TODO: Make this more generic wrapper. + */ +typedef struct _thread_state +{ + void ( *callback )( struct _thread_state *t, gpointer data ); +} thread_state; + +extern GThreadPool *tpool; + G_END_DECLS #endif // INCLUDE_ROFI_TYPES_H diff --git a/include/view-internal.h b/include/view-internal.h index efbf7987..cb160293 100644 --- a/include/view-internal.h +++ b/include/view-internal.h @@ -32,6 +32,7 @@ #include "widgets/textbox.h" #include "widgets/listview.h" #include "widgets/box.h" +#include "widgets/icon.h" #include "keyb.h" #include "xcb.h" #include "theme.h" @@ -68,6 +69,9 @@ struct RofiViewState /** #textbox containing the message entry */ textbox *mesg_tb; + + icon *cur_icon; + /** Array with the levenshtein distance for each eleemnt. */ int *distance; /** Array with the translation between the filtered and unfiltered list. */ diff --git a/include/widgets/icon.h b/include/widgets/icon.h new file mode 100644 index 00000000..c7bf556f --- /dev/null +++ b/include/widgets/icon.h @@ -0,0 +1,63 @@ +/* + * rofi + * + * MIT/X11 License + * Copyright © 2013-2018 Qball Cow <qball@gmpclient.org> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef ROFI_ICON_H +#define ROFI_ICON_H + +#include "widget.h" + +/** + * @defgroup icon icon + * @ingroup widget + * + * + * @{ + */ + +/** + * Abstract handle to the icon widget internal state. + */ +typedef struct _icon icon; + +/** + * @param parent The widget's parent + * @param name The name of the widget. + * + * @returns a newly created icon, free with #widget_free + */ +icon * icon_create ( widget *parent, const char *name ); + +/** + * @param widget The icon widget handle. + * @param size The size of the icon. + * + */ +void icon_set_size ( widget *icon, const int size ); + +void icon_set_surface ( icon *icon, cairo_surface_t *surf ); +/*@}*/ +#endif // ROFI_ICON_H diff --git a/include/widgets/widget-internal.h b/include/widgets/widget-internal.h index e32c8766..762f5723 100644 --- a/include/widgets/widget-internal.h +++ b/include/widgets/widget-internal.h @@ -94,6 +94,9 @@ struct _widget /** Name of widget (used for theming) */ char *name; const char *state; + + /** Used for reference counting */ + int ref_count; }; /** diff --git a/include/widgets/widget.h b/include/widgets/widget.h index 2c72f2e8..28fd94c3 100644 --- a/include/widgets/widget.h +++ b/include/widgets/widget.h @@ -322,5 +322,12 @@ int widget_get_absolute_xpos ( widget *wid ); * @returns the absolute y-position of widget of the widget in pixels. */ int widget_get_absolute_ypos ( widget *wid ); + +/** + * @param wid The widget handle + * + * Increment the reference count on the widget. + */ +void widget_ref ( widget *wid ); /*@}*/ #endif // ROFI_WIDGET_H diff --git a/lexer/theme-lexer.l b/lexer/theme-lexer.l index da7c184f..bc1516f2 100644 --- a/lexer/theme-lexer.l +++ b/lexer/theme-lexer.l @@ -30,6 +30,7 @@ %option nounput %option never-interactive %option bison-locations +%option bison-bridge %{ #include <stdio.h> diff --git a/meson.build b/meson.build index e9c93c44..8b48e294 100644 --- a/meson.build +++ b/meson.build @@ -137,8 +137,10 @@ rofi_sources = files( 'source/timings.c', 'source/history.c', 'source/theme.c', + 'source/rofi-icon-fetcher.c', 'source/css-colors.c', 'source/widgets/box.c', + 'source/widgets/icon.c', 'source/widgets/container.c', 'source/widgets/widget.c', 'source/widgets/textbox.c', @@ -165,6 +167,7 @@ rofi_sources = files( 'include/keyb.h', 'include/view.h', 'include/view-internal.h', + 'include/rofi-icon-fetcher.h', 'include/helper.h', 'include/helper-theme.h', 'include/timings.h', @@ -173,6 +176,7 @@ rofi_sources = files( 'include/rofi-types.h', 'include/css-colors.h', 'include/widgets/box.h', + 'include/widgets/icon.h', 'include/widgets/container.h', 'include/widgets/widget.h', 'include/widgets/widget-internal.h', diff --git a/source/dialogs/drun.c b/source/dialogs/drun.c index 95f21768..077f0e3e 100644 --- a/source/dialogs/drun.c +++ b/source/dialogs/drun.c @@ -50,19 +50,24 @@ #include "widgets/textbox.h" #include "history.h" #include "dialogs/drun.h" -#include "nkutils-xdg-theme.h" #include "xcb.h" +#include "rofi-icon-fetcher.h" + #define DRUN_CACHE_FILE "rofi3.druncache" #define DRUN_GROUP_NAME "Desktop Entry" + +typedef struct _DRunModePrivateData DRunModePrivateData; /** * Store extra information about the entry. * Currently the executable and if it should run in terminal. */ typedef struct { + thread_state st; + DRunModePrivateData *pd; /* Root */ char *root; /* Path to desktop file */ @@ -93,6 +98,8 @@ typedef struct GKeyFile *key_file; gint sort_index; + + uint32_t icon_fetch_uid; } DRunModeEntry; typedef struct @@ -119,24 +126,21 @@ static DRunEntryField matching_entry_fields[DRUN_MATCH_NUM_FIELDS] = { { .entry_field_name = "comment", .enabled = FALSE, } }; -typedef struct +struct _DRunModePrivateData { - NkXdgThemeContext *xdg_context; DRunModeEntry *entry_list; unsigned int cmd_list_length; unsigned int cmd_list_length_actual; // List of disabled entries. GHashTable *disabled_entries; unsigned int disabled_entries_length; - GThreadPool *pool; unsigned int expected_line_height; - DRunModeEntry quit_entry; // Theme const gchar *icon_theme; // DE gchar **current_desktop_list; -} DRunModePrivateData; +}; struct RegexEvalArg { @@ -398,6 +402,7 @@ static gboolean read_desktop_file ( DRunModePrivateData *pd, const char *root, c pd->entry_list[pd->cmd_list_length].sort_index = -pd->cmd_list_length; } pd->entry_list[pd->cmd_list_length].icon_size = 0; + pd->entry_list[pd->cmd_list_length].icon_fetch_uid = 0; pd->entry_list[pd->cmd_list_length].root = g_strdup ( root ); pd->entry_list[pd->cmd_list_length].path = g_strdup ( path ); pd->entry_list[pd->cmd_list_length].desktop_id = g_strdup ( id ); @@ -582,59 +587,6 @@ static void get_apps ( DRunModePrivateData *pd ) TICK_N ( "Sorting done." ); } -static void drun_icon_fetch ( gpointer data, gpointer user_data ) -{ - g_debug ( "Starting up icon fetching thread." ); - // as long as dr->icon is updated atomicly.. (is a pointer write atomic?) - // this should be fine running in another thread. - DRunModePrivateData *pd = (DRunModePrivateData *) user_data; - DRunModeEntry *dr = (DRunModeEntry *) data; - const gchar *themes[2] = { - config.drun_icon_theme, - NULL - }; - - if ( dr->icon_name == NULL ) { - return; - } - const gchar *icon_path; - gchar *icon_path_ = NULL; - - if ( g_path_is_absolute ( dr->icon_name ) ) { - icon_path = dr->icon_name; - } - else { - icon_path = icon_path_ = nk_xdg_theme_get_icon ( pd->xdg_context, themes, NULL, dr->icon_name, dr->icon_size, 1, TRUE ); - if ( icon_path_ == NULL ) { - g_debug ( "Failed to get Icon %s(%d): n/a", dr->icon_name, dr->icon_size ); - return; - } - else{ - g_debug ( "Found Icon %s(%d): %s", dr->icon_name, dr->icon_size, icon_path ); - } - } - cairo_surface_t *icon_surf = NULL; - if ( g_str_has_suffix ( icon_path, ".png" ) ) { - icon_surf = cairo_image_surface_create_from_png ( icon_path ); - } - else if ( g_str_has_suffix ( icon_path, ".svg" ) ) { - icon_surf = cairo_image_surface_create_from_svg ( icon_path, dr->icon_size ); - } - else { - g_debug ( "Icon type not yet supported: %s", icon_path ); - } - if ( icon_surf ) { - // Check if surface is valid. - if ( cairo_surface_status ( icon_surf ) != CAIRO_STATUS_SUCCESS ) { - g_debug ( "Icon failed to open: %s(%d): %s", dr->icon_name, dr->icon_size, icon_path ); - cairo_surface_destroy ( icon_surf ); - icon_surf = NULL; - } - dr->icon = icon_surf; - } - g_free ( icon_path_ ); - rofi_view_reload (); -} static void drun_mode_parse_entry_fields () { @@ -677,16 +629,6 @@ static int drun_mode_init ( Mode *sw ) if ( mode_get_private_data ( sw ) != NULL ) { return TRUE; } - - static const gchar * const drun_icon_fallback_themes[] = { - "Adwaita", - "gnome", - NULL - }; - const gchar *themes[2] = { - config.drun_icon_theme, - NULL - }; DRunModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) ); pd->disabled_entries = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, NULL ); mode_set_private_data ( sw, (void *) pd ); @@ -694,9 +636,6 @@ static int drun_mode_init ( Mode *sw ) const char *current_desktop = g_getenv ( "XDG_CURRENT_DESKTOP" ); pd->current_desktop_list = current_desktop ? g_strsplit ( current_desktop, ":", 0 ) : NULL; - // Theme - pd->xdg_context = nk_xdg_theme_context_new ( drun_icon_fallback_themes, NULL ); - nk_xdg_theme_preload_themes_icon ( pd->xdg_context, themes ); drun_mode_parse_entry_fields (); get_apps ( pd ); return TRUE; @@ -746,10 +685,6 @@ static ModeMode drun_mode_result ( Mode *sw, int mretv, char **input, unsigned i else if ( ( mretv & MENU_ENTRY_DELETE ) && selected_line < rmpd->cmd_list_length ) { // Possitive sort index means it is in history. if ( rmpd->entry_list[selected_line].sort_index >= 0 ) { - if ( rmpd->pool ) { - g_thread_pool_free ( rmpd->pool, TRUE, TRUE ); - rmpd->pool = NULL; - } delete_entry_history ( &( rmpd->entry_list[selected_line] ) ); drun_entry_clear ( &( rmpd->entry_list[selected_line] ) ); memmove ( &( rmpd->entry_list[selected_line] ), &rmpd->entry_list[selected_line + 1], @@ -764,16 +699,11 @@ static void drun_mode_destroy ( Mode *sw ) { DRunModePrivateData *rmpd = (DRunModePrivateData *) mode_get_private_data ( sw ); if ( rmpd != NULL ) { - if ( rmpd->pool ) { - g_thread_pool_free ( rmpd->pool, TRUE, TRUE ); - rmpd->pool = NULL; - } for ( size_t i = 0; i < rmpd->cmd_list_length; i++ ) { drun_entry_clear ( &( rmpd->entry_list[i] ) ); } g_hash_table_destroy ( rmpd->disabled_entries ); g_free ( rmpd->entry_list ); - nk_xdg_theme_context_free ( rmpd->xdg_context ); g_strfreev ( rmpd->current_desktop_list ); g_free ( rmpd ); @@ -808,16 +738,12 @@ static cairo_surface_t *_get_icon ( const Mode *sw, unsigned int selected_line, DRunModePrivateData *pd = (DRunModePrivateData *) mode_get_private_data ( sw ); g_return_val_if_fail ( pd->entry_list != NULL, NULL ); DRunModeEntry *dr = &( pd->entry_list[selected_line] ); - if ( pd->pool == NULL ) { - /* TODO: 4 threads good? */ - pd->pool = g_thread_pool_new ( drun_icon_fetch, pd, 4, FALSE, NULL ); - } - if ( dr->icon_size == 0 ) { - dr->icon_size = height; - //g_async_queue_push ( pd->icon_fetch_queue, dr ); - g_thread_pool_push ( pd->pool, dr, NULL ); + if ( dr->icon_name == NULL ) return NULL; + if ( dr->icon_fetch_uid >0){ + return rofi_icon_fetcher_get ( dr->icon_fetch_uid ); } - return dr->icon; + dr->icon_fetch_uid = rofi_icon_fetcher_query ( dr->icon_name, height ); + return NULL; } static char *drun_get_completion ( const Mode *sw, unsigned int index ) diff --git a/source/dialogs/run.c b/source/dialogs/run.c index 0465b9f7..0e9cae9c 100644 --- a/source/dialogs/run.c +++ b/source/dialogs/run.c @@ -55,6 +55,7 @@ #include "mode-private.h" #include "timings.h" +#include "rofi-icon-fetcher.h" /** * Name of the history file where previously chosen commands are stored. */ @@ -400,6 +401,7 @@ static char *_get_display_value ( const Mode *sw, unsigned int selected_line, G_ const RunModePrivateData *rmpd = (const RunModePrivateData *) sw->private_data; return get_entry ? g_strdup ( rmpd->cmd_list[selected_line] ) : NULL; } + static int run_token_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index ) { const RunModePrivateData *rmpd = (const RunModePrivateData *) sw->private_data; @@ -417,6 +419,7 @@ Mode run_mode = ._destroy = run_mode_destroy, ._token_match = run_token_match, ._get_display_value = _get_display_value, + ._get_icon = NULL, ._get_completion = NULL, ._preprocess_input = NULL, .private_data = NULL, diff --git a/source/dialogs/window.c b/source/dialogs/window.c index e6691401..d031d5d1 100644 --- a/source/dialogs/window.c +++ b/source/dialogs/window.c @@ -56,6 +56,8 @@ #include "timings.h" +#include "rofi-icon-fetcher.h" + #define WINLIST 32 #define CLIENTSTATE 10 @@ -108,6 +110,7 @@ typedef struct char *wmdesktopstr; cairo_surface_t *icon; gboolean icon_checked; + uint32_t icon_fetch_uid; } client; // window lists @@ -910,6 +913,13 @@ static cairo_surface_t *_get_icon ( const Mode *sw, unsigned int selected_line, c->icon = get_net_wm_icon ( rmpd->ids->array[selected_line], size ); c->icon_checked = TRUE; } + if ( c->icon == NULL && c->class ){ + if ( c->icon_fetch_uid > 0){ + return rofi_icon_fetcher_get ( c->icon_fetch_uid ); + } + c->icon_fetch_uid = rofi_icon_fetcher_query ( c->class, size); + + } return c->icon; } diff --git a/source/rofi-icon-fetcher.c b/source/rofi-icon-fetcher.c new file mode 100644 index 00000000..1c0dc6ca --- /dev/null +++ b/source/rofi-icon-fetcher.c @@ -0,0 +1,219 @@ +/* + * rofi + * + * MIT/X11 License + * Copyright © 2013-2017 Qball Cow <qball@gmpclient.org> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define G_LOG_DOMAIN "Util.IconFetcher" + +#include "rofi-icon-fetcher.h" +#include "rofi-types.h" +#include "helper.h" + + +typedef struct { + // Context for icon-themes. + NkXdgThemeContext *xdg_context; + + // On name. + GHashTable *icon_cache; + // On uid. + GHashTable *icon_cache_uid; + + uint32_t last_uid; + + +} IconFetcher; + + +typedef struct { + char *name; + GList *sizes; +} IconFetcherNameEntry; + +typedef struct { + thread_state state; + + GCond *cond; + GMutex *mutex; + unsigned int *acount; + + uint32_t uid; + int size; + cairo_surface_t *surface; + + IconFetcherNameEntry *entry; +} IconFetcherEntry; + +IconFetcher *rofi_icon_fetcher_data = NULL; + + +static void rofi_icon_fetch_entry_free ( gpointer data ) +{ + IconFetcherNameEntry *entry = (IconFetcherNameEntry*) data; + + // Free name/key. + g_free ( entry->name ); + + + for ( GList *iter = g_list_first ( entry->sizes ); iter; iter = g_list_next ( iter ) ) { + IconFetcherEntry *sentry = (IconFetcherEntry *)(iter->data); + + cairo_surface_destroy ( sentry->surface ); + g_free ( sentry ); + } + + g_list_free ( entry->sizes ); + g_free ( entry ); +} + + +void rofi_icon_fetcher_init ( void ) +{ + g_assert ( rofi_icon_fetcher_data == NULL ); + + static const gchar * const icon_fallback_themes[] = { + "Adwaita", + "gnome", + NULL + }; + const char *themes[2] = { NULL, NULL}; + + rofi_icon_fetcher_data = g_malloc0(sizeof(IconFetcher)); + + rofi_icon_fetcher_data->xdg_context = nk_xdg_theme_context_new ( icon_fallback_themes, NULL ); + nk_xdg_theme_preload_themes_icon ( rofi_icon_fetcher_data->xdg_context, themes ); + + + rofi_icon_fetcher_data->icon_cache_uid = g_hash_table_new ( g_direct_hash, g_direct_equal ); + rofi_icon_fetcher_data->icon_cache = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, rofi_icon_fetch_entry_free ); +} + + +void rofi_icon_fetcher_destroy ( void ) +{ + if ( rofi_icon_fetcher_data == NULL ) return; + + nk_xdg_theme_context_free ( rofi_icon_fetcher_data->xdg_context ); + + + g_hash_table_unref ( rofi_icon_fetcher_data->icon_cache_uid ); + g_hash_table_unref ( rofi_icon_fetcher_data->icon_cache ); + + g_free ( rofi_icon_fetcher_data ); +} +void rofi_view_reload (); +static void rofi_icon_fetcher_worker ( thread_state *sdata, G_GNUC_UNUSED gpointer user_data ) +{ + g_debug ( "starting up icon fetching thread." ); + // as long as dr->icon is updated atomicly.. (is a pointer write atomic?) + // this should be fine running in another thread. + IconFetcherEntry *sentry = (IconFetcherEntry*) sdata; + const gchar *themes[1] = { + NULL + }; + + const gchar *icon_path; + gchar *icon_path_ = NULL; + + if ( g_path_is_absolute ( sentry->entry->name ) ) { + icon_path = sentry->entry->name; + } + else { + icon_path = icon_path_ = nk_xdg_theme_get_icon ( rofi_icon_fetcher_data->xdg_context, themes, NULL, sentry->entry->name, sentry->size, 1, TRUE ); + if ( icon_path_ == NULL ) { + g_debug ( "failed to get icon %s(%d): n/a",sentry->entry->name, sentry->size ); + return; + } + else{ + g_debug ( "found icon %s(%d): %s", sentry->entry->name, sentry->size, icon_path ); + } + } + cairo_surface_t *icon_surf = NULL; + if ( g_str_has_suffix ( icon_path, ".png" ) ) { + icon_surf = cairo_image_surface_create_from_png ( icon_path ); + } + else if ( g_str_has_suffix ( icon_path, ".svg" ) ) { + icon_surf = cairo_image_surface_create_from_svg ( icon_path, sentry->size ); + } + else { + g_debug ( "icon type not yet supported: %s", icon_path ); + } + if ( icon_surf ) { + // check if surface is valid. + if ( cairo_surface_status ( icon_surf ) != CAIRO_STATUS_SUCCESS ) { + g_debug ( "icon failed to open: %s(%d): %s", sentry->entry->name, sentry->size, icon_path ); + cairo_surface_destroy ( icon_surf ); + icon_surf = NULL; + } + sentry->surface= icon_surf; + } + g_free ( icon_path_ ); + rofi_view_reload (); +} + + +uint32_t rofi_icon_fetcher_query ( const char *name, const int size ) +{ + g_debug ("Query: %s(%d)", name, size); + IconFetcherNameEntry *entry = g_hash_table_lookup ( rofi_icon_fetcher_data->icon_cache, name ); + if ( entry == NULL ) { + entry = g_new0(IconFetcherNameEntry,1); + entry->name = g_strdup(name); + g_hash_table_insert ( rofi_icon_fetcher_data->icon_cache, entry->name, entry ); + } + IconFetcherEntry *sentry; + for ( GList *iter = g_list_first(entry->sizes); iter; iter = g_list_next ( iter ) ) { + sentry = iter->data; + if ( sentry->size == size ){ + return sentry->uid; + } + } + + // Not found. + sentry = g_new0(IconFetcherEntry, 1); + sentry->uid = ++(rofi_icon_fetcher_data->last_uid); + sentry->size = size; + sentry->entry = entry; + sentry->surface = NULL; + + entry->sizes = g_list_prepend ( entry->sizes, sentry ); + g_hash_table_insert ( rofi_icon_fetcher_data->icon_cache_uid, GINT_TO_POINTER(sentry->uid), sentry ); + + // Push into fetching queue. + sentry->state.callback = rofi_icon_fetcher_worker; + g_thread_pool_push ( tpool, sentry, NULL); + + return sentry->uid; +} + + +cairo_surface_t * rofi_icon_fetcher_get ( const uint32_t uid ) +{ + IconFetcherEntry *sentry = g_hash_table_lookup ( rofi_icon_fetcher_data->icon_cache_uid, GINT_TO_POINTER(uid) ); + if ( sentry ) { + return sentry->surface; + } + return NULL; +} diff --git a/source/rofi.c b/source/rofi.c index 55925702..ac8695e2 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -68,6 +68,7 @@ #include "view-internal.h" #include "theme.h" +#include "rofi-icon-fetcher.h" #include "timings.h" @@ -442,6 +443,7 @@ static void cleanup () } TIMINGS_STOP (); rofi_collect_modi_destroy ( ); + rofi_icon_fetcher_destroy ( ); } /** @@ -956,6 +958,7 @@ int main ( int argc, char *argv[] ) } rofi_view_workers_initialize (); + rofi_icon_fetcher_init ( ); // Create pid file int pfd = create_pid_file ( pidfile ); diff --git a/source/view.c b/source/view.c index 1dba430c..721d065b 100644 --- a/source/view.c +++ b/source/view.c @@ -551,23 +551,24 @@ static RofiViewState * __rofi_view_state_create ( void ) { return g_malloc0 ( sizeof ( RofiViewState ) ); } -/** - * Structure with data to process by each worker thread. - */ -typedef struct _thread_state + +typedef struct _thread_state_view { + thread_state st; + + GCond *cond; + GMutex *mutex; + unsigned int *acount; + RofiViewState *state; unsigned int start; unsigned int stop; unsigned int count; - GCond *cond; - GMutex *mutex; - unsigned int *acount; const char *pattern; glong plen; - void ( *callback )( struct _thread_state *t, gpointer data ); -}thread_state; + +} thread_state_view; /** * @param data A thread_state object. * @param user_data User data to pass to thread_state callback @@ -578,14 +579,11 @@ static void rofi_view_call_thread ( gpointer data, gpointer user_data ) { thread_state *t = (thread_state *) data; t->callback ( t, user_data ); - g_mutex_lock ( t->mutex ); - ( *( t->acount ) )--; - g_cond_signal ( t->cond ); - g_mutex_unlock ( t->mutex ); } -static void filter_elements ( thread_state *t, G_GNUC_UNUSED gpointer user_data ) +static void filter_elements ( thread_state *ts, G_GNUC_UNUSED gpointer user_data ) { + thread_state_view *t = (thread_state_view *)ts; for ( unsigned int i = t->start; i < t->stop; i++ ) { int match = mode_token_match ( t->state->sw, t->state->tokens, i ); // If each token was matched, add it to list. @@ -606,6 +604,12 @@ static void filter_elements ( thread_state *t, G_GNUC_UNUSED gpointer user_data t->count++; } } + if ( t->acount != NULL ) { + g_mutex_lock ( t->mutex ); + ( *( t->acount ) )--; + g_cond_signal ( t->cond ); + g_mutex_unlock ( t->mutex ); + } } static void rofi_view_setup_fake_transparency ( const char* const fake_backg |