1 /*
2  *  Copyright (C) 2019-2020 Scoopta
3  *  This file is part of Wofi
4  *  Wofi is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 3 of the License, or
7     (at your option) any later version.
8 
9     Wofi is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with Wofi.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdio.h>
19 #include <stddef.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include <getopt.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <signal.h>
26 
27 #include <map.h>
28 #include <wofi.h>
29 #include <utils.h>
30 #include <config.h>
31 
32 #include <wayland-client.h>
33 
34 #include <gtk/gtk.h>
35 
36 static const char* nyan_colors[] = {"#FF0000", "#FFA500", "#FFFF00", "#00FF00", "#0000FF", "#FF00FF"};
37 static size_t nyan_color_l = sizeof(nyan_colors) / sizeof(char*);
38 
39 static char* CONFIG_LOCATION;
40 static char* COLORS_LOCATION;
41 static struct map* config;
42 static char* config_path;
43 static char* stylesheet;
44 static char* color_path;
45 static uint8_t nyan_shift = 0;
46 
47 struct option_node {
48 	char* option;
49 	struct wl_list link;
50 };
51 
get_exec_name(char * path)52 static char* get_exec_name(char* path) {
53 	char* slash = strrchr(path, '/');
54 	uint64_t offset;
55 	if(slash == NULL) {
56 		offset = 0;
57 	} else {
58 		offset = (slash - path) + 1;
59 	}
60 	return path + offset;
61 }
62 
print_usage(char ** argv)63 static void print_usage(char** argv) {
64 	printf("%s [options]\n", get_exec_name(argv[0]));
65 	printf("Options:\n");
66 	printf("--help\t\t-h\tDisplays this help message\n");
67 	printf("--fork\t\t-f\tForks the menu so you can close the terminal\n");
68 	printf("--conf\t\t-c\tSelects a config file to use\n");
69 	printf("--style\t\t-s\tSelects a stylesheet to use\n");
70 	printf("--color\t\t-C\tSelects a colors file to use\n");
71 	printf("--dmenu\t\t-d\tRuns in dmenu mode\n");
72 	printf("--show\t\t-S\tSpecifies the mode to run in\n");
73 	printf("--width\t\t-W\tSpecifies the surface width\n");
74 	printf("--height\t-H\tSpecifies the surface height\n");
75 	printf("--prompt\t-p\tPrompt to display\n");
76 	printf("--xoffset\t-x\tThe x offset\n");
77 	printf("--yoffset\t-y\tThe y offset\n");
78 	printf("--normal-window\t-n\tRender to a normal window\n");
79 	printf("--allow-images\t-I\tAllows images to be rendered\n");
80 	printf("--allow-markup\t-m\tAllows pango markup\n");
81 	printf("--cache-file\t-k\tSets the cache file to use\n");
82 	printf("--term\t\t-t\tSpecifies the terminal to use when running in a term\n");
83 	printf("--password\t-P\tRuns in password mode\n");
84 	printf("--exec-search\t-e\tMakes enter always use the search contents not the first result\n");
85 	printf("--hide-scroll\t-b\tHides the scroll bars\n");
86 	printf("--matching\t-M\tSets the matching method, default is contains\n");
87 	printf("--insensitive\t-i\tAllows case insensitive searching\n");
88 	printf("--parse-search\t-q\tParses the search text removing image escapes and pango\n");
89 	printf("--version\t-v\tPrints the version and then exits\n");
90 	printf("--location\t-l\tSets the location\n");
91 	printf("--no-actions\t-a\tDisables multiple actions for modes that support it\n");
92 	printf("--define\t-D\tSets a config option\n");
93 	printf("--lines\t\t-L\tSets the height in number of lines\n");
94 	printf("--columns\t-w\tSets the number of columns to display\n");
95 	printf("--sort-order\t-O\tSets the sort order\n");
96 	printf("--gtk-dark\t-G\tUses the dark variant of the current GTK theme\n");
97 	printf("--search\t-Q\tSearch for something immediately on open\n");
98 	printf("--monitor\t-o\tSets the monitor to open on\n");
99 	exit(0);
100 }
101 
wofi_load_css(bool nyan)102 void wofi_load_css(bool nyan) {
103 	if(access(stylesheet, R_OK) == 0) {
104 		FILE* file = fopen(stylesheet, "r");
105 		fseek(file, 0, SEEK_END);
106 		ssize_t size = ftell(file);
107 		fseek(file, 0, SEEK_SET);
108 		char* data = malloc(size + 1);
109 		fread(data, 1, size, file);
110 		fclose(file);
111 
112 		data[size] = 0;
113 		struct wl_list lines;
114 		struct node {
115 			char* line;
116 			struct wl_list link;
117 		};
118 		wl_list_init(&lines);
119 		if(nyan) {
120 			for(ssize_t count = nyan_shift; count < 100 + nyan_shift; ++count) {
121 				size_t i = count % nyan_color_l;
122 				struct node* entry = malloc(sizeof(struct node));
123 				entry->line = strdup(nyan_colors[i]);
124 				wl_list_insert(&lines, &entry->link);
125 			}
126 			nyan_shift = (nyan_shift + 1) % nyan_color_l;
127 		} else {
128 			if(access(color_path, R_OK) == 0) {
129 				file = fopen(color_path, "r");
130 				char* line = NULL;
131 				size_t line_size = 0;
132 				ssize_t line_l = 0;
133 				while((line_l = getline(&line, &line_size, file)) != -1) {
134 					struct node* entry = malloc(sizeof(struct node));
135 					line[line_l - 1] = 0;
136 					entry->line = malloc(line_l + 1);
137 					strcpy(entry->line, line);
138 					wl_list_insert(&lines, &entry->link);
139 				}
140 				fclose(file);
141 				free(line);
142 			}
143 		}
144 
145 		ssize_t count = wl_list_length(&lines) - 1;
146 		if(count > 99) {
147 			fprintf(stderr, "Woah there that's a lot of colors. Try having no more than 100, thanks\n");
148 			exit(1);
149 		}
150 		struct node* node;
151 		wl_list_for_each(node, &lines, link) {
152 			//Do --wofi-color replace
153 			const char* color = node->line;
154 			const char* wofi_color = "--wofi-color";
155 			char count_str[3];
156 			snprintf(count_str, 3, "%zu", count--);
157 			char* needle = utils_concat(2, wofi_color, count_str);
158 			size_t color_len = strlen(color);
159 			size_t needle_len = strlen(needle);
160 			if(color_len > needle_len) {
161 				free(needle);
162 				fprintf(stderr, "What color format is this, try #FFFFFF, kthxbi\n");
163 				continue;
164 			}
165 			char* replace = strstr(data, needle);
166 			while(replace != NULL) {
167 				memcpy(replace, color, color_len);
168 				memset(replace + color_len, ' ', needle_len - color_len);
169 				replace = strstr(data, needle);
170 			}
171 			free(needle);
172 
173 
174 			//Do --wofi-rgb-color replace
175 			if(color_len < 7) {
176 				fprintf(stderr, "What color format is this, try #FFFFFF, kthxbi\n");
177 				continue;
178 			}
179 			const char* wofi_rgb_color = "--wofi-rgb-color";
180 			needle = utils_concat(2, wofi_rgb_color, count_str);
181 			needle_len = strlen(needle);
182 			replace = strstr(data, needle);
183 			while(replace != NULL) {
184 				char r[3];
185 				char g[3];
186 				char b[3];
187 				memcpy(r, color + 1, 2);
188 				memcpy(g, color + 3, 2);
189 				memcpy(b, color + 5, 2);
190 				r[2] = 0;
191 				g[2] = 0;
192 				b[2] = 0;
193 				char rgb[14];
194 				snprintf(rgb, 14, "%ld, %ld, %ld", strtol(r, NULL, 16), strtol(g, NULL, 16), strtol(b, NULL, 16));
195 				size_t rgb_len = strlen(rgb);
196 				memcpy(replace, rgb, rgb_len);
197 				memset(replace + rgb_len, ' ', needle_len - rgb_len);
198 				replace = strstr(data, needle);
199 			}
200 			free(needle);
201 		}
202 		GtkCssProvider* css = gtk_css_provider_new();
203 		gtk_css_provider_load_from_data(css, data, strlen(data), NULL);
204 		free(data);
205 		struct node* tmp;
206 		wl_list_for_each_safe(node, tmp, &lines, link) {
207 			free(node->line);
208 			wl_list_remove(&node->link);
209 			free(node);
210 		}
211 		gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
212 	}
213 }
214 
sig(int32_t signum)215 static void sig(int32_t signum) {
216 	switch(signum) {
217 	case SIGTERM:
218 		exit(1);
219 		break;
220 	}
221 }
222 
main(int argc,char ** argv)223 int main(int argc, char** argv) {
224 
225 	const struct option opts[] = {
226 		{
227 			.name = "help",
228 			.has_arg = no_argument,
229 			.flag = NULL,
230 			.val = 'h'
231 		},
232 		{
233 			.name = "fork",
234 			.has_arg = no_argument,
235 			.flag = NULL,
236 			.val = 'f'
237 		},
238 		{
239 			.name = "conf",
240 			.has_arg = required_argument,
241 			.flag = NULL,
242 			.val = 'c'
243 		},
244 		{
245 			.name = "style",
246 			.has_arg = required_argument,
247 			.flag = NULL,
248 			.val = 's'
249 		},
250 		{
251 			.name = "color",
252 			.has_arg = required_argument,
253 			.flag = NULL,
254 			.val = 'C'
255 		},
256 		{
257 			.name = "dmenu",
258 			.has_arg = no_argument,
259 			.flag = NULL,
260 			.val = 'd'
261 		},
262 		{
263 			.name = "show",
264 			.has_arg = required_argument,
265 			.flag = NULL,
266 			.val = 'S'
267 		},
268 		{
269 			.name = "width",
270 			.has_arg = required_argument,
271 			.flag = NULL,
272 			.val = 'W'
273 		},
274 		{
275 			.name = "height",
276 			.has_arg = required_argument,
277 			.flag = NULL,
278 			.val = 'H'
279 		},
280 		{
281 			.name = "prompt",
282 			.has_arg = required_argument,
283 			.flag = NULL,
284 			.val = 'p'
285 		},
286 		{
287 			.name = "xoffset",
288 			.has_arg = required_argument,
289 			.flag = NULL,
290 			.val = 'x'
291 		},
292 		{
293 			.name = "yoffset",
294 			.has_arg = required_argument,
295 			.flag = NULL,
296 			.val = 'y'
297 		},
298 		{
299 			.name = "normal-window",
300 			.has_arg = no_argument,
301 			.flag = NULL,
302 			.val = 'n'
303 		},
304 		{
305 			.name = "allow-images",
306 			.has_arg = no_argument,
307 			.flag = NULL,
308 			.val = 'I'
309 		},
310 		{
311 			.name = "allow-markup",
312 			.has_arg = no_argument,
313 			.flag = NULL,
314 			.val = 'm'
315 		},
316 		{
317 			.name = "cache-file",
318 			.has_arg = required_argument,
319 			.flag = NULL,
320 			.val = 'k'
321 		},
322 		{
323 			.name = "term",
324 			.has_arg = required_argument,
325 			.flag = NULL,
326 			.val = 't'
327 		},
328 		{
329 			.name = "password",
330 			.has_arg = optional_argument,
331 			.flag = NULL,
332 			.val = 'P'
333 		},
334 		{
335 			.name = "exec-search",
336 			.has_arg = no_argument,
337 			.flag = NULL,
338 			.val = 'e'
339 		},
340 		{
341 			.name = "hide-scroll",
342 			.has_arg = no_argument,
343 			.flag = NULL,
344 			.val = 'b'
345 		},
346 		{
347 			.name = "matching",
348 			.has_arg = required_argument,
349 			.flag = NULL,
350 			.val = 'M'
351 		},
352 		{
353 			.name = "insensitive",
354 			.has_arg = no_argument,
355 			.flag = NULL,
356 			.val = 'i'
357 		},
358 		{
359 			.name = "parse-search",
360 			.has_arg = no_argument,
361 			.flag = NULL,
362 			.val = 'q'
363 		},
364 		{
365 			.name = "version",
366 			.has_arg = no_argument,
367 			.flag = NULL,
368 			.val = 'v'
369 		},
370 		{
371 			.name = "location",
372 			.has_arg = required_argument,
373 			.flag = NULL,
374 			.val = 'l'
375 		},
376 		{
377 			.name = "no-actions",
378 			.has_arg = no_argument,
379 			.flag = NULL,
380 			.val = 'a'
381 		},
382 		{
383 			.name = "define",
384 			.has_arg = required_argument,
385 			.flag = NULL,
386 			.val = 'D'
387 		},
388 		{
389 			.name = "lines",
390 			.has_arg = required_argument,
391 			.flag = NULL,
392 			.val = 'L'
393 		},
394 		{
395 			.name = "columns",
396 			.has_arg = required_argument,
397 			.flag = NULL,
398 			.val = 'w'
399 		},
400 		{
401 			.name = "sort-order",
402 			.has_arg = required_argument,
403 			.flag = NULL,
404 			.val = 'O'
405 		},
406 		{
407 			.name = "gtk-dark",
408 			.has_arg = no_argument,
409 			.flag = NULL,
410 			.val = 'G'
411 		},
412 		{
413 			.name = "search",
414 			.has_arg = required_argument,
415 			.flag = NULL,
416 			.val = 'Q'
417 		},
418 		{
419 			.name = "monitor",
420 			.has_arg = required_argument,
421 			.flag = NULL,
422 			.val = 'o'
423 		},
424 		{
425 			.name = NULL,
426 			.has_arg = 0,
427 			.flag = NULL,
428 			.val = 0
429 		}
430 	};
431 
432 	const char* config_str = NULL;
433 	char* style_str = NULL;
434 	char* color_str = NULL;
435 	char* mode = NULL;
436 	char* prompt = NULL;
437 	char* width = NULL;
438 	char* height = NULL;
439 	char* x = NULL;
440 	char* y = NULL;
441 	char* normal_window = NULL;
442 	char* allow_images = NULL;
443 	char* allow_markup = NULL;
444 	char* cache_file = NULL;
445 	char* terminal = NULL;
446 	char* password_char = "false";
447 	char* exec_search = NULL;
448 	char* hide_scroll = NULL;
449 	char* matching = NULL;
450 	char* insensitive = NULL;
451 	char* parse_search = NULL;
452 	char* location = NULL;
453 	char* no_actions = NULL;
454 	char* lines = NULL;
455 	char* columns = NULL;
456 	char* sort_order = NULL;
457 	char* gtk_dark = NULL;
458 	char* search = NULL;
459 	char* monitor = NULL;
460 
461 	struct wl_list options;
462 	wl_list_init(&options);
463 	struct option_node* node;
464 
465 	int opt;
466 	while((opt = getopt_long(argc, argv, "hfc:s:C:dS:W:H:p:x:y:nImk:t:P::ebM:iqvl:aD:L:w:O:GQ:o:", opts, NULL)) != -1) {
467 		switch(opt) {
468 		case 'h':
469 			print_usage(argv);
470 			break;
471 		case 'f':
472 			if(fork() > 0) {
473 				exit(0);
474 			}
475 			fclose(stdout);
476 			fclose(stderr);
477 			fclose(stdin);
478 			break;
479 		case 'c':
480 			config_str = optarg;
481 			break;
482 		case 's':
483 			style_str = optarg;
484 			break;
485 		case 'C':
486 			color_str = optarg;
487 			break;
488 		case 'd':
489 			mode = "dmenu";
490 			break;
491 		case 'S':
492 			mode = optarg;
493 			break;
494 		case 'W':
495 			width = optarg;
496 			break;
497 		case 'H':
498 			height = optarg;
499 			break;
500 		case 'p':
501 			prompt = optarg;
502 			break;
503 		case 'x':
504 			x = optarg;
505 			break;
506 		case 'y':
507 			y = optarg;
508 			break;
509 		case 'n':
510 			normal_window = "true";
511 			break;
512 		case 'I':
513 			allow_images = "true";
514 			break;
515 		case 'm':
516 			allow_markup = "true";
517 			break;
518 		case 'k':
519 			cache_file = optarg;
520 			break;
521 		case 't':
522 			terminal = optarg;
523 			break;
524 		case 'P':
525 			password_char = optarg;
526 			break;
527 		case 'e':
528 			exec_search = "true";
529 			break;
530 		case 'b':
531 			hide_scroll = "true";
532 			break;
533 		case 'M':
534 			matching = optarg;
535 			break;
536 		case 'i':
537 			insensitive = "true";
538 			break;
539 		case 'q':
540 			parse_search = "true";
541 			break;
542 		case 'v':
543 			printf(VERSION"\n");
544 			exit(0);
545 			break;
546 		case 'l':
547 			location = optarg;
548 			break;
549 		case 'a':
550 			no_actions = "true";
551 			break;
552 		case 'D':
553 			node = malloc(sizeof(struct option_node));
554 			node->option = optarg;
555 			wl_list_insert(&options, &node->link);
556 			break;
557 		case 'L':
558 			lines = optarg;
559 			break;
560 		case 'w':
561 			columns = optarg;
562 			break;
563 		case 'O':
564 			sort_order = optarg;
565 			break;
566 		case 'G':
567 			gtk_dark = "true";
568 			break;
569 		case 'Q':
570 			search = optarg;
571 			break;
572 		case 'o':
573 			monitor = optarg;
574 			break;
575 		}
576 	}
577 
578 	const char* home_dir = getenv("HOME");
579 	const char* xdg_conf = getenv("XDG_CONFIG_HOME");
580 	if(xdg_conf == NULL) {
581 		CONFIG_LOCATION = utils_concat(2, home_dir, "/.config/wofi");
582 	} else {
583 		CONFIG_LOCATION = utils_concat(2, xdg_conf, "/wofi");
584 	}
585 
586 	const char* xdg_cache = getenv("XDG_CACHE_HOME");
587 	if(xdg_cache == NULL) {
588 		COLORS_LOCATION = utils_concat(2, home_dir, "/.cache/wal/colors");
589 	} else {
590 		COLORS_LOCATION = utils_concat(2, xdg_cache, "/wal/colors");
591 	}
592 
593 	config = map_init();
594 
595 	//Check if --conf was specified
596 	if(config_str == NULL) {
597 		const char* config_f = "/config";
598 		config_path = utils_concat(2, CONFIG_LOCATION, config_f);
599 	} else {
600 		config_path = strdup(config_str);
601 	}
602 	if(access(config_path, R_OK) == 0) {
603 		config_load(config, config_path);
604 	}
605 	free(config_path);
606 
607 	if(style_str == NULL) {
608 		style_str = map_get(config, "style");
609 	}
610 
611 	//Check if --style was specified
612 	if(style_str == NULL) {
613 		style_str = map_get(config, "stylesheet");
614 		if(style_str == NULL) {
615 			const char* style_f = "/style.css";
616 			stylesheet = utils_concat(2, CONFIG_LOCATION, style_f);
617 		} else {
618 			if(style_str[0] == '/') {
619 				stylesheet = strdup(style_str);
620 			} else {
621 				stylesheet = utils_concat(3, CONFIG_LOCATION, "/", style_str);
622 			}
623 		}
624 	} else {
625 		stylesheet = strdup(style_str);
626 	}
627 
628 	if(color_str == NULL) {
629 		color_str = map_get(config, "color");
630 	}
631 
632 	//Check if --color was specified
633 	if(color_str == NULL) {
634 		color_str = map_get(config, "colors");
635 		if(color_str == NULL) {
636 			color_path = strdup(COLORS_LOCATION);
637 		} else {
638 			if(color_str[0] == '/') {
639 				color_path = strdup(color_str);
640 			} else {
641 				color_path = utils_concat(3, CONFIG_LOCATION, "/", color_str);
642 			}
643 		}
644 	} else {
645 		color_path = strdup(color_str);
646 	}
647 
648 	//Check if --gtk-dark was specified
649 	if(gtk_dark == NULL) {
650 		gtk_dark = map_get(config, "gtk_dark");
651 	}
652 
653 	free(COLORS_LOCATION);
654 
655 	struct option_node* tmp;
656 	wl_list_for_each_safe(node, tmp, &options, link) {
657 		config_put(config, node->option);
658 		wl_list_remove(&node->link);
659 		free(node);
660 	}
661 
662 	if(map_get(config, "show") != NULL) {
663 		map_put(config, "mode", map_get(config, "show"));
664 	}
665 
666 	if(strcmp(get_exec_name(argv[0]), "dmenu") == 0) {
667 		map_put(config, "mode", "dmenu");
668 		cache_file = "/dev/null";
669 	} else if(strcmp(get_exec_name(argv[0]), "wofi-askpass") == 0) {
670 		map_put(config, "mode", "dmenu");
671 		cache_file = "/dev/null";
672 		password_char = "*";
673 		prompt = "Password";
674 	} else if(mode != NULL) {
675 		map_put(config, "mode", mode);
676 	} else if(map_get(config, "mode") == NULL) {
677 		fprintf(stderr, "I need a mode, please give me a mode, that's what --show is for\n");
678 		exit(1);
679 	}
680 
681 	map_put(config, "config_dir", CONFIG_LOCATION);
682 
683 	if(width != NULL) {
684 		map_put(config, "width", width);
685 	}
686 	if(height != NULL) {
687 		map_put(config, "height", height);
688 	}
689 	if(prompt != NULL) {
690 		map_put(config, "prompt", prompt);
691 	}
692 	if(map_get(config, "xoffset") != NULL) {
693 		map_put(config, "x", map_get(config, "xoffset"));
694 	}
695 	if(x != NULL) {
696 		map_put(config, "x", x);
697 	}
698 	if(map_get(config, "yoffset") != NULL) {
699 		map_put(config, "y", map_get(config, "yoffset"));
700 	}
701 	if(y != NULL) {
702 		map_put(config, "y", y);
703 	}
704 	if(normal_window != NULL) {
705 		map_put(config, "normal_window", normal_window);
706 	}
707 	if(allow_images != NULL) {
708 		map_put(config, "allow_images", allow_images);
709 	}
710 	if(allow_markup != NULL) {
711 		map_put(config, "allow_markup", allow_markup);
712 	}
713 	if(cache_file != NULL) {
714 		map_put(config, "cache_file", cache_file);
715 	}
716 	if(terminal != NULL) {
717 		map_put(config, "term", terminal);
718 	}
719 	if(map_get(config, "password") != NULL) {
720 		map_put(config, "password_char", map_get(config, "password"));
721 	}
722 	if(password_char == NULL || (password_char != NULL && strcmp(password_char, "false") != 0)) {
723 		if(password_char == NULL) {
724 			password_char = "*";
725 		}
726 		map_put(config, "password_char", password_char);
727 	}
728 	if(exec_search != NULL) {
729 		map_put(config, "exec_search", exec_search);
730 	}
731 	if(hide_scroll != NULL) {
732 		map_put(config, "hide_scroll", hide_scroll);
733 	}
734 	if(matching != NULL) {
735 		map_put(config, "matching", matching);
736 	}
737 	if(insensitive != NULL) {
738 		map_put(config, "insensitive", insensitive);
739 	}
740 	if(parse_search != NULL) {
741 		map_put(config, "parse_search", parse_search);
742 	}
743 	if(location != NULL) {
744 		map_put(config, "location", location);
745 	}
746 	if(no_actions != NULL) {
747 		map_put(config, "no_actions", no_actions);
748 	}
749 	if(lines != NULL) {
750 		map_put(config, "lines", lines);
751 	}
752 	if(columns != NULL) {
753 		map_put(config, "columns", columns);
754 	}
755 	if(sort_order != NULL) {
756 		map_put(config, "sort_order", sort_order);
757 	}
758 	if(search != NULL) {
759 		map_put(config, "search", search);
760 	}
761 	if(monitor != NULL) {
762 		map_put(config, "monitor", monitor);
763 	}
764 
765 	struct sigaction sigact = {0};
766 	sigact.sa_handler = sig;
767 	sigaction(SIGTERM, &sigact, NULL);
768 
769 
770 	gtk_init(&argc, &argv);
771 
772 	if(gtk_dark != NULL && strcmp(gtk_dark, "true") == 0) {
773 		g_object_set(gtk_settings_get_default(),
774 			"gtk-application-prefer-dark-theme", TRUE, NULL);
775 	}
776 	wofi_load_css(false);
777 
778 	wofi_init(config);
779 	gtk_main();
780 	return 0;
781 }
782