summaryrefslogtreecommitdiffstats
path: root/swaynag/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaynag/config.c')
-rw-r--r--swaynag/config.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/swaynag/config.c b/swaynag/config.c
new file mode 100644
index 00000000..d6c5739d
--- /dev/null
+++ b/swaynag/config.c
@@ -0,0 +1,401 @@
1#define _XOPEN_SOURCE 700
2#define _POSIX_C_SOURCE 200112L
3#include <getopt.h>
4#include <stdlib.h>
5#include <wordexp.h>
6#include "log.h"
7#include "list.h"
8#include "readline.h"
9#include "swaynag/swaynag.h"
10#include "swaynag/types.h"
11#include "util.h"
12#include "wlr-layer-shell-unstable-v1-client-protocol.h"
13
14static char *read_from_stdin() {
15 char *buffer = NULL;
16 while (!feof(stdin)) {
17 char *line = read_line(stdin);
18 if (!line) {
19 continue;
20 }
21
22 size_t curlen = buffer ? strlen(buffer) : 0;
23 buffer = realloc(buffer, curlen + strlen(line) + 2);
24 snprintf(buffer + curlen, strlen(line) + 2, "%s\n", line);
25
26 free(line);
27 }
28
29 while (buffer && buffer[strlen(buffer) - 1] == '\n') {
30 buffer[strlen(buffer) - 1] = '\0';
31 }
32
33 return buffer;
34}
35
36int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag,
37 list_t *types, struct swaynag_type *type, char **config, bool *debug) {
38 enum type_options {
39 TO_COLOR_BACKGROUND = 256,
40 TO_COLOR_BORDER,
41 TO_COLOR_BORDER_BOTTOM,
42 TO_COLOR_BUTTON,
43 TO_COLOR_TEXT,
44 TO_THICK_BAR_BORDER,
45 TO_PADDING_MESSAGE,
46 TO_THICK_DET_BORDER,
47 TO_THICK_BTN_BORDER,
48 TO_GAP_BTN,
49 TO_GAP_BTN_DISMISS,
50 TO_MARGIN_BTN_RIGHT,
51 TO_PADDING_BTN,
52 };
53
54 static struct option opts[] = {
55 {"button", required_argument, NULL, 'b'},
56 {"config", required_argument, NULL, 'c'},
57 {"debug", no_argument, NULL, 'd'},
58 {"edge", required_argument, NULL, 'e'},
59 {"font", required_argument, NULL, 'f'},
60 {"help", no_argument, NULL, 'h'},
61 {"detailed-message", no_argument, NULL, 'l'},
62 {"detailed-button", required_argument, NULL, 'L'},
63 {"message", required_argument, NULL, 'm'},
64 {"output", required_argument, NULL, 'o'},
65 {"dismiss-button", required_argument, NULL, 's'},
66 {"type", required_argument, NULL, 't'},
67 {"version", no_argument, NULL, 'v'},
68
69 {"background", required_argument, NULL, TO_COLOR_BACKGROUND},
70 {"border", required_argument, NULL, TO_COLOR_BORDER},
71 {"border-bottom", required_argument, NULL, TO_COLOR_BORDER_BOTTOM},
72 {"button-background", required_argument, NULL, TO_COLOR_BUTTON},
73 {"text", required_argument, NULL, TO_COLOR_TEXT},
74 {"border-bottom-size", required_argument, NULL, TO_THICK_BAR_BORDER},
75 {"message-padding", required_argument, NULL, TO_PADDING_MESSAGE},
76 {"details-border-size", required_argument, NULL, TO_THICK_DET_BORDER},
77 {"button-border-size", required_argument, NULL, TO_THICK_BTN_BORDER},
78 {"button-gap", required_argument, NULL, TO_GAP_BTN},
79 {"button-dismiss-gap", required_argument, NULL, TO_GAP_BTN_DISMISS},
80 {"button-margin-right", required_argument, NULL, TO_MARGIN_BTN_RIGHT},
81 {"button-padding", required_argument, NULL, TO_PADDING_BTN},
82
83 {0, 0, 0, 0}
84 };
85
86 const char *usage =
87 "Usage: swaynag [options...]\n"
88 "\n"
89 " -b, --button <text> <action> Create a button with text that "
90 "executes action when pressed. Multiple buttons can be defined.\n"
91 " -c, --config <path> Path to config file.\n"
92 " -d, --debug Enable debugging.\n"
93 " -e, --edge top|bottom Set the edge to use.\n"
94 " -f, --font <font> Set the font to use.\n"
95 " -h, --help Show help message and quit.\n"
96 " -l, --detailed-message Read a detailed message from stdin.\n"
97 " -L, --detailed-button <text> Set the text of the detail button.\n"
98 " -m, --message <msg> Set the message text.\n"
99 " -o, --output <output> Set the output to use.\n"
100 " -s, --dismiss-button <text> Set the dismiss button text.\n"
101 " -t, --type <type> Set the message type.\n"
102 " -v, --version Show the version number and quit.\n"
103 "\n"
104 "The following appearance options can also be given:\n"
105 " --background RRGGBB[AA] Background color.\n"
106 " --border RRGGBB[AA] Border color.\n"
107 " --border-bottom RRGGBB[AA] Bottom border color.\n"
108 " --button-background RRGGBB[AA] Button background color.\n"
109 " --text RRGGBB[AA] Text color.\n"
110 " --border-bottom-size size Thickness of the bar border.\n"
111 " --message-padding padding Padding for the message.\n"
112 " --details-border-size size Thickness for the details border.\n"
113 " --button-border-size size Thickness for the button border.\n"
114 " --button-gap gap Size of the gap between buttons\n"
115 " --button-dismiss-gap gap Size of the gap for dismiss button.\n"
116 " --button-margin-right margin Margin from dismiss button to edge.\n"
117 " --button-padding padding Padding for the button text.\n";
118
119 optind = 1;
120 while (1) {
121 int c = getopt_long(argc, argv, "b:c:de:f:hlL:m:o:s:t:v", opts, NULL);
122 if (c == -1) {
123 break;
124 }
125 switch (c) {
126 case 'b': // Button
127 if (swaynag) {
128 if (optind >= argc) {
129 fprintf(stderr, "Missing action for button %s\n", optarg);
130 return EXIT_FAILURE;
131 }
132 struct swaynag_button *button;
133 button = calloc(sizeof(struct swaynag_button), 1);
134 button->text = strdup(optarg);
135 button->type = SWAYNAG_ACTION_COMMAND;
136 button->action = strdup(argv[optind]);
137 list_add(swaynag->buttons, button);
138 }
139 optind++;
140 break;
141 case 'c': // Config
142 if (config) {
143 *config = strdup(optarg);
144 }
145 break;
146 case 'd': // Debug
147 if (debug) {
148 *debug = true;
149 }
150 break;
151 case 'e': // Edge
152 if (type) {
153 if (strcmp(optarg, "top") == 0) {
154 type->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
155 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
156 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
157 } else if (strcmp(optarg, "bottom") == 0) {
158 type->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
159 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
160 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
161 } else {
162 fprintf(stderr, "Invalid edge: %s\n", optarg);
163 return EXIT_FAILURE;
164 }
165 }
166 break;
167 case 'f': // Font
168 if (type) {
169 free(type->font);
170 type->font = strdup(optarg);
171 }
172 break;
173 case 'l': // Detailed Message
174 if (swaynag) {
175 free(swaynag->details.message);
176 swaynag->details.message = read_from_stdin();
177 swaynag->details.button_up.text = strdup("▲");
178 swaynag->details.button_down.text = strdup("▼");
179 }
180 break;
181 case 'L': // Detailed Button Text
182 if (swaynag) {
183 free(swaynag->details.button_details.text);
184 swaynag->details.button_details.text = strdup(optarg);
185 }
186 break;
187 case 'm': // Message
188 if (swaynag) {
189 free(swaynag->message);
190 swaynag->message = strdup(optarg);
191 }
192 break;
193 case 'o': // Output
194 if (type) {
195 free(type->output);
196 type->output = strdup(optarg);
197 }
198 break;
199 case 's': // Dismiss Button Text
200 if (swaynag) {
201 struct swaynag_button *button_close;
202 button_close = swaynag->buttons->items[0];
203 free(button_close->text);
204 button_close->text = strdup(optarg);
205 }
206 break;
207 case 't': // Type
208 if (swaynag) {
209 swaynag->type = swaynag_type_get(types, optarg);
210 if (!swaynag->type) {
211 fprintf(stderr, "Unknown type %s\n", optarg);
212 return EXIT_FAILURE;
213 }
214 }
215 break;
216 case 'v': // Version
217 fprintf(stdout, "swaynag version " SWAY_VERSION "\n");
218 return -1;
219 case TO_COLOR_BACKGROUND: // Background color
220 if (type) {
221 type->background = parse_color(optarg);
222 }
223 break;
224 case TO_COLOR_BORDER: // Border color
225 if (type) {
226 type->border = parse_color(optarg);
227 }
228 break;
229 case TO_COLOR_BORDER_BOTTOM: // Bottom border color
230 if (type) {
231 type->border_bottom = parse_color(optarg);
232 }
233 break;
234 case TO_COLOR_BUTTON: // Button background color
235 if (type) {
236 type->button_background = parse_color(optarg);
237 }
238 break;
239 case TO_COLOR_TEXT: // Text color
240 if (type) {
241 type->text = parse_color(optarg);
242 }
243 break;
244 case TO_THICK_BAR_BORDER: // Bottom border thickness
245 if (type) {
246 type->bar_border_thickness = strtol(optarg, NULL, 0);
247 }
248 break;
249 case TO_PADDING_MESSAGE: // Message padding
250 if (type) {
251 type->message_padding = strtol(optarg, NULL, 0);
252 }
253 break;
254 case TO_THICK_DET_BORDER: // Details border thickness
255 if (type) {
256 type->details_border_thickness = strtol(optarg, NULL, 0);
257 }
258 break;
259 case TO_THICK_BTN_BORDER: // Button border thickness
260 if (type) {
261 type->button_border_thickness = strtol(optarg, NULL, 0);
262 }
263 break;
264 case TO_GAP_BTN: // Gap between buttons
265 if (type) {
266 type->button_gap = strtol(optarg, NULL, 0);
267 }
268 break;
269 case TO_GAP_BTN_DISMISS: // Gap between dismiss button
270 if (type) {
271 type->button_gap_close = strtol(optarg, NULL, 0);
272 }
273 break;
274 case TO_MARGIN_BTN_RIGHT: // Margin on the right side of button area
275 if (type) {
276 type->button_margin_right = strtol(optarg, NULL, 0);
277 }
278 break;
279 case TO_PADDING_BTN: // Padding for the button text
280 if (type) {
281 type->button_padding = strtol(optarg, NULL, 0);
282 }
283 break;
284 default: // Help or unknown flag
285 fprintf(c == 'h' ? stdout : stderr, "%s", usage);
286 return -1;
287 }
288 }
289
290 return 0;
291}
292
293static bool file_exists(const char *path) {
294 return path && access(path, R_OK) != -1;
295}
296
297char *swaynag_get_config_path(void) {
298 static const char *config_paths[] = {
299 "$HOME/.swaynag/config",
300 "$XDG_CONFIG_HOME/swaynag/config",
301 SYSCONFDIR "/swaynag/config",
302 };
303
304 if (!getenv("XDG_CONFIG_HOME")) {
305 char *home = getenv("HOME");
306 char *config_home = malloc(strlen(home) + strlen("/.config") + 1);
307 if (!config_home) {
308 wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config");
309 } else {
310 strcpy(config_home, home);
311 strcat(config_home, "/.config");
312 setenv("XDG_CONFIG_HOME", config_home, 1);
313 wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
314 free(config_home);
315 }
316 }
317
318 wordexp_t p;
319 char *path;
320 for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) {
321 if (wordexp(config_paths[i], &p, 0) == 0) {
322 path = strdup(p.we_wordv[0]);
323 wordfree(&p);
324 if (file_exists(path)) {
325 return path;
326 }
327 free(path);
328 }
329 }
330
331 return NULL;
332}
333
334int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) {
335 FILE *config = fopen(path, "r");
336 if (!config) {
337 fprintf(stderr, "Failed to read config. Running without it.\n");
338 return 0;
339 }
340
341 struct swaynag_type *type;
342 type = calloc(1, sizeof(struct swaynag_type));
343 type->name = strdup("<config>");
344 list_add(types, type);
345
346 char *line;
347 int line_number = 0;
348 while (!feof(config)) {
349 line = read_line(config);
350 if (!line) {
351 continue;
352 }
353
354 line_number++;
355 if (line[0] == '#') {
356 free(line);
357 continue;
358 }
359 if (strlen(line) == 0) {
360 free(line);
361 continue;
362 }
363
364 if (line[0] == '[') {
365 char *close = strchr(line, ']');
366 if (!close) {
367 free(line);
368 fclose(config);
369 fprintf(stderr, "Closing bracket not found on line %d\n",
370 line_number);
371 return 1;
372 }
373 char *name = calloc(1, close - line);
374 strncat(name, line + 1, close - line - 1);
375 type = swaynag_type_get(types, name);
376 if (!type) {
377 type = calloc(1, sizeof(struct swaynag_type));
378 type->name = strdup(name);
379 list_add(types, type);
380 }
381 free(name);
382 } else {
383 char flag[strlen(line) + 3];
384 sprintf(flag, "--%s", line);
385 char *argv[] = {"swaynag", flag};
386 int result;
387 result = swaynag_parse_options(2, argv, swaynag, types, type,
388 NULL, NULL);
389 if (result != 0) {
390 free(line);
391 fclose(config);
392 return result;
393 }
394 }
395
396 free(line);
397 }
398 fclose(config);
399 return 0;
400}
401