Dunst
Lightweight notification daemon
Loading...
Searching...
No Matches
icon-lookup.c
Go to the documentation of this file.
1/* SPDX-License-Identifier: BSD-3-Clause */
7
8#define _GNU_SOURCE
9#include "icon-lookup.h"
10
11#include <glib.h>
12#include <stdio.h>
13#include <unistd.h>
14#include <assert.h>
15
16#include "ini.h"
17#include "utils.h"
18#include "log.h"
19
20struct icon_theme *icon_themes = NULL;
21int icon_themes_count = 0;
22int *default_themes_index = NULL;
23int default_themes_count = 0;
24
25int get_icon_theme(char *name) {
26 for (int i = 0; i < icon_themes_count; i++) {
27 if (STR_EQ(icon_themes[i].subdir_theme, name)) {
28 return i;
29 }
30 }
31 return -1;
32}
33
47int load_icon_theme_from_dir(const char *icon_dir, const char *subdir_theme) {
48 LOG_D("Loading theme %s/%s", STR_NN(icon_dir), STR_NN(subdir_theme));
49 char *theme_index_dir = g_build_filename(icon_dir, subdir_theme, "index.theme", NULL);
50 FILE *theme_index = fopen(theme_index_dir, "r");
51 g_free(theme_index_dir);
52 if (!theme_index)
53 return -1;
54
55 struct ini *ini = load_ini_file(theme_index);
56 fclose(theme_index);
57 if (ini->section_count == 0) {
58 finish_ini(ini);
59 g_free(ini);
60 return -1;
61 }
62
63 icon_themes_count++;
64 icon_themes = g_realloc(icon_themes, icon_themes_count * sizeof(struct icon_theme));
65 int index = icon_themes_count - 1;
66 icon_themes[index].name = g_strdup(section_get_value(ini, &ini->sections[0], "Name"));
67 icon_themes[index].location = g_strdup(icon_dir);
68 icon_themes[index].subdir_theme = g_strdup(subdir_theme);
69 icon_themes[index].inherits_index = NULL;
70 icon_themes[index].inherits_count = 0;
71
72 // load theme directories
73 icon_themes[index].dirs_count = ini->section_count - 1;
74 icon_themes[index].dirs = g_malloc0_n(icon_themes[index].dirs_count, sizeof(struct icon_theme_dir));
75
76 for (int i = 0; i < icon_themes[index].dirs_count; i++) {
77 struct section section = ini->sections[i+1];
78 icon_themes[index].dirs[i].name = g_strdup(section.name);
79
80 // read size
81 const char *size_str = section_get_value(ini, &section, "Size");
82 safe_string_to_int(&icon_themes[index].dirs[i].size, size_str);
83
84 // read optional scale, defaulting to 1
85 const char *scale_str = section_get_value(ini, &section, "Scale");
86 icon_themes[index].dirs[i].scale = 1;
87 if (scale_str) {
88 safe_string_to_int(&icon_themes[index].dirs[i].scale, scale_str);
89 }
90
91 // read type
92 const char *type = section_get_value(ini, &section, "Type");
93 if (STR_EQ(type, "Fixed")) {
94 icon_themes[index].dirs[i].type = THEME_DIR_FIXED;
95 } else if (STR_EQ(type, "Scalable")) {
96 icon_themes[index].dirs[i].type = THEME_DIR_SCALABLE;
97 } else if (STR_EQ(type, "Threshold")) {
98 icon_themes[index].dirs[i].type = THEME_DIR_THRESHOLD;
99 } else {
100 // default to type threshold
101 icon_themes[index].dirs[i].type = THEME_DIR_THRESHOLD;
102 }
103
104 // read type-specific data
105 if (icon_themes[index].dirs[i].type == THEME_DIR_SCALABLE) {
106 const char *min_size = section_get_value(ini, &section, "MinSize");
107 if (min_size)
108 safe_string_to_int(&icon_themes[index].dirs[i].min_size, min_size);
109 else
110 icon_themes[index].dirs[i].min_size = icon_themes[index].dirs[i].size;
111
112 const char *max_size = section_get_value(ini, &section, "MaxSize");
113 if (max_size)
114 safe_string_to_int(&icon_themes[index].dirs[i].max_size, max_size);
115 else
116 icon_themes[index].dirs[i].max_size = icon_themes[index].dirs[i].size;
117
118 } else if (icon_themes[index].dirs[i].type == THEME_DIR_THRESHOLD) {
119 icon_themes[index].dirs[i].threshold = 2;
120 const char *threshold = section_get_value(ini, &section, "Threshold");
121 if (threshold) {
122 safe_string_to_int(&icon_themes[index].dirs[i].threshold, threshold);
123 }
124 }
125 }
126
127
128 // load inherited themes
129 if (!STR_EQ(icon_themes[index].name, "Hicolor")) {
130 char **inherits = string_to_array(get_value(ini, "Icon Theme", "Inherits"), ",");
131 icon_themes[index].inherits_count = string_array_length(inherits);
132 LOG_D("Theme has %i inherited themes", icon_themes[index].inherits_count);
133 if (icon_themes[index].inherits_count <= 0) {
134 // set fallback theme to hicolor if there are no inherits
135 g_strfreev(inherits);
136 inherits = g_malloc0_n(2, sizeof(char*));
137 inherits[0] = g_strdup("hicolor");
138 inherits[1] = NULL;
139 icon_themes[index].inherits_count = 1;
140 }
141
142 icon_themes[index].inherits_index = g_malloc0_n(icon_themes[index].inherits_count, sizeof(int));
143
144 for (int i = 0; inherits[i] != NULL; i++) {
145 LOG_D("inherits: %s", inherits[i]);
146 icon_themes[index].inherits_index[i] = get_icon_theme(inherits[i]);
147 if (icon_themes[index].inherits_index[i] == -1) {
148 LOG_D("Loading inherited theme");
149 // FIXME don't use a pointer to the theme,
150 // since it may be invalidated after realloc. Use an index instead
151 icon_themes[index].inherits_index[i] = load_icon_theme(inherits[i]);
152 }
153 }
154 g_strfreev(inherits);
155 }
156
157
158
159 finish_ini(ini);
160 g_free(ini);
161 return index;
162}
163
164// a list of directories where icon themes might be located
165GPtrArray *theme_path = NULL;
166
167void get_theme_path(void) {
168 theme_path = g_ptr_array_new_full(5, g_free);
169 const char *home = g_get_home_dir();
170 g_ptr_array_add(theme_path, g_build_filename(home, ".icons", NULL));
171
172 char *data_home_default = g_build_filename(home, ".local", "share", NULL);
173 add_paths_from_env(theme_path, "XDG_DATA_HOME", "icons", data_home_default);
174 g_free(data_home_default);
175
176 add_paths_from_env(theme_path, "XDG_DATA_DIRS", "icons", "/usr/local/share/:/usr/share/");
177 g_ptr_array_add(theme_path, g_strdup("/usr/share/pixmaps"));
178 for (size_t i = 0; i < theme_path->len; i++) {
179 LOG_D("Theme locations: %s", (char*)theme_path->pdata[i]);
180 }
181}
182
183// see icon-lookup.h
184int load_icon_theme(char *name) {
185 if(!theme_path) {
186 get_theme_path();
187 }
188
189 for (size_t i = 0; i < theme_path->len; i++) {
190 int theme_index = load_icon_theme_from_dir(theme_path->pdata[i], name);
191 if (theme_index != -1)
192 return theme_index;
193 }
194
195 LOG_W("Could not find theme %s", STR_NN(name));
196 return -1;
197}
198
199void finish_icon_theme_dir(struct icon_theme_dir *dir) {
200 if (!dir)
201 return;
202 g_free(dir->name);
203}
204
205void finish_icon_theme(struct icon_theme *theme) {
206 if (!theme)
207 return;
208 for (int i = 0; i < theme->dirs_count; i++) {
209 finish_icon_theme_dir(&theme->dirs[i]);
210 }
211 g_free(theme->name);
212 g_free(theme->location);
213 g_free(theme->subdir_theme);
214 g_free(theme->inherits_index);
215 g_free(theme->dirs);
216}
217
218void free_all_themes(void) {
219 g_free(default_themes_index);
220 default_themes_index = NULL;
221 default_themes_count = 0;
222 LOG_D("Finishing %i themes", icon_themes_count);
223 for (int i = 0; i < icon_themes_count; i++) {
224 finish_icon_theme(&icon_themes[i]);
225 }
226 g_free(icon_themes);
227 icon_themes_count = 0;
228 icon_themes = NULL;
229 g_ptr_array_unref(theme_path);
230 theme_path = NULL;
231}
232
233// see icon-lookup.h
234void add_default_theme(int theme_index) {
235 if (theme_index < 0) {
236 LOG_W("Invalid theme index: %i", theme_index);
237 return;
238 }
239 if (theme_index >= icon_themes_count) {
240 LOG_W("Invalid theme index: %i. Theme does not exists.",
241 theme_index);
242 return;
243 }
244 default_themes_count++;
245 default_themes_index = g_realloc(default_themes_index,
246 default_themes_count * sizeof(int));
247 default_themes_index[default_themes_count - 1] = theme_index;
248}
249
250// see icon-lookup.h
251char *find_icon_in_theme(const char *name, int theme_index, int size) {
252 struct icon_theme *theme = &icon_themes[theme_index];
253 LOG_D("Finding icon %s in theme %s", STR_NN(name), STR_NN(theme->name));
254 for (int i = 0; i < theme->dirs_count; i++) {
255 bool match_size = false;
256 struct icon_theme_dir dir = theme->dirs[i];
257 switch (dir.type) {
258 case THEME_DIR_FIXED:
259 match_size = dir.size == size;
260 break;
261
262 case THEME_DIR_SCALABLE:
263 match_size = dir.min_size <= size && dir.max_size >= size;
264 break;
265
266 case THEME_DIR_THRESHOLD:
267 match_size = (float)dir.size / dir.threshold <= size
268 && dir.size * dir.threshold >= size;
269 break;
270 }
271 if (match_size) {
272 const char *suffixes[] = { ".svg", ".svgz", ".png", ".xpm", NULL };
273 for (const char **suf = suffixes; *suf; suf++) {
274 char *name_with_extension = g_strconcat(name, *suf, NULL);
275 char *icon = g_build_filename(theme->location, theme->subdir_theme,
276 dir.name, name_with_extension,
277 NULL);
278 if (is_readable_file(icon)) {
279 g_free(name_with_extension);
280 return icon;
281 }
282 g_free(name_with_extension);
283 g_free(icon);
284 }
285 }
286 }
287 return NULL;
288}
289
290char *find_icon_in_theme_with_inherit(const char *name, int theme_index, int size) {
291 char *icon = find_icon_in_theme(name, theme_index, size);
292 if (icon)
293 return icon;
294
295 for (int i = 0; i < icon_themes[theme_index].inherits_count; i++) {
296 if (icon_themes[theme_index].inherits_index[i] <= 0)
297 continue; // inherited theme could not be found
298 icon = find_icon_in_theme(name,
299 icon_themes[theme_index].inherits_index[i],
300 size);
301 if (icon)
302 return icon;
303 }
304 return NULL;
305}
306
307/* see icon-lookup.h */
308char *find_icon_path(const char *name, int size) {
309 if (STR_EMPTY(name))
310 return NULL;
311
312 if (!default_themes_index) {
313 LOG_W("No icon theme has been set");
314 return NULL;
315 }
316
317 for (int i = 0; i < default_themes_count; i++) {
318 char *icon = find_icon_in_theme_with_inherit(name,
319 default_themes_index[i], size);
320 if (icon)
321 return icon;
322
323 }
324 return NULL;
325}
int load_icon_theme(char *name)
Load a theme with given name from a standard icon directory.
void free_all_themes(void)
Free all icon themes.
void add_default_theme(int theme_index)
Add theme to the list of default themes.
char * find_icon_path(const char *name, int size)
Find icon of specified size in the default theme or an inherited theme.
int load_icon_theme_from_dir(const char *icon_dir, const char *subdir_theme)
Load a theme from a directory.
Definition icon-lookup.c:47
char * find_icon_in_theme(const char *name, int theme_index, int size)
Find icon of specified size in selected theme.
Recursive icon lookup in theme directories.
Parser for INI config files.
Logging subsystem and helpers.
Definition ini.h:28
Definition ini.h:22
int string_array_length(char **s)
Returns the length of a string array, -1 if the input is NULL.
Definition utils.c:166
bool is_readable_file(const char *const path)
Check if file is readable.
Definition utils.c:437
bool safe_string_to_int(int *in, const char *str)
Convert string to int in a safe way.
Definition utils.c:229
void add_paths_from_env(GPtrArray *arr, char *env_name, char *subdir, char *alternative)
Adds the contents of env_name with subdir to the array, interpreting the environment variable as a co...
Definition utils.c:472
char ** string_to_array(const char *string, const char *delimiter)
Parse a string into a dynamic array of tokens, using the delimiter string.
Definition utils.c:154
String, time and other various helpers.
#define STR_EMPTY(s)
Test if a string is NULL or empty.
Definition utils.h:20
#define STR_EQ(a, b)
Test if string a and b contain the same chars.
Definition utils.h:26
#define STR_NN(s)
Get a non null string from a possibly null one.
Definition utils.h:35