1
0
Fork 0
mirror of https://github.com/davatorium/rofi.git synced 2024-11-18 13:54:36 -05:00

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
This commit is contained in:
Dave Davenport 2018-06-09 19:13:57 +02:00 committed by GitHub
parent ca1ae5dfcd
commit 441c511296
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 585 additions and 113 deletions

View file

@ -50,8 +50,8 @@ compiler:
- gcc - gcc
before_install: 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 https://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://debian.jpleau.ca/ jessie-backports main contrib non-free'
- sudo apt-get update -qq - 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- - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-

View file

@ -69,8 +69,10 @@ SOURCES=\
source/history.c\ source/history.c\
source/theme.c\ source/theme.c\
source/rofi-types.c\ source/rofi-types.c\
source/rofi-icon-fetcher.c\
source/widgets/box.c\ source/widgets/box.c\
source/widgets/container.c\ source/widgets/container.c\
source/widgets/icon.c\
source/widgets/widget.c\ source/widgets/widget.c\
source/widgets/textbox.c\ source/widgets/textbox.c\
source/widgets/listview.c\ source/widgets/listview.c\
@ -91,6 +93,7 @@ SOURCES=\
include/xcb-internal.h\ include/xcb-internal.h\
include/rofi.h\ include/rofi.h\
include/rofi-types.h\ include/rofi-types.h\
include/rofi-icon-fetcher.h\
include/mode.h\ include/mode.h\
include/mode-private.h\ include/mode-private.h\
include/settings.h\ include/settings.h\
@ -105,6 +108,7 @@ SOURCES=\
include/css-colors.h\ include/css-colors.h\
include/widgets/box.h\ include/widgets/box.h\
include/widgets/container.h\ include/widgets/container.h\
include/widgets/icon.h\
include/widgets/widget.h\ include/widgets/widget.h\
include/widgets/widget-internal.h\ include/widgets/widget-internal.h\
include/widgets/textbox.h\ include/widgets/textbox.h\

View file

@ -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

View file

@ -235,5 +235,16 @@ typedef struct rofi_int_matcher_t
gboolean invert; gboolean invert;
} rofi_int_matcher; } 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 G_END_DECLS
#endif // INCLUDE_ROFI_TYPES_H #endif // INCLUDE_ROFI_TYPES_H

View file

@ -32,6 +32,7 @@
#include "widgets/textbox.h" #include "widgets/textbox.h"
#include "widgets/listview.h" #include "widgets/listview.h"
#include "widgets/box.h" #include "widgets/box.h"
#include "widgets/icon.h"
#include "keyb.h" #include "keyb.h"
#include "xcb.h" #include "xcb.h"
#include "theme.h" #include "theme.h"
@ -68,6 +69,9 @@ struct RofiViewState
/** #textbox containing the message entry */ /** #textbox containing the message entry */
textbox *mesg_tb; textbox *mesg_tb;
icon *cur_icon;
/** Array with the levenshtein distance for each eleemnt. */ /** Array with the levenshtein distance for each eleemnt. */
int *distance; int *distance;
/** Array with the translation between the filtered and unfiltered list. */ /** Array with the translation between the filtered and unfiltered list. */

63
include/widgets/icon.h Normal file
View file

@ -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

View file

@ -94,6 +94,9 @@ struct _widget
/** Name of widget (used for theming) */ /** Name of widget (used for theming) */
char *name; char *name;
const char *state; const char *state;
/** Used for reference counting */
int ref_count;
}; };
/** /**

View file

@ -322,5 +322,12 @@ int widget_get_absolute_xpos ( widget *wid );
* @returns the absolute y-position of widget of the widget in pixels. * @returns the absolute y-position of widget of the widget in pixels.
*/ */
int widget_get_absolute_ypos ( widget *wid ); 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 #endif // ROFI_WIDGET_H

View file

