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 +#include +#include +#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 + * + * 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 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_name == NULL ) return NULL; + if ( dr->icon_fetch_uid >0){ + return rofi_icon_fetcher_get ( dr->icon_fetch_uid ); } - 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 ); - } - 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 + * + * 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_background ) { @@ -931,6 +935,11 @@ static void update_callback ( textbox *t, unsigned int index, void *udata, TextB cairo_surface_t *icon = mode_get_icon ( state->sw, state->line_map[index], icon_height ); textbox_icon ( t, icon ); + if ( state->cur_icon ) { + if ( index == listview_get_selected ( state->list_view ) ){ + icon_set_surface ( state->cur_icon, icon ); + } + } if ( state->tokens && config.show_match ) { RofiHighlightColorStyle th = { ROFI_HL_BOLD | ROFI_HL_UNDERLINE, { 0.0, 0.0, 0.0, 0.0 } }; @@ -988,6 +997,12 @@ void rofi_view_update ( RofiViewState *state, gboolean qr ) if ( state->overlay ) { widget_draw ( WIDGET ( state->overlay ), d ); } + + int selected = listview_get_selected ( state->list_view ); + cairo_surface_t *icon = mode_get_icon ( state->sw, state->line_map[selected], 32); + if ( state->cur_icon ) { + icon_set_surface ( state->cur_icon, icon ); + } TICK_N ( "widgets" ); cairo_surface_flush ( CacheState.edit_surf ); if ( qr ) { @@ -1029,7 +1044,7 @@ static void rofi_view_refilter ( RofiViewState *state ) * For large lists with 8 threads I see a factor three speedup of the whole function. */ unsigned int nt = MAX ( 1, state->num_lines / 500 ); - thread_state states[nt]; + thread_state_view states[nt]; GCond cond; GMutex mutex; g_mutex_init ( &mutex ); @@ -1046,7 +1061,7 @@ static void rofi_view_refilter ( RofiViewState *state ) states[i].acount = &count; states[i].plen = plen; states[i].pattern = pattern; - states[i].callback = filter_elements; + states[i].st.callback = filter_elements; if ( i > 0 ) { g_thread_pool_push ( tpool, &states[i], NULL ); } @@ -1637,6 +1652,13 @@ static void rofi_view_add_widget ( RofiViewState *state, widget *parent_widget, textbox *t = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0 ); box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); } + else if ( g_ascii_strncasecmp ( name, "icon", 4 ) == 0 ) { + icon *t = icon_create ( parent_widget, name ); + box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); + if ( rofi_theme_get_boolean ( WIDGET ( t ), "show-current", FALSE ) ) { + state->cur_icon = t; + } + } else { wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL ); box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE ); diff --git a/source/widgets/icon.c b/source/widgets/icon.c new file mode 100644 index 00000000..63ab3c60 --- /dev/null +++ b/source/widgets/icon.c @@ -0,0 +1,156 @@ +/* + * rofi + * + * MIT/X11 License + * Copyright © 2013-2018 Qball Cow + * + * 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 "Widgets.Icon" + +#include +#include +#include "widgets/widget.h" +#include "widgets/widget-internal.h" +#include "widgets/icon.h" +#include "theme.h" + +#include "rofi-icon-fetcher.h" + +struct _icon +{ + widget widget; + + // Size of the icon. + int size; + + uint32_t icon_fetch_id; + + // Source surface. + cairo_surface_t *icon; +}; + + +static int icon_get_desired_height ( widget *widget ) +{ + icon *b = (icon *) widget; + int height = b->size; + height += widget_padding_get_padding_height ( widget ); + return height; +} +static int icon_get_desired_width ( widget *widget ) +{ + icon *b = (icon *) widget; + int width = b->size; + width += widget_padding_get_padding_width ( widget ); + return width; +} + +static void icon_draw ( widget *wid, cairo_t *draw ) +{ + icon *b = (icon *) wid; + // If no icon is loaded. quit. + if ( b->icon == NULL && b->icon_fetch_id > 0 ) { + + b->icon = rofi_icon_fetcher_get ( b->icon_fetch_id ); + if ( b->icon ) { + cairo_surface_reference ( b->icon ); + } + } + if ( b->icon == NULL ) { + return; + } + int iconh = cairo_image_surface_get_height ( b->icon ); + int iconw = cairo_image_surface_get_width ( b->icon ); + int icons = MAX ( iconh, iconw ); + double scale = (double) b->size/ icons; + + cairo_save ( draw ); + + cairo_translate ( draw, ( b->size - iconw * scale ) / 2.0, ( b->size - iconh * scale ) / 2.0 ); + cairo_scale ( draw, scale, scale ); + cairo_set_source_surface ( draw, b->icon, 0, 0 ); + cairo_paint ( draw ); + cairo_restore ( draw ); + +} + +static void icon_free ( widget *wid ) +{ + icon *b = (icon *) wid; + + if ( b->icon ) { + cairo_surface_destroy ( b->icon ); + } + + g_free ( b ); +} + +static void icon_resize ( widget *widget, short w, short h ) +{ + icon *b = (icon *) widget; + if ( b->widget.w != w || b->widget.h != h ) { + b->widget.w = w; + b->widget.h = h; + widget_update ( widget ); + } +} + +void icon_set_surface ( icon *icon, cairo_surface_t *surf ) +{ + icon->icon_fetch_id = 0; + if ( icon->icon ) { + cairo_surface_destroy ( icon->icon ); + icon->icon = NULL; + } + if ( surf ) { + cairo_surface_reference ( surf ); + icon->icon = surf; + } + widget_queue_redraw ( WIDGET ( icon ) ); +} + +icon * icon_create ( widget *parent, const char *name ) +{ + icon *b = g_malloc0 ( sizeof ( icon ) ); + + b->size = 16; + // Initialize widget. + widget_init ( WIDGET ( b ), parent, WIDGET_TYPE_UNKNOWN, name ); + b->widget.draw = icon_draw; + b->widget.free = icon_free; + b->widget.resize = icon_resize; + b->widget.get_desired_height = icon_get_desired_height; + b->widget.get_desired_width = icon_get_desired_width; + + + b->size = rofi_theme_get_integer ( WIDGET ( b ), "size", b->size ); + + const char * filename = rofi_theme_get_string ( WIDGET ( b ), "filename", NULL ); + if ( filename ) { + b->icon_fetch_id = rofi_icon_fetcher_query ( filename, b->size ); + } + + + return b; +} + diff --git a/source/widgets/widget.c b/source/widgets/widget.c index 1562d3d0..22869959 100644 --- a/source/widgets/widget.c +++ b/source/widgets/widget.c @@ -27,6 +27,7 @@ #include #include +#include #include "widgets/widget.h" #include "widgets/widget-internal.h" #include "theme.h" @@ -36,6 +37,8 @@ void widget_init ( widget *wid, widget *parent, WidgetType type, const char *name ) { + wid->ref_count = 1; + wid->type = type; wid->parent = parent; wid->name = g_strdup ( name ); @@ -359,14 +362,26 @@ void widget_draw ( widget *widget, cairo_t *d ) } } } + +void widget_ref ( widget *wid ) +{ + g_assert ( wid != NULL ); + g_assert ( wid->ref_count > 0 ); + + atomic_fetch_add( &(wid->ref_count), 1); +} + void widget_free ( widget *wid ) { if ( wid ) { - if ( wid->name ) { - g_free ( wid->name ); - } - if ( wid->free ) { - wid->free ( wid ); + if ( atomic_fetch_sub ( &(wid->ref_count), 1) == 1 ) { + if ( wid->name ) { + g_free ( wid->name ); + } + if ( wid->free ) { + wid->free ( wid ); + } + return; } } }