diff options
author | Brian Ashworth <bosrsf04@gmail.com> | 2019-04-03 21:53:43 -0400 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2019-04-04 20:08:21 +0300 |
commit | 75e7bd24ccb9731065bb7f8313aa53ba11ddc420 (patch) | |
tree | 2522f31626a21b42c2496a32266eafd3b7d66f50 /swaybg | |
parent | idle_inhibit: fix crash during view destruction (diff) | |
download | sway-75e7bd24ccb9731065bb7f8313aa53ba11ddc420.tar.gz sway-75e7bd24ccb9731065bb7f8313aa53ba11ddc420.tar.zst sway-75e7bd24ccb9731065bb7f8313aa53ba11ddc420.zip |
swaybg: one instance for all outputs
This makes it so there will only be one swaybg instance running
instead of one per output. swaybg's cli has been changed to a xrandr
like interface, where you select an output and then change properties
for that output and then select another output and repeat. This also
makes it so swaybg is only killed and respawned when a background
changes or when reloading.
Diffstat (limited to 'swaybg')
-rw-r--r-- | swaybg/main.c | 452 |
1 files changed, 325 insertions, 127 deletions
diff --git a/swaybg/main.c b/swaybg/main.c index e66221f0..b983dd6a 100644 --- a/swaybg/main.c +++ b/swaybg/main.c | |||
@@ -1,10 +1,12 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
1 | #include <assert.h> | 2 | #include <assert.h> |
2 | #include <ctype.h> | 3 | #include <ctype.h> |
4 | #include <getopt.h> | ||
3 | #include <stdbool.h> | 5 | #include <stdbool.h> |
4 | #include <stdio.h> | 6 | #include <stdio.h> |
5 | #include <stdlib.h> | 7 | #include <stdlib.h> |
6 | #include <string.h> | 8 | #include <string.h> |
7 | #include <time.h> | 9 | #include <strings.h> |
8 | #include <wayland-client.h> | 10 | #include <wayland-client.h> |
9 | #include "background-image.h" | 11 | #include "background-image.h" |
10 | #include "cairo.h" | 12 | #include "cairo.h" |
@@ -14,49 +16,44 @@ | |||
14 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 16 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
15 | #include "xdg-output-unstable-v1-client-protocol.h" | 17 | #include "xdg-output-unstable-v1-client-protocol.h" |
16 | 18 | ||
17 | struct swaybg_state; | 19 | struct swaybg_state { |
18 | 20 | struct wl_display *display; | |
19 | struct swaybg_args { | 21 | struct wl_compositor *compositor; |
20 | const char *output; | 22 | struct wl_shm *shm; |
21 | const char *path; | 23 | struct zwlr_layer_shell_v1 *layer_shell; |
22 | enum background_mode mode; | 24 | struct zxdg_output_manager_v1 *xdg_output_manager; |
23 | const char *fallback; | 25 | struct wl_list configs; // struct swaybg_output_config::link |
26 | struct wl_list outputs; // struct swaybg_output::link | ||
27 | bool run_display; | ||
24 | }; | 28 | }; |
25 | 29 | ||
26 | struct swaybg_context { | 30 | struct swaybg_output_config { |
27 | uint32_t color; | 31 | char *output; |
28 | cairo_surface_t *image; | 32 | cairo_surface_t *image; |
33 | enum background_mode mode; | ||
34 | uint32_t color; | ||
35 | struct wl_list link; | ||
29 | }; | 36 | }; |
30 | 37 | ||
31 | struct swaybg_output { | 38 | struct swaybg_output { |
39 | uint32_t wl_name; | ||
32 | struct wl_output *wl_output; | 40 | struct wl_output *wl_output; |
33 | struct zxdg_output_v1 *xdg_output; | 41 | struct zxdg_output_v1 *xdg_output; |
34 | struct swaybg_state *state; | 42 | char *name; |
35 | struct wl_list link; | 43 | char *identifier; |
36 | 44 | ||
37 | int32_t scale; | 45 | struct swaybg_state *state; |
38 | }; | 46 | struct swaybg_output_config *config; |
39 | |||
40 | struct swaybg_state { | ||
41 | const struct swaybg_args *args; | ||
42 | struct swaybg_context context; | ||
43 | |||
44 | struct wl_display *display; | ||
45 | struct wl_compositor *compositor; | ||
46 | struct wl_shm *shm; | ||
47 | struct wl_list outputs; | ||
48 | struct zwlr_layer_shell_v1 *layer_shell; | ||
49 | struct zxdg_output_manager_v1 *xdg_output_manager; | ||
50 | 47 | ||
51 | struct swaybg_output *output; | ||
52 | struct wl_surface *surface; | 48 | struct wl_surface *surface; |
53 | struct wl_region *input_region; | ||
54 | struct zwlr_layer_surface_v1 *layer_surface; | 49 | struct zwlr_layer_surface_v1 *layer_surface; |
55 | |||
56 | bool run_display; | ||
57 | uint32_t width, height; | ||
58 | struct pool_buffer buffers[2]; | 50 | struct pool_buffer buffers[2]; |
59 | struct pool_buffer *current_buffer; | 51 | struct pool_buffer *current_buffer; |
52 | |||
53 | uint32_t width, height; | ||
54 | int32_t scale; | ||
55 | |||
56 | struct wl_list link; | ||
60 | }; | 57 | }; |
61 | 58 | ||
62 | bool is_valid_color(const char *color) { | 59 | bool is_valid_color(const char *color) { |
@@ -77,68 +74,82 @@ bool is_valid_color(const char *color) { | |||
77 | return true; | 74 | return true; |
78 | } | 75 | } |
79 | 76 | ||
80 | static void render_frame(struct swaybg_state *state) { | 77 | static void render_frame(struct swaybg_output *output) { |
81 | int buffer_width = state->width * state->output->scale, | 78 | int buffer_width = output->width * output->scale, |
82 | buffer_height = state->height * state->output->scale; | 79 | buffer_height = output->height * output->scale; |
83 | state->current_buffer = get_next_buffer(state->shm, | 80 | output->current_buffer = get_next_buffer(output->state->shm, |
84 | state->buffers, buffer_width, buffer_height); | 81 | output->buffers, buffer_width, buffer_height); |
85 | if (!state->current_buffer) { | 82 | if (!output->current_buffer) { |
86 | return; | 83 | return; |
87 | } | 84 | } |
88 | cairo_t *cairo = state->current_buffer->cairo; | 85 | cairo_t *cairo = output->current_buffer->cairo; |
89 | cairo_save(cairo); | 86 | cairo_save(cairo); |
90 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | 87 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); |
91 | cairo_paint(cairo); | 88 | cairo_paint(cairo); |
92 | cairo_restore(cairo); | 89 | cairo_restore(cairo); |
93 | if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { | 90 | if (output->config->mode == BACKGROUND_MODE_SOLID_COLOR) { |
94 | cairo_set_source_u32(cairo, state->context.color); | 91 | cairo_set_source_u32(cairo, output->config->color); |
95 | cairo_paint(cairo); | 92 | cairo_paint(cairo); |
96 | } else { | 93 | } else { |
97 | if (state->args->fallback && state->context.color) { | 94 | if (output->config->color) { |
98 | cairo_set_source_u32(cairo, state->context.color); | 95 | cairo_set_source_u32(cairo, output->config->color); |
99 | cairo_paint(cairo); | 96 | cairo_paint(cairo); |
100 | } | 97 | } |
101 | render_background_image(cairo, state->context.image, | 98 | render_background_image(cairo, output->config->image, |
102 | state->args->mode, buffer_width, buffer_height); | 99 | output->config->mode, buffer_width, buffer_height); |
103 | } | 100 | } |
104 | 101 | ||
105 | wl_surface_set_buffer_scale(state->surface, state->output->scale); | 102 | wl_surface_set_buffer_scale(output->surface, output->scale); |
106 | wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0); | 103 | wl_surface_attach(output->surface, output->current_buffer->buffer, 0, 0); |
107 | wl_surface_damage_buffer(state->surface, 0, 0, INT32_MAX, INT32_MAX); | 104 | wl_surface_damage_buffer(output->surface, 0, 0, INT32_MAX, INT32_MAX); |
108 | wl_surface_commit(state->surface); | 105 | wl_surface_commit(output->surface); |
109 | } | 106 | } |
110 | 107 | ||
111 | static bool prepare_context(struct swaybg_state *state) { | 108 | static void destroy_swaybg_output_config(struct swaybg_output_config *config) { |
112 | if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { | 109 | if (!config) { |
113 | state->context.color = parse_color(state->args->path); | 110 | return; |
114 | return is_valid_color(state->args->path); | ||
115 | } | 111 | } |
116 | if (state->args->fallback && is_valid_color(state->args->fallback)) { | 112 | wl_list_remove(&config->link); |
117 | state->context.color = parse_color(state->args->fallback); | 113 | free(config->output); |
114 | free(config); | ||
115 | } | ||
116 | |||
117 | static void destroy_swaybg_output(struct swaybg_output *output) { | ||
118 | if (!output) { | ||
119 | return; | ||
118 | } | 120 | } |
119 | if (!(state->context.image = load_background_image(state->args->path))) { | 121 | wl_list_remove(&output->link); |
120 | return false; | 122 | if (output->layer_surface != NULL) { |
123 | zwlr_layer_surface_v1_destroy(output->layer_surface); | ||
121 | } | 124 | } |
122 | return true; | 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); | ||
123 | } | 135 | } |
124 | 136 | ||
125 | static void layer_surface_configure(void *data, | 137 | static void layer_surface_configure(void *data, |
126 | struct zwlr_layer_surface_v1 *surface, | 138 | struct zwlr_layer_surface_v1 *surface, |
127 | uint32_t serial, uint32_t width, uint32_t height) { | 139 | uint32_t serial, uint32_t width, uint32_t height) { |
128 | struct swaybg_state *state = data; | 140 | struct swaybg_output *output = data; |
129 | state->width = width; | 141 | output->width = width; |
130 | state->height = height; | 142 | output->height = height; |
131 | zwlr_layer_surface_v1_ack_configure(surface, serial); | 143 | zwlr_layer_surface_v1_ack_configure(surface, serial); |
132 | render_frame(state); | 144 | render_frame(output); |
133 | } | 145 | } |
134 | 146 | ||
135 | static void layer_surface_closed(void *data, | 147 | static void layer_surface_closed(void *data, |
136 | struct zwlr_layer_surface_v1 *surface) { | 148 | struct zwlr_layer_surface_v1 *surface) { |
137 | struct swaybg_state *state = data; | 149 | struct swaybg_output *output = data; |
138 | zwlr_layer_surface_v1_destroy(state->layer_surface); | 150 | sway_log(SWAY_DEBUG, "Destroying output %s (%s)", |
139 | wl_surface_destroy(state->surface); | 151 | output->name, output->identifier); |
140 | wl_region_destroy(state->input_region); | 152 | destroy_swaybg_output(output); |
141 | state->run_display = false; | ||
142 | } | 153 | } |
143 | 154 | ||
144 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { | 155 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { |
@@ -164,12 +175,9 @@ static void output_done(void *data, struct wl_output *output) { | |||
164 | static void output_scale(void *data, struct wl_output *wl_output, | 175 | static void output_scale(void *data, struct wl_output *wl_output, |
165 | int32_t scale) { | 176 | int32_t scale) { |
166 | struct swaybg_output *output = data; | 177 | struct swaybg_output *output = data; |
167 | struct swaybg_state *state = output->state; | ||
168 | |||
169 | output->scale = scale; | 178 | output->scale = scale; |
170 | 179 | if (output->state->run_display && output->width > 0 && output->height > 0) { | |
171 | if (state->output == output && state->run_display) { | 180 | render_frame(output); |
172 | render_frame(state); | ||
173 | } | 181 | } |
174 | } | 182 | } |
175 | 183 | ||
@@ -190,24 +198,91 @@ static void xdg_output_handle_logical_size(void *data, | |||
190 | // Who cares | 198 | // Who cares |
191 | } | 199 | } |
192 | 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 | |||
193 | static void xdg_output_handle_name(void *data, | 213 | static void xdg_output_handle_name(void *data, |
194 | struct zxdg_output_v1 *xdg_output, const char *name) { | 214 | struct zxdg_output_v1 *xdg_output, const char *name) { |
195 | struct swaybg_output *output = data; | 215 | struct swaybg_output *output = data; |
196 | struct swaybg_state *state = output->state; | 216 | output->name = strdup(name); |
197 | if (strcmp(name, state->args->output) == 0) { | 217 | |
198 | assert(state->output == NULL); | 218 | // If description was sent first, the config may already be populated. If |
199 | state->output = output; | 219 | // there is an identifier config set, keep it. |
220 | if (!output->config || strcmp(output->config->output, "*") == 0) { | ||
221 | find_config(output, name); | ||
200 | } | 222 | } |
201 | } | 223 | } |
202 | 224 | ||
203 | static void xdg_output_handle_description(void *data, | 225 | static void xdg_output_handle_description(void *data, |
204 | struct zxdg_output_v1 *xdg_output, const char *description) { | 226 | struct zxdg_output_v1 *xdg_output, const char *description) { |
205 | // Who cares | 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); | ||
206 | } | 272 | } |
207 | 273 | ||
208 | static void xdg_output_handle_done(void *data, | 274 | static void xdg_output_handle_done(void *data, |
209 | struct zxdg_output_v1 *xdg_output) { | 275 | struct zxdg_output_v1 *xdg_output) { |
210 | // Who cares | 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 | } | ||
211 | } | 286 | } |
212 | 287 | ||
213 | static const struct zxdg_output_v1_listener xdg_output_listener = { | 288 | static const struct zxdg_output_v1_listener xdg_output_listener = { |
@@ -229,10 +304,18 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
229 | } else if (strcmp(interface, wl_output_interface.name) == 0) { | 304 | } else if (strcmp(interface, wl_output_interface.name) == 0) { |
230 | struct swaybg_output *output = calloc(1, sizeof(struct swaybg_output)); | 305 | struct swaybg_output *output = calloc(1, sizeof(struct swaybg_output)); |
231 | output->state = state; | 306 | output->state = state; |
307 | output->wl_name = name; | ||
232 | output->wl_output = | 308 | output->wl_output = |
233 | wl_registry_bind(registry, name, &wl_output_interface, 3); | 309 | wl_registry_bind(registry, name, &wl_output_interface, 3); |
234 | wl_output_add_listener(output->wl_output, &output_listener, output); | 310 | wl_output_add_listener(output->wl_output, &output_listener, output); |
235 | wl_list_insert(&state->outputs, &output->link); | 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 | } | ||
236 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { | 319 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { |
237 | state->layer_shell = | 320 | state->layer_shell = |
238 | wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); | 321 | wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); |
@@ -244,7 +327,16 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
244 | 327 | ||
245 | static void handle_global_remove(void *data, struct wl_registry *registry, | 328 | static void handle_global_remove(void *data, struct wl_registry *registry, |
246 | uint32_t name) { | 329 | uint32_t name) { |
247 | // who cares | 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 | } | ||
248 | } | 340 | } |
249 | 341 | ||
250 | static const struct wl_registry_listener registry_listener = { | 342 | static const struct wl_registry_listener registry_listener = { |
@@ -252,32 +344,159 @@ static const struct wl_registry_listener registry_listener = { | |||
252 | .global_remove = handle_global_remove, | 344 | .global_remove = handle_global_remove, |
253 | }; | 345 | }; |
254 | 346 | ||
255 | int main(int argc, const char **argv) { | 347 | static bool store_swaybg_output_config(struct swaybg_state *state, |
256 | sway_log_init(SWAY_DEBUG, NULL); | 348 | struct swaybg_output_config *config) { |
257 | 349 | struct swaybg_output_config *oc = NULL; | |
258 | struct swaybg_args args = {0}; | 350 | wl_list_for_each(oc, &state->configs, link) { |
259 | struct swaybg_state state = { .args = &args }; | 351 | if (strcmp(config->output, oc->output) == 0) { |
260 | wl_list_init(&state.outputs); | 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 | } | ||
261 | 371 | ||
262 | if (argc < 4 || argc > 5) { | 372 | static void parse_command_line(int argc, char **argv, |
263 | sway_log(SWAY_ERROR, "Do not run this program manually. " | 373 | struct swaybg_state *state) { |
264 | "See `man 5 sway-output` and look for background options."); | 374 | static struct option long_options[] = { |
265 | return 1; | 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); | ||
266 | } | 458 | } |
267 | 459 | ||
268 | args.output = argv[1]; | 460 | // Check for invalid options |
269 | args.path = argv[2]; | 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 | } | ||
270 | 473 | ||
271 | args.mode = parse_background_mode(argv[3]); | 474 | // Set default mode and remove empties |
272 | if (args.mode == BACKGROUND_MODE_INVALID) { | 475 | config = NULL; |
273 | return 1; | 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 | } | ||
274 | } | 485 | } |
486 | return; | ||
487 | no_output: | ||
488 | fprintf(stderr, "Cannot operate on NULL output config\n"); | ||
489 | exit(EXIT_FAILURE); | ||
490 | } | ||
275 | 491 | ||
276 | args.fallback = argc == 5 ? argv[4] : NULL; | 492 | int main(int argc, char **argv) { |
493 | sway_log_init(SWAY_DEBUG, NULL); | ||
277 | 494 | ||
278 | if (!prepare_context(&state)) { | 495 | struct swaybg_state state = {0}; |
279 | return 1; | 496 | wl_list_init(&state.configs); |
280 | } | 497 | wl_list_init(&state.outputs); |
498 | |||
499 | parse_command_line(argc, argv, &state); | ||
281 | 500 | ||
282 | state.display = wl_display_connect(NULL); | 501 | state.display = wl_display_connect(NULL); |
283 | if (!state.display) { | 502 | if (!state.display) { |
@@ -303,42 +522,21 @@ int main(int argc, const char **argv) { | |||
303 | zxdg_output_v1_add_listener(output->xdg_output, | 522 | zxdg_output_v1_add_listener(output->xdg_output, |
304 | &xdg_output_listener, output); | 523 | &xdg_output_listener, output); |
305 | } | 524 | } |
306 | // Second roundtrip to get xdg_output properties | ||
307 | wl_display_roundtrip(state.display); | ||
308 | if (state.output == NULL) { | ||
309 | sway_log(SWAY_ERROR, "Cannot find output '%s'", args.output); | ||
310 | return 1; | ||
311 | } | ||
312 | |||
313 | state.surface = wl_compositor_create_surface(state.compositor); | ||
314 | assert(state.surface); | ||
315 | |||
316 | // Empty input region | ||
317 | state.input_region = wl_compositor_create_region(state.compositor); | ||
318 | assert(state.input_region); | ||
319 | wl_surface_set_input_region(state.surface, state.input_region); | ||
320 | |||
321 | state.layer_surface = zwlr_layer_shell_v1_get_layer_surface( | ||
322 | state.layer_shell, state.surface, state.output->wl_output, | ||
323 | ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); | ||
324 | assert(state.layer_surface); | ||
325 | |||
326 | zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0); | ||
327 | zwlr_layer_surface_v1_set_anchor(state.layer_surface, | ||
328 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
329 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
330 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | | ||
331 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); | ||
332 | zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1); | ||
333 | zwlr_layer_surface_v1_add_listener(state.layer_surface, | ||
334 | &layer_surface_listener, &state); | ||
335 | wl_surface_commit(state.surface); | ||
336 | wl_display_roundtrip(state.display); | ||
337 | 525 | ||
338 | state.run_display = true; | 526 | state.run_display = true; |
339 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { | 527 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { |
340 | // This space intentionally left blank | 528 | // This space intentionally left blank |
341 | } | 529 | } |
342 | 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 | |||
343 | return 0; | 541 | return 0; |
344 | } | 542 | } |