21int icon_themes_count = 0;
22int *default_themes_index = NULL;
23int default_themes_count = 0;
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)) {
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);
55 struct ini *
ini = load_ini_file(theme_index);
57 if (
ini->section_count == 0) {
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;
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));
76 for (
int i = 0; i < icon_themes[index].dirs_count; i++) {
78 icon_themes[index].dirs[i].name = g_strdup(
section.name);
81 const char *size_str = section_get_value(
ini, &
section,
"Size");
85 const char *scale_str = section_get_value(
ini, &
section,
"Scale");
86 icon_themes[index].dirs[i].scale = 1;
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;
101 icon_themes[index].dirs[i].type = THEME_DIR_THRESHOLD;
105 if (icon_themes[index].dirs[i].type == THEME_DIR_SCALABLE) {
106 const char *min_size = section_get_value(
ini, &
section,
"MinSize");
110 icon_themes[index].dirs[i].min_size = icon_themes[index].dirs[i].size;
112 const char *max_size = section_get_value(
ini, &
section,
"MaxSize");
116 icon_themes[index].dirs[i].max_size = icon_themes[index].dirs[i].size;
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");
129 if (!
STR_EQ(icon_themes[index].name,
"Hicolor")) {
132 LOG_D(
"Theme has %i inherited themes", icon_themes[index].inherits_count);
133 if (icon_themes[index].inherits_count <= 0) {
135 g_strfreev(inherits);
136 inherits = g_malloc0_n(2,
sizeof(
char*));
137 inherits[0] = g_strdup(
"hicolor");
139 icon_themes[index].inherits_count = 1;
142 icon_themes[index].inherits_index = g_malloc0_n(icon_themes[index].inherits_count,
sizeof(
int));
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");
154 g_strfreev(inherits);
165GPtrArray *theme_path = NULL;
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));
172 char *data_home_default = g_build_filename(home,
".local",
"share", NULL);
174 g_free(data_home_default);
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]);
189 for (
size_t i = 0; i < theme_path->len; i++) {
191 if (theme_index != -1)
195 LOG_W(
"Could not find theme %s",
STR_NN(name));
205void finish_icon_theme(
struct icon_theme *theme) {
208 for (
int i = 0; i < theme->dirs_count; i++) {
209 finish_icon_theme_dir(&theme->dirs[i]);
212 g_free(theme->location);
213 g_free(theme->subdir_theme);
214 g_free(theme->inherits_index);
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]);
227 icon_themes_count = 0;
229 g_ptr_array_unref(theme_path);
235 if (theme_index < 0) {
236 LOG_W(
"Invalid theme index: %i", theme_index);
239 if (theme_index >= icon_themes_count) {
240 LOG_W(
"Invalid theme index: %i. Theme does not exists.",
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;
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;
258 case THEME_DIR_FIXED:
259 match_size = dir.size == size;
262 case THEME_DIR_SCALABLE:
263 match_size = dir.min_size <= size && dir.max_size >= size;
266 case THEME_DIR_THRESHOLD:
267 match_size = (float)dir.size / dir.threshold <= size
268 && dir.size * dir.threshold >= 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,
279 g_free(name_with_extension);
282 g_free(name_with_extension);
290char *find_icon_in_theme_with_inherit(
const char *name,
int theme_index,
int size) {
295 for (
int i = 0; i < icon_themes[theme_index].inherits_count; i++) {
296 if (icon_themes[theme_index].inherits_index[i] <= 0)
299 icon_themes[theme_index].inherits_index[i],
312 if (!default_themes_index) {
313 LOG_W(
"No icon theme has been set");
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);
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.
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.
int string_array_length(char **s)
Returns the length of a string array, -1 if the input is NULL.
bool is_readable_file(const char *const path)
Check if file is readable.
bool safe_string_to_int(int *in, const char *str)
Convert string to int in a safe way.
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...
char ** string_to_array(const char *string, const char *delimiter)
Parse a string into a dynamic array of tokens, using the delimiter string.
String, time and other various helpers.
#define STR_EMPTY(s)
Test if a string is NULL or empty.
#define STR_EQ(a, b)
Test if string a and b contain the same chars.
#define STR_NN(s)
Get a non null string from a possibly null one.