rofi  1.6.0
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 "rofi-icon-fetcher.h"
32 #include "rofi-types.h"
33 #include "helper.h"
34 #include "settings.h"
35 
36 #include "xcb.h"
37 #include "keyb.h"
38 #include "view.h"
39 
40 #include "nkutils-xdg-theme.h"
41 
42 #include <stdint.h>
43 #include <jpeglib.h>
44 
45 typedef struct
46 {
47  // Context for icon-themes.
48  NkXdgThemeContext *xdg_context;
49 
50  // On name.
51  GHashTable *icon_cache;
52  // On uid.
53  GHashTable *icon_cache_uid;
54 
55  uint32_t last_uid;
56 } IconFetcher;
57 
58 typedef struct
59 {
60  char *name;
61  GList *sizes;
63 
64 typedef struct
65 {
67 
68  GCond *cond;
69  GMutex *mutex;
70  unsigned int *acount;
71 
72  uint32_t uid;
73  int size;
74  cairo_surface_t *surface;
75 
78 
83 
84 static void rofi_icon_fetch_entry_free ( gpointer data )
85 {
87 
88  // Free name/key.
89  g_free ( entry->name );
90 
91  for ( GList *iter = g_list_first ( entry->sizes ); iter; iter = g_list_next ( iter ) ) {
92  IconFetcherEntry *sentry = (IconFetcherEntry *) ( iter->data );
93 
94  cairo_surface_destroy ( sentry->surface );
95  g_free ( sentry );
96  }
97 
98  g_list_free ( entry->sizes );
99  g_free ( entry );
100 }
101 
103 {
104  g_assert ( rofi_icon_fetcher_data == NULL );
105 
106  static const gchar * const icon_fallback_themes[] = {
107  "Adwaita",
108  "gnome",
109  NULL
110  };
111  const char *themes[2] = { config.icon_theme, NULL };
112 
113  rofi_icon_fetcher_data = g_malloc0 ( sizeof ( IconFetcher ) );
114 
115  rofi_icon_fetcher_data->xdg_context = nk_xdg_theme_context_new ( icon_fallback_themes, NULL );
116  nk_xdg_theme_preload_themes_icon ( rofi_icon_fetcher_data->xdg_context, themes );
117 
118  rofi_icon_fetcher_data->icon_cache_uid = g_hash_table_new ( g_direct_hash, g_direct_equal );
119  rofi_icon_fetcher_data->icon_cache = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, rofi_icon_fetch_entry_free );
120 }
121 
123 {
124  if ( rofi_icon_fetcher_data == NULL ) {
125  return;
126  }
127 
128  nk_xdg_theme_context_free ( rofi_icon_fetcher_data->xdg_context );
129 
130  g_hash_table_unref ( rofi_icon_fetcher_data->icon_cache_uid );
131  g_hash_table_unref ( rofi_icon_fetcher_data->icon_cache );
132 
133  g_free ( rofi_icon_fetcher_data );
134 }
135 
136 static cairo_surface_t* cairo_image_surface_create_from_jpeg_private ( struct jpeg_decompress_struct* cinfo )
137 {
138  cairo_surface_t* surface = 0;
139  unsigned char * data = 0;
140  unsigned char * rgb = 0;
141 
142  jpeg_read_header ( cinfo, TRUE );
143  jpeg_start_decompress ( cinfo );
144 
145  surface = cairo_image_surface_create ( CAIRO_FORMAT_RGB24, cinfo->image_width, cinfo->image_height );
146  data = cairo_image_surface_get_data ( surface );
147  rgb = (unsigned char *) ( malloc ( cinfo->output_width * cinfo->output_components ) );
148 
149  while ( cinfo->output_scanline < cinfo->output_height ) {
150  unsigned int i;
151  int scanline = cinfo->output_scanline * cairo_image_surface_get_stride ( surface );
152 
153  jpeg_read_scanlines ( cinfo, &rgb, 1 );
154 
155  for ( i = 0; i < cinfo->output_width; i++ ) {
156  int offset = scanline + ( i * 4 );
157 
158  data[offset + 3] = 255;
159  data[offset + 2] = rgb[( i * 3 )];
160  data[offset + 1] = rgb[( i * 3 ) + 1];
161  data[offset ] = rgb[( i * 3 ) + 2];
162  }
163  }
164 
165  free ( rgb );
166 
167  jpeg_finish_decompress ( cinfo );
168  jpeg_destroy_decompress ( cinfo );
169 
170  cairo_surface_mark_dirty ( surface );
171 
172  return surface;
173 }
174 
175 static cairo_surface_t* cairo_image_surface_create_from_jpeg ( const char* file )
176 {
177  struct jpeg_decompress_struct cinfo;
178  struct jpeg_error_mgr jerr;
179  cairo_surface_t * surface;
180  FILE * infile;
181 
182  if ( ( infile = fopen ( file, "rb" ) ) == NULL ) {
183  return NULL;
184  }
185 
186  cinfo.err = jpeg_std_error ( &jerr );
187 
188  jpeg_create_decompress ( &cinfo );
189  jpeg_stdio_src ( &cinfo, infile );
190 
192 
193  fclose ( infile );
194 
195  return surface;
196 }
197 
198 static void rofi_icon_fetcher_worker ( thread_state *sdata, G_GNUC_UNUSED gpointer user_data )
199 {
200  g_debug ( "starting up icon fetching thread." );
201  // as long as dr->icon is updated atomicly.. (is a pointer write atomic?)
202  // this should be fine running in another thread.
203  IconFetcherEntry *sentry = (IconFetcherEntry *) sdata;
204  const gchar *themes[] = {
206  NULL
207  };
208 
209  const gchar *icon_path;
210  gchar *icon_path_ = NULL;
211 
212  if ( g_path_is_absolute ( sentry->entry->name ) ) {
213  icon_path = sentry->entry->name;
214  }
215  else {
216  icon_path = icon_path_ = nk_xdg_theme_get_icon ( rofi_icon_fetcher_data->xdg_context, themes, NULL, sentry->entry->name, sentry->size, 1, TRUE );
217  if ( icon_path_ == NULL ) {
218  g_debug ( "failed to get icon %s(%d): n/a", sentry->entry->name, sentry->size );
219  return;
220  }
221  else{
222  g_debug ( "found icon %s(%d): %s", sentry->entry->name, sentry->size, icon_path );
223  }
224  }
225  cairo_surface_t *icon_surf = NULL;
226  if ( g_str_has_suffix ( icon_path, ".png" ) ) {
227  icon_surf = cairo_image_surface_create_from_png ( icon_path );
228  }
229  else if ( g_str_has_suffix ( icon_path, ".jpeg" ) || g_str_has_suffix ( icon_path, ".jpg" ) ) {
230  icon_surf = cairo_image_surface_create_from_jpeg ( icon_path );
231  }
232  else if ( g_str_has_suffix ( icon_path, ".svg" ) ) {
233  icon_surf = cairo_image_surface_create_from_svg ( icon_path, sentry->size );
234  }
235  else {
236  g_debug ( "icon type not yet supported: %s", icon_path );
237  }
238  if ( icon_surf ) {
239  if ( cairo_surface_status ( icon_surf ) == CAIRO_STATUS_SUCCESS ) {
240  float sw = sentry->size / (float) cairo_image_surface_get_width ( icon_surf );
241  float sh = sentry->size / (float) cairo_image_surface_get_height ( icon_surf );
242 
243  float scale = ( sw > sh ) ? sh : sw;
244  if ( scale < 0.5 ) {
245  cairo_surface_t * surface = cairo_image_surface_create (
246  cairo_image_surface_get_format ( icon_surf ),
247  cairo_image_surface_get_width ( icon_surf ) * scale,
248  cairo_image_surface_get_height ( icon_surf ) * scale );
249 
250  cairo_t *d = cairo_create ( surface );
251  cairo_scale ( d, scale, scale );
252  cairo_set_source_surface ( d, icon_surf, 0.0, 0.0 );
253  cairo_pattern_set_filter ( cairo_get_source ( d ), CAIRO_FILTER_FAST );
254  cairo_paint ( d );
255 
256  cairo_destroy ( d );
257  cairo_surface_destroy ( icon_surf );
258  icon_surf = surface;
259  }
260  }
261  // check if surface is valid.
262  if ( cairo_surface_status ( icon_surf ) != CAIRO_STATUS_SUCCESS ) {
263  g_debug ( "icon failed to open: %s(%d): %s", sentry->entry->name, sentry->size, icon_path );
264  cairo_surface_destroy ( icon_surf );
265  icon_surf = NULL;
266  }
267  sentry->surface = icon_surf;
268  }
269  g_free ( icon_path_ );
270  rofi_view_reload ();
271 }
272 
273 uint32_t rofi_icon_fetcher_query ( const char *name, const int size )
274 {
275  g_debug ( "Query: %s(%d)", name, size );
276  IconFetcherNameEntry *entry = g_hash_table_lookup ( rofi_icon_fetcher_data->icon_cache, name );
277  if ( entry == NULL ) {
278  entry = g_new0 ( IconFetcherNameEntry, 1 );
279  entry->name = g_strdup ( name );
280  g_hash_table_insert ( rofi_icon_fetcher_data->icon_cache, entry->name, entry );
281  }
282  IconFetcherEntry *sentry;
283  for ( GList *iter = g_list_first ( entry->sizes ); iter; iter = g_list_next ( iter ) ) {
284  sentry = iter->data;
285  if ( sentry->size == size ) {
286  return sentry->uid;
287  }
288  }
289 
290  // Not found.
291  sentry = g_new0 ( IconFetcherEntry, 1 );
292  sentry->uid = ++( rofi_icon_fetcher_data->last_uid );
293  sentry->size = size;
294  sentry->entry = entry;
295  sentry->surface = NULL;
296 
297  entry->sizes = g_list_prepend ( entry->sizes, sentry );
298  g_hash_table_insert ( rofi_icon_fetcher_data->icon_cache_uid, GINT_TO_POINTER ( sentry->uid ), sentry );
299 
300  // Push into fetching queue.
302  g_thread_pool_push ( tpool, sentry, NULL );
303 
304  return sentry->uid;
305 }
306 
307 cairo_surface_t * rofi_icon_fetcher_get ( const uint32_t uid )
308 {
309  IconFetcherEntry *sentry = g_hash_table_lookup ( rofi_icon_fetcher_data->icon_cache_uid, GINT_TO_POINTER ( uid ) );
310  if ( sentry ) {
311  return sentry->surface;
312  }
313  return NULL;
314 }
rofi_icon_fetcher_worker
static void rofi_icon_fetcher_worker(thread_state *sdata, G_GNUC_UNUSED gpointer user_data)
Definition: rofi-icon-fetcher.c:198
IconFetcherEntry::cond
GCond * cond
Definition: rofi-icon-fetcher.c:68
IconFetcherEntry::acount
unsigned int * acount
Definition: rofi-icon-fetcher.c:70
IconFetcherEntry::surface
cairo_surface_t * surface
Definition: rofi-icon-fetcher.c:74
IconFetcherEntry::entry
IconFetcherNameEntry * entry
Definition: rofi-icon-fetcher.c:76
IconFetcher::last_uid
uint32_t last_uid
Definition: rofi-icon-fetcher.c:55
_thread_state
Definition: rofi-types.h:277
settings.h
IconFetcherNameEntry::sizes
GList * sizes
Definition: rofi-icon-fetcher.c:61
rofi-types.h
cairo_image_surface_create_from_jpeg
static cairo_surface_t * cairo_image_surface_create_from_jpeg(const char *file)
Definition: rofi-icon-fetcher.c:175
rofi_icon_fetcher_data
IconFetcher * rofi_icon_fetcher_data
Definition: rofi-icon-fetcher.c:82
cairo_image_surface_create_from_svg
cairo_surface_t * cairo_image_surface_create_from_svg(const gchar *file, int height)
Definition: helper.c:1094
tpool
GThreadPool * tpool
Definition: view.c:83
rofi_icon_fetcher_destroy
void rofi_icon_fetcher_destroy(void)
Definition: rofi-icon-fetcher.c:122
IconFetcherEntry::mutex
GMutex * mutex
Definition: rofi-icon-fetcher.c:69
rofi_icon_fetch_entry_free
static void rofi_icon_fetch_entry_free(gpointer data)
Definition: rofi-icon-fetcher.c:84
IconFetcherEntry::state
thread_state state
Definition: rofi-icon-fetcher.c:66
IconFetcherEntry
Definition: rofi-icon-fetcher.c:65
Settings::icon_theme
char * icon_theme
Definition: settings.h:100
IconFetcherEntry::uid
uint32_t uid
Definition: rofi-icon-fetcher.c:72
keyb.h
rofi_icon_fetcher_get
cairo_surface_t * rofi_icon_fetcher_get(const uint32_t uid)
Definition: rofi-icon-fetcher.c:307
xcb.h
IconFetcher::icon_cache_uid
GHashTable * icon_cache_uid
Definition: rofi-icon-fetcher.c:53
IconFetcher::icon_cache
GHashTable * icon_cache
Definition: rofi-icon-fetcher.c:51
IconFetcherNameEntry
Definition: rofi-icon-fetcher.c:59
view.h
rofi_icon_fetcher_query
uint32_t rofi_icon_fetcher_query(const char *name, const int size)
Definition: rofi-icon-fetcher.c:273
IconFetcherEntry::size
int size
Definition: rofi-icon-fetcher.c:73
rofi-icon-fetcher.h
IconFetcherNameEntry::name
char * name
Definition: rofi-icon-fetcher.c:60
helper.h
_thread_state::callback
void(* callback)(struct _thread_state *t, gpointer data)
Definition: rofi-types.h:278
IconFetcher::xdg_context
NkXdgThemeContext * xdg_context
Definition: rofi-icon-fetcher.c:48
rofi_icon_fetcher_init
void rofi_icon_fetcher_init(void)
Definition: rofi-icon-fetcher.c:102
config
Settings config
cairo_image_surface_create_from_jpeg_private
static cairo_surface_t * cairo_image_surface_create_from_jpeg_private(struct jpeg_decompress_struct *cinfo)
Definition: rofi-icon-fetcher.c:136
IconFetcher
Definition: rofi-icon-fetcher.c:46
rofi_view_reload
void rofi_view_reload(void)
Definition: view.c:466