rofi  1.6.1
rofi-icon-fetcher.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2020 Qball Cow <qball@gmpclient.org>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
29 #define G_LOG_DOMAIN "Helpers.IconFetcher"
30 
31 #include <stdlib.h>
32 #include <config.h>
33 
34 #include "rofi-icon-fetcher.h"
35 #include "rofi-types.h"
36 #include "helper.h"
37 #include "settings.h"
38 
39 #include "xcb.h"
40 #include "keyb.h"
41 #include "view.h"
42 
43 #include "nkutils-xdg-theme.h"
44 #include "nkutils-enum.h"
45 
46 #include <stdint.h>
47 
48 #include <gdk-pixbuf/gdk-pixbuf.h>
49 
50 typedef struct
51 {
52  // Context for icon-themes.
53  NkXdgThemeContext *xdg_context;
54 
55  // On name.
56  GHashTable *icon_cache;
57  // On uid.
58  GHashTable *icon_cache_uid;
59 
60  // list extensions
62  uint32_t last_uid;
63 } IconFetcher;
64 
65 typedef struct
66 {
67  char *name;
68  GList *sizes;
70 
71 typedef struct
72 {
74 
75  GCond *cond;
76  GMutex *mutex;
77  unsigned int *acount;
78 
79  uint32_t uid;
80  int size;
81  cairo_surface_t *surface;
82 
85 
90 
91 static void rofi_icon_fetch_entry_free ( gpointer data )
92 {
94 
95  // Free name/key.
96  g_free ( entry->name );
97 
98  for ( GList *iter = g_list_first ( entry->sizes ); iter; iter = g_list_next ( iter ) ) {
99  IconFetcherEntry *sentry = (IconFetcherEntry *) ( iter->data );
100 
101  cairo_surface_destroy ( sentry->surface );
102  g_free ( sentry );
103  }
104 
105  g_list_free ( entry->sizes );
106  g_free ( entry );
107 }
108 
110 {
111  g_assert ( rofi_icon_fetcher_data == NULL );
112 
113  static const gchar * const icon_fallback_themes[] = {
114  "Adwaita",
115  "gnome",
116  NULL
117  };
118  const char *themes[2] = { config.icon_theme, NULL };
119 
120  rofi_icon_fetcher_data = g_malloc0 ( sizeof ( IconFetcher ) );
121 
122  rofi_icon_fetcher_data->xdg_context = nk_xdg_theme_context_new ( icon_fallback_themes, NULL );
123  nk_xdg_theme_preload_themes_icon ( rofi_icon_fetcher_data->xdg_context, themes );
124 
125  rofi_icon_fetcher_data->icon_cache_uid = g_hash_table_new ( g_direct_hash, g_direct_equal );
126  rofi_icon_fetcher_data->icon_cache = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, rofi_icon_fetch_entry_free );
127 
128  GSList *l = gdk_pixbuf_get_formats ();
129  for ( GSList *li = l; li != NULL; li = g_slist_next ( li ) ) {
130  gchar **exts = gdk_pixbuf_format_get_extensions ( (GdkPixbufFormat *) li->data );
131 
132  for ( unsigned int i = 0; exts && exts[i]; i++ ) {
134  g_info ( "Add image extension: %s", exts[i] );
135  exts[i] = NULL;
136  }
137 
138  g_free ( exts );
139  }
140  g_slist_free ( l );
141 }
142 
144 {
145  if ( rofi_icon_fetcher_data == NULL ) {
146  return;
147  }
148 
149  nk_xdg_theme_context_free ( rofi_icon_fetcher_data->xdg_context );
150 
151  g_hash_table_unref ( rofi_icon_fetcher_data->icon_cache_uid );
152  g_hash_table_unref ( rofi_icon_fetcher_data->icon_cache );
153 
154  g_list_foreach ( rofi_icon_fetcher_data->supported_extensions, (GFunc) g_free, NULL );
156  g_free ( rofi_icon_fetcher_data );
157 }
158 
159 /*
160  * _rofi_icon_fetcher_get_icon_surface and alpha_mult
161  * are inspired by gdk_cairo_set_source_pixbuf
162  * GDK is:
163  * Copyright (C) 2011-2018 Red Hat, Inc.
164  */
165 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
166 #define RED_BYTE 2
167 #define GREEN_BYTE 1
168 #define BLUE_BYTE 0
169 #define ALPHA_BYTE 3
170 #else
171 #define RED_BYTE 1
172 #define GREEN_BYTE 2
173 #define BLUE_BYTE 3
174 #define ALPHA_BYTE 0
175 #endif
176 
177 static inline guchar alpha_mult ( guchar c, guchar a )
178 {
179  guint16 t;
180  switch ( a )
181  {
182  case 0xff:
183  return c;
184  case 0x00:
185  return 0x00;
186  default:
187  t = c * a + 0x7f;
188  return ( ( t >> 8 ) + t ) >> 8;
189  }
190 }
191 
192 static cairo_surface_t * rofi_icon_fetcher_get_surface_from_pixbuf ( GdkPixbuf
193  *pixbuf )
194 {
195  gint width, height;
196  const guchar *pixels;
197  gint stride;
198  gboolean alpha;
199 
200  if ( pixbuf == NULL ) {
201  return NULL;
202  }
203 
204  width = gdk_pixbuf_get_width ( pixbuf );
205  height = gdk_pixbuf_get_height ( pixbuf );
206  pixels = gdk_pixbuf_read_pixels ( pixbuf );
207  stride = gdk_pixbuf_get_rowstride ( pixbuf );
208  alpha = gdk_pixbuf_get_has_alpha ( pixbuf );
209 
210  cairo_surface_t *surface = NULL;
211 
212  gint cstride;
213  guint lo, o;
214  guchar a = 0xff;
215  const guchar *pixels_end, *line, *line_end;
216  guchar *cpixels, *cline;
217 
218  pixels_end = pixels + height * stride;
219  o = alpha ? 4 : 3;
220  lo = o * width;
221 
222  surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, width, height );
223  cpixels = cairo_image_surface_get_data ( surface );
224  cstride = cairo_image_surface_get_stride ( surface );
225 
226  cairo_surface_flush ( surface );
227  while ( pixels < pixels_end ) {
228  line = pixels;
229  line_end = line + lo;
230  cline = cpixels;
231 
232  while ( line < line_end ) {
233  if ( alpha ) {
234  a = line[3];
235  }
236  cline[RED_BYTE] = alpha_mult ( line[0], a );
237  cline[GREEN_BYTE] = alpha_mult ( line[1], a );
238  cline[BLUE_BYTE] = alpha_mult ( line[2], a );
239  cline[ALPHA_BYTE] = a;
240 
241  line += o;
242  cline += 4;
243  }
244 
245  pixels += stride;
246  cpixels += cstride;
247  }
248  cairo_surface_mark_dirty ( surface );
249  cairo_surface_flush ( surface );
250 
251  return surface;
252 }
253 
254 gboolean rofi_icon_fetcher_file_is_image ( const char * const path )
255 {
256  if ( path == NULL ) {
257  return FALSE;
258  }
259  const char *suf = strrchr ( path, '.' );
260  if ( suf == NULL ) {
261  return FALSE;
262  }
263  suf++;
264 
265  for ( GList *iter = rofi_icon_fetcher_data->supported_extensions; iter != NULL; iter = g_list_next ( iter ) ) {
266  if ( g_ascii_strcasecmp ( iter->data, suf ) == 0 ) {
267  return TRUE;
268  }
269  }
270  return FALSE;
271 }
272 
273 static void rofi_icon_fetcher_worker ( thread_state *sdata, G_GNUC_UNUSED gpointer user_data )
274 {
275  g_debug ( "starting up icon fetching thread." );
276  // as long as dr->icon is updated atomicly.. (is a pointer write atomic?)
277  // this should be fine running in another thread.
278  IconFetcherEntry *sentry = (IconFetcherEntry *) sdata;
279  const gchar *themes[] = {
281  NULL
282  };
283 
284  const gchar *icon_path;
285  gchar *icon_path_ = NULL;
286 
287  if ( g_path_is_absolute ( sentry->entry->name ) ) {
288  icon_path = sentry->entry->name;
289  }
290  else {
291  icon_path = icon_path_ = nk_xdg_theme_get_icon ( rofi_icon_fetcher_data->xdg_context, themes, NULL, sentry->entry->name, sentry->size, 1, TRUE );
292  if ( icon_path_ == NULL ) {
293  g_debug ( "failed to get icon %s(%d): n/a", sentry->entry->name, sentry->size );
294  return;
295  }
296  else{
297  g_debug ( "found icon %s(%d): %s", sentry->entry->name, sentry->size, icon_path );
298  }
299  }
300  cairo_surface_t *icon_surf = NULL;
301 
302  const char *suf = strrchr ( icon_path, '.' );
303  if ( suf == NULL ) {
304  return;
305  }
306 
307  GError *error = NULL;
308  GdkPixbuf *pb = gdk_pixbuf_new_from_file_at_scale ( icon_path, sentry->size, sentry->size, TRUE, &error );
309  if ( error != NULL ) {
310  g_warning ( "Failed to load image: %s", error->message );
311  g_error_free ( error );
312  if ( pb ) {
313  g_object_unref ( pb );
314  }
315  }
316  else {
318  g_object_unref ( pb );
319  }
320 
321  sentry->surface = icon_surf;
322  g_free ( icon_path_ );
323  rofi_view_reload ();
324 }
325 
326 uint32_t rofi_icon_fetcher_query ( const char *name, const int size )
327 {
328  g_debug ( "Query: %s(%d)", name, size );
329  IconFetcherNameEntry *entry = g_hash_table_lookup ( rofi_icon_fetcher_data->icon_cache, name );
330  if ( entry == NULL ) {
331  entry = g_new0 ( IconFetcherNameEntry, 1 );
332  entry->name = g_strdup ( name );
333  g_hash_table_insert ( rofi_icon_fetcher_data->icon_cache, entry->name, entry );
334  }
335  IconFetcherEntry *sentry;
336  for ( GList *iter = g_list_first ( entry->sizes ); iter; iter = g_list_next ( iter ) ) {
337  sentry = iter->data;
338  if ( sentry->size == size ) {
339  return sentry->uid;
340  }
341  }
342 
343  // Not found.
344  sentry = g_new0 ( IconFetcherEntry, 1 );
345  sentry->uid = ++( rofi_icon_fetcher_data->last_uid );
346  sentry->size = size;
347  sentry->entry = entry;
348  sentry->surface = NULL;
349 
350  entry->sizes = g_list_prepend ( entry->sizes, sentry );
351  g_hash_table_insert ( rofi_icon_fetcher_data->icon_cache_uid, GINT_TO_POINTER ( sentry->uid ), sentry );
352 
353  // Push into fetching queue.
355  g_thread_pool_push ( tpool, sentry, NULL );
356 
357  return sentry->uid;
358 }
359 
360 cairo_surface_t * rofi_icon_fetcher_get ( const uint32_t uid )
361 {
362  IconFetcherEntry *sentry = g_hash_table_lookup ( rofi_icon_fetcher_data->icon_cache_uid, GINT_TO_POINTER ( uid ) );
363  if ( sentry ) {
364  return sentry->surface;
365  }
366  return NULL;
367 }
rofi_icon_fetcher_worker
static void rofi_icon_fetcher_worker(thread_state *sdata, G_GNUC_UNUSED gpointer user_data)
Definition: rofi-icon-fetcher.c:273
IconFetcherEntry::cond
GCond * cond
Definition: rofi-icon-fetcher.c:75
IconFetcherEntry::acount
unsigned int * acount
Definition: rofi-icon-fetcher.c:77
IconFetcherEntry::surface
cairo_surface_t * surface
Definition: rofi-icon-fetcher.c:81
IconFetcherEntry::entry
IconFetcherNameEntry * entry
Definition: rofi-icon-fetcher.c:83
IconFetcher::last_uid
uint32_t last_uid
Definition: rofi-icon-fetcher.c:62
_thread_state
Definition: rofi-types.h:279
settings.h
IconFetcherNameEntry::sizes
GList * sizes
Definition: rofi-icon-fetcher.c:68
GREEN_BYTE
#define GREEN_BYTE
Definition: rofi-icon-fetcher.c:167
rofi-types.h
alpha_mult
static guchar alpha_mult(guchar c, guchar a)
Definition: rofi-icon-fetcher.c:177
ALPHA_BYTE
#define ALPHA_BYTE
Definition: rofi-icon-fetcher.c:169
rofi_icon_fetcher_data
IconFetcher * rofi_icon_fetcher_data
Definition: rofi-icon-fetcher.c:89
RED_BYTE
#define RED_BYTE
Definition: rofi-icon-fetcher.c:166
tpool
GThreadPool * tpool
Definition: view.c:83
rofi_icon_fetcher_destroy
void rofi_icon_fetcher_destroy(void)
Definition: rofi-icon-fetcher.c:143
IconFetcherEntry::mutex
GMutex * mutex
Definition: rofi-icon-fetcher.c:76
rofi_icon_fetch_entry_free
static void rofi_icon_fetch_entry_free(gpointer data)
Definition: rofi-icon-fetcher.c:91
IconFetcherEntry::state
thread_state state
Definition: rofi-icon-fetcher.c:73
IconFetcherEntry
Definition: rofi-icon-fetcher.c:72
Settings::icon_theme
char * icon_theme
Definition: settings.h:100
IconFetcherEntry::uid
uint32_t uid
Definition: rofi-icon-fetcher.c:79
keyb.h
rofi_icon_fetcher_get
cairo_surface_t * rofi_icon_fetcher_get(const uint32_t uid)
Definition: rofi-icon-fetcher.c:360
xcb.h
IconFetcher::icon_cache_uid
GHashTable * icon_cache_uid
Definition: rofi-icon-fetcher.c:58
IconFetcher::icon_cache
GHashTable * icon_cache
Definition: rofi-icon-fetcher.c:56
IconFetcherNameEntry
Definition: rofi-icon-fetcher.c:66
IconFetcher::supported_extensions
GList * supported_extensions
Definition: rofi-icon-fetcher.c:61
rofi_icon_fetcher_get_surface_from_pixbuf
static cairo_surface_t * rofi_icon_fetcher_get_surface_from_pixbuf(GdkPixbuf *pixbuf)
Definition: rofi-icon-fetcher.c:192
view.h
rofi_icon_fetcher_query
uint32_t rofi_icon_fetcher_query(const char *name, const int size)
Definition: rofi-icon-fetcher.c:326
IconFetcherEntry::size
int size
Definition: rofi-icon-fetcher.c:80
rofi-icon-fetcher.h
BLUE_BYTE
#define BLUE_BYTE
Definition: rofi-icon-fetcher.c:168
IconFetcherNameEntry::name
char * name
Definition: rofi-icon-fetcher.c:67
helper.h
rofi_icon_fetcher_file_is_image
gboolean rofi_icon_fetcher_file_is_image(const char *const path)
Definition: rofi-icon-fetcher.c:254
_thread_state::callback
void(* callback)(struct _thread_state *t, gpointer data)
Definition: rofi-types.h:280
IconFetcher::xdg_context
NkXdgThemeContext * xdg_context
Definition: rofi-icon-fetcher.c:53
rofi_icon_fetcher_init
void rofi_icon_fetcher_init(void)
Definition: rofi-icon-fetcher.c:109
config
Settings config
IconFetcher
Definition: rofi-icon-fetcher.c:51
rofi_view_reload
void rofi_view_reload(void)
Definition: view.c:469