From a28730edee1896242012f80fd0e63e7966652e02 Mon Sep 17 00:00:00 2001
From: Drew DeVault <sir@cmpwn.com>
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 00000000..2988c2e2
--- /dev/null
+++ b/common/background-image.c
@@ -0,0 +1,100 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <wlr/util/log.h>
+#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 4ad47077..851e7bbf 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 00000000..e160e63d
--- /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 c282a707..ffefcd08 100644
--- a/swaybg/main.c
+++ b/swaybg/main.c
@@ -7,20 +7,12 @@
 #include <time.h>
 #include <wayland-client.h>
 #include <wlr/util/log.h>
+#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 <sir@cmpwn.com>
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 2988c2e2..1a6c0df0 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 e160e63d..15935ffd 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 01788fd9..d02a446b 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 ffefcd08..679b8c20 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 c2615951..8673694d 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -1,387 +1,235 @@
-#define _XOPEN_SOURCE 500
-#include "wayland-swaylock-client-protocol.h"
-#include <xkbcommon/xkbcommon.h>
-#include <xkbcommon/xkbcommon-names.h>
-#include <security/pam_appl.h>
-#include <json-c/json.h>
+#define _XOPEN_SOURCE 700
+#define _POSIX_C_SOURCE 200112L
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <getopt.h>
-#include <signal.h>
-#include <stdbool.h>
+#include <sys/stat.h>
+#include <time.h>
 #include <unistd.h>