@ -30,6 +30,7 @@
%option nounput %option nounput
%option never-interactive %option never-interactive
%option bison-locations %option bison-locations
%option bison-bridge
%{ %{
#include <stdio.h> #include <stdio.h>

View file

@ -137,8 +137,10 @@ rofi_sources = files(
'source/timings.c', 'source/timings.c',
'source/history.c', 'source/history.c',
'source/theme.c', 'source/theme.c',
'source/rofi-icon-fetcher.c',
'source/css-colors.c', 'source/css-colors.c',
'source/widgets/box.c', 'source/widgets/box.c',
'source/widgets/icon.c',
'source/widgets/container.c', 'source/widgets/container.c',
'source/widgets/widget.c', 'source/widgets/widget.c',
'source/widgets/textbox.c', 'source/widgets/textbox.c',
@ -165,6 +167,7 @@ rofi_sources = files(
'include/keyb.h', 'include/keyb.h',
'include/view.h', 'include/view.h',
'include/view-internal.h', 'include/view-internal.h',
'include/rofi-icon-fetcher.h',
'include/helper.h', 'include/helper.h',
'include/helper-theme.h', 'include/helper-theme.h',
'include/timings.h', 'include/timings.h',
@ -173,6 +176,7 @@ rofi_sources = files(
'include/rofi-types.h', 'include/rofi-types.h',
'include/css-colors.h', 'include/css-colors.h',
'include/widgets/box.h', 'include/widgets/box.h',
'include/widgets/icon.h',
'include/widgets/container.h', 'include/widgets/container.h',
'include/widgets/widget.h', 'include/widgets/widget.h',
'include/widgets/widget-internal.h', 'include/widgets/widget-internal.h',

View file

@ -50,19 +50,24 @@
#include "widgets/textbox.h" #include "widgets/textbox.h"
#include "history.h" #include "history.h"
#include "dialogs/drun.h" #include "dialogs/drun.h"
#include "nkutils-xdg-theme.h"
#include "xcb.h" #include "xcb.h"
#include "rofi-icon-fetcher.h"
#define DRUN_CACHE_FILE "rofi3.druncache" #define DRUN_CACHE_FILE "rofi3.druncache"
#define DRUN_GROUP_NAME "Desktop Entry" #define DRUN_GROUP_NAME "Desktop Entry"
typedef struct _DRunModePrivateData DRunModePrivateData;
/** /**
* Store extra information about the entry. * Store extra information about the entry.
* Currently the executable and if it should run in terminal. * Currently the executable and if it should run in terminal.
*/ */
typedef struct typedef struct
{ {
thread_state st;
DRunModePrivateData *pd;
/* Root */ /* Root */
char *root; char *root;
/* Path to desktop file */ /* Path to desktop file */
@ -93,6 +98,8 @@ typedef struct
GKeyFile *key_file; GKeyFile *key_file;
gint sort_index; gint sort_index;
uint32_t icon_fetch_uid;
} DRunModeEntry; } DRunModeEntry;
typedef struct typedef struct
@ -119,24 +126,21 @@ static DRunEntryField matching_entry_fields[DRUN_MATCH_NUM_FIELDS] = {
{ .entry_field_name = "comment", .enabled = FALSE, } { .entry_field_name = "comment", .enabled = FALSE, }
}; };
typedef struct struct _DRunModePrivateData
{ {
NkXdgThemeContext *xdg_context;
DRunModeEntry *entry_list; DRunModeEntry *entry_list;
unsigned int cmd_list_length; unsigned int cmd_list_length;
unsigned int cmd_list_length_actual; unsigned int cmd_list_length_actual;
// List of disabled entries. // List of disabled entries.
GHashTable *disabled_entries; GHashTable *disabled_entries;
unsigned int disabled_entries_length; unsigned int disabled_entries_length;
GThreadPool *pool;
unsigned int expected_line_height; unsigned int expected_line_height;
DRunModeEntry quit_entry;
// Theme // Theme
const gchar *icon_theme; const gchar *icon_theme;
// DE // DE
gchar **current_desktop_list; gchar **current_desktop_list;
} DRunModePrivateData; };
struct RegexEvalArg 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].sort_index = -pd->cmd_list_length;
} }
pd->entry_list[pd->cmd_list_length].icon_size = 0; 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].root = g_strdup ( root );
pd->entry_list[pd->cmd_list_length].path = g_strdup ( path ); pd->entry_list[pd->cmd_list_length].path = g_strdup ( path );
pd->entry_list[pd->cmd_list_length].desktop_id = g_strdup ( id ); 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." ); 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 () 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 ) { if ( mode_get_private_data ( sw ) != NULL ) {
return TRUE; 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 ) ); DRunModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
pd->disabled_entries = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, NULL ); pd->disabled_entries = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, NULL );
mode_set_private_data ( sw, (void *) pd ); 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" ); const char *current_desktop = g_getenv ( "XDG_CURRENT_DESKTOP" );
pd->current_desktop_list = current_desktop ? g_strsplit ( current_desktop, ":", 0 ) : NULL; 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 (); drun_mode_parse_entry_fields ();
get_apps ( pd ); get_apps ( pd );
return TRUE; 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 ) { else if ( ( mretv & MENU_ENTRY_DELETE ) && selected_line < rmpd->cmd_list_length ) {
// Possitive sort index means it is in history. // Possitive sort index means it is in history.
if ( rmpd->entry_list[selected_line].sort_index >= 0 ) { 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] ) ); delete_entry_history ( &( rmpd->entry_list[selected_line] ) );
drun_entry_clear ( &( 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], 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 ); DRunModePrivateData *rmpd = (DRunModePrivateData *) mode_get_private_data ( sw );
if ( rmpd != NULL ) { 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++ ) { for ( size_t i = 0; i < rmpd->cmd_list_length; i++ ) {
drun_entry_clear ( &( rmpd->entry_list[i] ) ); drun_entry_clear ( &( rmpd->entry_list[i] ) );
} }
g_hash_table_destroy ( rmpd->disabled_entries ); g_hash_table_destroy ( rmpd->disabled_entries );
g_free ( rmpd->entry_list ); g_free ( rmpd->entry_list );
nk_xdg_theme_context_free ( rmpd->xdg_context );
g_strfreev ( rmpd->current_desktop_list ); g_strfreev ( rmpd->current_desktop_list );
g_free ( rmpd ); 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 ); DRunModePrivateData *pd = (DRunModePrivateData *) mode_get_private_data ( sw );
g_return_val_if_fail ( pd->entry_list != NULL, NULL ); g_return_val_if_fail ( pd->entry_list != NULL, NULL );
DRunModeEntry *dr = &( pd->entry_list[selected_line] ); DRunModeEntry *dr = &( pd->entry_list[selected_line] );
if ( pd->pool == NULL ) { if ( dr->icon_name == NULL ) return NULL;
/* TODO: 4 threads good? */ if ( dr->icon_fetch_uid >0){
pd->pool = g_thread_pool_new ( drun_icon_fetch, pd, 4, FALSE, NULL ); return rofi_icon_fetcher_get ( dr->icon_fetch_uid );
} }
if ( dr->icon_size == 0 ) { dr->icon_fetch_uid = rofi_icon_fetcher_query ( dr->icon_name, height );
dr->icon_size = height; return NULL;
//g_async_queue_push ( pd->icon_fetch_queue, dr );
g_thread_pool_push ( pd->pool, dr, NULL );
}
return dr->icon;
} }
static char *drun_get_completion ( const Mode *sw, unsigned int index ) static char *drun_get_completion ( const Mode *sw, unsigned int index )

