diff options
Diffstat (limited to 'swaybg/main.c')
-rw-r--r-- | swaybg/main.c | 542 |
1 files changed, 0 insertions, 542 deletions
diff --git a/swaybg/main.c b/swaybg/main.c deleted file mode 100644 index b983dd6a..00000000 --- a/swaybg/main.c +++ /dev/null | |||
@@ -1,542 +0,0 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include <ctype.h> | ||
4 | #include <getopt.h> | ||
5 | #include <stdbool.h> | ||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <string.h> | ||
9 | #include <strings.h> | ||
10 | #include <wayland-client.h> | ||
11 | #include "background-image.h" | ||
12 | #include "cairo.h" | ||
13 | #include "log.h" | ||
14 | #include "pool-buffer.h" | ||
15 | #include "util.h" | ||
16 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | ||
17 | #include "xdg-output-unstable-v1-client-protocol.h" | ||
18 | |||
19 | struct swaybg_state { | ||
20 | struct wl_display *display; | ||
21 | struct wl_compositor *compositor; | ||
22 | struct wl_shm *shm; | ||
23 | struct zwlr_layer_shell_v1 *layer_shell; | ||
24 | struct zxdg_output_manager_v1 *xdg_output_manager; | ||
25 | struct wl_list configs; // struct swaybg_output_config::link | ||
26 | struct wl_list outputs; // struct swaybg_output::link | ||
27 | bool run_display; | ||
28 | }; | ||
29 | |||
30 | struct swaybg_output_config { | ||
31 | char *output; | ||
32 | cairo_surface_t *image; | ||
33 | enum background_mode mode; | ||
34 | uint32_t color; | ||
35 | struct wl_list link; | ||
36 | }; | ||
37 | |||
38 | struct swaybg_output { | ||
39 | uint32_t wl_name; | ||
40 | struct wl_output *wl_output; | ||
41 | struct zxdg_output_v1 *xdg_output; | ||
42 | char *name; | ||
43 | char *identifier; | ||
44 | |||
45 | struct swaybg_state *state; | ||
46 | struct swaybg_output_config *config; | ||
47 | |||
48 | struct wl_surface *surface; | ||
49 | struct zwlr_layer_surface_v1 *layer_surface; | ||
50 | struct pool_buffer buffers[2]; | ||
51 | struct pool_buffer *current_buffer; | ||
52 | |||
53 | uint32_t width, height; | ||
54 | int32_t scale; | ||
55 | |||
56 | struct wl_list link; | ||
57 | }; | ||
58 | |||
59 | bool is_valid_color(const char *color) { | ||
60 | int len = strlen(color); | ||
61 | if (len != 7 || color[0] != '#') { | ||
62 | sway_log(SWAY_ERROR, "%s is not a valid color for swaybg. " | ||
63 | "Color should be specified as #rrggbb (no alpha).", color); | ||
64 | return false; | ||
65 | } | ||
66 | |||
67 | int i; | ||
68 | for (i = 1; i < len; ++i) { | ||
69 | if (!isxdigit(color[i])) { | ||
70 | return false; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return true; | ||
75 | } | ||
76 | |||
77 | static void render_frame(struct swaybg_output *output) { | ||
78 | int buffer_width = output->width * output->scale, | ||
79 | buffer_height = output->height * output->scale; | ||
80 | output->current_buffer = get_next_buffer(output->state->shm, | ||
81 | output->buffers, buffer_width, buffer_height); | ||
82 | if (!output->current_buffer) { | ||
83 | return; | ||
84 | } | ||
85 | cairo_t *cairo = output->current_buffer->cairo; | ||
86 | cairo_save(cairo); | ||
87 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | ||
88 | cairo_paint(cairo); | ||
89 | cairo_restore(cairo); | ||
90 | if (output->config->mode == BACKGROUND_MODE_SOLID_COLOR) { | ||
91 | cairo_set_source_u32(cairo, output->config->color); | ||
92 | cairo_paint(cairo); | ||
93 | } else { | ||
94 | if (output->config->color) { | ||
95 | cairo_set_source_u32(cairo, output->config->color); | ||
96 | cairo_paint(cairo); | ||
97 | } | ||
98 | render_background_image(cairo, output->config->image, | ||
99 | output->config->mode, buffer_width, buffer_height); | ||
100 | } | ||
101 | |||
102 | wl_surface_set_buffer_scale(output->surface, output->scale); | ||
103 | wl_surface_attach(output->surface, output->current_buffer->buffer, 0, 0); | ||
104 | wl_surface_damage_buffer(output->surface, 0, 0, INT32_MAX, INT32_MAX); | ||
105 | wl_surface_commit(output->surface); | ||
106 | } | ||
107 | |||
108 | static void destroy_swaybg_output_config(struct swaybg_output_config *config) { | ||
109 | if (!config) { | ||
110 | return; | ||
111 | } | ||
112 | wl_list_remove(&config->link); | ||
113 | free(config->output); | ||
114 | free(config); | ||
115 | } | ||
116 | |||
117 | static void destroy_swaybg_output(struct swaybg_output *output) { | ||
118 | if (!output) { | ||
119 | return; | ||
120 | } | ||
121 | wl_list_remove(&output->link); | ||
122 | if (output->layer_surface != NULL) { | ||
123 | zwlr_layer_surface_v1_destroy(output->layer_surface); | ||
124 | } | ||
125 | if (output->surface != NULL) { | ||
126 | wl_surface_destroy(output->surface); | ||
127 | } | ||
128 | zxdg_output_v1_destroy(output->xdg_output); | ||
129 | wl_output_destroy(output->wl_output); | ||
130 | destroy_buffer(&output->buffers[0]); | ||
131 | destroy_buffer(&output->buffers[1]); | ||
132 | free(output->name); | ||
133 | free(output->identifier); | ||
134 | free(output); | ||
135 | } | ||
136 | |||
137 | static void layer_surface_configure(void *data, | ||
138 | struct zwlr_layer_surface_v1 *surface, | ||
139 | uint32_t serial, uint32_t width, uint32_t height) { | ||
140 | struct swaybg_output *output = data; | ||
141 | output->width = width; | ||
142 | output->height = height; | ||
143 | zwlr_layer_surface_v1_ack_configure(surface, serial); | ||
144 | render_frame(output); | ||
145 | } | ||
146 | |||
147 | static void layer_surface_closed(void *data, | ||
148 | struct zwlr_layer_surface_v1 *surface) { | ||
149 | struct swaybg_output *output = data; | ||
150 | sway_log(SWAY_DEBUG, "Destroying output %s (%s)", | ||
151 | output->name, output->identifier); | ||
152 | destroy_swaybg_output(output); | ||
153 | } | ||
154 | |||
155 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { | ||
156 | .configure = layer_surface_configure, | ||
157 | .closed = layer_surface_closed, | ||
158 | }; | ||
159 | |||
160 | static void output_geometry(void *data, struct wl_output *output, int32_t x, | ||
161 | int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, | ||
162 | const char *make, const char *model, int32_t transform) { | ||
163 | // Who cares | ||
164 | } | ||
165 | |||
166 | static void output_mode(void *data, struct wl_output *output, uint32_t flags, | ||
167 | int32_t width, int32_t height, int32_t refresh) { | ||
168 | // Who cares | ||
169 | } | ||
170 | |||
171 | static void output_done(void *data, struct wl_output *output) { | ||
172 | // Who cares | ||
173 | } | ||
174 | |||
175 | static void output_scale(void *data, struct wl_output *wl_output, | ||
176 | int32_t scale) { | ||
177 | struct swaybg_output *output = data; | ||
178 | output->scale = scale; | ||
179 | if (output->state->run_display && output->width > 0 && output->height > 0) { | ||
180 | render_frame(output); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | static const struct wl_output_listener output_listener = { | ||
185 | .geometry = output_geometry, | ||
186 | .mode = output_mode, | ||
187 | .done = output_done, | ||
188 | .scale = output_scale, | ||
189 | }; | ||
190 | |||
191 | static void xdg_output_handle_logical_position(void *data, | ||
192 | struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { | ||
193 | // Who cares | ||
194 | } | ||
195 | |||
196 | static void xdg_output_handle_logical_size(void *data, | ||
197 | struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { | ||
198 | // Who cares | ||
199 | } | ||
200 | |||
201 | static void find_config(struct swaybg_output *output, const char *name) { | ||
202 | struct swaybg_output_config *config = NULL; | ||
203 | wl_list_for_each(config, &output->state->configs, link) { | ||
204 | if (strcmp(config->output, name) == 0) { | ||
205 | output->config = config; | ||
206 | return; | ||
207 | } else if (!output->config && strcmp(config->output, "*") == 0) { | ||
208 | output->config = config; | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | static void xdg_output_handle_name(void *data, | ||
214 | struct zxdg_output_v1 *xdg_output, const char *name) { | ||
215 | struct swaybg_output *output = data; | ||
216 | output->name = strdup(name); | ||
217 | |||
218 | // If description was sent first, the config may already be populated. If | ||
219 | // there is an identifier config set, keep it. | ||
220 | if (!output->config || strcmp(output->config->output, "*") == 0) { | ||
221 | find_config(output, name); | ||
222 | } | ||
223 | } | ||
224 | |||
225 | static void xdg_output_handle_description(void *data, | ||
226 | struct zxdg_output_v1 *xdg_output, const char *description) { | ||
227 | struct swaybg_output *output = data; | ||
228 | |||
229 | // wlroots currently sets the description to `make model serial (name)` | ||
230 | // If this changes in the future, this will need to be modified. | ||
231 | char *paren = strrchr(description, '('); | ||
232 | if (paren) { | ||
233 | size_t length = paren - description; | ||
234 | output->identifier = malloc(length); | ||
235 | if (!output->identifier) { | ||
236 | sway_log(SWAY_ERROR, "Failed to allocate output identifier"); | ||
237 | return; | ||
238 | } | ||
239 | strncpy(output->identifier, description, length); | ||
240 | output->identifier[length - 1] = '\0'; | ||
241 | |||
242 | find_config(output, output->identifier); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | static void create_layer_surface(struct swaybg_output *output) { | ||
247 | output->surface = wl_compositor_create_surface(output->state->compositor); | ||
248 | assert(output->surface); | ||
249 | |||
250 | // Empty input region | ||
251 | struct wl_region *input_region = | ||
252 | wl_compositor_create_region(output->state->compositor); | ||
253 | assert(input_region); | ||
254 | wl_surface_set_input_region(output->surface, input_region); | ||
255 | wl_region_destroy(input_region); | ||
256 | |||
257 | output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( | ||
258 | output->state->layer_shell, output->surface, output->wl_output, | ||
259 | ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); | ||
260 | assert(output->layer_surface); | ||
261 | |||
262 | zwlr_layer_surface_v1_set_size(output->layer_surface, 0, 0); | ||
263 | zwlr_layer_surface_v1_set_anchor(output->layer_surface, | ||
264 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
265 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
266 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | | ||
267 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); | ||
268 | zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1); | ||
269 | zwlr_layer_surface_v1_add_listener(output->layer_surface, | ||
270 | &layer_surface_listener, output); | ||
271 | wl_surface_commit(output->surface); | ||
272 | } | ||
273 | |||
274 | static void xdg_output_handle_done(void *data, | ||
275 | struct zxdg_output_v1 *xdg_output) { | ||
276 | struct swaybg_output *output = data; | ||
277 | if (!output->config) { | ||
278 | sway_log(SWAY_DEBUG, "Could not find config for output %s (%s)", | ||
279 | output->name, output->identifier); | ||
280 | destroy_swaybg_output(output); | ||
281 | } else if (!output->layer_surface) { | ||
282 | sway_log(SWAY_DEBUG, "Found config %s for output %s (%s)", | ||
283 | output->config->output, output->name, output->identifier); | ||
284 | create_layer_surface(output); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | static const struct zxdg_output_v1_listener xdg_output_listener = { | ||
289 | .logical_position = xdg_output_handle_logical_position, | ||
290 | .logical_size = xdg_output_handle_logical_size, | ||
291 | .name = xdg_output_handle_name, | ||
292 | .description = xdg_output_handle_description, | ||
293 | .done = xdg_output_handle_done, | ||
294 | }; | ||
295 | |||
296 | static void handle_global(void *data, struct wl_registry *registry, | ||
297 | uint32_t name, const char *interface, uint32_t version) { | ||
298 | struct swaybg_state *state = data; | ||
299 | if (strcmp(interface, wl_compositor_interface.name) == 0) { | ||
300 | state->compositor = | ||
301 | wl_registry_bind(registry, name, &wl_compositor_interface, 4); | ||
302 | } else if (strcmp(interface, wl_shm_interface.name) == 0) { | ||
303 | state->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); | ||
304 | } else if (strcmp(interface, wl_output_interface.name) == 0) { | ||
305 | struct swaybg_output *output = calloc(1, sizeof(struct swaybg_output)); | ||
306 | output->state = state; | ||
307 | output->wl_name = name; | ||
308 | output->wl_output = | ||
309 | wl_registry_bind(registry, name, &wl_output_interface, 3); | ||
310 | wl_output_add_listener(output->wl_output, &output_listener, output); | ||
311 | wl_list_insert(&state->outputs, &output->link); | ||
312 | |||
313 | if (state->run_display) { | ||
314 | output->xdg_output = zxdg_output_manager_v1_get_xdg_output( | ||
315 | state->xdg_output_manager, output->wl_output); | ||
316 | zxdg_output_v1_add_listener(output->xdg_output, | ||
317 | &xdg_output_listener, output); | ||
318 | } | ||
319 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { | ||
320 | state->layer_shell = | ||
321 | wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); | ||
322 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { | ||
323 | state->xdg_output_manager = wl_registry_bind(registry, name, | ||
324 | &zxdg_output_manager_v1_interface, 2); | ||
325 | } | ||
326 | } | ||
327 | |||
328 | static void handle_global_remove(void *data, struct wl_registry *registry, | ||
329 | uint32_t name) { | ||
330 | struct swaybg_state *state = data; | ||
331 | struct swaybg_output *output, *tmp; | ||
332 | wl_list_for_each_safe(output, tmp, &state->outputs, link) { | ||
333 | if (output->wl_name == name) { | ||
334 | sway_log(SWAY_DEBUG, "Destroying output %s (%s)", | ||
335 | output->name, output->identifier); | ||
336 | destroy_swaybg_output(output); | ||
337 | break; | ||
338 | } | ||
339 | } | ||
340 | } | ||
341 | |||
342 | static const struct wl_registry_listener registry_listener = { | ||
343 | .global = handle_global, | ||
344 | .global_remove = handle_global_remove, | ||
345 | }; | ||
346 | |||
347 | static bool store_swaybg_output_config(struct swaybg_state *state, | ||
348 | struct swaybg_output_config *config) { | ||
349 | struct swaybg_output_config *oc = NULL; | ||
350 | wl_list_for_each(oc, &state->configs, link) { | ||
351 | if (strcmp(config->output, oc->output) == 0) { | ||
352 | // Merge on top | ||
353 | if (config->image) { | ||
354 | free(oc->image); | ||
355 | oc->image = config->image; | ||
356 | config->image = NULL; | ||
357 | } | ||
358 | if (config->color) { | ||
359 | oc->color = config->color; | ||
360 | } | ||
361 | if (config->mode != BACKGROUND_MODE_INVALID) { | ||
362 | oc->mode = config->mode; | ||
363 | } | ||
364 | return false; | ||
365 | } | ||
366 | } | ||
367 | // New config, just add it | ||
368 | wl_list_insert(&state->configs, &config->link); | ||
369 | return true; | ||
370 | } | ||
371 | |||
372 | static void parse_command_line(int argc, char **argv, | ||
373 | struct swaybg_state *state) { | ||
374 | static struct option long_options[] = { | ||
375 | {"color", required_argument, NULL, 'c'}, | ||
376 | {"help", no_argument, NULL, 'h'}, | ||
377 | {"image", required_argument, NULL, 'i'}, | ||
378 | {"mode", required_argument, NULL, 'm'}, | ||
379 | {"output", required_argument, NULL, 'o'}, | ||
380 | {"version", no_argument, NULL, 'v'}, | ||
381 | {0, 0, 0, 0} | ||
382 | }; | ||
383 | |||
384 | const char *usage = | ||
385 | "Usage: swaybg <options...>\n" | ||
386 | "\n" | ||
387 | " -c, --color Set the background color.\n" | ||
388 | " -h, --help Show help message and quit.\n" | ||
389 | " -i, --image Set the image to display.\n" | ||
390 | " -m, --mode Set the mode to use for the image.\n" | ||
391 | " -o, --output Set the output to operate on or * for all.\n" | ||
392 | " -v, --version Show the version number and quit.\n" | ||
393 | "\n" | ||
394 | "Background Modes:\n" | ||
395 | " stretch, fit, fill, center, tile, or solid_color\n"; | ||
396 | |||
397 | struct swaybg_output_config *config = NULL; | ||
398 | |||
399 | int c; | ||
400 | while (1) { | ||
401 | int option_index = 0; | ||
402 | c = getopt_long(argc, argv, "c:hi:m:o:v", long_options, &option_index); | ||
403 | if (c == -1) { | ||
404 | break; | ||
405 | } | ||
406 | switch (c) { | ||
407 | case 'c': // color | ||
408 | if (!config) { | ||
409 | goto no_output; | ||
410 | } | ||
411 | if (!is_valid_color(optarg)) { | ||
412 | sway_log(SWAY_ERROR, "Invalid color: %s", optarg); | ||
413 | continue; | ||
414 | } | ||
415 | config->color = parse_color(optarg); | ||
416 | break; | ||
417 | case 'i': // image | ||
418 | if (!config) { | ||
419 | goto no_output; | ||
420 | } | ||
421 | free(config->image); | ||
422 | config->image = load_background_image(optarg); | ||
423 | if (!config->image) { | ||
424 | sway_log(SWAY_ERROR, "Failed to load image: %s", optarg); | ||
425 | } | ||
426 | break; | ||
427 | case 'm': // mode | ||
428 | if (!config) { | ||
429 | goto no_output; | ||
430 | } | ||
431 | config->mode = parse_background_mode(optarg); | ||
432 | if (config->mode == BACKGROUND_MODE_INVALID) { | ||
433 | sway_log(SWAY_ERROR, "Invalid mode: %s", optarg); | ||
434 | } | ||
435 | break; | ||
436 | case 'o': // output | ||
437 | if (config && !store_swaybg_output_config(state, config)) { | ||
438 | // Empty config or merged on top of an existing one | ||
439 | destroy_swaybg_output_config(config); | ||
440 | } | ||
441 | config = calloc(sizeof(struct swaybg_output_config), 1); | ||
442 | config->output = strdup(optarg); | ||
443 | config->mode = BACKGROUND_MODE_INVALID; | ||
444 | wl_list_init(&config->link); // init for safe removal | ||
445 | break; | ||
446 | case 'v': // version | ||
447 | fprintf(stdout, "swaybg version " SWAY_VERSION "\n"); | ||
448 | exit(EXIT_SUCCESS); | ||
449 | break; | ||
450 | default: | ||
451 | fprintf(c == 'h' ? stdout : stderr, "%s", usage); | ||
452 | exit(c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE); | ||
453 | } | ||
454 | } | ||
455 | if (config && !store_swaybg_output_config(state, config)) { | ||
456 | // Empty config or merged on top of an existing one | ||
457 | destroy_swaybg_output_config(config); | ||
458 | } | ||
459 | |||
460 | // Check for invalid options | ||
461 | if (optind < argc) { | ||
462 | config = NULL; | ||
463 | struct swaybg_output_config *tmp = NULL; | ||
464 | wl_list_for_each_safe(config, tmp, &state->configs, link) { | ||
465 | destroy_swaybg_output_config(config); | ||
466 | } | ||
467 | // continue into empty list | ||
468 | } | ||
469 | if (wl_list_empty(&state->configs)) { | ||
470 | fprintf(stderr, "%s", usage); | ||
471 | exit(EXIT_FAILURE); | ||
472 | } | ||
473 | |||
474 | // Set default mode and remove empties | ||
475 | config = NULL; | ||
476 | struct swaybg_output_config *tmp = NULL; | ||
477 | wl_list_for_each_safe(config, tmp, &state->configs, link) { | ||
478 | if (!config->image && !config->color) { | ||
479 | destroy_swaybg_output_config(config); | ||
480 | } else if (config->mode == BACKGROUND_MODE_INVALID) { | ||
481 | config->mode = config->image | ||
482 | ? BACKGROUND_MODE_STRETCH | ||
483 | : BACKGROUND_MODE_SOLID_COLOR; | ||
484 | } | ||
485 | } | ||
486 | return; | ||
487 | no_output: | ||
488 | fprintf(stderr, "Cannot operate on NULL output config\n"); | ||
489 | exit(EXIT_FAILURE); | ||
490 | } | ||
491 | |||
492 | int main(int argc, char **argv) { | ||
493 | sway_log_init(SWAY_DEBUG, NULL); | ||
494 | |||
495 | struct swaybg_state state = {0}; | ||
496 | wl_list_init(&state.configs); | ||
497 | wl_list_init(&state.outputs); | ||
498 | |||
499 | parse_command_line(argc, argv, &state); | ||
500 | |||
501 | state.display = wl_display_connect(NULL); | ||
502 | if (!state.display) { | ||
503 | sway_log(SWAY_ERROR, "Unable to connect to the compositor. " | ||
504 | "If your compositor is running, check or set the " | ||
505 | "WAYLAND_DISPLAY environment variable."); | ||
506 | return 1; | ||
507 | } | ||
508 | |||
509 | struct wl_registry *registry = wl_display_get_registry(state.display); | ||
510 | wl_registry_add_listener(registry, ®istry_listener, &state); | ||
511 | wl_display_roundtrip(state.display); | ||
512 | if (state.compositor == NULL || state.shm == NULL || | ||
513 | state.layer_shell == NULL || state.xdg_output_manager == NULL) { | ||
514 | sway_log(SWAY_ERROR, "Missing a required Wayland interface"); | ||
515 | return 1; | ||
516 | } | ||
517 | |||
518 | struct swaybg_output *output; | ||
519 | wl_list_for_each(output, &state.outputs, link) { | ||
520 | output->xdg_output = zxdg_output_manager_v1_get_xdg_output( | ||
521 | state.xdg_output_manager, output->wl_output); | ||
522 | zxdg_output_v1_add_listener(output->xdg_output, | ||
523 | &xdg_output_listener, output); | ||
524 | } | ||
525 | |||
526 | state.run_display = true; | ||
527 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { | ||
528 | // This space intentionally left blank | ||
529 | } | ||
530 | |||
531 | struct swaybg_output *tmp_output; | ||
532 | wl_list_for_each_safe(output, tmp_output, &state.outputs, link) { | ||
533 | destroy_swaybg_output(output); | ||
534 | } | ||
535 | |||
536 | struct swaybg_output_config *config = NULL, *tmp_config = NULL; | ||
537 | wl_list_for_each_safe(config, tmp_config, &state.configs, link) { | ||
538 | destroy_swaybg_output_config(config); | ||
539 | } | ||
540 | |||
541 | return 0; | ||
542 | } | ||