From a28730edee1896242012f80fd0e63e7966652e02 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 2 Apr 2018 21:57:13 -0400 Subject: [PATCH 01/19] Move swaybg background rendering into common/ swaylock will use it too --- common/background-image.c | 100 ++++++++++++++++++++++++++++++++ common/meson.build | 20 +++---- include/background-image.h | 19 +++++++ swaybg/main.c | 113 ++----------------------------------- 4 files changed, 133 insertions(+), 119 deletions(-) create mode 100644 common/background-image.c create mode 100644 include/background-image.h diff --git a/common/background-image.c b/common/background-image.c new file mode 100644 index 000000000..2988c2e26 --- /dev/null +++ b/common/background-image.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include "background-image.h" +#include "cairo.h" + +cairo_surface_t *load_background_image(const char *path) { + cairo_surface_t *image; +#ifdef HAVE_GDK_PIXBUF + GError *err = NULL; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); + if (!pixbuf) { + wlr_log(L_ERROR, "Failed to load background image."); + return false; + } + image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); + g_object_unref(pixbuf); +#else + image = cairo_image_surface_create_from_png(path); +#endif //HAVE_GDK_PIXBUF + if (!image) { + wlr_log(L_ERROR, "Failed to read background image."); + return NULL; + } + if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { + wlr_log(L_ERROR, "Failed to read background image: %s." +#ifndef HAVE_GDK_PIXBUF + "\nSway was compiled without gdk_pixbuf support, so only" + "\nPNG images can be loaded. This is the likely cause." +#endif //HAVE_GDK_PIXBUF + , cairo_status_to_string(cairo_surface_status(image))); + return NULL; + } + return image; +} + +void render_background_image(cairo_t *cairo, cairo_surface_t *image, + enum background_mode mode, int buffer_width, int buffer_height, + int buffer_scale) { + double width = cairo_image_surface_get_width(image); + double height = cairo_image_surface_get_height(image); + + switch (mode) { + case BACKGROUND_MODE_STRETCH: + cairo_scale(cairo, + (double)buffer_width / width, + (double)buffer_height / height); + cairo_set_source_surface(cairo, image, 0, 0); + break; + case BACKGROUND_MODE_FILL: { + double window_ratio = (double)buffer_width / buffer_height; + double bg_ratio = width / height; + + if (window_ratio > bg_ratio) { + double scale = (double)buffer_width / width; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + 0, (double)buffer_height / 2 / scale - height / 2); + } else { + double scale = (double)buffer_height / height; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + (double)buffer_width / 2 / scale - width / 2, 0); + } + break; + } + case BACKGROUND_MODE_FIT: { + double window_ratio = (double)buffer_width / buffer_height; + double bg_ratio = width / height; + + if (window_ratio > bg_ratio) { + double scale = (double)buffer_height / height; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + (double)buffer_width / 2 / scale - width / 2, 0); + } else { + double scale = (double)buffer_width / width; + cairo_scale(cairo, scale, scale); + cairo_set_source_surface(cairo, image, + 0, (double)buffer_height / 2 / scale - height / 2); + } + break; + } + case BACKGROUND_MODE_CENTER: + cairo_set_source_surface(cairo, image, + (double)buffer_width / 2 - width / 2, + (double)buffer_height / 2 - height / 2); + break; + case BACKGROUND_MODE_TILE: { + cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + cairo_set_source(cairo, pattern); + break; + } + case BACKGROUND_MODE_SOLID_COLOR: + assert(0); + break; + } + cairo_paint(cairo); +} diff --git a/common/meson.build b/common/meson.build index 4ad470774..851e7bbf5 100644 --- a/common/meson.build +++ b/common/meson.build @@ -1,17 +1,7 @@ -deps = [ - cairo, - pango, - pangocairo, - wlroots -] - -if gdk_pixbuf.found() - deps += [gdk_pixbuf] -endif - lib_sway_common = static_library( 'sway-common', files( + 'background-image.c', 'cairo.c', 'ipc-client.c', 'log.c', @@ -21,6 +11,12 @@ lib_sway_common = static_library( 'stringop.c', 'util.c' ), - dependencies: deps, + dependencies: [ + cairo, + gdk_pixbuf, + pango, + pangocairo, + wlroots + ], include_directories: sway_inc ) diff --git a/include/background-image.h b/include/background-image.h new file mode 100644 index 000000000..e160e63d4 --- /dev/null +++ b/include/background-image.h @@ -0,0 +1,19 @@ +#ifndef _SWAY_BACKGROUND_IMAGE_H +#define _SWAY_BACKGROUND_IMAGE_H +#include "cairo.h" + +enum background_mode { + BACKGROUND_MODE_STRETCH, + BACKGROUND_MODE_FILL, + BACKGROUND_MODE_FIT, + BACKGROUND_MODE_CENTER, + BACKGROUND_MODE_TILE, + BACKGROUND_MODE_SOLID_COLOR, +}; + +cairo_surface_t *load_background_image(const char *path); +void render_background_image(cairo_t *cairo, cairo_surface_t *image, + enum background_mode mode, int buffer_width, int buffer_height, + int buffer_scale); + +#endif diff --git a/swaybg/main.c b/swaybg/main.c index c282a7074..ffefcd089 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -7,20 +7,12 @@ #include #include #include +#include "background-image.h" #include "pool-buffer.h" #include "cairo.h" #include "util.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -enum background_mode { - BACKGROUND_MODE_STRETCH, - BACKGROUND_MODE_FILL, - BACKGROUND_MODE_FIT, - BACKGROUND_MODE_CENTER, - BACKGROUND_MODE_TILE, - BACKGROUND_MODE_SOLID_COLOR, -}; - struct swaybg_args { int output_idx; const char *path; @@ -71,85 +63,16 @@ bool is_valid_color(const char *color) { return true; } -static void render_image(struct swaybg_state *state) { - cairo_t *cairo = state->current_buffer->cairo; - cairo_surface_t *image = state->context.image; - double width = cairo_image_surface_get_width(image); - double height = cairo_image_surface_get_height(image); - int buffer_width = state->width * state->scale; - int buffer_height = state->height * state->scale; - - switch (state->args->mode) { - case BACKGROUND_MODE_STRETCH: - cairo_scale(cairo, (double)buffer_width / width, - (double)buffer_height / height); - cairo_set_source_surface(cairo, image, 0, 0); - break; - case BACKGROUND_MODE_FILL: { - double window_ratio = (double)buffer_width / buffer_height; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double)buffer_width / width; - cairo_scale(cairo, scale, scale); - cairo_set_source_surface(cairo, image, - 0, (double)buffer_height / 2 / scale - height / 2); - } else { - double scale = (double)buffer_height / height; - cairo_scale(cairo, scale, scale); - cairo_set_source_surface(cairo, image, - (double)buffer_width / 2 / scale - width / 2, 0); - } - break; - } - case BACKGROUND_MODE_FIT: { - double window_ratio = (double)buffer_width / buffer_height; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double)buffer_height / height; - cairo_scale(cairo, scale, scale); - cairo_set_source_surface(cairo, image, - (double)buffer_width / 2 / scale - width / 2, 0); - } else { - double scale = (double)buffer_width / width; - cairo_scale(cairo, scale, scale); - cairo_set_source_surface(cairo, image, - 0, (double)buffer_height / 2 / scale - height / 2); - } - break; - } - case BACKGROUND_MODE_CENTER: - cairo_set_source_surface(cairo, image, - (double)buffer_width / 2 - width / 2, - (double)buffer_height / 2 - height / 2); - break; - case BACKGROUND_MODE_TILE: { - cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); - cairo_set_source(cairo, pattern); - break; - } - case BACKGROUND_MODE_SOLID_COLOR: - assert(0); - break; - } - cairo_paint(cairo); -} - static void render_frame(struct swaybg_state *state) { state->current_buffer = get_next_buffer(state->shm, state->buffers, state->width * state->scale, state->height * state->scale); cairo_t *cairo = state->current_buffer->cairo; - - switch (state->args->mode) { - case BACKGROUND_MODE_SOLID_COLOR: + if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { cairo_set_source_u32(cairo, state->context.color); cairo_paint(cairo); - break; - default: - render_image(state); - break; + } else { + render_background_image(cairo, state->context.image, + state->args->mode, state->width, state->height, state->scale); } wl_surface_set_buffer_scale(state->surface, state->scale); @@ -163,31 +86,7 @@ static bool prepare_context(struct swaybg_state *state) { state->context.color = parse_color(state->args->path); return is_valid_color(state->args->path); } -#ifdef HAVE_GDK_PIXBUF - GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(state->args->path, &err); - if (!pixbuf) { - wlr_log(L_ERROR, "Failed to load background image."); - return false; - } - state->context.image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); - g_object_unref(pixbuf); -#else - state->context.image = cairo_image_surface_create_from_png( - state->args->path); -#endif //HAVE_GDK_PIXBUF - if (!state->context.image) { - wlr_log(L_ERROR, "Failed to read background image."); - return false; - } - if (cairo_surface_status(state->context.image) != CAIRO_STATUS_SUCCESS) { - wlr_log(L_ERROR, "Failed to read background image: %s." -#ifndef HAVE_GDK_PIXBUF - "\nSway was compiled without gdk_pixbuf support, so only" - "\nPNG images can be loaded. This is the likely cause." -#endif //HAVE_GDK_PIXBUF - , cairo_status_to_string( - cairo_surface_status(state->context.image))); + if (!(state->context.image = load_background_image(state->args->path))) { return false; } return true; From b32bf595aeae7f8ac68354e45a80c0438374ec17 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 2 Apr 2018 22:48:13 -0400 Subject: [PATCH 02/19] Initial swaylock port --- common/background-image.c | 22 +- include/background-image.h | 5 +- meson.build | 2 + swaybg/main.c | 28 +- swaylock/main.c | 1010 ++++++++++-------------------------- swaylock/meson.build | 18 + 6 files changed, 327 insertions(+), 758 deletions(-) create mode 100644 swaylock/meson.build diff --git a/common/background-image.c b/common/background-image.c index 2988c2e26..1a6c0df05 100644 --- a/common/background-image.c +++ b/common/background-image.c @@ -4,6 +4,24 @@ #include "background-image.h" #include "cairo.h" +enum background_mode parse_background_mode(const char *mode) { + if (strcmp(mode, "stretch") == 0) { + return BACKGROUND_MODE_STRETCH; + } else if (strcmp(mode, "fill") == 0) { + return BACKGROUND_MODE_FILL; + } else if (strcmp(mode, "fit") == 0) { + return BACKGROUND_MODE_FIT; + } else if (strcmp(mode, "center") == 0) { + return BACKGROUND_MODE_CENTER; + } else if (strcmp(mode, "tile") == 0) { + return BACKGROUND_MODE_TILE; + } else if (strcmp(mode, "solid_color") == 0) { + return BACKGROUND_MODE_SOLID_COLOR; + } + wlr_log(L_ERROR, "Unsupported background mode: %s", mode); + return BACKGROUND_MODE_INVALID; +} + cairo_surface_t *load_background_image(const char *path) { cairo_surface_t *image; #ifdef HAVE_GDK_PIXBUF @@ -35,8 +53,7 @@ cairo_surface_t *load_background_image(const char *path) { } void render_background_image(cairo_t *cairo, cairo_surface_t *image, - enum background_mode mode, int buffer_width, int buffer_height, - int buffer_scale) { + enum background_mode mode, int buffer_width, int buffer_height) { double width = cairo_image_surface_get_width(image); double height = cairo_image_surface_get_height(image); @@ -93,6 +110,7 @@ void render_background_image(cairo_t *cairo, cairo_surface_t *image, break; } case BACKGROUND_MODE_SOLID_COLOR: + case BACKGROUND_MODE_INVALID: assert(0); break; } diff --git a/include/background-image.h b/include/background-image.h index e160e63d4..15935ffd3 100644 --- a/include/background-image.h +++ b/include/background-image.h @@ -9,11 +9,12 @@ enum background_mode { BACKGROUND_MODE_CENTER, BACKGROUND_MODE_TILE, BACKGROUND_MODE_SOLID_COLOR, + BACKGROUND_MODE_INVALID, }; +enum background_mode parse_background_mode(const char *mode); cairo_surface_t *load_background_image(const char *path); void render_background_image(cairo_t *cairo, cairo_surface_t *image, - enum background_mode mode, int buffer_width, int buffer_height, - int buffer_scale); + enum background_mode mode, int buffer_width, int buffer_height); #endif diff --git a/meson.build b/meson.build index 01788fd9f..d02a446b5 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) pixman = dependency('pixman-1') libcap = dependency('libcap') libinput = dependency('libinput') +libpam = cc.find_library('libpam') math = cc.find_library('m') rt = cc.find_library('rt') git = find_program('git', required: false) @@ -105,6 +106,7 @@ subdir('swaymsg') subdir('client') subdir('swaybg') subdir('swaybar') +subdir('swaylock') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/swaybg/main.c b/swaybg/main.c index ffefcd089..679b8c205 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -64,15 +64,17 @@ bool is_valid_color(const char *color) { } static void render_frame(struct swaybg_state *state) { - state->current_buffer = get_next_buffer(state->shm, state->buffers, - state->width * state->scale, state->height * state->scale); + 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, state->width, state->height, state->scale); + state->args->mode, buffer_width, buffer_height); } wl_surface_set_buffer_scale(state->surface, state->scale); @@ -193,24 +195,10 @@ int main(int argc, const char **argv) { args.output_idx = atoi(argv[1]); args.path = argv[2]; - args.mode = BACKGROUND_MODE_STRETCH; - if (strcmp(argv[3], "stretch") == 0) { - args.mode = BACKGROUND_MODE_STRETCH; - } else if (strcmp(argv[3], "fill") == 0) { - args.mode = BACKGROUND_MODE_FILL; - } else if (strcmp(argv[3], "fit") == 0) { - args.mode = BACKGROUND_MODE_FIT; - } else if (strcmp(argv[3], "center") == 0) { - args.mode = BACKGROUND_MODE_CENTER; - } else if (strcmp(argv[3], "tile") == 0) { - args.mode = BACKGROUND_MODE_TILE; - } else if (strcmp(argv[3], "solid_color") == 0) { - args.mode = BACKGROUND_MODE_SOLID_COLOR; - } else { - wlr_log(L_ERROR, "Unsupported background mode: %s", argv[3]); + args.mode = parse_background_mode(argv[3]); + if (args.mode == BACKGROUND_MODE_INVALID) { return 1; } - if (!prepare_context(&state)) { return 1; } @@ -244,10 +232,10 @@ int main(int argc, const char **argv) { zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1); zwlr_layer_surface_v1_add_listener(state.layer_surface, &layer_surface_listener, &state); - state.run_display = true; wl_surface_commit(state.surface); wl_display_roundtrip(state.display); + state.run_display = true; while (wl_display_dispatch(state.display) != -1 && state.run_display) { // This space intentionally left blank } diff --git a/swaylock/main.c b/swaylock/main.c index c26159519..8673694d2 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -1,387 +1,235 @@ -#define _XOPEN_SOURCE 500 -#include "wayland-swaylock-client-protocol.h" -#include -#include -#include -#include +#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include +#include +#include #include -#include "client/window.h" -#include "client/registry.h" -#include "client/cairo.h" -#include "swaylock/swaylock.h" -#include "ipc-client.h" -#include "log.h" +#include +#include +#include "background-image.h" +#include "pool-buffer.h" +#include "cairo.h" #include "util.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" -struct registry *registry; -struct render_data render_data; -struct lock_config *config; -bool show_indicator = true; +struct swaylock_args { + uint32_t color; + enum background_mode mode; + bool show_indicator; +}; -void wl_dispatch_events() { - wl_display_flush(registry->display); - if (wl_display_dispatch(registry->display) == -1) { - sway_log(L_ERROR, "failed to run wl_display_dispatch"); - exit(1); +struct swaylock_state { + struct wl_display *display; + struct wl_compositor *compositor; + struct zwlr_layer_shell_v1 *layer_shell; + struct wl_shm *shm; + struct wl_list contexts; + struct swaylock_args args; + bool run_display; +}; + +struct swaylock_context { + cairo_surface_t *image; + struct swaylock_state *state; + struct wl_output *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; + struct wl_list link; +}; + +static void daemonize() { + if (fork() == 0) { + int devnull = open("/dev/null", O_RDWR); + dup2(STDOUT_FILENO, devnull); + dup2(STDERR_FILENO, devnull); + chdir("/"); + } else { + exit(0); } } -void sigalarm_handler(int sig) { +static void render_frame(struct swaylock_context *context) { + struct swaylock_state *state = context->state; + context->current_buffer = get_next_buffer(state->shm, + context->buffers, context->width, context->height); + cairo_t *cairo = context->current_buffer->cairo; + if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { + cairo_set_source_u32(cairo, state->args.color); + cairo_paint(cairo); + } else { + render_background_image(cairo, context->image, + state->args.mode, context->width, context->height); + } + wl_surface_attach(context->surface, context->current_buffer->buffer, 0, 0); + wl_surface_damage(context->surface, 0, 0, context->width, context->height); + wl_surface_commit(context->surface); +} + +static void layer_surface_configure(void *data, + struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) { + struct swaylock_context *context = data; + context->width = width; + context->height = height; + zwlr_layer_surface_v1_ack_configure(surface, serial); + render_frame(context); +} + +static void layer_surface_closed(void *data, + struct zwlr_layer_surface_v1 *surface) { + struct swaylock_context *context = data; + zwlr_layer_surface_v1_destroy(context->layer_surface); + wl_surface_destroy(context->surface); + context->state->run_display = false; +} + +struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, + .closed = layer_surface_closed, +}; + +static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) { + wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0); +} + +static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) { + // Who cares +} + +static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + // Who cares +} + +static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + // Who cares +} + +static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) { + // Who cares +} + +static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { + // Who cares +} + +static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) { + // Who cares +} + +static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) { + // Who cares +} + +static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) { + // Who cares +} + +struct wl_pointer_listener pointer_listener = { + .enter = wl_pointer_enter, + .leave = wl_pointer_leave, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = wl_pointer_axis, + .frame = wl_pointer_frame, + .axis_source = wl_pointer_axis_source, + .axis_stop = wl_pointer_axis_stop, + .axis_discrete = wl_pointer_axis_discrete, +}; + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) { + if ((caps & WL_SEAT_CAPABILITY_POINTER)) { + struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(pointer, &pointer_listener, NULL); + } +} + +static void seat_handle_name(void *data, struct wl_seat *wl_seat, + const char *name) { + // Who cares +} + +const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = seat_handle_name, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + struct swaylock_state *state = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 1); + } 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_seat_interface.name) == 0) { + struct wl_seat *seat = wl_registry_bind( + registry, name, &wl_seat_interface, 1); + wl_seat_add_listener(seat, &seat_listener, NULL); + } 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); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + struct swaylock_context *context = + calloc(1, sizeof(struct swaylock_context)); + context->state = state; + context->output = wl_registry_bind(registry, name, + &wl_output_interface, 1); + wl_list_insert(&state->contexts, &context->link); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + // who cares +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; + +static struct swaylock_state state; + +static void sigalarm_handler(int sig) { signal(SIGALRM, SIG_IGN); - // Hide typing indicator - render_data.auth_state = AUTH_STATE_IDLE; - render(&render_data, config); - wl_display_flush(registry->display); + // TODO: Hide typing indicator signal(SIGALRM, sigalarm_handler); } -void sway_terminate(int exit_code) { - int i; - for (i = 0; i < render_data.surfaces->length; ++i) { - struct window *window = render_data.surfaces->items[i]; - window_teardown(window); - } - list_free(render_data.surfaces); - if (registry) { - registry_teardown(registry); - } - exit(exit_code); -} - -char *password; -int password_size; -enum line_source line_source = LINE_SOURCE_DEFAULT; - -struct lock_config *init_config() { - struct lock_config *config = calloc(1, sizeof(struct lock_config)); - - config->font = strdup("sans-serif"); - config->colors.text = 0x000000FF; - - config->colors.line = 0x000000FF; - config->colors.separator = 0x000000FF; - - config->colors.input_cursor = 0x33DB00FF; - config->colors.backspace_cursor = 0xDB3300FF; - - config->colors.normal.inner_ring = 0x000000BF; - config->colors.normal.outer_ring = 0x337D00FF; - - config->colors.validating.inner_ring = 0x0072FFBF; - config->colors.validating.outer_ring = 0x3300FAFF; - - config->colors.invalid.inner_ring = 0xFA0000BF; - config->colors.invalid.outer_ring = 0x7D3300FF; - - config->radius = 50; - config->thickness = 10; - - return config; -} - -void free_config(struct lock_config *config) { - free(config->font); - free(config); -} - -int function_conversation(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr) { - - const char* msg_style_names[] = { - NULL, - "PAM_PROMPT_ECHO_OFF", - "PAM_PROMPT_ECHO_ON", - "PAM_ERROR_MSG", - "PAM_TEXT_INFO", - }; - - /* PAM expects an array of responses, one for each message */ - struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response)); - *resp = pam_reply; - - for(int i=0; imsg_style], - msg[i]->msg); - - switch (msg[i]->msg_style) { - case PAM_PROMPT_ECHO_OFF: - case PAM_PROMPT_ECHO_ON: - pam_reply[i].resp = password; - break; - - case PAM_ERROR_MSG: - case PAM_TEXT_INFO: - break; - } - } - - return PAM_SUCCESS; -} - -/** - * Note: PAM will free() 'password' during the process - */ -bool verify_password() { - struct passwd *passwd = getpwuid(getuid()); - char *username = passwd->pw_name; - - const struct pam_conv local_conversation = { function_conversation, NULL }; - pam_handle_t *local_auth_handle = NULL; - int pam_err; - if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { - sway_abort("PAM returned %d\n", pam_err); - } - if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { - return false; - } - if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { - return false; - } - return true; -} - -void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) { - int redraw_screen = 0; - char *password_realloc; - int i; - - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - switch (sym) { - case XKB_KEY_KP_Enter: - case XKB_KEY_Return: - render_data.auth_state = AUTH_STATE_VALIDATING; - - render(&render_data, config); - // Make sure our render call will actually be displayed on the screen - wl_dispatch_events(); - - if (verify_password()) { - exit(0); - } - - render_data.auth_state = AUTH_STATE_INVALID; - redraw_screen = 1; - - password_size = 1024; - password = malloc(password_size); - password[0] = '\0'; - break; - case XKB_KEY_BackSpace: - i = strlen(password); - if (i > 0) { - password[i - 1] = '\0'; - render_data.auth_state = AUTH_STATE_BACKSPACE; - redraw_screen = 1; - } - break; - case XKB_KEY_Control_L: - case XKB_KEY_Control_R: - case XKB_KEY_Shift_L: - case XKB_KEY_Shift_R: - case XKB_KEY_Caps_Lock: - case XKB_KEY_Shift_Lock: - case XKB_KEY_Meta_L: - case XKB_KEY_Meta_R: - case XKB_KEY_Alt_L: - case XKB_KEY_Alt_R: - case XKB_KEY_Super_L: - case XKB_KEY_Super_R: - case XKB_KEY_Hyper_L: - case XKB_KEY_Hyper_R: - break; // don't draw screen on modifier keys - case XKB_KEY_Escape: - case XKB_KEY_u: - case XKB_KEY_U: - // clear password buffer on ctrl-u (or escape for i3lock compatibility) - if (sym == XKB_KEY_Escape || xkb_state_mod_name_is_active(registry->input->xkb.state, - XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) { - render_data.auth_state = AUTH_STATE_BACKSPACE; - redraw_screen = 1; - - password_size = 1024; - free(password); - password = malloc(password_size); - password[0] = '\0'; - break; - } - /* fallthrough */ - default: - render_data.auth_state = AUTH_STATE_INPUT; - redraw_screen = 1; - i = strlen(password); - if (i + 1 == password_size) { - password_size += 1024; - password_realloc = realloc(password, password_size); - // reset password if realloc fails. - if (password_realloc == NULL) { - password_size = 1024; - free(password); - password = malloc(password_size); - password[0] = '\0'; - break; - } else { - password = password_realloc; - } - } - password[i] = (char)codepoint; - password[i + 1] = '\0'; - break; - } - if (redraw_screen) { - render(&render_data, config); - wl_dispatch_events(); - // Hide the indicator after a couple of seconds - alarm(5); - } - } -} - -void render_color(struct window *window, uint32_t color) { - cairo_set_source_u32(window->cairo, color); - cairo_paint(window->cairo); -} - -void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) { - double width = cairo_image_surface_get_width(image); - double height = cairo_image_surface_get_height(image); - int wwidth = window->width * window->scale; - int wheight = window->height * window->scale; - - switch (scaling_mode) { - case SCALING_MODE_STRETCH: - cairo_scale(window->cairo, - (double) wwidth / width, - (double) wheight / height); - cairo_set_source_surface(window->cairo, image, 0, 0); - break; - case SCALING_MODE_FILL: - { - double window_ratio = (double) wwidth / wheight; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double) wwidth / width; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - 0, - (double) wheight/2 / scale - height/2); - } else { - double scale = (double) wheight / height; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 / scale - width/2, - 0); - } - break; - } - case SCALING_MODE_FIT: - { - double window_ratio = (double) wwidth / wheight; - double bg_ratio = width / height; - - if (window_ratio > bg_ratio) { - double scale = (double) wheight / height; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 / scale - width/2, - 0); - } else { - double scale = (double) wwidth / width; - cairo_scale(window->cairo, scale, scale); - cairo_set_source_surface(window->cairo, image, - 0, - (double) wheight/2 / scale - height/2); - } - break; - } - case SCALING_MODE_CENTER: - cairo_set_source_surface(window->cairo, image, - (double) wwidth/2 - width/2, - (double) wheight/2 - height/2); - break; - case SCALING_MODE_TILE: - { - cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); - cairo_set_source(window->cairo, pattern); - break; - } - } - - cairo_paint(window->cairo); -} - -cairo_surface_t *load_image(char *image_path) { - cairo_surface_t *image = NULL; - -#ifdef WITH_GDK_PIXBUF - GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err); - if (!pixbuf) { - sway_abort("Failed to load background image: %s", err->message); - } - image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); - g_object_unref(pixbuf); -#else - image = cairo_image_surface_create_from_png(image_path); -#endif //WITH_GDK_PIXBUF - if (!image) { - sway_abort("Failed to read background image."); - } - - return image; -} - int main(int argc, char **argv) { - const char *scaling_mode_str = "fit", *socket_path = NULL; - int i; - void *images = NULL; - config = init_config(); - - render_data.num_images = 0; - render_data.color_set = 0; - render_data.color = 0xFFFFFFFF; - render_data.auth_state = AUTH_STATE_IDLE; - - init_log(L_INFO); - // Install SIGALARM handler (for hiding the typing indicator) signal(SIGALRM, sigalarm_handler); static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"color", required_argument, NULL, 'c'}, {"image", required_argument, NULL, 'i'}, - {"scaling", required_argument, NULL, 0}, + {"scaling", required_argument, NULL, 's'}, {"tiling", no_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, {"socket", required_argument, NULL, 'p'}, {"no-unlock-indicator", no_argument, NULL, 'u'}, {"daemonize", no_argument, NULL, 'f'}, - {"font", required_argument, NULL, 0}, - {"line-uses-ring", no_argument, NULL, 'r'}, - {"line-uses-inside", no_argument, NULL, 's'}, - {"textcolor", required_argument, NULL, 0}, - {"insidevercolor", required_argument, NULL, 0}, - {"insidewrongcolor", required_argument, NULL, 0}, - {"insidecolor", required_argument, NULL, 0}, - {"ringvercolor", required_argument, NULL, 0}, - {"ringwrongcolor", required_argument, NULL, 0}, - {"ringcolor", required_argument, NULL, 0}, - {"linecolor", required_argument, NULL, 0}, - {"separatorcolor", required_argument, NULL, 0}, - {"keyhlcolor", required_argument, NULL, 0}, - {"bshlcolor", required_argument, NULL, 0}, - {"indicator-radius", required_argument, NULL, 0}, - {"indicator-thickness", required_argument, NULL, 0}, {0, 0, 0, 0} }; @@ -390,415 +238,109 @@ int main(int argc, char **argv) { "\n" " -h, --help Show help message and quit.\n" " -c, --color Turn the screen into the given color instead of white.\n" - " --scaling Scaling mode: stretch, fill, fit, center, tile.\n" + " -s, --scaling Scaling mode: stretch, fill, fit, center, tile.\n" " -t, --tiling Same as --scaling=tile.\n" " -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" - " For more information see `man swaylock`\n"; + " -f, --daemonize Detach from the controlling terminal.\n" + " --socket Use the specified socket.\n"; - - registry = registry_poll(); + struct swaylock_args args = { + .mode = BACKGROUND_MODE_SOLID_COLOR, + .color = 0xFFFFFFFF, + .show_indicator = true, + }; + state.args = args; + wlr_log_init(L_DEBUG, NULL); int c; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "hc:i:srtvuf", long_options, &option_index); + c = getopt_long(argc, argv, "hc:i:s:tvuf", long_options, &option_index); if (c == -1) { break; } switch (c) { - case 'c': - { - render_data.color = parse_color(optarg); - render_data.color_set = 1; + case 'c': { + state.args.color = parse_color(optarg); + state.args.mode = BACKGROUND_MODE_SOLID_COLOR; break; } case 'i': - { - char *image_path = strchr(optarg, ':'); - if (image_path == NULL) { - if (render_data.num_images == 0) { - // Provided image without output - render_data.image = load_image(optarg); - render_data.num_images = -1; - } else { - sway_log(L_ERROR, "output must be defined for all --images or no --images"); - exit(EXIT_FAILURE); - } - } else { - // Provided image for all outputs - if (render_data.num_images == 0) { - images = calloc(registry->outputs->length, sizeof(char*) * 2); - } else if (render_data.num_images == -1) { - sway_log(L_ERROR, "output must be defined for all --images or no --images"); - exit(EXIT_FAILURE); - } - - image_path[0] = '\0'; - ((char**) images)[render_data.num_images * 2] = optarg; - ((char**) images)[render_data.num_images++ * 2 + 1] = ++image_path; + // TODO + return 1; + case 's': + state.args.mode = parse_background_mode(optarg); + if (state.args.mode == BACKGROUND_MODE_INVALID) { + return 1; } break; - } case 't': - scaling_mode_str = "tile"; - break; - case 'p': - socket_path = optarg; + // TODO break; case 'v': - fprintf(stdout, "swaylock version " SWAY_VERSION "\n"); - exit(EXIT_SUCCESS); - break; +#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE + fprintf(stdout, "swaylock version %s (%s, branch \"%s\")\n", + SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH); +#else + fprintf(stdout, "version unknown\n"); +#endif + return 0; case 'u': - show_indicator = false; + state.args.show_indicator = false; break; - case 'f': { - pid_t t = fork(); - if (t == -1) { - sway_log(L_ERROR, "daemon call failed"); - exit(EXIT_FAILURE); - } else if (t > 0) { - exit(0); - } - break; - } - case 'r': - if (line_source != LINE_SOURCE_DEFAULT) { - sway_log(L_ERROR, "line source options conflict"); - exit(EXIT_FAILURE); - } - line_source = LINE_SOURCE_RING; - break; - case 's': - if (line_source != LINE_SOURCE_DEFAULT) { - sway_log(L_ERROR, "line source options conflict"); - exit(EXIT_FAILURE); - } - line_source = LINE_SOURCE_INSIDE; - break; - case 0: - if (strcmp(long_options[option_index].name, "font") == 0) { - free(config->font); - config->font = strdup(optarg); - } else if (strcmp(long_options[option_index].name, "scaling") == 0) { - scaling_mode_str = optarg; - } else if (strcmp(long_options[option_index].name, "textcolor") == 0) { - config->colors.text = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "insidevercolor") == 0) { - config->colors.validating.inner_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "insidewrongcolor") == 0) { - config->colors.invalid.inner_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "insidecolor") == 0) { - config->colors.normal.inner_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "ringvercolor") == 0) { - config->colors.validating.outer_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "ringwrongcolor") == 0) { - config->colors.invalid.outer_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "ringcolor") == 0) { - config->colors.normal.outer_ring = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "linecolor") == 0) { - config->colors.line = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "separatorcolor") == 0) { - config->colors.separator = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "keyhlcolor") == 0) { - config->colors.input_cursor = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "bshlcolor") == 0) { - config->colors.backspace_cursor = parse_color(optarg); - } else if (strcmp(long_options[option_index].name, "indicator-radius") == 0) { - config->radius = atoi(optarg); - } else if (strcmp(long_options[option_index].name, "indicator-thickness") == 0) { - config->thickness = atoi(optarg); - } + case 'f': + daemonize(); break; default: fprintf(stderr, "%s", usage); - exit(EXIT_FAILURE); + return 1; } } - render_data.scaling_mode = SCALING_MODE_STRETCH; - if (strcmp(scaling_mode_str, "stretch") == 0) { - render_data.scaling_mode = SCALING_MODE_STRETCH; - } else if (strcmp(scaling_mode_str, "fill") == 0) { - render_data.scaling_mode = SCALING_MODE_FILL; - } else if (strcmp(scaling_mode_str, "fit") == 0) { - render_data.scaling_mode = SCALING_MODE_FIT; - } else if (strcmp(scaling_mode_str, "center") == 0) { - render_data.scaling_mode = SCALING_MODE_CENTER; - } else if (strcmp(scaling_mode_str, "tile") == 0) { - render_data.scaling_mode = SCALING_MODE_TILE; - } else { - sway_abort("Unsupported scaling mode: %s", scaling_mode_str); + wl_list_init(&state.contexts); + + assert(state.display = wl_display_connect(NULL)); + + 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.shm); + + if (wl_list_empty(&state.contexts)) { + wlr_log(L_DEBUG, "Exiting - no outputs to show on."); + return 0; } - password_size = 1024; - password = malloc(password_size); - password[0] = '\0'; - render_data.surfaces = create_list(); - if (!socket_path) { - socket_path = get_socketpath(); - if (!socket_path) { - sway_abort("Unable to retrieve socket path"); - } + struct swaylock_context *context; + wl_list_for_each(context, &state.contexts, link) { + assert(context->surface = + wl_compositor_create_surface(state.compositor)); + + context->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + state.layer_shell, context->surface, context->output, + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen"); + assert(context->layer_surface); + + zwlr_layer_surface_v1_set_size(context->layer_surface, 0, 0); + zwlr_layer_surface_v1_set_anchor(context->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(context->layer_surface, -1); + zwlr_layer_surface_v1_set_keyboard_interactivity( + context->layer_surface, true); + zwlr_layer_surface_v1_add_listener(context->layer_surface, + &layer_surface_listener, context); + wl_surface_commit(context->surface); + wl_display_roundtrip(state.display); } - if (!registry) { - sway_abort("Unable to connect to wayland compositor"); + state.run_display = true; + while (wl_display_dispatch(state.display) != -1 && state.run_display) { + // This space intentionally left blank } - - if (!registry->swaylock) { - sway_abort("swaylock requires the compositor to support the swaylock extension."); - } - - if (registry->pointer) { - // We don't want swaylock to have a pointer - wl_pointer_destroy(registry->pointer); - registry->pointer = NULL; - } - - for (i = 0; i < registry->outputs->length; ++i) { - struct output_state *output = registry->outputs->items[i]; - struct window *window = window_setup(registry, - output->width, output->height, output->scale, true); - if (!window) { - sway_abort("Failed to create surfaces."); - } - list_add(render_data.surfaces, window); - } - - registry->input->notify = notify_key; - - // Different background for the output - if (render_data.num_images >= 1) { - char **displays_paths = images; - render_data.images = calloc(registry->outputs->length, sizeof(cairo_surface_t*)); - - int socketfd = ipc_open_socket(socket_path); - uint32_t len = 0; - char *outputs = ipc_single_command(socketfd, IPC_GET_OUTPUTS, "", &len); - struct json_object *json_outputs = json_tokener_parse(outputs); - - for (i = 0; i < registry->outputs->length; ++i) { - if (displays_paths[i * 2] != NULL) { - for (int j = 0;; ++j) { - if (j >= json_object_array_length(json_outputs)) { - sway_log(L_ERROR, "%s is not an extant output", displays_paths[i * 2]); - exit(EXIT_FAILURE); - } - - struct json_object *dsp_name, *at_j = json_object_array_get_idx(json_outputs, j); - if (!json_object_object_get_ex(at_j, "name", &dsp_name)) { - sway_abort("output doesn't have a name field"); - } - if (!strcmp(displays_paths[i * 2], json_object_get_string(dsp_name))) { - render_data.images[j] = load_image(displays_paths[i * 2 + 1]); - break; - } - } - } - } - - json_object_put(json_outputs); - close(socketfd); - free(displays_paths); - } - - render(&render_data, config); - bool locked = false; - while (wl_display_dispatch(registry->display) != -1) { - if (!locked) { - for (i = 0; i < registry->outputs->length; ++i) { - struct output_state *output = registry->outputs->items[i]; - struct window *window = render_data.surfaces->items[i]; - lock_set_lock_surface(registry->swaylock, output->output, window->surface); - } - locked = true; - } - } - - // Free surfaces - if (render_data.num_images == -1) { - cairo_surface_destroy(render_data.image); - } else if (render_data.num_images >= 1) { - for (i = 0; i < registry->outputs->length; ++i) { - if (render_data.images[i] != NULL) { - cairo_surface_destroy(render_data.images[i]); - } - } - free(render_data.images); - } - - for (i = 0; i < render_data.surfaces->length; ++i) { - struct window *window = render_data.surfaces->items[i]; - window_teardown(window); - } - list_free(render_data.surfaces); - registry_teardown(registry); - - free_config(config); - return 0; } - -void render(struct render_data *render_data, struct lock_config *config) { - int i; - for (i = 0; i < render_data->surfaces->length; ++i) { - sway_log(L_DEBUG, "Render surface %d of %d", i, render_data->surfaces->length); - struct window *window = render_data->surfaces->items[i]; - if (!window_prerender(window) || !window->cairo) { - continue; - } - int wwidth = window->width * window->scale; - int wheight = window->height * window->scale; - - cairo_save(window->cairo); - cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR); - cairo_paint(window->cairo); - cairo_restore(window->cairo); - - // Reset the transformation matrix - cairo_identity_matrix(window->cairo); - - if (render_data->num_images == 0 || render_data->color_set) { - render_color(window, render_data->color); - } - - if (render_data->num_images == -1) { - // One background for all - render_image(window, render_data->image, render_data->scaling_mode); - } else if (render_data->num_images >= 1) { - // Different backgrounds - if (render_data->images[i] != NULL) { - render_image(window, render_data->images[i], render_data->scaling_mode); - } - } - - // Reset the transformation matrix again - cairo_identity_matrix(window->cairo); - - // Draw specific values (copied from i3) - const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; - const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; - - // Add visual indicator - if (show_indicator && render_data->auth_state != AUTH_STATE_IDLE) { - // Draw circle - cairo_set_line_width(window->cairo, config->thickness); - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, 0, 2 * M_PI); - switch (render_data->auth_state) { - case AUTH_STATE_INPUT: - case AUTH_STATE_BACKSPACE: { - cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring); - cairo_fill_preserve(window->cairo); - cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring); - cairo_stroke(window->cairo); - } break; - case AUTH_STATE_VALIDATING: { - cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring); - cairo_fill_preserve(window->cairo); - cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring); - cairo_stroke(window->cairo); - } break; - case AUTH_STATE_INVALID: { - cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring); - cairo_fill_preserve(window->cairo); - cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring); - cairo_stroke(window->cairo); - } break; - default: break; - } - - // Draw a message - char *text = NULL; - cairo_set_source_u32(window->cairo, config->colors.text); - cairo_select_font_face(window->cairo, config->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); - cairo_set_font_size(window->cairo, config->radius/3.0f); - switch (render_data->auth_state) { - case AUTH_STATE_VALIDATING: - text = "verifying"; - break; - case AUTH_STATE_INVALID: - text = "wrong"; - break; - default: break; - } - - if (text) { - cairo_text_extents_t extents; - double x, y; - - cairo_text_extents(window->cairo, text, &extents); - x = wwidth/2 - ((extents.width/2) + extents.x_bearing); - y = wheight/2 - ((extents.height/2) + extents.y_bearing); - - cairo_move_to(window->cairo, x, y); - cairo_show_text(window->cairo, text); - cairo_close_path(window->cairo); - cairo_new_sub_path(window->cairo); - } - - // Typing indicator: Highlight random part on keypress - if (render_data->auth_state == AUTH_STATE_INPUT || render_data->auth_state == AUTH_STATE_BACKSPACE) { - static double highlight_start = 0; - highlight_start += (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_RANGE); - if (render_data->auth_state == AUTH_STATE_INPUT) { - cairo_set_source_u32(window->cairo, config->colors.input_cursor); - } else { - cairo_set_source_u32(window->cairo, config->colors.backspace_cursor); - } - cairo_stroke(window->cairo); - - // Draw borders - cairo_set_source_u32(window->cairo, config->colors.separator); - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_BORDER_THICKNESS); - cairo_stroke(window->cairo); - - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start + TYPE_INDICATOR_RANGE, (highlight_start + TYPE_INDICATOR_RANGE) + TYPE_INDICATOR_BORDER_THICKNESS); - cairo_stroke(window->cairo); - } - - switch(line_source) { - case LINE_SOURCE_RING: - switch(render_data->auth_state) { - case AUTH_STATE_VALIDATING: - cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring); - break; - case AUTH_STATE_INVALID: - cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring); - break; - default: - cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring); - } - break; - case LINE_SOURCE_INSIDE: - switch(render_data->auth_state) { - case AUTH_STATE_VALIDATING: - cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring); - break; - case AUTH_STATE_INVALID: - cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring); - break; - default: - cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring); - break; - } - break; - default: - cairo_set_source_u32(window->cairo, config->colors.line); - break; - } - // Draw inner + outer border of the circle - cairo_set_line_width(window->cairo, 2.0); - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius - config->thickness/2, 0, 2*M_PI); - cairo_stroke(window->cairo); - cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius + config->thickness/2, 0, 2*M_PI); - cairo_stroke(window->cairo); - } - window_render(window); - } -} diff --git a/swaylock/meson.build b/swaylock/meson.build new file mode 100644 index 000000000..5b886ded6 --- /dev/null +++ b/swaylock/meson.build @@ -0,0 +1,18 @@ +executable( + 'swaylock', + 'main.c', + include_directories: [sway_inc], + dependencies: [ + cairo, + client_protos, + gdk_pixbuf, + libpam, + math, + pango, + pangocairo, + wayland_client, + wlroots, + ], + link_with: [lib_sway_common, lib_sway_client], + install: true +) From 402e1d90f01256365f589972b7b2fc4462224ed1 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 2 Apr 2018 22:55:30 -0400 Subject: [PATCH 03/19] Grab keyboard off of the seat --- swaylock/main.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/swaylock/main.c b/swaylock/main.c index 8673694d2..948d661be 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -93,11 +93,51 @@ static void layer_surface_closed(void *data, context->state->run_display = false; } -struct zwlr_layer_surface_v1_listener layer_surface_listener = { +static struct zwlr_layer_surface_v1_listener layer_surface_listener = { .configure = layer_surface_configure, .closed = layer_surface_closed, }; +static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, + uint32_t format, int32_t fd, uint32_t size) { + // TODO +} + +static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { + // Who cares +} + +static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface) { + // Who cares +} + +static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { + // TODO +} + +static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + // TODO +} + +static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, + int32_t rate, int32_t delay) { + // TODO +} + +static struct wl_keyboard_listener keyboard_listener = { + .keymap = keyboard_keymap, + .enter = keyboard_enter, + .leave = keyboard_leave, + .key = keyboard_key, + .modifiers = keyboard_modifiers, + .repeat_info = keyboard_repeat_info, +}; + static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { @@ -143,7 +183,7 @@ static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, // Who cares } -struct wl_pointer_listener pointer_listener = { +static struct wl_pointer_listener pointer_listener = { .enter = wl_pointer_enter, .leave = wl_pointer_leave, .motion = wl_pointer_motion, @@ -157,10 +197,15 @@ struct wl_pointer_listener pointer_listener = { static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { + struct swaylock_state *state = data; if ((caps & WL_SEAT_CAPABILITY_POINTER)) { struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(pointer, &pointer_listener, NULL); } + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(keyboard, &keyboard_listener, state); + } } static void seat_handle_name(void *data, struct wl_seat *wl_seat, @@ -185,7 +230,7 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, wl_seat_interface.name) == 0) { struct wl_seat *seat = wl_registry_bind( registry, name, &wl_seat_interface, 1); - wl_seat_add_listener(seat, &seat_listener, NULL); + wl_seat_add_listener(seat, &seat_listener, state); } 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); From 0bd40ce86bf53b4349aae1e98aae50bcd86b2505 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 2 Apr 2018 23:07:43 -0400 Subject: [PATCH 04/19] Set up an XKB context for the keyboard --- swaylock/main.c | 104 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/swaylock/main.c b/swaylock/main.c index 948d661be..555b1d64e 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -8,11 +8,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "background-image.h" #include "pool-buffer.h" #include "cairo.h" @@ -25,6 +27,59 @@ struct swaylock_args { bool show_indicator; }; +enum mod_bit { + MOD_SHIFT = 1<<0, + MOD_CAPS = 1<<1, + MOD_CTRL = 1<<2, + MOD_ALT = 1<<3, + MOD_MOD2 = 1<<4, + MOD_MOD3 = 1<<5, + MOD_LOGO = 1<<6, + MOD_MOD5 = 1<<7, +}; + +enum mask { + MASK_SHIFT, + MASK_CAPS, + MASK_CTRL, + MASK_ALT, + MASK_MOD2, + MASK_MOD3, + MASK_LOGO, + MASK_MOD5, + MASK_LAST +}; + +const char *XKB_MASK_NAMES[MASK_LAST] = { + XKB_MOD_NAME_SHIFT, + XKB_MOD_NAME_CAPS, + XKB_MOD_NAME_CTRL, + XKB_MOD_NAME_ALT, + "Mod2", + "Mod3", + XKB_MOD_NAME_LOGO, + "Mod5", +}; + +const enum mod_bit XKB_MODS[MASK_LAST] = { + MOD_SHIFT, + MOD_CAPS, + MOD_CTRL, + MOD_ALT, + MOD_MOD2, + MOD_MOD3, + MOD_LOGO, + MOD_MOD5 +}; + +struct swaylock_xkb { + uint32_t modifiers; + struct xkb_state *state; + struct xkb_context *context; + struct xkb_keymap *keymap; + xkb_mod_mask_t masks[MASK_LAST]; +}; + struct swaylock_state { struct wl_display *display; struct wl_compositor *compositor; @@ -32,6 +87,7 @@ struct swaylock_state { struct wl_shm *shm; struct wl_list contexts; struct swaylock_args args; + struct swaylock_xkb xkb; bool run_display; }; @@ -100,7 +156,29 @@ static struct zwlr_layer_surface_v1_listener layer_surface_listener = { static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { - // TODO + struct swaylock_state *state = data; + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format); + exit(1); + } + char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (map_shm == MAP_FAILED) { + close(fd); + wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting"); + exit(1); + } + struct xkb_keymap *keymap = xkb_keymap_new_from_string( + state->xkb.context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0); + munmap(map_shm, size); + close(fd); + assert(keymap); + struct xkb_state *xkb_state = xkb_state_new(keymap); + assert(xkb_state); + xkb_keymap_unref(state->xkb.keymap); + xkb_state_unref(state->xkb.state); + state->xkb.keymap = keymap; + state->xkb.state = xkb_state; } static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, @@ -114,14 +192,30 @@ static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, } static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - // TODO + uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) { + struct swaylock_state *state = data; + enum wl_keyboard_key_state key_state = _key_state; + xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb.state, key + 8); + uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ? + key + 8 : 0; + uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode); + wlr_log(L_DEBUG, "%c %d", codepoint, sym); } static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { - // TODO + struct swaylock_state *state = data; + xkb_state_update_mask(state->xkb.state, + mods_depressed, mods_latched, mods_locked, 0, 0, group); + xkb_mod_mask_t mask = xkb_state_serialize_mods(state->xkb.state, + XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); + state->xkb.modifiers = 0; + for (uint32_t i = 0; i < MASK_LAST; ++i) { + if (mask & state->xkb.masks[i]) { + state->xkb.modifiers |= XKB_MODS[i]; + } + } } static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, @@ -345,7 +439,7 @@ int main(int argc, char **argv) { } wl_list_init(&state.contexts); - + state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); assert(state.display = wl_display_connect(NULL)); struct wl_registry *registry = wl_display_get_registry(state.display); From 1008d4cc9105e18074f8152ec5d6679aef8ebc5f Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 2 Apr 2018 23:14:37 -0400 Subject: [PATCH 05/19] Split seat code into its own file --- include/swaylock/seat.h | 38 ++++++ include/swaylock/swaylock.h | 85 +++++-------- swaylock/main.c | 246 +----------------------------------- swaylock/meson.build | 6 +- swaylock/seat.c | 187 +++++++++++++++++++++++++++ 5 files changed, 262 insertions(+), 300 deletions(-) create mode 100644 include/swaylock/seat.h create mode 100644 swaylock/seat.c diff --git a/include/swaylock/seat.h b/include/swaylock/seat.h new file mode 100644 index 000000000..44bc37d55 --- /dev/null +++ b/include/swaylock/seat.h @@ -0,0 +1,38 @@ +#ifndef _SWAYLOCK_SEAT_H +#define _SWAYLOCK_SEAT_H +#include + +enum mod_bit { + MOD_SHIFT = 1<<0, + MOD_CAPS = 1<<1, + MOD_CTRL = 1<<2, + MOD_ALT = 1<<3, + MOD_MOD2 = 1<<4, + MOD_MOD3 = 1<<5, + MOD_LOGO = 1<<6, + MOD_MOD5 = 1<<7, +}; + +enum mask { + MASK_SHIFT, + MASK_CAPS, + MASK_CTRL, + MASK_ALT, + MASK_MOD2, + MASK_MOD3, + MASK_LOGO, + MASK_MOD5, + MASK_LAST +}; + +struct swaylock_xkb { + uint32_t modifiers; + struct xkb_state *state; + struct xkb_context *context; + struct xkb_keymap *keymap; + xkb_mod_mask_t masks[MASK_LAST]; +}; + +extern const struct wl_seat_listener seat_listener; + +#endif diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index eeed094e0..e2673aae1 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -1,66 +1,41 @@ #ifndef _SWAYLOCK_H #define _SWAYLOCK_H +#include +#include +#include +#include "background-image.h" +#include "cairo.h" +#include "pool-buffer.h" +#include "swaylock/seat.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" -#include "client/cairo.h" - -enum scaling_mode { - SCALING_MODE_STRETCH, - SCALING_MODE_FILL, - SCALING_MODE_FIT, - SCALING_MODE_CENTER, - SCALING_MODE_TILE, -}; - -enum auth_state { - AUTH_STATE_IDLE, - AUTH_STATE_INPUT, - AUTH_STATE_BACKSPACE, - AUTH_STATE_VALIDATING, - AUTH_STATE_INVALID, -}; - -enum line_source { - LINE_SOURCE_DEFAULT, - LINE_SOURCE_RING, - LINE_SOURCE_INSIDE, -}; - -struct render_data { - list_t *surfaces; - // Output specific images - cairo_surface_t **images; - // OR one image for all outputs: - cairo_surface_t *image; - int num_images; - int color_set; +struct swaylock_args { uint32_t color; - enum scaling_mode scaling_mode; - enum auth_state auth_state; + enum background_mode mode; + bool show_indicator; }; -struct lock_colors { - uint32_t inner_ring; - uint32_t outer_ring; +struct swaylock_state { + struct wl_display *display; + struct wl_compositor *compositor; + struct zwlr_layer_shell_v1 *layer_shell; + struct wl_shm *shm; + struct wl_list contexts; + struct swaylock_args args; + struct swaylock_xkb xkb; + bool run_display; }; -struct lock_config { - char *font; - - struct { - uint32_t text; - uint32_t line; - uint32_t separator; - uint32_t input_cursor; - uint32_t backspace_cursor; - struct lock_colors normal; - struct lock_colors validating; - struct lock_colors invalid; - } colors; - - int radius; - int thickness; +struct swaylock_context { + cairo_surface_t *image; + struct swaylock_state *state; + struct wl_output *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; + struct wl_list link; }; -void render(struct render_data* render_data, struct lock_config *config); - #endif diff --git a/swaylock/main.c b/swaylock/main.c index 555b1d64e..7602e47e1 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -8,101 +8,19 @@ #include #include #include -#include #include #include #include #include #include -#include +#include "swaylock/seat.h" +#include "swaylock/swaylock.h" #include "background-image.h" #include "pool-buffer.h" #include "cairo.h" #include "util.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -struct swaylock_args { - uint32_t color; - enum background_mode mode; - bool show_indicator; -}; - -enum mod_bit { - MOD_SHIFT = 1<<0, - MOD_CAPS = 1<<1, - MOD_CTRL = 1<<2, - MOD_ALT = 1<<3, - MOD_MOD2 = 1<<4, - MOD_MOD3 = 1<<5, - MOD_LOGO = 1<<6, - MOD_MOD5 = 1<<7, -}; - -enum mask { - MASK_SHIFT, - MASK_CAPS, - MASK_CTRL, - MASK_ALT, - MASK_MOD2, - MASK_MOD3, - MASK_LOGO, - MASK_MOD5, - MASK_LAST -}; - -const char *XKB_MASK_NAMES[MASK_LAST] = { - XKB_MOD_NAME_SHIFT, - XKB_MOD_NAME_CAPS, - XKB_MOD_NAME_CTRL, - XKB_MOD_NAME_ALT, - "Mod2", - "Mod3", - XKB_MOD_NAME_LOGO, - "Mod5", -}; - -const enum mod_bit XKB_MODS[MASK_LAST] = { - MOD_SHIFT, - MOD_CAPS, - MOD_CTRL, - MOD_ALT, - MOD_MOD2, - MOD_MOD3, - MOD_LOGO, - MOD_MOD5 -}; - -struct swaylock_xkb { - uint32_t modifiers; - struct xkb_state *state; - struct xkb_context *context; - struct xkb_keymap *keymap; - xkb_mod_mask_t masks[MASK_LAST]; -}; - -struct swaylock_state { - struct wl_display *display; - struct wl_compositor *compositor; - struct zwlr_layer_shell_v1 *layer_shell; - struct wl_shm *shm; - struct wl_list contexts; - struct swaylock_args args; - struct swaylock_xkb xkb; - bool run_display; -}; - -struct swaylock_context { - cairo_surface_t *image; - struct swaylock_state *state; - struct wl_output *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; - struct wl_list link; -}; - static void daemonize() { if (fork() == 0) { int devnull = open("/dev/null", O_RDWR); @@ -149,169 +67,11 @@ static void layer_surface_closed(void *data, context->state->run_display = false; } -static struct zwlr_layer_surface_v1_listener layer_surface_listener = { +static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { .configure = layer_surface_configure, .closed = layer_surface_closed, }; -static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, - uint32_t format, int32_t fd, uint32_t size) { - struct swaylock_state *state = data; - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - close(fd); - wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format); - exit(1); - } - char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (map_shm == MAP_FAILED) { - close(fd); - wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting"); - exit(1); - } - struct xkb_keymap *keymap = xkb_keymap_new_from_string( - state->xkb.context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0); - munmap(map_shm, size); - close(fd); - assert(keymap); - struct xkb_state *xkb_state = xkb_state_new(keymap); - assert(xkb_state); - xkb_keymap_unref(state->xkb.keymap); - xkb_state_unref(state->xkb.state); - state->xkb.keymap = keymap; - state->xkb.state = xkb_state; -} - -static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - // Who cares -} - -static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, struct wl_surface *surface) { - // Who cares -} - -static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) { - struct swaylock_state *state = data; - enum wl_keyboard_key_state key_state = _key_state; - xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb.state, key + 8); - uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ? - key + 8 : 0; - uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode); - wlr_log(L_DEBUG, "%c %d", codepoint, sym); -} - -static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, - uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) { - struct swaylock_state *state = data; - xkb_state_update_mask(state->xkb.state, - mods_depressed, mods_latched, mods_locked, 0, 0, group); - xkb_mod_mask_t mask = xkb_state_serialize_mods(state->xkb.state, - XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); - state->xkb.modifiers = 0; - for (uint32_t i = 0; i < MASK_LAST; ++i) { - if (mask & state->xkb.masks[i]) { - state->xkb.modifiers |= XKB_MODS[i]; - } - } -} - -static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, - int32_t rate, int32_t delay) { - // TODO -} - -static struct wl_keyboard_listener keyboard_listener = { - .keymap = keyboard_keymap, - .enter = keyboard_enter, - .leave = keyboard_leave, - .key = keyboard_key, - .modifiers = keyboard_modifiers, - .repeat_info = keyboard_repeat_info, -}; - -static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, struct wl_surface *surface, - wl_fixed_t surface_x, wl_fixed_t surface_y) { - wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0); -} - -static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, struct wl_surface *surface) { - // Who cares -} - -static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, - uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - // Who cares -} - -static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, - uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - // Who cares -} - -static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, - uint32_t time, uint32_t axis, wl_fixed_t value) { - // Who cares -} - -static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { - // Who cares -} - -static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, - uint32_t axis_source) { - // Who cares -} - -static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, - uint32_t time, uint32_t axis) { - // Who cares -} - -static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, - uint32_t axis, int32_t discrete) { - // Who cares -} - -static struct wl_pointer_listener pointer_listener = { - .enter = wl_pointer_enter, - .leave = wl_pointer_leave, - .motion = wl_pointer_motion, - .button = wl_pointer_button, - .axis = wl_pointer_axis, - .frame = wl_pointer_frame, - .axis_source = wl_pointer_axis_source, - .axis_stop = wl_pointer_axis_stop, - .axis_discrete = wl_pointer_axis_discrete, -}; - -static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, - enum wl_seat_capability caps) { - struct swaylock_state *state = data; - if ((caps & WL_SEAT_CAPABILITY_POINTER)) { - struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(pointer, &pointer_listener, NULL); - } - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { - struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(keyboard, &keyboard_listener, state); - } -} - -static void seat_handle_name(void *data, struct wl_seat *wl_seat, - const char *name) { - // Who cares -} - -const struct wl_seat_listener seat_listener = { - .capabilities = seat_handle_capabilities, - .name = seat_handle_name, -}; - static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct swaylock_state *state = data; diff --git a/swaylock/meson.build b/swaylock/meson.build index 5b886ded6..2f2733f84 100644 --- a/swaylock/meson.build +++ b/swaylock/meson.build @@ -1,6 +1,8 @@ executable( - 'swaylock', - 'main.c', + 'swaylock', [ + 'main.c', + 'seat.c' + ], include_directories: [sway_inc], dependencies: [ cairo, diff --git a/swaylock/seat.c b/swaylock/seat.c new file mode 100644 index 000000000..522200f23 --- /dev/null +++ b/swaylock/seat.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include "swaylock/swaylock.h" +#include "swaylock/seat.h" + +const char *XKB_MASK_NAMES[MASK_LAST] = { + XKB_MOD_NAME_SHIFT, + XKB_MOD_NAME_CAPS, + XKB_MOD_NAME_CTRL, + XKB_MOD_NAME_ALT, + "Mod2", + "Mod3", + XKB_MOD_NAME_LOGO, + "Mod5", +}; + +const enum mod_bit XKB_MODS[MASK_LAST] = { + MOD_SHIFT, + MOD_CAPS, + MOD_CTRL, + MOD_ALT, + MOD_MOD2, + MOD_MOD3, + MOD_LOGO, + MOD_MOD5 +}; + +static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, + uint32_t format, int32_t fd, uint32_t size) { + struct swaylock_state *state = data; + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format); + exit(1); + } + char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (map_shm == MAP_FAILED) { + close(fd); + wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting"); + exit(1); + } + struct xkb_keymap *keymap = xkb_keymap_new_from_string( + state->xkb.context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0); + munmap(map_shm, size); + close(fd); + assert(keymap); + struct xkb_state *xkb_state = xkb_state_new(keymap); + assert(xkb_state); + xkb_keymap_unref(state->xkb.keymap); + xkb_state_unref(state->xkb.state); + state->xkb.keymap = keymap; + state->xkb.state = xkb_state; +} + +static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { + // Who cares +} + +static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, struct wl_surface *surface) { + // Who cares +} + +static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) { + struct swaylock_state *state = data; + enum wl_keyboard_key_state key_state = _key_state; + xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb.state, key + 8); + uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ? + key + 8 : 0; + uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode); + wlr_log(L_DEBUG, "%c %d", codepoint, sym); +} + +static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + struct swaylock_state *state = data; + xkb_state_update_mask(state->xkb.state, + mods_depressed, mods_latched, mods_locked, 0, 0, group); + xkb_mod_mask_t mask = xkb_state_serialize_mods(state->xkb.state, + XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); + state->xkb.modifiers = 0; + for (uint32_t i = 0; i < MASK_LAST; ++i) { + if (mask & state->xkb.masks[i]) { + state->xkb.modifiers |= XKB_MODS[i]; + } + } +} + +static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, + int32_t rate, int32_t delay) { + // TODO +} + +static const struct wl_keyboard_listener keyboard_listener = { + .keymap = keyboard_keymap, + .enter = keyboard_enter, + .leave = keyboard_leave, + .key = keyboard_key, + .modifiers = keyboard_modifiers, + .repeat_info = keyboard_repeat_info, +}; + +static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) { + wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0); +} + +static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) { + // Who cares +} + +static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + // Who cares +} + +static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + // Who cares +} + +static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) { + // Who cares +} + +static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { + // Who cares +} + +static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) { + // Who cares +} + +static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) { + // Who cares +} + +static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) { + // Who cares +} + +static const struct wl_pointer_listener pointer_listener = { + .enter = wl_pointer_enter, + .leave = wl_pointer_leave, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = wl_pointer_axis, + .frame = wl_pointer_frame, + .axis_source = wl_pointer_axis_source, + .axis_stop = wl_pointer_axis_stop, + .axis_discrete = wl_pointer_axis_discrete, +}; + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) { + struct swaylock_state *state = data; + if ((caps & WL_SEAT_CAPABILITY_POINTER)) { + struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(pointer, &pointer_listener, NULL); + } + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(keyboard, &keyboard_listener, state); + } +} + +static void seat_handle_name(void *data, struct wl_seat *wl_seat, + const char *name) { + // Who cares +} + +const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = seat_handle_name, +}; From 1fe3cb8965e70f8f05f28512e66d76c49453a196 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 2 Apr 2018 23:43:19 -0400 Subject: [PATCH 06/19] Link swaylock to xkbcommon --- swaylock/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/swaylock/meson.build b/swaylock/meson.build index 2f2733f84..2a1f029a5 100644 --- a/swaylock/meson.build +++ b/swaylock/meson.build @@ -12,6 +12,7 @@ executable( math, pango, pangocairo, + xkbcommon, wayland_client, wlroots, ], From 066143adef7adc6e76e43e1990db2f75fe984b42 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 14:31:30 -0400 Subject: [PATCH 07/19] Add password buffer, refactor rendering/surfaces --- common/meson.build | 1 + common/unicode.c | 101 ++++++++++++++++++++++++++++++++++++ include/swaylock/swaylock.h | 15 +++++- include/unicode.h | 33 ++++++++++++ swaylock/main.c | 79 +++++++++++----------------- swaylock/meson.build | 2 + swaylock/password.c | 57 ++++++++++++++++++++ swaylock/render.c | 21 ++++++++ swaylock/seat.c | 4 +- 9 files changed, 262 insertions(+), 51 deletions(-) create mode 100644 common/unicode.c create mode 100644 include/unicode.h create mode 100644 swaylock/password.c create mode 100644 swaylock/render.c diff --git a/common/meson.build b/common/meson.build index 851e7bbf5..44a295084 100644 --- a/common/meson.build +++ b/common/meson.build @@ -9,6 +9,7 @@ lib_sway_common = static_library( 'pango.c', 'readline.c', 'stringop.c', + 'unicode.c', 'util.c' ), dependencies: [ diff --git a/common/unicode.c b/common/unicode.c new file mode 100644 index 000000000..38a9b48eb --- /dev/null +++ b/common/unicode.c @@ -0,0 +1,101 @@ +#include +#include +#include "unicode.h" + +size_t utf8_chsize(uint32_t ch) { + if (ch < 0x80) { + return 1; + } else if (ch < 0x800) { + return 2; + } else if (ch < 0x10000) { + return 3; + } + return 4; +} + +static const uint8_t masks[] = { + 0x7F, + 0x1F, + 0x0F, + 0x07, + 0x03, + 0x01 +}; + +uint32_t utf8_decode(const char **char_str) { + uint8_t **s = (uint8_t **)char_str; + + uint32_t cp = 0; + if (**s < 128) { + // shortcut + cp = **s; + ++*s; + return cp; + } + int size = utf8_size((char *)*s); + if (size == -1) { + ++*s; + return UTF8_INVALID; + } + uint8_t mask = masks[size - 1]; + cp = **s & mask; + ++*s; + while (--size) { + cp <<= 6; + cp |= **s & 0x3f; + ++*s; + } + return cp; +} + +size_t utf8_encode(char *str, uint32_t ch) { + size_t len = 0; + uint8_t first; + + if (ch < 0x80) { + first = 0; + len = 1; + } else if (ch < 0x800) { + first = 0xc0; + len = 2; + } else if (ch < 0x10000) { + first = 0xe0; + len = 3; + } else { + first = 0xf0; + len = 4; + } + + for (size_t i = len - 1; i > 0; --i) { + str[i] = (ch & 0x3f) | 0x80; + ch >>= 6; + } + + str[0] = ch | first; + return len; +} + + +static const struct { + uint8_t mask; + uint8_t result; + int octets; +} sizes[] = { + { 0x80, 0x00, 1 }, + { 0xE0, 0xC0, 2 }, + { 0xF0, 0xE0, 3 }, + { 0xF8, 0xF0, 4 }, + { 0xFC, 0xF8, 5 }, + { 0xFE, 0xF8, 6 }, + { 0x80, 0x80, -1 }, +}; + +int utf8_size(const char *s) { + uint8_t c = (uint8_t)*s; + for (size_t i = 0; i < sizeof(sizes) / 2; ++i) { + if ((c & sizes[i].mask) == sizes[i].result) { + return sizes[i].octets; + } + } + return -1; +} diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index e2673aae1..f3b0b58bb 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -15,18 +15,25 @@ struct swaylock_args { bool show_indicator; }; +struct swaylock_password { + size_t size; + size_t len; + char *buffer; +}; + struct swaylock_state { struct wl_display *display; struct wl_compositor *compositor; struct zwlr_layer_shell_v1 *layer_shell; struct wl_shm *shm; - struct wl_list contexts; + struct wl_list surfaces; struct swaylock_args args; + struct swaylock_password password; struct swaylock_xkb xkb; bool run_display; }; -struct swaylock_context { +struct swaylock_surface { cairo_surface_t *image; struct swaylock_state *state; struct wl_output *output; @@ -38,4 +45,8 @@ struct swaylock_context { struct wl_list link; }; +void swaylock_handle_key(struct swaylock_state *state, + xkb_keysym_t keysym, uint32_t codepoint); +void render_frame(struct swaylock_surface *surface); + #endif diff --git a/include/unicode.h b/include/unicode.h new file mode 100644 index 000000000..e2ee9588c --- /dev/null +++ b/include/unicode.h @@ -0,0 +1,33 @@ +#ifndef _SWAY_UNICODE_H +#define _SWAY_UNICODE_H +#include +#include + +// Technically UTF-8 supports up to 6 byte codepoints, but Unicode itself +// doesn't really bother with more than 4. +#define UTF8_MAX_SIZE 4 + +#define UTF8_INVALID 0x80 + +/** + * Grabs the next UTF-8 character and advances the string pointer + */ +uint32_t utf8_decode(const char **str); + +/** + * Encodes a character as UTF-8 and returns the length of that character. + */ +size_t utf8_encode(char *str, uint32_t ch); + +/** + * Returns the size of the next UTF-8 character + */ +int utf8_size(const char *str); + +/** + * Returns the size of a UTF-8 character + */ +size_t utf8_chsize(uint32_t ch); + +#endif + diff --git a/swaylock/main.c b/swaylock/main.c index 7602e47e1..c8fdc2f44 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -32,39 +32,22 @@ static void daemonize() { } } -static void render_frame(struct swaylock_context *context) { - struct swaylock_state *state = context->state; - context->current_buffer = get_next_buffer(state->shm, - context->buffers, context->width, context->height); - cairo_t *cairo = context->current_buffer->cairo; - if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { - cairo_set_source_u32(cairo, state->args.color); - cairo_paint(cairo); - } else { - render_background_image(cairo, context->image, - state->args.mode, context->width, context->height); - } - wl_surface_attach(context->surface, context->current_buffer->buffer, 0, 0); - wl_surface_damage(context->surface, 0, 0, context->width, context->height); - wl_surface_commit(context->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 swaylock_context *context = data; - context->width = width; - context->height = height; - zwlr_layer_surface_v1_ack_configure(surface, serial); - render_frame(context); + struct swaylock_surface *surface = data; + surface->width = width; + surface->height = height; + zwlr_layer_surface_v1_ack_configure(layer_surface, serial); + render_frame(surface); } static void layer_surface_closed(void *data, - struct zwlr_layer_surface_v1 *surface) { - struct swaylock_context *context = data; - zwlr_layer_surface_v1_destroy(context->layer_surface); - wl_surface_destroy(context->surface); - context->state->run_display = false; + struct zwlr_layer_surface_v1 *layer_surface) { + struct swaylock_surface *surface = data; + zwlr_layer_surface_v1_destroy(surface->layer_surface); + wl_surface_destroy(surface->surface); + surface->state->run_display = false; } static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { @@ -89,12 +72,12 @@ static void handle_global(void *data, struct wl_registry *registry, state->layer_shell = wl_registry_bind( registry, name, &zwlr_layer_shell_v1_interface, 1); } else if (strcmp(interface, wl_output_interface.name) == 0) { - struct swaylock_context *context = - calloc(1, sizeof(struct swaylock_context)); - context->state = state; - context->output = wl_registry_bind(registry, name, + struct swaylock_surface *surface = + calloc(1, sizeof(struct swaylock_surface)); + surface->state = state; + surface->output = wl_registry_bind(registry, name, &wl_output_interface, 1); - wl_list_insert(&state->contexts, &context->link); + wl_list_insert(&state->surfaces, &surface->link); } } @@ -198,7 +181,7 @@ int main(int argc, char **argv) { } } - wl_list_init(&state.contexts); + wl_list_init(&state.surfaces); state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); assert(state.display = wl_display_connect(NULL)); @@ -207,33 +190,33 @@ int main(int argc, char **argv) { wl_display_roundtrip(state.display); assert(state.compositor && state.layer_shell && state.shm); - if (wl_list_empty(&state.contexts)) { + if (wl_list_empty(&state.surfaces)) { wlr_log(L_DEBUG, "Exiting - no outputs to show on."); return 0; } - struct swaylock_context *context; - wl_list_for_each(context, &state.contexts, link) { - assert(context->surface = + struct swaylock_surface *surface; + wl_list_for_each(surface, &state.surfaces, link) { + assert(surface->surface = wl_compositor_create_surface(state.compositor)); - context->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - state.layer_shell, context->surface, context->output, + surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + state.layer_shell, surface->surface, surface->output, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen"); - assert(context->layer_surface); + assert(surface->layer_surface); - zwlr_layer_surface_v1_set_size(context->layer_surface, 0, 0); - zwlr_layer_surface_v1_set_anchor(context->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(context->layer_surface, -1); + zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1); zwlr_layer_surface_v1_set_keyboard_interactivity( - context->layer_surface, true); - zwlr_layer_surface_v1_add_listener(context->layer_surface, - &layer_surface_listener, context); - wl_surface_commit(context->surface); + surface->layer_surface, true); + zwlr_layer_surface_v1_add_listener(surface->layer_surface, + &layer_surface_listener, surface); + wl_surface_commit(surface->surface); wl_display_roundtrip(state.display); } diff --git a/swaylock/meson.build b/swaylock/meson.build index 2a1f029a5..3cde47a44 100644 --- a/swaylock/meson.build +++ b/swaylock/meson.build @@ -1,6 +1,8 @@ executable( 'swaylock', [ 'main.c', + 'password.c', + 'render.c', 'seat.c' ], include_directories: [sway_inc], diff --git a/swaylock/password.c b/swaylock/password.c new file mode 100644 index 000000000..da67205de --- /dev/null +++ b/swaylock/password.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include "swaylock/swaylock.h" +#include "swaylock/seat.h" +#include "unicode.h" + +static void backspace(struct swaylock_password *pw) { + if (pw->len != 0) { + pw->buffer[--pw->len] = 0; + } +} + +static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { + if (!pw->buffer) { + pw->size = 8; + if (!(pw->buffer = malloc(pw->size))) { + // TODO: Display error + return; + } + pw->buffer[0] = 0; + } + size_t utf8_size = utf8_chsize(codepoint); + if (pw->len + utf8_size + 1 >= pw->size) { + size_t size = pw->size * 2; + char *buffer = realloc(pw->buffer, size); + if (!buffer) { + // TODO: Display error + return; + } + pw->size = size; + pw->buffer = buffer; + } + utf8_encode(&pw->buffer[pw->len], codepoint); + pw->buffer[pw->len + utf8_size] = 0; + pw->len += utf8_size; +} + +void swaylock_handle_key(struct swaylock_state *state, + xkb_keysym_t keysym, uint32_t codepoint) { + switch (keysym) { + case XKB_KEY_KP_Enter: /* fallthrough */ + case XKB_KEY_Return: + // TODO: Attempt password + break; + case XKB_KEY_BackSpace: + backspace(&state->password); + break; + default: + if (codepoint) { + append_ch(&state->password, codepoint); + } + break; + } +} diff --git a/swaylock/render.c b/swaylock/render.c new file mode 100644 index 000000000..8fc472819 --- /dev/null +++ b/swaylock/render.c @@ -0,0 +1,21 @@ +#include +#include "cairo.h" +#include "background-image.h" +#include "swaylock/swaylock.h" + +void render_frame(struct swaylock_surface *surface) { + struct swaylock_state *state = surface->state; + surface->current_buffer = get_next_buffer(state->shm, + surface->buffers, surface->width, surface->height); + cairo_t *cairo = surface->current_buffer->cairo; + if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { + cairo_set_source_u32(cairo, state->args.color); + cairo_paint(cairo); + } else { + render_background_image(cairo, surface->image, + state->args.mode, surface->width, surface->height); + } + 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/swaylock/seat.c b/swaylock/seat.c index 522200f23..6c46bb417 100644 --- a/swaylock/seat.c +++ b/swaylock/seat.c @@ -73,7 +73,9 @@ static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ? key + 8 : 0; uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode); - wlr_log(L_DEBUG, "%c %d", codepoint, sym); + if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED) { + swaylock_handle_key(state, sym, codepoint); + } } static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, From e902de34db2354335c1cbd6baf2fcf7e82509b63 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 14:49:56 -0400 Subject: [PATCH 08/19] Verify passwords --- swaylock/password.c | 58 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/swaylock/password.c b/swaylock/password.c index da67205de..9af7fe16e 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -7,6 +9,58 @@ #include "swaylock/seat.h" #include "unicode.h" +static int function_conversation(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *data) { + struct swaylock_password *pw = data; + /* PAM expects an array of responses, one for each message */ + struct pam_response *pam_reply = calloc( + num_msg, sizeof(struct pam_response)); + *resp = pam_reply; + for (int i = 0; i < num_msg; ++i) { + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + pam_reply[i].resp = pw->buffer; + break; + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + break; + } + } + return PAM_SUCCESS; +} + +static bool attempt_password(struct swaylock_password *pw) { + struct passwd *passwd = getpwuid(getuid()); + char *username = passwd->pw_name; + const struct pam_conv local_conversation = { + function_conversation, pw + }; + pam_handle_t *local_auth_handle = NULL; + int pam_err; + if ((pam_err = pam_start("swaylock", username, + &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { + wlr_log(L_ERROR, "PAM returned error %d", pam_err); + } + if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { + wlr_log(L_ERROR, "pam_authenticate failed"); + goto fail; + } + if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { + wlr_log(L_ERROR, "pam_end failed"); + goto fail; + } + // PAM freed this + pw->buffer = NULL; + pw->len = pw->size = 0; + return true; +fail: + // PAM freed this + pw->buffer = NULL; + pw->len = pw->size = 0; + return false; +} + static void backspace(struct swaylock_password *pw) { if (pw->len != 0) { pw->buffer[--pw->len] = 0; @@ -43,7 +97,9 @@ void swaylock_handle_key(struct swaylock_state *state, switch (keysym) { case XKB_KEY_KP_Enter: /* fallthrough */ case XKB_KEY_Return: - // TODO: Attempt password + if (attempt_password(&state->password)) { + exit(0); + } break; case XKB_KEY_BackSpace: backspace(&state->password); From d053acbed6fea0f73eb79ac800c1342f8afadeb8 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 15:04:31 -0400 Subject: [PATCH 09/19] R E N D E R I N G --- common/background-image.c | 3 +- include/swaylock/swaylock.h | 10 +++ swaylock/main.c | 14 ++++- swaylock/password.c | 20 ++++-- swaylock/render.c | 121 +++++++++++++++++++++++++++++++++++- 5 files changed, 159 insertions(+), 9 deletions(-) diff --git a/common/background-image.c b/common/background-image.c index 1a6c0df05..e5fb4433e 100644 --- a/common/background-image.c +++ b/common/background-image.c @@ -28,7 +28,8 @@ cairo_surface_t *load_background_image(const char *path) { GError *err = NULL; GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); if (!pixbuf) { - wlr_log(L_ERROR, "Failed to load background image."); + wlr_log(L_ERROR, "Failed to load background image (%s).", + err->message); return false; } image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index f3b0b58bb..ddca633d8 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -9,6 +9,14 @@ #include "swaylock/seat.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +enum auth_state { + AUTH_STATE_IDLE, + AUTH_STATE_INPUT, + AUTH_STATE_BACKSPACE, + AUTH_STATE_VALIDATING, + AUTH_STATE_INVALID, +}; + struct swaylock_args { uint32_t color; enum background_mode mode; @@ -30,6 +38,7 @@ struct swaylock_state { struct swaylock_args args; struct swaylock_password password; struct swaylock_xkb xkb; + enum auth_state auth_state; bool run_display; }; @@ -48,5 +57,6 @@ struct swaylock_surface { void swaylock_handle_key(struct swaylock_state *state, xkb_keysym_t keysym, uint32_t codepoint); void render_frame(struct swaylock_surface *surface); +void render_frames(struct swaylock_state *state); #endif diff --git a/swaylock/main.c b/swaylock/main.c index c8fdc2f44..ce337e247 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -133,6 +133,7 @@ int main(int argc, char **argv) { .color = 0xFFFFFFFF, .show_indicator = true, }; + cairo_surface_t *background_image = NULL; state.args = args; wlr_log_init(L_DEBUG, NULL); @@ -150,8 +151,13 @@ int main(int argc, char **argv) { break; } case 'i': - // TODO - return 1; + // TODO: Multiple background images (bleh) + background_image = load_background_image(optarg); + if (!background_image) { + return 1; + } + state.args.mode = BACKGROUND_MODE_FILL; + break; case 's': state.args.mode = parse_background_mode(optarg); if (state.args.mode == BACKGROUND_MODE_INVALID) { @@ -159,7 +165,7 @@ int main(int argc, char **argv) { } break; case 't': - // TODO + state.args.mode = BACKGROUND_MODE_TILE; break; case 'v': #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE @@ -197,6 +203,8 @@ int main(int argc, char **argv) { struct swaylock_surface *surface; wl_list_for_each(surface, &state.surfaces, link) { + surface->image = background_image; + assert(surface->surface = wl_compositor_create_surface(state.compositor)); diff --git a/swaylock/password.c b/swaylock/password.c index 9af7fe16e..2bdf151fd 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -50,21 +50,23 @@ static bool attempt_password(struct swaylock_password *pw) { wlr_log(L_ERROR, "pam_end failed"); goto fail; } - // PAM freed this + // PAM frees this pw->buffer = NULL; pw->len = pw->size = 0; return true; fail: - // PAM freed this + // PAM frees this pw->buffer = NULL; pw->len = pw->size = 0; return false; } -static void backspace(struct swaylock_password *pw) { +static bool backspace(struct swaylock_password *pw) { if (pw->len != 0) { pw->buffer[--pw->len] = 0; + return true; } + return false; } static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { @@ -97,17 +99,27 @@ void swaylock_handle_key(struct swaylock_state *state, switch (keysym) { case XKB_KEY_KP_Enter: /* fallthrough */ case XKB_KEY_Return: + state->auth_state = AUTH_STATE_VALIDATING; + render_frames(state); if (attempt_password(&state->password)) { exit(0); } + state->auth_state = AUTH_STATE_INVALID; + render_frames(state); break; case XKB_KEY_BackSpace: - backspace(&state->password); + if (backspace(&state->password)) { + state->auth_state = AUTH_STATE_BACKSPACE; + render_frames(state); + } break; default: if (codepoint) { append_ch(&state->password, codepoint); + state->auth_state = AUTH_STATE_INPUT; + render_frames(state); } break; } + // TODO: Expire state in a few seconds } diff --git a/swaylock/render.c b/swaylock/render.c index 8fc472819..90db71e3a 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -1,21 +1,140 @@ #include +#include #include "cairo.h" #include "background-image.h" #include "swaylock/swaylock.h" +#define M_PI 3.14159265358979323846 + void render_frame(struct swaylock_surface *surface) { struct swaylock_state *state = surface->state; surface->current_buffer = get_next_buffer(state->shm, surface->buffers, surface->width, surface->height); cairo_t *cairo = surface->current_buffer->cairo; + cairo_identity_matrix(cairo); if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { cairo_set_source_u32(cairo, state->args.color); cairo_paint(cairo); } else { + // TODO: hidpi render_background_image(cairo, surface->image, - state->args.mode, surface->width, surface->height); + state->args.mode, surface->width, surface->height, 1); } + cairo_identity_matrix(cairo); + + const int ARC_RADIUS = 50; + const int ARC_THICKNESS = 10; + const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; + const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; + if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) { + // Draw circle + cairo_set_line_width(cairo, ARC_THICKNESS); + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS, 0, 2 * M_PI); + switch (state->auth_state) { + case AUTH_STATE_INPUT: + case AUTH_STATE_BACKSPACE: { + cairo_set_source_rgba(cairo, 0, 0, 0, 0.75); + cairo_fill_preserve(cairo); + cairo_set_source_rgb(cairo, 51.0 / 255, 125.0 / 255, 0); + cairo_stroke(cairo); + } break; + case AUTH_STATE_VALIDATING: { + cairo_set_source_rgba(cairo, 0, 114.0 / 255, 255.0 / 255, 0.75); + cairo_fill_preserve(cairo); + cairo_set_source_rgb(cairo, 51.0 / 255, 0, 250.0 / 255); + cairo_stroke(cairo); + } break; + case AUTH_STATE_INVALID: { + cairo_set_source_rgba(cairo, 250.0 / 255, 0, 0, 0.75); + cairo_fill_preserve(cairo); + cairo_set_source_rgb(cairo, 125.0 / 255, 51.0 / 255, 0); + cairo_stroke(cairo); + } break; + default: break; + } + + // Draw a message + char *text = NULL; + cairo_set_source_rgb(cairo, 0, 0, 0); + cairo_select_font_face(cairo, "sans-serif", + CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cairo, ARC_RADIUS / 3.0f); + switch (state->auth_state) { + case AUTH_STATE_VALIDATING: + text = "verifying"; + break; + case AUTH_STATE_INVALID: + text = "wrong"; + break; + default: break; + } + + if (text) { + cairo_text_extents_t extents; + double x, y; + cairo_text_extents(cairo, text, &extents); + x = (surface->width / 2) - + (extents.width / 2 + extents.x_bearing); + y = (surface->height / 2) - + (extents.height / 2 + extents.y_bearing); + + cairo_move_to(cairo, x, y); + cairo_show_text(cairo, text); + cairo_close_path(cairo); + cairo_new_sub_path(cairo); + } + + // Typing indicator: Highlight random part on keypress + if (state->auth_state == AUTH_STATE_INPUT + || state->auth_state == AUTH_STATE_BACKSPACE) { + static double highlight_start = 0; + highlight_start += + (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS, highlight_start, + highlight_start + TYPE_INDICATOR_RANGE); + if (state->auth_state == AUTH_STATE_INPUT) { + cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0); + } else { + cairo_set_source_rgb(cairo, 219.0 / 255, 51.0 / 255, 0); + } + cairo_stroke(cairo); + + // Draw borders + cairo_set_source_rgb(cairo, 0, 0, 0); + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS, highlight_start, + highlight_start + TYPE_INDICATOR_BORDER_THICKNESS); + cairo_stroke(cairo); + + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS, highlight_start + TYPE_INDICATOR_RANGE, + highlight_start + TYPE_INDICATOR_RANGE + + TYPE_INDICATOR_BORDER_THICKNESS); + cairo_stroke(cairo); + } + + // Draw inner + outer border of the circle + cairo_set_source_rgb(cairo, 0, 0, 0); + cairo_set_line_width(cairo, 2.0); + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS - ARC_THICKNESS / 2, 0, 2 * M_PI); + cairo_stroke(cairo); + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS + ARC_THICKNESS / 2, 0, 2 * M_PI); + cairo_stroke(cairo); + } + 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); + wl_display_roundtrip(state->display); +} + +void render_frames(struct swaylock_state *state) { + struct swaylock_surface *surface; + wl_list_for_each(surface, &state->surfaces, link) { + render_frame(surface); + } } From 62a736a1961db42acefc4c5746c0f080b152f2b6 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 15:33:02 -0400 Subject: [PATCH 10/19] Actually let's not do that TODO --- swaylock/main.c | 8 -------- swaylock/password.c | 1 - 2 files changed, 9 deletions(-) diff --git a/swaylock/main.c b/swaylock/main.c index ce337e247..7f502eb16 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -93,15 +93,7 @@ static const struct wl_registry_listener registry_listener = { static struct swaylock_state state; -static void sigalarm_handler(int sig) { - signal(SIGALRM, SIG_IGN); - // TODO: Hide typing indicator - signal(SIGALRM, sigalarm_handler); -} - int main(int argc, char **argv) { - signal(SIGALRM, sigalarm_handler); - static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"color", required_argument, NULL, 'c'}, diff --git a/swaylock/password.c b/swaylock/password.c index 2bdf151fd..2927a9a9d 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -121,5 +121,4 @@ void swaylock_handle_key(struct swaylock_state *state, } break; } - // TODO: Expire state in a few seconds } From 3ede718c06194651146f05de4d8889620b159f87 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 15:42:32 -0400 Subject: [PATCH 11/19] Refocus the last focused container on lock exit --- sway/input/seat.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 4a99e9eb4..a6b425989 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -424,11 +424,18 @@ void seat_set_focus(struct sway_seat *seat, void seat_set_focus_layer(struct sway_seat *seat, struct wlr_layer_surface *layer) { - if (!layer) { + if (!layer && seat->focused_layer) { seat->focused_layer = NULL; + struct sway_container *c = seat_get_focus(seat); + if (c) { + wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", c, + container_type_to_str(c->type), c->name); + // Hack to get seat to re-focus the return value of get_focus + seat_set_focus(seat, c->parent); + seat_set_focus(seat, c); + } return; - } - if (seat->focused_layer == layer) { + } else if (!layer || seat->focused_layer == layer) { return; } if (seat->has_focus) { From 06fbd51ff5563f548599615a6baf5a1854bf9983 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 16:16:42 -0400 Subject: [PATCH 12/19] Add input inhibitor to input manager --- include/sway/input/input-manager.h | 5 +++++ include/sway/input/seat.h | 3 +++ sway/input/input-manager.c | 27 +++++++++++++++++++++++++++ sway/input/seat.c | 5 +++++ 4 files changed, 40 insertions(+) diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 8e39a4a7a..89a3ac71b 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -1,6 +1,7 @@ #ifndef _SWAY_INPUT_INPUT_MANAGER_H #define _SWAY_INPUT_INPUT_MANAGER_H #include +#include #include "sway/server.h" #include "sway/config.h" #include "list.h" @@ -23,7 +24,11 @@ struct sway_input_manager { struct wl_list devices; struct wl_list seats; + struct wlr_input_inhibit_manager *inhibit; + struct wl_listener new_input; + struct wl_listener inhibit_activate; + struct wl_listener inhibit_deactivate; }; struct sway_input_manager *input_manager_create(struct sway_server *server); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 137fcd229..53031d70a 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -64,6 +64,9 @@ void seat_set_focus_warp(struct sway_seat *seat, void seat_set_focus_layer(struct sway_seat *seat, struct wlr_layer_surface *layer); +void seat_set_exclusive_client(struct sway_seat *seat, + struct wl_client *client); + struct sway_container *seat_get_focus(struct sway_seat *seat); /** diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index c3507f651..3b2d1d557 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "sway/config.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" @@ -263,6 +264,24 @@ static void handle_new_input(struct wl_listener *listener, void *data) { input_device->device_destroy.notify = handle_device_destroy; } +static void handle_inhibit_activate(struct wl_listener *listener, void *data) { + struct sway_input_manager *input_manager = wl_container_of( + listener, input_manager, inhibit_activate); + struct sway_seat *seat; + wl_list_for_each(seat, &input_manager->seats, link) { + seat_set_exclusive_client(seat, input_manager->inhibit->active_client); + } +} + +static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { + struct sway_input_manager *input_manager = wl_container_of( + listener, input_manager, inhibit_deactivate); + struct sway_seat *seat; + wl_list_for_each(seat, &input_manager->seats, link) { + seat_set_exclusive_client(seat, NULL); + } +} + struct sway_input_manager *input_manager_create( struct sway_server *server) { struct sway_input_manager *input = @@ -281,6 +300,14 @@ struct sway_input_manager *input_manager_create( input->new_input.notify = handle_new_input; wl_signal_add(&server->backend->events.new_input, &input->new_input); + input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); + input->inhibit_activate.notify = handle_inhibit_activate; + wl_signal_add(&input->inhibit->events.activate, + &input->inhibit_activate); + input->inhibit_deactivate.notify = handle_inhibit_deactivate; + wl_signal_add(&input->inhibit->events.deactivate, + &input->inhibit_deactivate); + return input; } diff --git a/sway/input/seat.c b/sway/input/seat.c index a6b425989..318fa9f6f 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -460,6 +460,11 @@ void seat_set_focus_layer(struct sway_seat *seat, } } +void seat_set_exclusive_client(struct sway_seat *seat, + struct wl_client *client) { + // TODO +} + struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, struct sway_container *container) { return seat_get_focus_by_type(seat, container, C_TYPES); From b7e779491232b825f6edc0b199e7564e93f1e332 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 17:03:29 -0400 Subject: [PATCH 13/19] Implement input-inhibit in sway, swaylock --- include/sway/input/seat.h | 5 ++ include/swaylock/swaylock.h | 1 + protocols/meson.build | 6 +- protocols/wlr-input-inhibitor-unstable-v1.xml | 67 ++++++++++++++++++ sway/input/cursor.c | 6 +- sway/input/input-manager.c | 8 +++ sway/input/seat.c | 68 +++++++++++++++++-- swaylock/main.c | 10 +++ 8 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 protocols/wlr-input-inhibitor-unstable-v1.xml diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 53031d70a..4b0fc3c16 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -32,6 +32,9 @@ struct sway_seat { // If the focused layer is set, views cannot receive keyboard focus struct wlr_layer_surface *focused_layer; + // If exclusive_client is set, no other clients will receive input events + struct wl_client *exclusive_client; + struct wl_listener focus_destroy; struct wl_listener new_container; @@ -88,4 +91,6 @@ void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config); struct seat_config *seat_get_config(struct sway_seat *seat); +bool seat_allow_input(struct sway_seat *seat, struct wlr_surface *surface); + #endif diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index ddca633d8..06c94ead0 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -33,6 +33,7 @@ struct swaylock_state { struct wl_display *display; struct wl_compositor *compositor; struct zwlr_layer_shell_v1 *layer_shell; + struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager; struct wl_shm *shm; struct wl_list surfaces; struct swaylock_args args; diff --git a/protocols/meson.build b/protocols/meson.build index 0887cf866..7f83b16b6 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -22,12 +22,14 @@ wayland_scanner_server = generator( client_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], - ['wlr-layer-shell-unstable-v1.xml'] + ['wlr-layer-shell-unstable-v1.xml'], + ['wlr-input-inhibitor-unstable-v1.xml'] ] server_protocols = [ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], - ['wlr-layer-shell-unstable-v1.xml'] + ['wlr-layer-shell-unstable-v1.xml'], + ['wlr-input-inhibitor-unstable-v1.xml'] ] client_protos_src = [] diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml new file mode 100644 index 000000000..b62d1bb44 --- /dev/null +++ b/protocols/wlr-input-inhibitor-unstable-v1.xml @@ -0,0 +1,67 @@ + + + + Copyright © 2018 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to prevent input events from being sent to + any surfaces but its own, which is useful for example in lock screen + software. It is assumed that access to this interface will be locked down + to whitelisted clients by the compositor. + + + + + Activates the input inhibitor. As long as the inhibitor is active, the + compositor will not send input events to other clients. + + + + + + + + + + + + While this resource exists, input to clients other than the owner of the + inhibitor resource will not receive input events. The client that owns + this resource will receive all input events normally. The compositor will + also disable all of its own input processing (such as keyboard shortcuts) + while the inhibitor is active. + + The compositor may continue to send input events to selected clients, + such as an on-screen keyboard (via the input-method protocol). + + + + + Destroy the inhibitor and allow other clients to receive input. + + + + diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 9229e92d1..c56445ebc 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -146,8 +146,10 @@ static void cursor_send_pointer_motion(struct sway_cursor *cursor, // send pointer enter/leave if (surface != NULL) { - wlr_seat_pointer_notify_enter(seat, surface, sx, sy); - wlr_seat_pointer_notify_motion(seat, time, sx, sy); + if (seat_allow_input(cursor->seat, surface)) { + wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(seat, time, sx, sy); + } } else { wlr_seat_pointer_clear_focus(seat); } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 3b2d1d557..f71a06e49 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -279,6 +279,14 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) struct sway_seat *seat; wl_list_for_each(seat, &input_manager->seats, link) { seat_set_exclusive_client(seat, NULL); + struct sway_container *previous = seat_get_focus(seat); + if (previous) { + wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, + container_type_to_str(previous->type), previous->name); + // Hack to get seat to re-focus the return value of get_focus + seat_set_focus(seat, previous->parent); + seat_set_focus(seat, previous); + } } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 318fa9f6f..0e26dde4b 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1,5 +1,7 @@ #define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 199309L #include +#include #include #include #include @@ -9,6 +11,7 @@ #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" #include "sway/ipc-server.h" +#include "sway/layers.h" #include "sway/output.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -350,6 +353,11 @@ void seat_configure_xcursor(struct sway_seat *seat) { seat->cursor->cursor->y); } +bool seat_allow_input(struct sway_seat *seat, struct wlr_surface *surface) { + struct wl_client *client = wl_resource_get_client(surface->resource); + return !seat->exclusive_client || seat->exclusive_client == client; +} + void seat_set_focus_warp(struct sway_seat *seat, struct sway_container *container, bool warp) { if (seat->focused_layer) { @@ -371,6 +379,12 @@ void seat_set_focus_warp(struct sway_seat *seat, wl_list_remove(&seat_con->link); wl_list_insert(&seat->focus_stack, &seat_con->link); + if (container->type == C_VIEW && !seat_allow_input( + seat, container->sway_view->surface)) { + wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited"); + return; + } + if (container->type == C_VIEW) { seat_send_focus(seat, container); } @@ -426,13 +440,13 @@ void seat_set_focus_layer(struct sway_seat *seat, struct wlr_layer_surface *layer) { if (!layer && seat->focused_layer) { seat->focused_layer = NULL; - struct sway_container *c = seat_get_focus(seat); - if (c) { - wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", c, - container_type_to_str(c->type), c->name); + struct sway_container *previous = seat_get_focus(seat); + if (previous) { + wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, + container_type_to_str(previous->type), previous->name); // Hack to get seat to re-focus the return value of get_focus - seat_set_focus(seat, c->parent); - seat_set_focus(seat, c); + seat_set_focus(seat, previous->parent); + seat_set_focus(seat, previous); } return; } else if (!layer || seat->focused_layer == layer) { @@ -462,7 +476,47 @@ void seat_set_focus_layer(struct sway_seat *seat, void seat_set_exclusive_client(struct sway_seat *seat, struct wl_client *client) { - // TODO + if (!client) { + seat->exclusive_client = client; + // Triggers a refocus of the topmost surface layer if necessary + // TODO: Make layer surface focus per-output based on cursor position + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output = root_container.children->items[i]; + if (!sway_assert(output->type == C_OUTPUT, + "root container has non-output child")) { + continue; + } + arrange_layers(output->sway_output); + } + return; + } + if (seat->focused_layer) { + if (wl_resource_get_client(seat->focused_layer->resource) != client) { + seat_set_focus_layer(seat, NULL); + } + } + if (seat->has_focus) { + struct sway_container *focus = seat_get_focus(seat); + if (focus->type == C_VIEW && wl_resource_get_client( + focus->sway_view->surface->resource) != client) { + seat_set_focus(seat, NULL); + } + } + if (seat->wlr_seat->pointer_state.focused_client) { + if (seat->wlr_seat->pointer_state.focused_client->client != client) { + wlr_seat_pointer_clear_focus(seat->wlr_seat); + } + } + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct wlr_touch_point *point; + wl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) { + if (point->client->client != client) { + wlr_seat_touch_point_clear_focus(seat->wlr_seat, + now.tv_nsec / 1000, point->touch_id); + } + } + seat->exclusive_client = client; } struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, diff --git a/swaylock/main.c b/swaylock/main.c index 7f502eb16..6cd4e41d4 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -19,6 +19,7 @@ #include "pool-buffer.h" #include "cairo.h" #include "util.h" +#include "wlr-input-inhibitor-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static void daemonize() { @@ -71,6 +72,9 @@ static void handle_global(void *data, struct wl_registry *registry, } 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); + } 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, wl_output_interface.name) == 0) { struct swaylock_surface *surface = calloc(1, sizeof(struct swaylock_surface)); @@ -187,6 +191,10 @@ int main(int argc, char **argv) { wl_registry_add_listener(registry, ®istry_listener, &state); wl_display_roundtrip(state.display); assert(state.compositor && state.layer_shell && state.shm); + if (!state.input_inhibit_manager) { + wlr_log(L_ERROR, "Compositor does not support the input inhibitor " + "protocol, refusing to run insecurely"); + } if (wl_list_empty(&state.surfaces)) { wlr_log(L_DEBUG, "Exiting - no outputs to show on."); @@ -220,6 +228,8 @@ int main(int argc, char **argv) { wl_display_roundtrip(state.display); } + zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager); + state.run_display = true; while (wl_display_dispatch(state.display) != -1 && state.run_display) { // This space intentionally left blank From 46b388995d7d50a39d13fce9417e2ad0d2cf749f Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 19:15:14 -0400 Subject: [PATCH 14/19] Add hidpi support to swaylock --- include/swaylock/swaylock.h | 1 + swaylock/main.c | 35 +++++++++++++++++++++++++++++-- swaylock/render.c | 41 +++++++++++++++++++++---------------- 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index 06c94ead0..173e8b125 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -52,6 +52,7 @@ struct swaylock_surface { struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; uint32_t width, height; + int32_t scale; struct wl_list link; }; diff --git a/swaylock/main.c b/swaylock/main.c index 6cd4e41d4..1eda3afcd 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -56,12 +56,42 @@ 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) { + // Who cares +} + +static void 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) { + // Who cares +} + +static void output_scale(void *data, struct wl_output *output, int32_t factor) { + struct swaylock_surface *surface = data; + surface->scale = factor; + if (surface->state->run_display) { + render_frames(surface->state); + } +} + +struct wl_output_listener output_listener = { + .geometry = output_geometry, + .mode = output_mode, + .done = output_done, + .scale = output_scale, +}; + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct swaylock_state *state = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { state->compositor = wl_registry_bind(registry, name, - &wl_compositor_interface, 1); + &wl_compositor_interface, 3); } else if (strcmp(interface, wl_shm_interface.name) == 0) { state->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); @@ -80,7 +110,8 @@ static void handle_global(void *data, struct wl_registry *registry, calloc(1, sizeof(struct swaylock_surface)); surface->state = state; surface->output = wl_registry_bind(registry, name, - &wl_output_interface, 1); + &wl_output_interface, 3); + wl_output_add_listener(surface->output, &output_listener, surface); wl_list_insert(&state->surfaces, &surface->link); } } diff --git a/swaylock/render.c b/swaylock/render.c index 90db71e3a..2469e60b1 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -9,28 +9,32 @@ void render_frame(struct swaylock_surface *surface) { struct swaylock_state *state = surface->state; surface->current_buffer = get_next_buffer(state->shm, - surface->buffers, surface->width, surface->height); + surface->buffers, + surface->width * surface->scale, + surface->height * surface->scale); cairo_t *cairo = surface->current_buffer->cairo; cairo_identity_matrix(cairo); + + int buffer_width = surface->width * surface->scale; + int buffer_height = surface->height * surface->scale; if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { cairo_set_source_u32(cairo, state->args.color); cairo_paint(cairo); } else { - // TODO: hidpi render_background_image(cairo, surface->image, - state->args.mode, surface->width, surface->height, 1); + state->args.mode, buffer_width, buffer_height); } cairo_identity_matrix(cairo); - const int ARC_RADIUS = 50; - const int ARC_THICKNESS = 10; - const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; - const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; + int ARC_RADIUS = 50 * surface->scale; + int ARC_THICKNESS = 10 * surface->scale; + float TYPE_INDICATOR_RANGE = M_PI / 3.0f; + float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f * surface->scale; + if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) { // Draw circle cairo_set_line_width(cairo, ARC_THICKNESS); - cairo_arc(cairo, surface->width / 2, surface->height / 2, - ARC_RADIUS, 0, 2 * M_PI); + cairo_arc(cairo, buffer_width / 2, buffer_height / 2, ARC_RADIUS, 0, 2 * M_PI); switch (state->auth_state) { case AUTH_STATE_INPUT: case AUTH_STATE_BACKSPACE: { @@ -74,9 +78,9 @@ void render_frame(struct swaylock_surface *surface) { cairo_text_extents_t extents; double x, y; cairo_text_extents(cairo, text, &extents); - x = (surface->width / 2) - + x = (buffer_width / 2) - (extents.width / 2 + extents.x_bearing); - y = (surface->height / 2) - + y = (buffer_height / 2) - (extents.height / 2 + extents.y_bearing); cairo_move_to(cairo, x, y); @@ -91,7 +95,7 @@ void render_frame(struct swaylock_surface *surface) { static double highlight_start = 0; highlight_start += (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; - cairo_arc(cairo, surface->width / 2, surface->height / 2, + cairo_arc(cairo, buffer_width / 2, buffer_height / 2, ARC_RADIUS, highlight_start, highlight_start + TYPE_INDICATOR_RANGE); if (state->auth_state == AUTH_STATE_INPUT) { @@ -103,12 +107,12 @@ void render_frame(struct swaylock_surface *surface) { // Draw borders cairo_set_source_rgb(cairo, 0, 0, 0); - cairo_arc(cairo, surface->width / 2, surface->height / 2, + cairo_arc(cairo, buffer_width / 2, buffer_height / 2, ARC_RADIUS, highlight_start, highlight_start + TYPE_INDICATOR_BORDER_THICKNESS); cairo_stroke(cairo); - cairo_arc(cairo, surface->width / 2, surface->height / 2, + cairo_arc(cairo, buffer_width / 2, buffer_height / 2, ARC_RADIUS, highlight_start + TYPE_INDICATOR_RANGE, highlight_start + TYPE_INDICATOR_RANGE + TYPE_INDICATOR_BORDER_THICKNESS); @@ -117,17 +121,18 @@ void render_frame(struct swaylock_surface *surface) { // Draw inner + outer border of the circle cairo_set_source_rgb(cairo, 0, 0, 0); - cairo_set_line_width(cairo, 2.0); - cairo_arc(cairo, surface->width / 2, surface->height / 2, + cairo_set_line_width(cairo, 2.0 * surface->scale); + cairo_arc(cairo, buffer_width / 2, buffer_height / 2, ARC_RADIUS - ARC_THICKNESS / 2, 0, 2 * M_PI); cairo_stroke(cairo); - cairo_arc(cairo, surface->width / 2, surface->height / 2, + cairo_arc(cairo, buffer_width / 2, buffer_height / 2, ARC_RADIUS + ARC_THICKNESS / 2, 0, 2 * M_PI); cairo_stroke(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_damage(surface->surface, 0, 0, buffer_width, buffer_height); wl_surface_commit(surface->surface); wl_display_roundtrip(state->display); } From d2d050d59cf19c583b100e6e3637ed9a06a8863f Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 21:25:42 -0400 Subject: [PATCH 15/19] Address review feedback --- include/sway/input/seat.h | 2 +- sway/input/cursor.c | 2 +- sway/input/seat.c | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 4b0fc3c16..d1cfbe4c2 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -91,6 +91,6 @@ void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config); struct seat_config *seat_get_config(struct sway_seat *seat); -bool seat_allow_input(struct sway_seat *seat, struct wlr_surface *surface); +bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c56445ebc..195ddce93 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -146,7 +146,7 @@ static void cursor_send_pointer_motion(struct sway_cursor *cursor, // send pointer enter/leave if (surface != NULL) { - if (seat_allow_input(cursor->seat, surface)) { + if (seat_is_input_allowed(cursor->seat, surface)) { wlr_seat_pointer_notify_enter(seat, surface, sx, sy); wlr_seat_pointer_notify_motion(seat, time, sx, sy); } diff --git a/sway/input/seat.c b/sway/input/seat.c index 0e26dde4b..0699324ab 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -353,7 +353,8 @@ void seat_configure_xcursor(struct sway_seat *seat) { seat->cursor->cursor->y); } -bool seat_allow_input(struct sway_seat *seat, struct wlr_surface *surface) { +bool seat_is_input_allowed(struct sway_seat *seat, + struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); return !seat->exclusive_client || seat->exclusive_client == client; } @@ -379,7 +380,7 @@ void seat_set_focus_warp(struct sway_seat *seat, wl_list_remove(&seat_con->link); wl_list_insert(&seat->focus_stack, &seat_con->link); - if (container->type == C_VIEW && !seat_allow_input( + if (container->type == C_VIEW && !seat_is_input_allowed( seat, container->sway_view->surface)) { wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited"); return; From 0138f79b4aae563e1223737856ab44c8634c76b0 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 22:00:29 -0400 Subject: [PATCH 16/19] Move extra roundtrip into password.c --- swaylock/password.c | 1 + swaylock/render.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/swaylock/password.c b/swaylock/password.c index 2927a9a9d..06c1180ca 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -101,6 +101,7 @@ void swaylock_handle_key(struct swaylock_state *state, case XKB_KEY_Return: state->auth_state = AUTH_STATE_VALIDATING; render_frames(state); + wl_display_roundtrip(state->display); if (attempt_password(&state->password)) { exit(0); } diff --git a/swaylock/render.c b/swaylock/render.c index 2469e60b1..79609e96f 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -134,7 +134,6 @@ void render_frame(struct swaylock_surface *surface) { wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0); wl_surface_damage(surface->surface, 0, 0, buffer_width, buffer_height); wl_surface_commit(surface->surface); - wl_display_roundtrip(state->display); } void render_frames(struct swaylock_state *state) { From 218a3787d26296103225b7680bacdfd5fc6d2955 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 22:01:29 -0400 Subject: [PATCH 17/19] Import stdlib.h and define POSIX macro for rand() --- swaylock/render.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/swaylock/render.c b/swaylock/render.c index 79609e96f..cb3ed276a 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -1,5 +1,7 @@ -#include +#define _POSIX_C_SOURCE 199506L #include +#include +#include #include "cairo.h" #include "background-image.h" #include "swaylock/swaylock.h" From 053b51c3ff63147aed25400efb42c00ee3de90b9 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 22:03:48 -0400 Subject: [PATCH 18/19] exit() needs stdlib.h inb4 acrisci --- swaylock/seat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/swaylock/seat.c b/swaylock/seat.c index 6c46bb417..21db7c4fb 100644 --- a/swaylock/seat.c +++ b/swaylock/seat.c @@ -1,4 +1,5 @@ #include +#include #include #include #include From 5d444b34f6af17894e2808c9d25948db625dabde Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 4 Apr 2018 18:52:44 -0400 Subject: [PATCH 19/19] Address review feedback from @emersion --- swaylock/main.c | 7 ++++--- swaylock/password.c | 3 ++- swaylock/render.c | 46 ++++++++++++++++++++++++--------------------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/swaylock/main.c b/swaylock/main.c index 1eda3afcd..1d5221849 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -216,7 +216,8 @@ int main(int argc, char **argv) { wl_list_init(&state.surfaces); state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - assert(state.display = wl_display_connect(NULL)); + state.display = wl_display_connect(NULL); + assert(state.display); struct wl_registry *registry = wl_display_get_registry(state.display); wl_registry_add_listener(registry, ®istry_listener, &state); @@ -236,8 +237,8 @@ int main(int argc, char **argv) { wl_list_for_each(surface, &state.surfaces, link) { surface->image = background_image; - assert(surface->surface = - wl_compositor_create_surface(state.compositor)); + 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, diff --git a/swaylock/password.c b/swaylock/password.c index 06c1180ca..1839f991b 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -103,7 +103,8 @@ void swaylock_handle_key(struct swaylock_state *state, render_frames(state); wl_display_roundtrip(state->display); if (attempt_password(&state->password)) { - exit(0); + state->run_display = false; + break; } state->auth_state = AUTH_STATE_INVALID; render_frames(state); diff --git a/swaylock/render.c b/swaylock/render.c index cb3ed276a..cd387be55 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -7,18 +7,22 @@ #include "swaylock/swaylock.h" #define M_PI 3.14159265358979323846 +const int ARC_RADIUS = 50; +const int ARC_THICKNESS = 10; +const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; +const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; void render_frame(struct swaylock_surface *surface) { struct swaylock_state *state = surface->state; - surface->current_buffer = get_next_buffer(state->shm, - surface->buffers, - surface->width * surface->scale, - surface->height * surface->scale); - cairo_t *cairo = surface->current_buffer->cairo; - cairo_identity_matrix(cairo); int buffer_width = surface->width * surface->scale; int buffer_height = surface->height * surface->scale; + + surface->current_buffer = get_next_buffer(state->shm, + surface->buffers, buffer_width, buffer_height); + cairo_t *cairo = surface->current_buffer->cairo; + cairo_identity_matrix(cairo); + if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { cairo_set_source_u32(cairo, state->args.color); cairo_paint(cairo); @@ -28,15 +32,15 @@ void render_frame(struct swaylock_surface *surface) { } cairo_identity_matrix(cairo); - int ARC_RADIUS = 50 * surface->scale; - int ARC_THICKNESS = 10 * surface->scale; - float TYPE_INDICATOR_RANGE = M_PI / 3.0f; - float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f * surface->scale; + int arc_radius = ARC_RADIUS * surface->scale; + int arc_thickness = ARC_THICKNESS * surface->scale; + float type_indicator_border_thickness = + TYPE_INDICATOR_BORDER_THICKNESS * surface->scale; if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) { // Draw circle - cairo_set_line_width(cairo, ARC_THICKNESS); - cairo_arc(cairo, buffer_width / 2, buffer_height / 2, ARC_RADIUS, 0, 2 * M_PI); + cairo_set_line_width(cairo, arc_thickness); + cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, 0, 2 * M_PI); switch (state->auth_state) { case AUTH_STATE_INPUT: case AUTH_STATE_BACKSPACE: { @@ -65,7 +69,7 @@ void render_frame(struct swaylock_surface *surface) { cairo_set_source_rgb(cairo, 0, 0, 0); cairo_select_font_face(cairo, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); - cairo_set_font_size(cairo, ARC_RADIUS / 3.0f); + cairo_set_font_size(cairo, arc_radius / 3.0f); switch (state->auth_state) { case AUTH_STATE_VALIDATING: text = "verifying"; @@ -98,7 +102,7 @@ void render_frame(struct swaylock_surface *surface) { highlight_start += (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; cairo_arc(cairo, buffer_width / 2, buffer_height / 2, - ARC_RADIUS, highlight_start, + arc_radius, highlight_start, highlight_start + TYPE_INDICATOR_RANGE); if (state->auth_state == AUTH_STATE_INPUT) { cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0); @@ -110,14 +114,14 @@ void render_frame(struct swaylock_surface *surface) { // Draw borders cairo_set_source_rgb(cairo, 0, 0, 0); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, - ARC_RADIUS, highlight_start, - highlight_start + TYPE_INDICATOR_BORDER_THICKNESS); + arc_radius, highlight_start, + highlight_start + type_indicator_border_thickness); cairo_stroke(cairo); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, - ARC_RADIUS, highlight_start + TYPE_INDICATOR_RANGE, + arc_radius, highlight_start + TYPE_INDICATOR_RANGE, highlight_start + TYPE_INDICATOR_RANGE + - TYPE_INDICATOR_BORDER_THICKNESS); + type_indicator_border_thickness); cairo_stroke(cairo); } @@ -125,16 +129,16 @@ void render_frame(struct swaylock_surface *surface) { cairo_set_source_rgb(cairo, 0, 0, 0); cairo_set_line_width(cairo, 2.0 * surface->scale); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, - ARC_RADIUS - ARC_THICKNESS / 2, 0, 2 * M_PI); + arc_radius - arc_thickness / 2, 0, 2 * M_PI); cairo_stroke(cairo); cairo_arc(cairo, buffer_width / 2, buffer_height / 2, - ARC_RADIUS + ARC_THICKNESS / 2, 0, 2 * M_PI); + arc_radius + arc_thickness / 2, 0, 2 * M_PI); cairo_stroke(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, buffer_width, buffer_height); + wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height); wl_surface_commit(surface->surface); }