diff --git a/sway/config/output.c b/sway/config/output.c index f24e7d66..2cbfa3cd 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -229,28 +229,29 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) { terminate_swaybg(output->bg_pid); } if (oc && oc->background && config->swaybg_command) { - wlr_log(WLR_DEBUG, "Setting background for output %d to %s", - output_i, oc->background); + wlr_log(WLR_DEBUG, "Setting background for output %s to %s", + wlr_output->name, oc->background); - size_t len = snprintf(NULL, 0, "%s %d \"%s\" %s %s", - config->swaybg_command, output_i, oc->background, - oc->background_option, - oc->background_fallback ? oc->background_fallback : ""); + size_t len = snprintf(NULL, 0, "%s \"%s\" \"%s\" %s %s", + config->swaybg_command, wlr_output->name, oc->background, + oc->background_option, + oc->background_fallback ? oc->background_fallback : ""); char *command = malloc(len + 1); if (!command) { wlr_log(WLR_DEBUG, "Unable to allocate swaybg command"); return; } - snprintf(command, len + 1, "%s %d \"%s\" %s %s", - config->swaybg_command, output_i, oc->background, - oc->background_option, - oc->background_fallback ? oc->background_fallback : ""); + snprintf(command, len + 1, "%s \"%s\" \"%s\" %s %s", + config->swaybg_command, wlr_output->name, oc->background, + oc->background_option, + oc->background_fallback ? oc->background_fallback : ""); wlr_log(WLR_DEBUG, "-> %s", command); char *const cmd[] = { "sh", "-c", command, NULL }; output->bg_pid = fork(); if (output->bg_pid == 0) { execvp(cmd[0], cmd); + wlr_log_errno(WLR_ERROR, "Failed to execute swaybg"); } else { free(command); } diff --git a/swaybg/main.c b/swaybg/main.c index 45b7a913..e3ef3d7c 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -8,13 +8,16 @@ #include #include #include "background-image.h" -#include "pool-buffer.h" #include "cairo.h" +#include "pool-buffer.h" #include "util.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" + +struct swaybg_state; struct swaybg_args { - int output_idx; + const char *output; const char *path; enum background_mode mode; const char *fallback; @@ -25,23 +28,33 @@ struct swaybg_context { cairo_surface_t *image; }; +struct swaybg_output { + struct wl_output *wl_output; + struct zxdg_output_v1 *xdg_output; + struct swaybg_state *state; + struct wl_list link; + + int32_t scale; +}; + struct swaybg_state { const struct swaybg_args *args; struct swaybg_context context; struct wl_display *display; struct wl_compositor *compositor; - struct zwlr_layer_shell_v1 *layer_shell; struct wl_shm *shm; + struct wl_list outputs; + struct zwlr_layer_shell_v1 *layer_shell; + struct zxdg_output_manager_v1 *xdg_output_manager; - struct wl_output *output; + struct swaybg_output *output; struct wl_surface *surface; struct wl_region *input_region; struct zwlr_layer_surface_v1 *layer_surface; bool run_display; uint32_t width, height; - int32_t scale; struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; }; @@ -65,8 +78,8 @@ bool is_valid_color(const char *color) { } static void render_frame(struct swaybg_state *state) { - int buffer_width = state->width * state->scale, - buffer_height = state->height * state->scale; + int buffer_width = state->width * state->output->scale, + buffer_height = state->height * state->output->scale; state->current_buffer = get_next_buffer(state->shm, state->buffers, buffer_width, buffer_height); if (!state->current_buffer) { @@ -89,7 +102,7 @@ static void render_frame(struct swaybg_state *state) { state->args->mode, buffer_width, buffer_height); } - wl_surface_set_buffer_scale(state->surface, state->scale); + wl_surface_set_buffer_scale(state->surface, state->output->scale); wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0); wl_surface_damage(state->surface, 0, 0, state->width, state->height); wl_surface_commit(state->surface); @@ -148,10 +161,14 @@ static void output_done(void *data, struct wl_output *output) { // Who cares } -static void output_scale(void *data, struct wl_output *output, int32_t factor) { - struct swaybg_state *state = data; - state->scale = factor; - if (state->run_display) { +static void output_scale(void *data, struct wl_output *wl_output, + int32_t scale) { + struct swaybg_output *output = data; + struct swaybg_state *state = output->state; + + output->scale = scale; + + if (state->output == output && state->run_display) { render_frame(state); } } @@ -163,26 +180,65 @@ static const struct wl_output_listener output_listener = { .scale = output_scale, }; +static void xdg_output_handle_logical_position(void *data, + struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { + // Who cares +} + +static void xdg_output_handle_logical_size(void *data, + struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { + // Who cares +} + +static void xdg_output_handle_name(void *data, + struct zxdg_output_v1 *xdg_output, const char *name) { + struct swaybg_output *output = data; + struct swaybg_state *state = output->state; + if (strcmp(name, state->args->output) == 0) { + assert(state->output == NULL); + state->output = output; + } +} + +static void xdg_output_handle_description(void *data, + struct zxdg_output_v1 *xdg_output, const char *description) { + // Who cares +} + +static void xdg_output_handle_done(void *data, + struct zxdg_output_v1 *xdg_output) { + // Who cares +} + +static const struct zxdg_output_v1_listener xdg_output_listener = { + .logical_position = xdg_output_handle_logical_position, + .logical_size = xdg_output_handle_logical_size, + .name = xdg_output_handle_name, + .description = xdg_output_handle_description, + .done = xdg_output_handle_done, +}; + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct swaybg_state *state = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { - state->compositor = wl_registry_bind(registry, name, - &wl_compositor_interface, 3); + state->compositor = + wl_registry_bind(registry, name, &wl_compositor_interface, 3); } else if (strcmp(interface, wl_shm_interface.name) == 0) { - state->shm = wl_registry_bind(registry, name, - &wl_shm_interface, 1); + state->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, wl_output_interface.name) == 0) { - static int output_idx = 0; - if (output_idx == state->args->output_idx) { - state->output = wl_registry_bind(registry, name, - &wl_output_interface, 3); - wl_output_add_listener(state->output, &output_listener, state); - } - output_idx++; + struct swaybg_output *output = calloc(1, sizeof(struct swaybg_output)); + output->state = state; + output->wl_output = + wl_registry_bind(registry, name, &wl_output_interface, 3); + wl_output_add_listener(output->wl_output, &output_listener, output); + wl_list_insert(&state->outputs, &output->link); } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { - state->layer_shell = wl_registry_bind( - registry, name, &zwlr_layer_shell_v1_interface, 1); + state->layer_shell = + wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { + state->xdg_output_manager = wl_registry_bind(registry, name, + &zxdg_output_manager_v1_interface, 2); } } @@ -197,17 +253,19 @@ static const struct wl_registry_listener registry_listener = { }; int main(int argc, const char **argv) { - struct swaybg_args args = {0}; - struct swaybg_state state = {0}; - state.args = &args; wlr_log_init(WLR_DEBUG, NULL); + struct swaybg_args args = {0}; + struct swaybg_state state = { .args = &args }; + wl_list_init(&state.outputs); + if (argc < 4 || argc > 5) { wlr_log(WLR_ERROR, "Do not run this program manually. " - "See man 5 sway and look for output options."); + "See `man 5 sway-output` and look for background options."); return 1; } - args.output_idx = atoi(argv[1]); + + args.output = argv[1]; args.path = argv[2]; args.mode = parse_background_mode(argv[3]); @@ -232,20 +290,36 @@ int main(int argc, const char **argv) { struct wl_registry *registry = wl_display_get_registry(state.display); wl_registry_add_listener(registry, ®istry_listener, &state); wl_display_roundtrip(state.display); - assert(state.compositor && state.layer_shell && state.output && state.shm); + if (state.compositor == NULL || state.shm == NULL || + state.layer_shell == NULL || state.xdg_output_manager == NULL) { + wlr_log(WLR_ERROR, "Missing a required Wayland interface"); + return 1; + } - // Second roundtrip to get output properties + struct swaybg_output *output; + wl_list_for_each(output, &state.outputs, link) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + state.xdg_output_manager, output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, + &xdg_output_listener, output); + } + // Second roundtrip to get xdg_output properties wl_display_roundtrip(state.display); + if (state.output == NULL) { + wlr_log(WLR_ERROR, "Cannot find output '%s'", args.output); + return 1; + } state.surface = wl_compositor_create_surface(state.compositor); assert(state.surface); + // Empty input region state.input_region = wl_compositor_create_region(state.compositor); assert(state.input_region); wl_surface_set_input_region(state.surface, state.input_region); state.layer_surface = zwlr_layer_shell_v1_get_layer_surface( - state.layer_shell, state.surface, state.output, + state.layer_shell, state.surface, state.output->wl_output, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); assert(state.layer_surface); @@ -265,5 +339,6 @@ int main(int argc, const char **argv) { while (wl_display_dispatch(state.display) != -1 && state.run_display) { // This space intentionally left blank } + return 0; }