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:
parent
ca1ae5dfcd
commit
441c511296
18 changed files with 585 additions and 113 deletions
|
@ -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-
|
||||
|
||||
|
|
|
@ -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\
|
||||
|
|
21
include/rofi-icon-fetcher.h
Normal file
21
include/rofi-icon-fetcher.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
63
include/widgets/icon.h
Normal file
63
include/widgets/icon.h
Normal 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
|
|
@ -94,6 +94,9 @@ struct _widget
|
|||
/** Name of widget (used for theming) */
|
||||
char *name;
|
||||
const char *state;
|
||||
|
||||
/** Used for reference counting */
|
||||
int ref_count;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
%option nounput
|
||||
%option never-interactive
|
||||
%option bison-locations
|
||||
%option bison-bridge
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
219
source/rofi-icon-fetcher.c
Normal file
219
source/rofi-icon-fetcher.c
Normal 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;
|
||||
}
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
156
source/widgets/icon.c
Normal file
156
source/widgets/icon.c
Normal 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;
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <glib.h>
|
||||
#include <math.h>
|
||||
#include <stdatomic.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue