diff --git a/include/sway/config.h b/include/sway/config.h index 75acd4f2a..cebdee0b1 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -474,11 +474,11 @@ void free_sway_binding(struct sway_binding *sb); struct sway_binding *sway_binding_dup(struct sway_binding *sb); -void load_swaybars(); +void load_swaybars(void); void invoke_swaybar(struct bar_config *bar); -void terminate_swaybg(pid_t pid); +void load_swaybg(void); struct bar_config *default_bar_config(void); diff --git a/include/sway/output.h b/include/sway/output.h index b6cda83c2..814e0f946 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -31,8 +31,6 @@ struct sway_output { struct wl_list link; - pid_t bg_pid; - struct { struct wl_signal destroy; } events; diff --git a/include/swaybg.h b/include/swaybg.h new file mode 100644 index 000000000..30507fe27 --- /dev/null +++ b/include/swaybg.h @@ -0,0 +1,54 @@ +#ifndef _SWAYBG_H +#define _SWAYBG_H +#include +#include +#include +#include "background-image.h" +#include "cairo.h" +#include "pool-buffer.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" + +struct swaybg_args { + uint32_t color; + enum background_mode mode; +}; + +// There is exactly one swaybg_image for each -i argument +struct swaybg_image { + char *path; + char *output_name; + cairo_surface_t *cairo_surface; + struct wl_list link; +}; + +struct swaybg_state { + struct wl_display *display; + struct wl_compositor *compositor; + struct zwlr_layer_shell_v1 *layer_shell; + struct wl_shm *shm; + struct wl_list surfaces; + struct wl_list images; + struct swaybg_args args; + bool run_display; + struct zxdg_output_manager_v1 *zxdg_output_manager; +}; + +struct swaybg_surface { + cairo_surface_t *image; + struct swaybg_state *state; + struct wl_output *output; + uint32_t output_global_name; + struct zxdg_output_v1 *xdg_output; + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; + struct pool_buffer buffers[2]; + struct pool_buffer *current_buffer; + uint32_t width, height; + int32_t scale; + char *output_name; + struct wl_list link; +}; + +void render_surface(struct swaybg_surface *surface); + +#endif diff --git a/meson.build b/meson.build index 1d40581aa..d97da343a 100644 --- a/meson.build +++ b/meson.build @@ -72,9 +72,10 @@ if scdoc.found() 'sway/sway.5.scd', 'sway/sway-bar.5.scd', 'sway/sway-input.5.scd', + 'swaybg/swaybg.1.scd', + 'swayidle/swayidle.1.scd', 'swaylock/swaylock.1.scd', 'swaymsg/swaymsg.1.scd', - 'swayidle/swayidle.1.scd', ] foreach filename : man_files topic = filename.split('.')[-3].split('/')[-1] diff --git a/sway/commands/reload.c b/sway/commands/reload.c index c6715f9ca..20f95a936 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -11,6 +11,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); } + load_swaybg(); load_swaybars(); arrange_and_commit(&root_container); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/config/bar.c b/sway/config/bar.c index 3a74331e4..11abf7af9 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -225,7 +225,7 @@ static bool active_output(const char *name) { return false; } -void load_swaybars() { +void load_swaybars(void) { for (int i = 0; i < config->bars->length; ++i) { struct bar_config *bar = config->bars->items[i]; bool apply = false; diff --git a/sway/config/bg.c b/sway/config/bg.c new file mode 100644 index 000000000..a050e44d0 --- /dev/null +++ b/sway/config/bg.c @@ -0,0 +1,92 @@ +#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sway/config.h" +#include "stringop.h" +#include "list.h" +#include "log.h" + +static pid_t swaybg_pid = -1; + +static void terminate_swaybg(void) { + wlr_log(WLR_DEBUG, "Terminating swaybg %d", swaybg_pid); + int ret = kill(-swaybg_pid, SIGTERM); + if (ret != 0) { + wlr_log_errno(WLR_ERROR, "Unable to terminate swaybg %d", swaybg_pid); + } else { + waitpid(swaybg_pid, NULL, 0); + } + swaybg_pid = 0; +} + +void load_swaybg(void) { + if (swaybg_pid >= 0) { + terminate_swaybg(); + } + + swaybg_pid = fork(); + if (swaybg_pid < 0) { + wlr_log(WLR_ERROR, "Failed to fork()"); + return; + } else if (swaybg_pid == 0) { + setsid(); + + const char *cmd = config->swaybg_command ? + config->swaybg_command : "swaybg"; + + size_t argv_cap = 2 + 4 * config->output_configs->length; + size_t argv_len = 0; + char *argv[argv_cap]; + argv[argv_len++] = "swaybg"; + + for (int i = 0; i < config->output_configs->length; ++i) { + struct output_config *oc = config->output_configs->items[i]; + + if (oc->background_option) { + if (strcmp(oc->background_option, "solid_color") == 0) { + argv[argv_len++] = "--color"; + argv[argv_len++] = oc->background; + continue; + } + + argv[argv_len++] = "--scaling"; + argv[argv_len++] = oc->background_option; + } + + if (oc->background) { + char *arg; + if (strcmp(oc->name, "*") == 0) { + arg = oc->background; + } else { + arg = malloc(strlen(oc->name) + 1 + + strlen(oc->background) + 1); + strcpy(arg, oc->name); + strcat(arg, ":"); + strcat(arg, oc->background); + } + + argv[argv_len++] = "--image"; + argv[argv_len++] = arg; + } + } + + argv[argv_len++] = NULL; + assert(argv_len <= argv_cap); + + execvp(cmd, argv); + + wlr_log(WLR_ERROR, "Failed to exec swaybg"); + exit(EXIT_FAILURE); + } + + wlr_log(WLR_ERROR, "Spawned swaybg %d", swaybg_pid); +} diff --git a/sway/config/output.c b/sway/config/output.c index 205e2633c..7c741190a 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -2,8 +2,6 @@ #include #include #include -#include -#include #include #include #include @@ -113,16 +111,6 @@ static void set_mode(struct wlr_output *output, int width, int height, } } -void terminate_swaybg(pid_t pid) { - int ret = kill(pid, SIGTERM); - if (ret != 0) { - wlr_log(WLR_ERROR, "Unable to terminate swaybg [pid: %d]", pid); - } else { - int status; - waitpid(pid, &status, 0); - } -} - void apply_output_config(struct output_config *oc, struct sway_container *output) { assert(output->type == C_OUTPUT); @@ -132,10 +120,6 @@ void apply_output_config(struct output_config *oc, struct sway_container *output if (oc && oc->enabled == 0) { struct sway_output *sway_output = output->sway_output; - if (output->sway_output->bg_pid != 0) { - terminate_swaybg(output->sway_output->bg_pid); - output->sway_output->bg_pid = 0; - } container_destroy(output); sway_output->swayc = NULL; wlr_output_layout_remove(root_container.sway_root->output_layout, @@ -165,58 +149,14 @@ void apply_output_config(struct output_config *oc, struct sway_container *output wlr_output_layout_add_auto(output_layout, wlr_output); } - if (!oc || !oc->background) { - // Look for a * config for background - int i = list_seq_find(config->output_configs, output_name_cmp, "*"); - if (i >= 0) { - oc = config->output_configs->items[i]; - } else { - oc = NULL; - } - } - - int output_i; - for (output_i = 0; output_i < root_container.children->length; ++output_i) { - if (root_container.children->items[output_i] == output) { - break; - } - } - - if (oc && oc->background) { - if (output->sway_output->bg_pid != 0) { - terminate_swaybg(output->sway_output->bg_pid); - } - - wlr_log(WLR_DEBUG, "Setting background for output %d to %s", - output_i, oc->background); - - size_t len = snprintf(NULL, 0, "%s %d %s %s", - config->swaybg_command ? config->swaybg_command : "swaybg", - output_i, oc->background, oc->background_option); - 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", - config->swaybg_command ? config->swaybg_command : "swaybg", - output_i, oc->background, oc->background_option); - wlr_log(WLR_DEBUG, "-> %s", command); - - char *const cmd[] = { "sh", "-c", command, NULL }; - output->sway_output->bg_pid = fork(); - if (output->sway_output->bg_pid == 0) { - execvp(cmd[0], cmd); - } - } if (oc && oc->dpms_state != DPMS_IGNORE) { switch (oc->dpms_state) { case DPMS_ON: - wlr_log(WLR_DEBUG, "Turning on screen"); + wlr_log(WLR_DEBUG, "Turning on output %s", oc->name); wlr_output_enable(wlr_output, true); break; case DPMS_OFF: - wlr_log(WLR_DEBUG, "Turning off screen"); + wlr_log(WLR_DEBUG, "Turning off output %s", oc->name); wlr_output_enable(wlr_output, false); break; case DPMS_IGNORE: diff --git a/sway/meson.build b/sway/meson.build index 6fc78db3a..498613f72 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -25,9 +25,10 @@ sway_sources = files( 'input/keyboard.c', 'config/bar.c', + 'config/bg.c', + 'config/input.c', 'config/output.c', 'config/seat.c', - 'config/input.c', 'commands/assign.c', 'commands/bar.c', diff --git a/sway/server.c b/sway/server.c index 1d8eb9640..e9f21035a 100644 --- a/sway/server.c +++ b/sway/server.c @@ -148,5 +148,6 @@ void server_run(struct sway_server *server) { wlr_backend_destroy(server->backend); return; } + load_swaybg(); wl_display_run(server->wl_display); } diff --git a/swaybg/main.c b/swaybg/main.c index 1796b2453..5ab118902 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -1,116 +1,82 @@ +#define _POSIX_C_SOURCE 200809L #include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include "background-image.h" -#include "pool-buffer.h" -#include "cairo.h" +#include +#include "log.h" +#include "stringop.h" +#include "swaybg.h" #include "util.h" -#include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" -struct swaybg_args { - int output_idx; - const char *path; - enum background_mode mode; -}; - -struct swaybg_context { - uint32_t color; - cairo_surface_t *image; -}; - -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_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; -}; - -bool is_valid_color(const char *color) { - int len = strlen(color); - if (len != 7 || color[0] != '#') { - wlr_log(WLR_ERROR, "%s is not a valid color for swaybg. " - "Color should be specified as #rrggbb (no alpha).", color); - return false; - } - - int i; - for (i = 1; i < len; ++i) { - if (!isxdigit(color[i])) { - return false; - } - } - - return true; +void sway_terminate(void) { + exit(0); } -static void render_frame(struct swaybg_state *state) { - int buffer_width = state->width * state->scale, - buffer_height = state->height * state->scale; - state->current_buffer = get_next_buffer(state->shm, - state->buffers, buffer_width, buffer_height); - cairo_t *cairo = state->current_buffer->cairo; - if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { - cairo_set_source_u32(cairo, state->context.color); - cairo_paint(cairo); - } else { - render_background_image(cairo, state->context.image, - state->args->mode, buffer_width, buffer_height); +static void destroy_surface(struct swaybg_surface *surface) { + wl_list_remove(&surface->link); + if (surface->layer_surface != NULL) { + zwlr_layer_surface_v1_destroy(surface->layer_surface); } - - wl_surface_set_buffer_scale(state->surface, state->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); + if (surface->surface != NULL) { + wl_surface_destroy(surface->surface); + } + destroy_buffer(&surface->buffers[0]); + destroy_buffer(&surface->buffers[1]); + wl_output_destroy(surface->output); + free(surface); } -static bool prepare_context(struct swaybg_state *state) { - if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { - state->context.color = parse_color(state->args->path); - return is_valid_color(state->args->path); - } - if (!(state->context.image = load_background_image(state->args->path))) { - return false; - } - return true; +static const struct zwlr_layer_surface_v1_listener layer_surface_listener; + +static cairo_surface_t *select_image(struct swaybg_state *state, + struct swaybg_surface *surface); + +static void create_layer_surface(struct swaybg_surface *surface) { + struct swaybg_state *state = surface->state; + + surface->image = select_image(state, surface); + + surface->surface = wl_compositor_create_surface(state->compositor); + assert(surface->surface); + + surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + state->layer_shell, surface->surface, surface->output, + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); + assert(surface->layer_surface); + + zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0); + zwlr_layer_surface_v1_set_anchor(surface->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); + zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1); + zwlr_layer_surface_v1_add_listener(surface->layer_surface, + &layer_surface_listener, surface); + + struct wl_region *input_region = + wl_compositor_create_region(state->compositor); + wl_surface_set_input_region(surface->surface, input_region); + wl_region_destroy(input_region); + + wl_surface_commit(surface->surface); } static void layer_surface_configure(void *data, - struct zwlr_layer_surface_v1 *surface, + struct zwlr_layer_surface_v1 *layer_surface, uint32_t serial, uint32_t width, uint32_t height) { - struct swaybg_state *state = data; - state->width = width; - state->height = height; - zwlr_layer_surface_v1_ack_configure(surface, serial); - render_frame(state); + struct swaybg_surface *surface = data; + surface->width = width; + surface->height = height; + zwlr_layer_surface_v1_ack_configure(layer_surface, serial); + render_surface(surface); } static void layer_surface_closed(void *data, - struct zwlr_layer_surface_v1 *surface) { - struct swaybg_state *state = data; - zwlr_layer_surface_v1_destroy(state->layer_surface); - wl_surface_destroy(state->surface); - wl_region_destroy(state->input_region); - state->run_display = false; + struct zwlr_layer_surface_v1 *layer_surface) { + struct swaybg_surface *surface = data; + destroy_surface(surface); } static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { @@ -118,34 +84,70 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { .closed = layer_surface_closed, }; -static void output_geometry(void *data, struct wl_output *output, int32_t x, - int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, - const char *make, const char *model, int32_t transform) { +static void handle_wl_output_geometry(void *data, struct wl_output *output, + int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, + int32_t subpixel, const char *make, const char *model, + int32_t transform) { // Who cares } -static void output_mode(void *data, struct wl_output *output, uint32_t flags, - int32_t width, int32_t height, int32_t refresh) { +static void handle_wl_output_mode(void *data, struct wl_output *output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) { // Who cares } -static void output_done(void *data, struct wl_output *output) { +static void handle_wl_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) { - render_frame(state); +static void handle_wl_output_scale(void *data, struct wl_output *output, + int32_t factor) { + struct swaybg_surface *surface = data; + surface->scale = factor; + if (surface->state->run_display) { + render_surface(surface); } } -static const struct wl_output_listener output_listener = { - .geometry = output_geometry, - .mode = output_mode, - .done = output_done, - .scale = output_scale, +struct wl_output_listener _wl_output_listener = { + .geometry = handle_wl_output_geometry, + .mode = handle_wl_output_mode, + .done = handle_wl_output_done, + .scale = handle_wl_output_scale, +}; + +static void handle_xdg_output_logical_size(void *data, struct zxdg_output_v1 *output, + int width, int height) { + // Who cares +} + +static void handle_xdg_output_logical_position(void *data, + struct zxdg_output_v1 *output, int x, int y) { + // Who cares +} + +static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *output, + const char *name) { + struct swaybg_surface *surface = data; + surface->xdg_output = output; + surface->output_name = strdup(name); +} + +static void handle_xdg_output_description(void *data, struct zxdg_output_v1 *output, + const char *description) { + // Who cares +} + +static void handle_xdg_output_done(void *data, struct zxdg_output_v1 *output) { + // Who cares +} + +struct zxdg_output_v1_listener _xdg_output_listener = { + .logical_position = handle_xdg_output_logical_position, + .logical_size = handle_xdg_output_logical_size, + .done = handle_xdg_output_done, + .name = handle_xdg_output_name, + .description = handle_xdg_output_description, }; static void handle_global(void *data, struct wl_registry *registry, @@ -153,27 +155,43 @@ static void handle_global(void *data, struct wl_registry *registry, struct swaybg_state *state = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { state->compositor = wl_registry_bind(registry, name, - &wl_compositor_interface, 3); + &wl_compositor_interface, 3); } else if (strcmp(interface, wl_shm_interface.name) == 0) { 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++; + &wl_shm_interface, 1); } 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); + registry, name, &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { + state->zxdg_output_manager = wl_registry_bind( + registry, name, &zxdg_output_manager_v1_interface, 2); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + struct swaybg_surface *surface = + calloc(1, sizeof(struct swaybg_surface)); + surface->state = state; + surface->output = wl_registry_bind(registry, name, + &wl_output_interface, 3); + surface->output_global_name = name; + wl_output_add_listener(surface->output, &_wl_output_listener, surface); + wl_list_insert(&state->surfaces, &surface->link); + + if (state->run_display) { + create_layer_surface(surface); + wl_display_roundtrip(state->display); + } } } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { - // who cares + struct swaybg_state *state = data; + struct swaybg_surface *surface; + wl_list_for_each(surface, &state->surfaces, link) { + if (surface->output_global_name == name) { + destroy_surface(surface); + break; + } + } } static const struct wl_registry_listener registry_listener = { @@ -181,26 +199,137 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; -int main(int argc, const char **argv) { - struct swaybg_args args = {0}; +static cairo_surface_t *select_image(struct swaybg_state *state, + struct swaybg_surface *surface) { + struct swaybg_image *image; + cairo_surface_t *default_image = NULL; + wl_list_for_each(image, &state->images, link) { + if (lenient_strcmp(image->output_name, surface->output_name) == 0) { + return image->cairo_surface; + } else if (!image->output_name) { + default_image = image->cairo_surface; + } + } + return default_image; +} + +static void load_image(char *arg, struct swaybg_state *state) { + // [:] + struct swaybg_image *image = calloc(1, sizeof(struct swaybg_image)); + char *separator = strchr(arg, ':'); + if (separator) { + *separator = '\0'; + image->output_name = strdup(arg); + image->path = strdup(separator + 1); + } else { + image->output_name = NULL; + image->path = strdup(arg); + } + + bool exists = false; + struct swaybg_image *iter_image; + wl_list_for_each(iter_image, &state->images, link) { + if (lenient_strcmp(iter_image->output_name, image->output_name) == 0) { + exists = true; + break; + } + } + if (exists) { + if (image->output_name) { + wlr_log(WLR_ERROR, "Multiple images defined for output %s", + image->output_name); + } else { + wlr_log(WLR_ERROR, "Multiple default images defined"); + } + } + + // Bash doesn't replace the ~ with $HOME if the output name is supplied + wordexp_t p; + if (wordexp(image->path, &p, 0) == 0) { + free(image->path); + image->path = strdup(p.we_wordv[0]); + wordfree(&p); + } + + // Load the actual image + image->cairo_surface = load_background_image(image->path); + if (!image->cairo_surface) { + free(image); + return; + } + wl_list_insert(&state->images, &image->link); + state->args.mode = BACKGROUND_MODE_FILL; + wlr_log(WLR_DEBUG, "Loaded image %s for output %s", + image->path, image->output_name ? image->output_name : "*"); +} + +static const char usage[] = + "Usage: swaybg [options...]\n" + "\n" + " -h, --help Show help message and quit.\n" + " -c, --color Turn the screen into the given color instead of white.\n" + " -s, --scaling Scaling mode: stretch, fill, fit, center, tile.\n" + " -v, --version Show the version number and quit.\n" + " -i, --image [:] Display the given image.\n"; + +int main(int argc, char *argv[]) { + static const struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"color", required_argument, NULL, 'c'}, + {"image", required_argument, NULL, 'i'}, + {"scaling", required_argument, NULL, 's'}, + {"version", no_argument, NULL, 'v'}, + {"socket", required_argument, NULL, 'p'}, + {0} + }; + struct swaybg_state state = {0}; - state.args = &args; + state.args = (struct swaybg_args){ + .mode = BACKGROUND_MODE_SOLID_COLOR, + .color = 0xFFFFFFFF, + }; + wl_list_init(&state.images); + wl_list_init(&state.surfaces); + wlr_log_init(WLR_DEBUG, NULL); - if (argc != 4) { - wlr_log(WLR_ERROR, "Do not run this program manually. " - "See man 5 sway and look for output options."); - return 1; - } - args.output_idx = atoi(argv[1]); - args.path = argv[2]; - - args.mode = parse_background_mode(argv[3]); - if (args.mode == BACKGROUND_MODE_INVALID) { - return 1; - } - if (!prepare_context(&state)) { - return 1; + int c; + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "hc:i:s:v", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'c': { + state.args.color = parse_color(optarg); + state.args.mode = BACKGROUND_MODE_SOLID_COLOR; + break; + } + case 'i': + load_image(optarg, &state); + break; + case 's': + state.args.mode = parse_background_mode(optarg); + if (state.args.mode == BACKGROUND_MODE_INVALID) { + return 1; + } + break; + case 't': + state.args.mode = BACKGROUND_MODE_TILE; + break; + case 'v': +#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE + fprintf(stdout, "swaybg version %s (%s, branch \"%s\")\n", + SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH); +#else + fprintf(stdout, "version unknown\n"); +#endif + return 0; + default: + fprintf(stderr, "%s", usage); + return 1; + } } state.display = wl_display_connect(NULL); @@ -209,34 +338,26 @@ 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); + assert(state.compositor && state.layer_shell && state.shm); - // Second roundtrip to get output properties - wl_display_roundtrip(state.display); + if (state.zxdg_output_manager) { + struct swaybg_surface *surface; + wl_list_for_each(surface, &state.surfaces, link) { + surface->xdg_output = zxdg_output_manager_v1_get_xdg_output( + state.zxdg_output_manager, surface->output); + zxdg_output_v1_add_listener( + surface->xdg_output, &_xdg_output_listener, surface); + } + wl_display_roundtrip(state.display); + } else { + wlr_log(WLR_INFO, "Compositor does not support zxdg output manager, " + "images assigned to named outputs will not work"); + } - state.surface = wl_compositor_create_surface(state.compositor); - assert(state.surface); - - 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, - ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); - assert(state.layer_surface); - - zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0); - zwlr_layer_surface_v1_set_anchor(state.layer_surface, - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); - zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1); - zwlr_layer_surface_v1_add_listener(state.layer_surface, - &layer_surface_listener, &state); - wl_surface_commit(state.surface); - wl_display_roundtrip(state.display); + struct swaybg_surface *surface; + wl_list_for_each(surface, &state.surfaces, link) { + create_layer_surface(surface); + } state.run_display = true; while (wl_display_dispatch(state.display) != -1 && state.run_display) { diff --git a/swaybg/meson.build b/swaybg/meson.build index 8704de6d3..451e2b3b9 100644 --- a/swaybg/meson.build +++ b/swaybg/meson.build @@ -1,6 +1,6 @@ executable( 'swaybg', - 'main.c', + ['main.c', 'render.c'], include_directories: [sway_inc], dependencies: [ cairo, diff --git a/swaybg/render.c b/swaybg/render.c new file mode 100644 index 000000000..ffb7c9871 --- /dev/null +++ b/swaybg/render.c @@ -0,0 +1,40 @@ +#include +#include +#include "background-image.h" +#include "cairo.h" +#include "swaybg.h" + +void render_surface(struct swaybg_surface *surface) { + struct swaybg_state *state = surface->state; + + int buffer_width = surface->width * surface->scale; + int buffer_height = surface->height * surface->scale; + if (buffer_width == 0 || buffer_height == 0) { + return; // not yet configured + } + + surface->current_buffer = get_next_buffer(state->shm, surface->buffers, + buffer_width, buffer_height); + if (surface->current_buffer == NULL) { + return; + } + + cairo_t *cairo = surface->current_buffer->cairo; + cairo_identity_matrix(cairo); + + cairo_save(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) { + cairo_set_source_u32(cairo, state->args.color); + cairo_paint(cairo); + } else { + render_background_image(cairo, surface->image, + state->args.mode, buffer_width, buffer_height); + } + cairo_restore(cairo); + + wl_surface_set_buffer_scale(surface->surface, surface->scale); + wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0); + wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height); + wl_surface_commit(surface->surface); +} diff --git a/swaybg/swaybg.1.scd b/swaybg/swaybg.1.scd new file mode 100644 index 000000000..7bde3c18e --- /dev/null +++ b/swaybg/swaybg.1.scd @@ -0,0 +1,37 @@ +swaybg(1) + +# NAME + +swaybg - Screen background for Wayland + +# SYNOPSIS + +_swaybg_ [options...] + +Displays a background in your Wayland session. + +# OPTIONS + +*-h, --help* + Show help message and quit. + +*-c, --color* + Turn the screen into the given color. If -i is used, this sets the + background of the image to the given color. Defaults to white (FFFFFF), or + transparent (00000000) if an image is in use. + +*-i, --image* [:] + Display the given image, optionally only on the given output. Use -c to set + a background color. + +*--scaling* + Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_. + +*-v, --version* + Show the version number and quit. + +# AUTHORS + +Maintained by Drew DeVault , who is assisted by other open +source contributors. For more information about sway development, see +https://github.com/swaywm/sway.