aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/tray/icon.c
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-03-28 23:04:20 -0400
committerLibravatar Drew DeVault <sir@cmpwn.com>2018-03-29 22:11:08 -0400
commitcab1352801b62d1b8a12ca1c995cb24445ce4bc9 (patch)
treebc67373916c06d48700c4f69b8c2470a2f86887f /swaybar/tray/icon.c
parentAllow sway IPC clients to fall back to i3 socket (diff)
downloadsway-cab1352801b62d1b8a12ca1c995cb24445ce4bc9.tar.gz
sway-cab1352801b62d1b8a12ca1c995cb24445ce4bc9.tar.zst
sway-cab1352801b62d1b8a12ca1c995cb24445ce4bc9.zip
Start port of swaybar to layer shell
This starts up the event loop and wayland display and shims out the basic top level rendering concepts. Also includes some changes to incorporate pango into the 1.x codebase properly.
Diffstat (limited to 'swaybar/tray/icon.c')
-rw-r--r--swaybar/tray/icon.c400
1 files changed, 0 insertions, 400 deletions
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c
deleted file mode 100644
index c146bf32..00000000
--- a/swaybar/tray/icon.c
+++ /dev/null
@@ -1,400 +0,0 @@
1#define _XOPEN_SOURCE 700
2#define _POSIX_C_SOURCE 200809L
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7#include <dirent.h>
8#include <sys/stat.h>
9#include <stdbool.h>
10#include <stdint.h>
11#include <limits.h>
12#include "swaybar/tray/icon.h"
13#include "swaybar/bar.h"
14#include "swaybar/config.h"
15#include "stringop.h"
16#include "log.h"
17
18/**
19 * REVIEW:
20 * This file repeats lots of "costly" operations that are the same for every
21 * icon. It's possible to create a dictionary or some other structure to cache
22 * these, though it may complicate things somewhat.
23 *
24 * Also parsing (index.theme) is currently pretty messy, so that could be made
25 * much better as well. Over all, things work, but are not optimal.
26 */
27
28/* Finds all themes that the given theme inherits */
29static list_t *find_inherits(const char *theme_dir) {
30 const char inherits[] = "Inherits";
31 const char index_name[] = "index.theme";
32 list_t *themes = create_list();
33 FILE *index = NULL;
34 char *path = malloc(strlen(theme_dir) + sizeof(index_name));
35 if (!path) {
36 goto fail;
37 }
38 if (!themes) {
39 goto fail;
40 }
41
42 strcpy(path, theme_dir);
43 strcat(path, index_name);
44
45 index = fopen(path, "r");
46 if (!index) {
47 goto fail;
48 }
49
50 char *buf = NULL;
51 size_t n = 0;
52 while (!feof(index) && getline(&buf, &n, index) != -1) {
53 if (n <= sizeof(inherits) + 1) {
54 continue;
55 }
56 if (strncmp(inherits, buf, sizeof(inherits) - 1) == 0) {
57 char *themestr = buf + sizeof(inherits);
58 themes = split_string(themestr, ",");
59 break;
60 }
61 }
62 free(buf);
63
64fail:
65 free(path);
66 if (index) {
67 fclose(index);
68 }
69 return themes;
70}
71
72static bool isdir(const char *path) {
73 struct stat statbuf;
74 if (stat(path, &statbuf) != -1) {
75 if (S_ISDIR(statbuf.st_mode)) {
76 return true;
77 }
78 }
79 return false;
80
81}
82
83/**
84 * Returns the directory of a given theme if it exists.
85 * The returned pointer must be freed.
86 */
87static char *find_theme_dir(const char *theme) {
88 char *basedir;
89 char *icon_dir;
90
91 if (!theme) {
92 return NULL;
93 }
94
95 if (!(icon_dir = malloc(1024))) {
96 sway_log(L_ERROR, "Out of memory!");
97 goto fail;
98 }
99
100 if ((basedir = getenv("HOME"))) {
101 if (snprintf(icon_dir, 1024, "%s/.icons/%s", basedir, theme) >= 1024) {
102 sway_log(L_ERROR, "Path too long to render");
103 // XXX perhaps just goto trying in /usr/share? This
104 // shouldn't happen anyway, but might with a long global
105 goto fail;
106 }
107
108 if (isdir(icon_dir)) {
109 return icon_dir;
110 }
111 }
112
113 if ((basedir = getenv("XDG_DATA_DIRS"))) {
114 if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) {
115 sway_log(L_ERROR, "Path too long to render");
116 // ditto
117 goto fail;
118 }
119
120 if (isdir(icon_dir)) {
121 return icon_dir;
122 }
123 }
124
125 // Spec says use "/usr/share/pixmaps/", but I see everything in
126 // "/usr/share/icons/" look it both, I suppose.
127 if (snprintf(icon_dir, 1024, "/usr/share/pixmaps/%s", theme) >= 1024) {
128 sway_log(L_ERROR, "Path too long to render");
129 goto fail;
130 }
131 if (isdir(icon_dir)) {
132 return icon_dir;
133 }
134
135 if (snprintf(icon_dir, 1024, "/usr/share/icons/%s", theme) >= 1024) {
136 sway_log(L_ERROR, "Path too long to render");
137 goto fail;
138 }
139 if (isdir(icon_dir)) {
140 return icon_dir;
141 }
142
143fail:
144 free(icon_dir);
145 sway_log(L_ERROR, "Could not find dir for theme: %s", theme);
146 return NULL;
147}
148
149/**
150 * Returns all theme dirs needed to be looked in for an icon.
151 * Does not check for duplicates
152 */
153static list_t *find_all_theme_dirs(const char *theme) {
154 list_t *dirs = create_list();
155 if (!dirs) {
156 return NULL;
157 }
158 char *dir = find_theme_dir(theme);
159 if (dir) {
160 list_add(dirs, dir);
161 list_t *inherits = find_inherits(dir);
162 list_cat(dirs, inherits);
163 list_free(inherits);
164 }
165 dir = find_theme_dir("hicolor");
166 if (dir) {
167 list_add(dirs, dir);
168 }
169
170 return dirs;
171}
172
173struct subdir {
174 int size;
175 char name[];
176};
177
178static int subdir_str_cmp(const void *_subdir, const void *_str) {
179 const struct subdir *subdir = _subdir;
180 const char *str = _str;
181 return strcmp(subdir->name, str);
182}
183/**
184 * Helper to find_subdirs. Acts similar to `split_string(subdirs, ",")` but
185 * generates a list of struct subdirs
186 */
187static list_t *split_subdirs(char *subdir_str) {
188 list_t *subdir_list = create_list();
189 char *copy = strdup(subdir_str);
190 if (!subdir_list || !copy) {
191 list_free(subdir_list);
192 free(copy);
193 return NULL;
194 }
195
196 char *token;
197 token = strtok(copy, ",");
198 while(token) {
199 int len = strlen(token) + 1;
200 struct subdir *subdir =
201 malloc(sizeof(struct subdir) + sizeof(char [len]));
202 if (!subdir) {
203 // Return what we have
204 return subdir_list;
205 }
206 subdir->size = 0;
207 strcpy(subdir->name, token);
208
209 list_add(subdir_list, subdir);
210
211 token = strtok(NULL, ",");
212 }
213 free(copy);
214
215 return subdir_list;
216}
217/**
218 * Returns a list of all subdirectories of a theme.
219 * Take note: the subdir names are all relative to `theme_dir` and must be
220 * combined with it to form a valid directory.
221 *
222 * Each member of the list is of type (struct subdir *) this struct contains
223 * the name of the subdir, along with size information. These must be freed
224 * bye the caller.
225 *
226 * This currently ignores min and max sizes of icons.
227 */
228static list_t* find_theme_subdirs(const char *theme_dir) {
229 const char index_name[] = "/index.theme";
230 list_t *dirs = NULL;
231 char *path = malloc(strlen(theme_dir) + sizeof(index_name));
232 FILE *index = NULL;
233 if (!path) {
234 sway_log(L_ERROR, "Failed to allocate memory");
235 goto fail;
236 }
237
238 strcpy(path, theme_dir);
239 strcat(path, index_name);
240
241 index = fopen(path, "r");
242 if (!index) {
243 sway_log(L_ERROR, "Could not open file: %s", path);
244 goto fail;
245 }
246
247 char *buf = NULL;
248 size_t n = 0;
249 const char directories[] = "Directories";
250 while (!feof(index) && getline(&buf, &n, index) != -1) {
251 if (n <= sizeof(directories) + 1) {
252 continue;
253 }
254 if (strncmp(directories, buf, sizeof(directories) - 1) == 0) {
255 char *dirstr = buf + sizeof(directories);
256 dirs = split_subdirs(dirstr);
257 break;
258 }
259 }
260 // Now, find the size of each dir
261 struct subdir *current_subdir = NULL;
262 const char size[] = "Size";
263 while (!feof(index) && getline(&buf, &n, index) != -1) {
264 if (buf[0] == '[') {
265 int len = strlen(buf);
266 if (buf[len-1] == '\n') {
267 len--;
268 }
269 // replace ']'
270 buf[len-1] = '\0';
271
272 int index;
273 if ((index = list_seq_find(dirs, subdir_str_cmp, buf+1)) != -1) {
274 current_subdir = (dirs->items[index]);
275 }
276 }
277
278 if (strncmp(size, buf, sizeof(size) - 1) == 0) {
279 if (current_subdir) {
280 current_subdir->size = atoi(buf + sizeof(size));
281 }
282 }
283 }
284 free(buf);
285fail:
286 free(path);
287 if (index) {
288 fclose(index);
289 }
290 return dirs;
291}
292
293/* Returns the file of an icon given its name and size */
294static char *find_icon_file(const char *name, int size) {
295 int namelen = strlen(name);
296 list_t *dirs = find_all_theme_dirs(swaybar.config->icon_theme);
297 if (!dirs) {
298 return NULL;
299 }
300 int min_size_diff = INT_MAX;
301 char *current_file = NULL;
302
303 for (int i = 0; i < dirs->length; ++i) {
304 char *dir = dirs->items[i];
305 list_t *subdirs = find_theme_subdirs(dir);
306
307 if (!subdirs) {
308 continue;
309 }
310
311 for (int i = 0; i < subdirs->length; ++i) {
312 struct subdir *subdir = subdirs->items[i];
313
314 // Only use an unsized if we don't already have a
315 // canidate this should probably change to allow svgs
316 if (!subdir->size && current_file) {
317 continue;
318 }
319
320 int size_diff = abs(size - subdir->size);
321
322 if (size_diff >= min_size_diff) {
323 continue;
324 }
325
326 char *path = malloc(strlen(subdir->name) + strlen(dir) + 2);
327
328 strcpy(path, dir);
329 path[strlen(dir)] = '/';
330 strcpy(path + strlen(dir) + 1, subdir->name);
331
332 DIR *icons = opendir(path);
333 if (!icons) {
334 free(path);
335 continue;
336 }
337
338 struct dirent *direntry;
339 while ((direntry = readdir(icons)) != NULL) {
340 int len = strlen(direntry->d_name);
341 if (len <= namelen + 2) { //must have some ext
342 continue;
343 }
344 if (strncmp(direntry->d_name, name, namelen) == 0) {
345 char *ext = direntry->d_name + namelen + 1;
346#ifdef WITH_GDK_PIXBUF
347 if (strcmp(ext, "png") == 0 ||
348 strcmp(ext, "xpm") == 0 ||
349 strcmp(ext, "svg") == 0) {
350#else
351 if (strcmp(ext, "png") == 0) {
352#endif
353 free(current_file);
354 char *icon_path = malloc(strlen(path) + len + 2);
355
356 strcpy(icon_path, path);
357 icon_path[strlen(path)] = '/';
358 strcpy(icon_path + strlen(path) + 1, direntry->d_name);
359 current_file = icon_path;
360 min_size_diff = size_diff;
361 }
362 }
363 }
364 free(path);
365 closedir(icons);
366 }
367 free_flat_list(subdirs);
368 }
369 free_flat_list(dirs);
370
371 return current_file;
372}
373
374cairo_surface_t *find_icon(const char *name, int size) {
375 char *image_path = find_icon_file(name, size);
376 if (image_path == NULL) {
377 return NULL;
378 }
379
380 cairo_surface_t *image = NULL;
381#ifdef WITH_GDK_PIXBUF
382 GError *err = NULL;
383 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err);
384 if (!pixbuf) {
385 sway_log(L_ERROR, "Failed to load icon image: %s", err->message);
386 }
387 image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
388 g_object_unref(pixbuf);
389#else
390 // TODO make svg work? cairo supports it. maybe remove gdk alltogether
391 image = cairo_image_surface_create_from_png(image_path);
392#endif //WITH_GDK_PIXBUF
393 if (!image) {
394 sway_log(L_ERROR, "Could not read icon image");
395 return NULL;
396 }
397
398 free(image_path);
399 return image;
400}