2018-06-09 13:13:57 -04:00
|
|
|
/*
|
|
|
|
* rofi
|
|
|
|
*
|
|
|
|
* MIT/X11 License
|
2020-01-01 06:23:12 -05:00
|
|
|
* Copyright © 2013-2020 Qball Cow <qball@gmpclient.org>
|
2018-06-09 13:13:57 -04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-06-12 05:45:42 -04:00
|
|
|
/** The log domain of this Helper. */
|
|
|
|
#define G_LOG_DOMAIN "Helpers.IconFetcher"
|
2018-06-09 13:13:57 -04:00
|
|
|
|
|
|
|
#include "rofi-icon-fetcher.h"
|
|
|
|
#include "rofi-types.h"
|
|
|
|
#include "helper.h"
|
2018-06-09 16:17:32 -04:00
|
|
|
#include "settings.h"
|
2018-06-09 13:13:57 -04:00
|
|
|
|
2018-06-12 05:28:31 -04:00
|
|
|
#include "xcb.h"
|
|
|
|
#include "keyb.h"
|
|
|
|
#include "view.h"
|
|
|
|
|
2018-07-14 12:05:34 -04:00
|
|
|
#include "nkutils-xdg-theme.h"
|
2018-06-09 13:13:57 -04:00
|
|
|
|
2018-08-08 09:55:13 -04:00
|
|
|
typedef struct
|
|
|
|
{
|
2018-06-09 13:13:57 -04:00
|
|
|
// Context for icon-themes.
|
|
|
|
NkXdgThemeContext *xdg_context;
|
|
|
|
|
|
|
|
// On name.
|
2018-08-08 09:55:13 -04:00
|
|
|
GHashTable *icon_cache;
|
2018-06-09 13:13:57 -04:00
|
|
|
// On uid.
|
2018-08-08 09:55:13 -04:00
|
|
|
GHashTable *icon_cache_uid;
|
2018-06-09 13:13:57 -04:00
|
|
|
|
2018-08-08 09:55:13 -04:00
|
|
|
uint32_t last_uid;
|
2018-06-09 13:13:57 -04:00
|
|
|
} IconFetcher;
|
|
|
|
|
2018-08-08 09:55:13 -04:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char *name;
|
2018-06-09 13:13:57 -04:00
|
|
|
GList *sizes;
|
|
|
|
} IconFetcherNameEntry;
|
|
|
|
|
2018-08-08 09:55:13 -04:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
thread_state state;
|
2018-06-09 13:13:57 -04:00
|
|
|
|
2018-08-08 09:55:13 -04:00
|
|
|
GCond *cond;
|
|
|
|
GMutex *mutex;
|
|
|
|
unsigned int *acount;
|
2018-06-09 13:13:57 -04:00
|
|
|
|
2018-08-08 09:55:13 -04:00
|
|
|
uint32_t uid;
|
|
|
|
int size;
|
|
|
|
cairo_surface_t *surface;
|
2018-06-09 13:13:57 -04:00
|
|
|
|
|
|
|
IconFetcherNameEntry *entry;
|
|
|
|
} IconFetcherEntry;
|
|
|
|
|
2018-06-12 08:11:08 -04:00
|
|
|
/**
|
|
|
|
* The icon fetcher internal state.
|
|
|
|
*/
|
2018-06-09 13:13:57 -04:00
|
|
|
IconFetcher *rofi_icon_fetcher_data = NULL;
|
|
|
|
|
|
|
|
static void rofi_icon_fetch_entry_free ( gpointer data )
|
|
|
|
{
|
2018-08-08 09:55:13 -04:00
|
|
|
IconFetcherNameEntry *entry = (IconFetcherNameEntry *) data;
|
2018-06-09 13:13:57 -04:00
|
|
|
|
|
|
|
// Free name/key.
|
|
|
|
g_free ( entry->name );
|
|
|
|
|
|
|
|
for ( GList *iter = g_list_first ( entry->sizes ); iter; iter = g_list_next ( iter ) ) {
|
2018-08-08 09:55:13 -04:00
|
|
|
IconFetcherEntry *sentry = (IconFetcherEntry *) ( iter->data );
|
2018-06-09 13:13:57 -04:00
|
|
|
|
|
|
|
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
|
|
|
|
};
|
2018-08-08 09:55:13 -04:00
|
|
|
const char *themes[2] = { config.icon_theme, NULL };
|
2018-06-09 13:13:57 -04:00
|
|
|
|
2018-08-08 09:55:13 -04:00
|
|
|
rofi_icon_fetcher_data = g_malloc0 ( sizeof ( IconFetcher ) );
|
2018-06-09 13:13:57 -04:00
|
|
|
|
|
|
|
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 )
|
|
|
|
{
|
2018-08-08 09:55:13 -04:00
|
|
|
if ( rofi_icon_fetcher_data == NULL ) {
|
|
|
|
return;
|
|
|
|
}
|
2018-06-09 13:13:57 -04:00
|
|
|
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
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.
|
2018-08-08 09:55:13 -04:00
|
|
|
IconFetcherEntry *sentry = (IconFetcherEntry *) sdata;
|
|
|
|
const gchar *themes[] = {
|
2018-06-09 16:17:32 -04:00
|
|
|
config.icon_theme,
|
2018-06-09 13:13:57 -04:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2018-08-08 09:55:13 -04:00
|
|
|
const gchar *icon_path;
|
|
|
|
gchar *icon_path_ = NULL;
|
2018-06-09 13:13:57 -04:00
|
|
|
|
|
|
|
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 ) {
|
2018-08-08 09:55:13 -04:00
|
|
|
g_debug ( "failed to get icon %s(%d): n/a", sentry->entry->name, sentry->size );
|
2018-06-09 13:13:57 -04:00
|
|
|
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;
|
|
|
|
}
|
2018-08-08 09:55:13 -04:00
|
|
|
sentry->surface = icon_surf;
|
2018-06-09 13:13:57 -04:00
|
|
|
}
|
|
|
|
g_free ( icon_path_ );
|
|
|
|
rofi_view_reload ();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t rofi_icon_fetcher_query ( const char *name, const int size )
|
|
|
|
{
|
2018-08-08 09:55:13 -04:00
|
|
|
g_debug ( "Query: %s(%d)", name, size );
|
2018-06-09 13:13:57 -04:00
|
|
|
IconFetcherNameEntry *entry = g_hash_table_lookup ( rofi_icon_fetcher_data->icon_cache, name );
|
|
|
|
if ( entry == NULL ) {
|
2018-08-08 09:55:13 -04:00
|
|
|
entry = g_new0 ( IconFetcherNameEntry, 1 );
|
|
|
|
entry->name = g_strdup ( name );
|
2018-06-09 13:13:57 -04:00
|
|
|
g_hash_table_insert ( rofi_icon_fetcher_data->icon_cache, entry->name, entry );
|
|
|
|
}
|
|
|
|
IconFetcherEntry *sentry;
|
2018-08-08 09:55:13 -04:00
|
|
|
for ( GList *iter = g_list_first ( entry->sizes ); iter; iter = g_list_next ( iter ) ) {
|
2018-06-09 13:13:57 -04:00
|
|
|
sentry = iter->data;
|
2018-08-08 09:55:13 -04:00
|
|
|
if ( sentry->size == size ) {
|
2018-06-09 13:13:57 -04:00
|
|
|
return sentry->uid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not found.
|
2018-08-08 09:55:13 -04:00
|
|
|
sentry = g_new0 ( IconFetcherEntry, 1 );
|
|
|
|
sentry->uid = ++( rofi_icon_fetcher_data->last_uid );
|
|
|
|
sentry->size = size;
|
|
|
|
sentry->entry = entry;
|
2018-06-09 13:13:57 -04:00
|
|
|
sentry->surface = NULL;
|
|
|
|
|
|
|
|
entry->sizes = g_list_prepend ( entry->sizes, sentry );
|
2018-08-08 09:55:13 -04:00
|
|
|
g_hash_table_insert ( rofi_icon_fetcher_data->icon_cache_uid, GINT_TO_POINTER ( sentry->uid ), sentry );
|
2018-06-09 13:13:57 -04:00
|
|
|
|
|
|
|
// Push into fetching queue.
|
|
|
|
sentry->state.callback = rofi_icon_fetcher_worker;
|
2018-08-08 09:55:13 -04:00
|
|
|
g_thread_pool_push ( tpool, sentry, NULL );
|
2018-06-09 13:13:57 -04:00
|
|
|
|
|
|
|
return sentry->uid;
|
|
|
|
}
|
|
|
|
|
|
|
|
cairo_surface_t * rofi_icon_fetcher_get ( const uint32_t uid )
|
|
|
|
{
|
2018-08-08 09:55:13 -04:00
|
|
|
IconFetcherEntry *sentry = g_hash_table_lookup ( rofi_icon_fetcher_data->icon_cache_uid, GINT_TO_POINTER ( uid ) );
|
2018-06-09 13:13:57 -04:00
|
|
|
if ( sentry ) {
|
|
|
|
return sentry->surface;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|