View file

@ -55,6 +55,7 @@
#include "mode-private.h" #include "mode-private.h"
#include "timings.h" #include "timings.h"
#include "rofi-icon-fetcher.h"
/** /**
* Name of the history file where previously chosen commands are stored. * 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; const RunModePrivateData *rmpd = (const RunModePrivateData *) sw->private_data;
return get_entry ? g_strdup ( rmpd->cmd_list[selected_line] ) : NULL; 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 ) static int run_token_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index )
{ {
const RunModePrivateData *rmpd = (const RunModePrivateData *) sw->private_data; const RunModePrivateData *rmpd = (const RunModePrivateData *) sw->private_data;
@ -417,6 +419,7 @@ Mode run_mode =
._destroy = run_mode_destroy, ._destroy = run_mode_destroy,
._token_match = run_token_match, ._token_match = run_token_match,
._get_display_value = _get_display_value, ._get_display_value = _get_display_value,
._get_icon = NULL,
._get_completion = NULL, ._get_completion = NULL,
._preprocess_input = NULL, ._preprocess_input = NULL,
.private_data = NULL, .private_data = NULL,

View file

@ -56,6 +56,8 @@
#include "timings.h" #include "timings.h"
#include "rofi-icon-fetcher.h"
#define WINLIST 32 #define WINLIST 32
#define CLIENTSTATE 10 #define CLIENTSTATE 10
@ -108,6 +110,7 @@ typedef struct
char *wmdesktopstr; char *wmdesktopstr;
cairo_surface_t *icon; cairo_surface_t *icon;
gboolean icon_checked; gboolean icon_checked;
uint32_t icon_fetch_uid;
} client; } client;
// window lists // 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 = get_net_wm_icon ( rmpd->ids->array[selected_line], size );
c->icon_checked = TRUE; 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; return c->icon;
} }

219
source/rofi-icon-fetcher.c Normal file
View file

@ -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;
}

View file

@ -68,6 +68,7 @@
#include "view-internal.h" #include "view-internal.h"
#include "theme.h" #include "theme.h"
#include "rofi-icon-fetcher.h"
#include "timings.h" #include "timings.h"
@ -442,6 +443,7 @@ static void cleanup ()
} }
TIMINGS_STOP (); TIMINGS_STOP ();
rofi_collect_modi_destroy ( ); rofi_collect_modi_destroy ( );
rofi_icon_fetcher_destroy ( );
} }
/** /**
@ -956,6 +958,7 @@ int main ( int argc, char *argv[] )
} }
rofi_view_workers_initialize (); rofi_view_workers_initialize ();
rofi_icon_fetcher_init ( );
// Create pid file // Create pid file
int pfd = create_pid_file ( pidfile ); int pfd = create_pid_file ( pidfile );

View file

@ -551,23 +551,24 @@ static RofiViewState * __rofi_view_state_create ( void )
{ {
return g_malloc0 ( sizeof ( RofiViewState ) ); return g_malloc0 ( sizeof ( RofiViewState ) );
} }
/**
* Structure with data to process by each worker thread. typedef struct _thread_state_view
*/
typedef struct _thread_state
{ {
RofiViewState *state; thread_state st;
unsigned int start;
unsigned int stop;
unsigned int count;
GCond *cond; GCond *cond;
GMutex *mutex; GMutex *mutex;
unsigned int *acount; unsigned int *acount;
RofiViewState *state;
unsigned int start;
unsigned int stop;
unsigned int count;
const char *pattern; const char *pattern;
glong plen; glong plen;
void ( *callback )( struct _thread_state *t, gpointer data );
}thread_state; } thread_state_view;
/** /**
* @param data A thread_state object. * @param data A thread_state object.
* @param user_data User data to pass to thread_state callback * @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; thread_state *t = (thread_state *) data;
t->callback ( t, user_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++ ) { for ( unsigned int i = t->start; i < t->stop; i++ ) {
int match = mode_token_match ( t->state->sw, t->state->tokens, i ); int match = mode_token_match ( t->state->sw, t->state->tokens, i );
// If each token was matched, add it to list. // 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++; 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 ) 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 ); cairo_surface_t *icon = mode_get_icon ( state->sw, state->line_map[index], icon_height );
textbox_icon ( t, icon ); 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 ) { if ( state->tokens && config.show_match ) {
RofiHighlightColorStyle th = { ROFI_HL_BOLD | ROFI_HL_UNDERLINE, { 0.0, 0.0, 0.0, 0.0 } }; 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 ) { if ( state->overlay ) {
widget_draw ( WIDGET ( state->overlay ), d ); 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" ); TICK_N ( "widgets" );
cairo_surface_flush ( CacheState.edit_surf ); cairo_surface_flush ( CacheState.edit_surf );
if ( qr ) { 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. * 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 ); unsigned int nt = MAX ( 1, state->num_lines / 500 );
thread_state states[nt]; thread_state_view states[nt];
GCond cond; GCond cond;
GMutex mutex; GMutex mutex;
g_mutex_init ( &mutex ); g_mutex_init ( &mutex );
@ -1046,7 +1061,7 @@ static void rofi_view_refilter ( RofiViewState *state )
states[i].acount = &count; states[i].acount = &count;
states[i].plen = plen; states[i].plen = plen;
states[i].pattern = pattern; states[i].pattern = pattern;
states[i].callback = filter_elements; states[i].st.callback = filter_elements;
if ( i > 0 ) { if ( i > 0 ) {
g_thread_pool_push ( tpool, &states[i], NULL ); 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 ); 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 ); 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 { else {
wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL ); wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL );
box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE ); box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE );

156
source/widgets/icon.c Normal file
View file

@ -0,0 +1,156 @@
/*
* 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.
*
*/
#define G_LOG_DOMAIN "Widgets.Icon"
#include <config.h>
#include <stdio.h>
#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;
}

View file

@ -27,6 +27,7 @@
#include <glib.h> #include <glib.h>
#include <math.h> #include <math.h>
#include <stdatomic.h>
#include "widgets/widget.h" #include "widgets/widget.h"
#include "widgets/widget-internal.h" #include "widgets/widget-internal.h"
#include "theme.h" #include "theme.h"
@ -36,6 +37,8 @@
void widget_init ( widget *wid, widget *parent, WidgetType type, const char *name ) void widget_init ( widget *wid, widget *parent, WidgetType type, const char *name )
{ {
wid->ref_count = 1;
wid->type = type; wid->type = type;
wid->parent = parent; wid->parent = parent;
wid->name = g_strdup ( name ); wid->name = g_strdup ( name );
@ -359,15 +362,27 @@ 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 ) void widget_free ( widget *wid )
{ {
if ( wid ) { if ( wid ) {
if ( atomic_fetch_sub ( &(wid->ref_count), 1) == 1 ) {
if ( wid->name ) { if ( wid->name ) {
g_free ( wid->name ); g_free ( wid->name );
} }
if ( wid->free ) { if ( wid->free ) {
wid->free ( wid ); wid->free ( wid );
} }
return;
}
} }
} }