-#include "client/window.h"
-#include "client/registry.h"
-#include "client/cairo.h"
-#include "swaylock/swaylock.h"
-#include "ipc-client.h"
-#include "log.h"
+#include <wayland-client.h>
+#include <wlr/util/log.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 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; i<num_msg; ++i) {
-		sway_log(L_DEBUG, "msg[%d]: (%s) %s", i,
-				msg_style_names[msg[i]->msg_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 <rrggbb[aa]>       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 [<output>:]<path>  Display the given image.\n"
 		"  -u, --no-unlock-indicator      Disable the unlock indicator.\n"
-		"  -f, --daemonize                Detach from the controlling terminal.\n"
-		"  --socket <socket>              Use the specified socket.\n"
-		"  For more information see `man swaylock`\n";
+		"  -f, --daemonize                Detach from the controlling terminal.\n" 
+		"  --socket <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, &registry_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 00000000..5b886ded
--- /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 <sir@cmpwn.com>
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 8673694d..948d661b 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 <sir@cmpwn.com>
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 948d661b..555b1d64 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -8,11 +8,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mman.h>
 #include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
 #include <wayland-client.h>
 #include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
 #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 <sir@cmpwn.com>
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 00000000..44bc37d5
--- /dev/null
+++ b/include/swaylock/seat.h
@@ -0,0 +1,38 @@
+#ifndef _SWAYLOCK_SEAT_H
+#define _SWAYLOCK_SEAT_H
+#include <xkbcommon/xkbcommon.h>
+
+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 eeed094e..e2673aae 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -1,66 +1,41 @@
 #ifndef _SWAYLOCK_H
 #define _SWAYLOCK_H
+#include <stdbool.h>
+#include <stdint.h>
+#include <wayland-client.h>
+#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 555b1d64..7602e47e 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -8,101 +8,19 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/mman.h>
 #include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
 #include <wayland-client.h>
 #include <wlr/util/log.h>
-#include <xkbcommon/xkbcommon.h>
+#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 5b886ded..2f2733f8 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 00000000..522200f2
--- /dev/null
+++ b/swaylock/seat.c
@@ -0,0 +1,187 @@
+#include <assert.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+#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 <sir@cmpwn.com>
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 2f2733f8..2a1f029a 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 <sir@cmpwn.com>
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 851e7bbf..44a29508 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 00000000..38a9b48e
--- /dev/null
+++ b/common/unicode.c
@@ -0,0 +1,101 @@
+#include <stdint.h>
+#include <stddef.h>
+#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 e2673aae..f3b0b58b 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 00000000..e2ee9588
--- /dev/null
+++ b/include/unicode.h
@@ -0,0 +1,33 @@
+#ifndef _SWAY_UNICODE_H
+#define _SWAY_UNICODE_H
+#include <stddef.h>
+#include <stdint.h>
+
+// 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 7602e47e..c8fdc2f4 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 2a1f029a..3cde47a4 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 00000000..da67205d
--- /dev/null
+++ b/swaylock/password.c
@@ -0,0 +1,57 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+#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 00000000..8fc47281
--- /dev/null
+++ b/swaylock/render.c
@@ -0,0 +1,21 @@
+#include <wayland-client.h>
+#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 522200f2..6c46bb41 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 <sir@cmpwn.com>
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 da67205d..9af7fe16 100644
--- a/swaylock/password.c
+++ b/swaylock/password.c
@@ -1,4 +1,6 @@
 #include <assert.h>
+#include <pwd.h>
+#include <security/pam_appl.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <wlr/util/log.h>
@@ -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 <sir@cmpwn.com>
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 1a6c0df0..e5fb4433 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 f3b0b58b..ddca633d 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 c8fdc2f4..ce337e24 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 9af7fe16..2bdf151f 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 8fc47281..90db71e3 100644
--- a/swaylock/render.c
+++ b/swaylock/render.c
@@ -1,21 +1,140 @@
 #include <wayland-client.h>
+#include <math.h>
 #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 <sir@cmpwn.com>
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 ce337e24..7f502eb1 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 2bdf151f..2927a9a9 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 <sir@cmpwn.com>
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 4a99e9eb..a6b42598 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 <sir@cmpwn.com>
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 8e39a4a7..89a3ac71 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 <libinput.h>
+#include <wlr/types/wlr_input_inhibitor.h>
 #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 137fcd22..53031d70 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 c3507f65..3b2d1d55 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -7,6 +7,7 @@
 #include <libinput.h>
 #include <math.h>
 #include <wlr/backend/libinput.h>
+#include <wlr/types/wlr_input_inhibitor.h>
 #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 a6b42598..318fa9f6 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 <sir@cmpwn.com>
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 53031d70..4b0fc3c1 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 ddca633d..06c94ead 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 0887cf86..7f83b16b 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 00000000..b62d1bb4
--- /dev/null
+++ b/protocols/wlr-input-inhibitor-unstable-v1.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_input_inhibit_unstable_v1">
+  <copyright>
+    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.
+  </copyright>
+
+  <interface name="zwlr_input_inhibit_manager_v1" version="1">
+    <description summary="inhibits input events to other clients">
+      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.
+    </description>
+
+    <request name="get_inhibitor">
+      <description summary="inhibit input to other clients">
+        Activates the input inhibitor. As long as the inhibitor is active, the
+        compositor will not send input events to other clients.
+      </description>
+      <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
+    </request>
+
+    <enum name="error">
+      <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
+    </enum>
+  </interface>
+
+  <interface name="zwlr_input_inhibitor_v1" version="1">
+    <description summary="inhibits input 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).
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the input inhibitor object">
+        Destroy the inhibitor and allow other clients to receive input.
+      </description>
+    </request>
+  </interface>
+</protocol>
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 9229e92d..c56445eb 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 3b2d1d55..f71a06e4 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 318fa9f6..0e26dde4 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 <assert.h>
+#include <time.h>
 #include <wlr/types/wlr_cursor.h>
 #include <wlr/types/wlr_output_layout.h>
 #include <wlr/types/wlr_xcursor_manager.h>
@@ -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 7f502eb1..6cd4e41d 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, &registry_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 <sir@cmpwn.com>
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 06c94ead..173e8b12 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 6cd4e41d..1eda3afc 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 90db71e3..2469e60b 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 <sir@cmpwn.com>
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 4b0fc3c1..d1cfbe4c 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 c56445eb..195ddce9 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 0e26dde4..0699324a 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 <sir@cmpwn.com>
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 2927a9a9..06c1180c 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 2469e60b..79609e96 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 <sir@cmpwn.com>
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 79609e96..cb3ed276 100644
--- a/swaylock/render.c
+++ b/swaylock/render.c
@@ -1,5 +1,7 @@
-#include <wayland-client.h>
+#define _POSIX_C_SOURCE 199506L
 #include <math.h>
+#include <stdlib.h>
+#include <wayland-client.h>
 #include "cairo.h"
 #include "background-image.h"
 #include "swaylock/swaylock.h"

From 053b51c3ff63147aed25400efb42c00ee3de90b9 Mon Sep 17 00:00:00 2001
From: Drew DeVault <sir@cmpwn.com>
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 6c46bb41..21db7c4f 100644
--- a/swaylock/seat.c
+++ b/swaylock/seat.c
@@ -1,4 +1,5 @@
 #include <assert.h>
+#include <stdlib.h>
 #include <sys/mman.h>
 #include <unistd.h>
 #include <wlr/util/log.h>

From 5d444b34f6af17894e2808c9d25948db625dabde Mon Sep 17 00:00:00 2001
From: Drew DeVault <sir@cmpwn.com>
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 1eda3afc..1d522184 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, &registry_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 06c1180c..1839f991 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 cb3ed276..cd387be5 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);
 }