From 9d99e5c2e72a464554b034c014437a495bb7b409 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 19 May 2018 08:47:47 +1000 Subject: [PATCH] Swaylock: Allow per-output images --- include/swaylock/swaylock.h | 12 ++++ meson.build | 2 +- protocols/meson.build | 6 +- swaylock/main.c | 138 ++++++++++++++++++++++++++++++------ swaylock/render.c | 2 +- 5 files changed, 136 insertions(+), 24 deletions(-) diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index 07b908d7..e161ada9 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -37,23 +37,35 @@ struct swaylock_state { struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager; struct wl_shm *shm; struct wl_list surfaces; + struct wl_list images; struct swaylock_args args; struct swaylock_password password; struct swaylock_xkb xkb; enum auth_state auth_state; bool run_display; + struct zxdg_output_manager_v1 *zxdg_output_manager; }; struct swaylock_surface { cairo_surface_t *image; struct swaylock_state *state; struct wl_output *output; + 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; +}; + +// There is exactly one swaylock_image for each -i argument +struct swaylock_image { + char *path; + char *output_name; + cairo_surface_t *cairo_surface; struct wl_list link; }; diff --git a/meson.build b/meson.build index b943236f..d4ee1a11 100644 --- a/meson.build +++ b/meson.build @@ -29,7 +29,7 @@ wayland_server = dependency('wayland-server') wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_egl = dependency('wayland-egl') -wayland_protos = dependency('wayland-protocols') +wayland_protos = dependency('wayland-protocols', version: '>=1.14') xkbcommon = dependency('xkbcommon') cairo = dependency('cairo') pango = dependency('pango') diff --git a/protocols/meson.build b/protocols/meson.build index 9966c02f..a031245c 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -29,16 +29,18 @@ wayland_scanner_server = generator( client_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], + [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['idle.xml'], - ['wlr-input-inhibitor-unstable-v1.xml'] + ['wlr-input-inhibitor-unstable-v1.xml'], ] server_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], + [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], - ['wlr-input-inhibitor-unstable-v1.xml'] + ['wlr-input-inhibitor-unstable-v1.xml'], ] client_protos_src = [] diff --git a/swaylock/main.c b/swaylock/main.c index 200c1b5f..91c28bd2 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "swaylock/seat.h" #include "swaylock/swaylock.h" @@ -20,9 +21,11 @@ #include "pool-buffer.h" #include "cairo.h" #include "log.h" +#include "stringop.h" #include "util.h" #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" void sway_terminate(int exit_code) { exit(exit_code); @@ -84,22 +87,23 @@ 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, +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, +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) { +static void handle_wl_output_scale(void *data, struct wl_output *output, + int32_t factor) { struct swaylock_surface *surface = data; surface->scale = factor; if (surface->state->run_display) { @@ -107,11 +111,46 @@ static void output_scale(void *data, struct wl_output *output, int32_t factor) { } } -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) { + wlr_log(L_DEBUG, "output name is %s", name); + struct swaylock_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, @@ -133,13 +172,16 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) { state->input_inhibit_manager = wl_registry_bind( registry, name, &zwlr_input_inhibit_manager_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 swaylock_surface *surface = calloc(1, sizeof(struct swaylock_surface)); surface->state = state; surface->output = wl_registry_bind(registry, name, &wl_output_interface, 3); - wl_output_add_listener(surface->output, &output_listener, surface); + wl_output_add_listener(surface->output, &_wl_output_listener, surface); wl_list_insert(&state->surfaces, &surface->link); } } @@ -154,6 +196,53 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; +static cairo_surface_t *select_image(struct swaylock_state *state, + struct swaylock_surface *surface) { + struct swaylock_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 swaylock_state *state) { + // [:] + struct swaylock_image *image = malloc(sizeof(struct swaylock_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); + } + + // 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(L_DEBUG, "Loaded image %s for output %s", + image->path, image->output_name ? image->output_name : "*"); +} + static struct swaylock_state state; int main(int argc, char **argv) { @@ -180,16 +269,15 @@ int main(int argc, char **argv) { " -v, --version Show the version number and quit.\n" " -i, --image [:] Display the given image.\n" " -u, --no-unlock-indicator Disable the unlock indicator.\n" - " -f, --daemonize Detach from the controlling terminal.\n" - " --socket Use the specified socket.\n"; + " -f, --daemonize Detach from the controlling terminal.\n"; struct swaylock_args args = { .mode = BACKGROUND_MODE_SOLID_COLOR, .color = 0xFFFFFFFF, .show_indicator = true, }; - cairo_surface_t *background_image = NULL; state.args = args; + wl_list_init(&state.images); wlr_log_init(L_DEBUG, NULL); int c; @@ -206,12 +294,7 @@ int main(int argc, char **argv) { break; } case 'i': - // TODO: Multiple background images (bleh) - background_image = load_background_image(optarg); - if (!background_image) { - return 1; - } - state.args.mode = BACKGROUND_MODE_FILL; + load_image(optarg, &state); break; case 's': state.args.mode = parse_background_mode(optarg); @@ -261,6 +344,7 @@ int main(int argc, char **argv) { if (!state.input_inhibit_manager) { wlr_log(L_ERROR, "Compositor does not support the input inhibitor " "protocol, refusing to run insecurely"); + return 1; } if (wl_list_empty(&state.surfaces)) { @@ -268,9 +352,23 @@ int main(int argc, char **argv) { return 0; } + if (state.zxdg_output_manager) { + struct swaylock_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(L_INFO, "Compositor does not support zxdg output manager, " + "images assigned to named outputs will not work"); + } + struct swaylock_surface *surface; wl_list_for_each(surface, &state.surfaces, link) { - surface->image = background_image; + surface->image = select_image(&state, surface); surface->surface = wl_compositor_create_surface(state.compositor); assert(surface->surface); diff --git a/swaylock/render.c b/swaylock/render.c index 7d9d25a5..cc40f4e9 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -23,7 +23,7 @@ void render_frame(struct swaylock_surface *surface) { cairo_t *cairo = surface->current_buffer->cairo; cairo_identity_matrix(cairo); - if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { + if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) { cairo_set_source_u32(cairo, state->args.color); cairo_paint(cairo); } else {