44ceea7 ยท 1 month ago 7 commits
   1#define _POSIX_C_SOURCE 200809L
   2#include <assert.h>
   3#include <ctype.h>
   4#include <errno.h>
   5#include <fcntl.h>
   6#include <getopt.h>
   7#include <poll.h>
   8#include <signal.h>
   9#include <stdbool.h>
  10#include <stdio.h>
  11#include <stdlib.h>
  12#include <string.h>
  13#include <sys/mman.h>
  14#include <sys/stat.h>
  15#include <time.h>
  16#include <unistd.h>
  17#include <wayland-client.h>
  18#include <wordexp.h>
  19#include "background-image.h"
  20#include "cairo.h"
  21#include "comm.h"
  22#include "log.h"
  23#include "loop.h"
  24#include "password-buffer.h"
  25#include "pool-buffer.h"
  26#include "seat.h"
  27#include "swaylock.h"
  28#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
  29#include "wlr-layer-shell-unstable-v1-client-protocol.h"
  30#include "wlr-screencopy-unstable-v1-client-protocol.h"
  31#include "ext-session-lock-v1-client-protocol.h"
  32
  33// returns a positive integer in milliseconds
  34static uint32_t parse_seconds(const char *seconds) {
  35	char *endptr;
  36	errno = 0;
  37	float val = strtof(seconds, &endptr);
  38	if (errno != 0) {
  39		swaylock_log(LOG_DEBUG, "Invalid number for seconds %s, defaulting to 0", seconds);
  40		return 0;
  41	}
  42	if (endptr == seconds) {
  43		swaylock_log(LOG_DEBUG, "No digits were found in %s, defaulting to 0", seconds);
  44		return 0;
  45	}
  46	if (val < 0) {
  47		swaylock_log(LOG_DEBUG, "Negative seconds not allowed for %s, defaulting to 0", seconds);
  48		return 0;
  49	}
  50
  51	return (uint32_t)floor(val * 1000);
  52}
  53
  54static uint32_t parse_color(const char *color) {
  55	if (color[0] == '#') {
  56		++color;
  57	}
  58
  59	int len = strlen(color);
  60	if (len != 6 && len != 8) {
  61		swaylock_log(LOG_DEBUG, "Invalid color %s, defaulting to 0xFFFFFFFF",
  62				color);
  63		return 0xFFFFFFFF;
  64	}
  65	uint32_t res = (uint32_t)strtoul(color, NULL, 16);
  66	if (strlen(color) == 6) {
  67		res = (res << 8) | 0xFF;
  68	}
  69	return res;
  70}
  71
  72static const char *parse_screen_pos(const char *str, struct swaylock_effect_screen_pos *pos) {
  73	char *eptr;
  74	float res = strtof(str, &eptr);
  75	if (eptr == str)
  76		return NULL;
  77
  78	pos->pos = res;
  79	if (eptr[0] == '%') {
  80		pos->is_percent = true;
  81		return eptr + 1;
  82	} else {
  83		pos->is_percent = false;
  84		return eptr;
  85	}
  86}
  87
  88static const char *parse_screen_pos_pair(const char *str, char delim,
  89		struct swaylock_effect_screen_pos *pos1,
  90		struct swaylock_effect_screen_pos *pos2) {
  91	struct swaylock_effect_screen_pos tpos1, tpos2;
  92	str = parse_screen_pos(str, &tpos1);
  93	if (str == NULL || str[0] != delim)
  94		return NULL;
  95
  96	str = parse_screen_pos(str + 1, &tpos2);
  97	if (str == NULL)
  98		return NULL;
  99
 100	pos1->pos = tpos1.pos;
 101	pos1->is_percent = tpos1.is_percent;
 102	pos2->pos = tpos2.pos;
 103	pos2->is_percent = tpos2.is_percent;
 104	return str;
 105}
 106
 107static const char *parse_constant(const char *str1, const char *str2) {
 108	size_t len = strlen(str2);
 109	if (strncmp(str1, str2, len) == 0) {
 110		return str1 + len;
 111	} else {
 112		return NULL;
 113	}
 114}
 115
 116static int parse_gravity_from_xy(float x, float y) {
 117	if (x >= 0 && y >= 0)
 118		return EFFECT_COMPOSE_GRAV_NW;
 119	else if (x >= 0 && y < 0)
 120		return EFFECT_COMPOSE_GRAV_SW;
 121	else if (x < 0 && y >= 0)
 122		return EFFECT_COMPOSE_GRAV_NE;
 123	else
 124		return EFFECT_COMPOSE_GRAV_SE;
 125}
 126
 127static void parse_effect_compose(const char *str, struct swaylock_effect *effect) {
 128	effect->e.compose.x = effect->e.compose.y = (struct swaylock_effect_screen_pos) { 50, 1 }; // 50%
 129	effect->e.compose.w = effect->e.compose.h = (struct swaylock_effect_screen_pos) { -1, 0 }; // -1
 130	effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_CENTER;
 131	effect->e.compose.imgpath = NULL;
 132
 133	// Parse position if they exist
 134	const char *s = parse_screen_pos_pair(str, ',', &effect->e.compose.x, &effect->e.compose.y);
 135	if (s == NULL) {
 136		s = str;
 137	} else {
 138		// If we're given an x/y position, determine gravity automatically
 139		// from whether x and y is positive or not
 140		effect->e.compose.gravity = parse_gravity_from_xy(
 141				effect->e.compose.x.pos, effect->e.compose.y.pos);
 142		s += 1;
 143		str = s;
 144	}
 145
 146	// Parse dimensions if they exist
 147	s = parse_screen_pos_pair(str, 'x', &effect->e.compose.w, &effect->e.compose.h);
 148	if (s == NULL) {
 149		s = str;
 150	} else {
 151		s += 1;
 152		str = s;
 153	}
 154
 155	// Parse gravity if it exists
 156	if ((s = parse_constant(str, "center;")) != NULL)
 157		effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_CENTER;
 158	else if ((s = parse_constant(str, "northwest;")) != NULL)
 159		effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_NW;
 160	else if ((s = parse_constant(str, "northeast;")) != NULL)
 161		effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_NE;
 162	else if ((s = parse_constant(str, "southwest;")) != NULL)
 163		effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_SW;
 164	else if ((s = parse_constant(str, "southeast;")) != NULL)
 165		effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_SE;
 166	else if ((s = parse_constant(str, "north;")) != NULL)
 167		effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_N;
 168	else if ((s = parse_constant(str, "south;")) != NULL)
 169		effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_S;
 170	else if ((s = parse_constant(str, "east;")) != NULL)
 171		effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_E;
 172	else if ((s = parse_constant(str, "west;")) != NULL)
 173		effect->e.compose.gravity = EFFECT_COMPOSE_GRAV_W;
 174	if (s == NULL) {
 175		s = str;
 176	} else {
 177		str = s;
 178	}
 179
 180	// The rest is the file name
 181	effect->e.compose.imgpath = strdup(str);
 182}
 183
 184int lenient_strcmp(char *a, char *b) {
 185	if (a == b) {
 186		return 0;
 187	} else if (!a) {
 188		return -1;
 189	} else if (!b) {
 190		return 1;
 191	} else {
 192		return strcmp(a, b);
 193	}
 194}
 195
 196static int daemonize_start() {
 197	swaylock_trace();
 198	int fds[2];
 199	if (pipe(fds) != 0) {
 200		swaylock_log(LOG_ERROR, "Failed to pipe");
 201		exit(1);
 202	}
 203	if (fork() == 0) {
 204		setsid();
 205		close(fds[0]);
 206		int devnull = open("/dev/null", O_RDWR);
 207		dup2(STDOUT_FILENO, devnull);
 208		dup2(STDERR_FILENO, devnull);
 209		close(devnull);
 210		uint8_t success = 0;
 211		if (chdir("/") != 0) {
 212			write(fds[1], &success, 1);
 213			exit(1);
 214		}
 215		return fds[1];
 216	} else {
 217		close(fds[1]);
 218		uint8_t success;
 219		if (read(fds[0], &success, 1) != 1 || !success) {
 220			swaylock_log(LOG_ERROR, "Failed to daemonize");
 221			exit(1);
 222		}
 223		close(fds[0]);
 224		exit(0);
 225	}
 226}
 227
 228static void daemonize_done(void *fdptr) {
 229	swaylock_trace();
 230	int *fd = (int *)fdptr;
 231	if (*fd < 0) {
 232		return;
 233	}
 234
 235	uint8_t success = 1;
 236	if (write(*fd, &success, 1) != 1) {
 237		swaylock_log(LOG_ERROR, "Failed to tell parent process that daemonization is done");
 238		exit(1);
 239	}
 240	close(*fd);
 241	*fd = -1;
 242}
 243
 244static void destroy_surface(struct swaylock_surface *surface) {
 245	swaylock_log(LOG_DEBUG, "Destroy surface for output %s", surface->output_name);
 246
 247	wl_list_remove(&surface->link);
 248	if (surface->layer_surface != NULL) {
 249		zwlr_layer_surface_v1_destroy(surface->layer_surface);
 250	}
 251	if (surface->ext_session_lock_surface_v1 != NULL) {
 252		ext_session_lock_surface_v1_destroy(surface->ext_session_lock_surface_v1);
 253	}
 254	if (surface->surface != NULL) {
 255		wl_surface_destroy(surface->surface);
 256	}
 257	destroy_buffer(&surface->buffers[0]);
 258	destroy_buffer(&surface->buffers[1]);
 259	destroy_buffer(&surface->indicator_buffers[0]);
 260	destroy_buffer(&surface->indicator_buffers[1]);
 261	wl_output_destroy(surface->output);
 262	free(surface);
 263}
 264
 265static const struct zwlr_layer_surface_v1_listener layer_surface_listener;
 266static const struct ext_session_lock_surface_v1_listener ext_session_lock_surface_v1_listener;
 267
 268static cairo_surface_t *select_image(struct swaylock_state *state,
 269		struct swaylock_surface *surface);
 270
 271static bool surface_is_opaque(struct swaylock_surface *surface) {
 272	if (!fade_is_complete(&surface->fade)) {
 273		return false;
 274	}
 275	if (surface->image) {
 276		return cairo_surface_get_content(surface->image) == CAIRO_CONTENT_COLOR;
 277	}
 278	return (surface->state->args.colors.background & 0xff) == 0xff;
 279}
 280
 281static void create_surface(struct swaylock_surface *surface) {
 282	struct swaylock_state *state = surface->state;
 283
 284	if (state->args.allow_fade && state->args.fade_in) {
 285		surface->fade.target_time = state->args.fade_in;
 286	}
 287
 288	surface->image = select_image(state, surface);
 289
 290	surface->surface = wl_compositor_create_surface(state->compositor);
 291	assert(surface->surface);
 292
 293	surface->child = wl_compositor_create_surface(state->compositor);
 294	assert(surface->child);
 295	surface->subsurface = wl_subcompositor_get_subsurface(state->subcompositor, surface->child, surface->surface);
 296	assert(surface->subsurface);
 297	wl_subsurface_set_sync(surface->subsurface);
 298
 299	if (state->ext_session_lock_v1) {
 300		surface->ext_session_lock_surface_v1 = ext_session_lock_v1_get_lock_surface(
 301				state->ext_session_lock_v1, surface->surface, surface->output);
 302		ext_session_lock_surface_v1_add_listener(surface->ext_session_lock_surface_v1,
 303				&ext_session_lock_surface_v1_listener, surface);
 304	} else {
 305		surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
 306				state->layer_shell, surface->surface, surface->output,
 307				ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen");
 308
 309		zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0);
 310		zwlr_layer_surface_v1_set_anchor(surface->layer_surface,
 311				ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
 312				ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
 313				ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
 314				ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
 315		zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1);
 316		zwlr_layer_surface_v1_set_keyboard_interactivity(
 317				surface->layer_surface, true);
 318		zwlr_layer_surface_v1_add_listener(surface->layer_surface,
 319				&layer_surface_listener, surface);
 320		surface->events_pending += 1;
 321	}
 322
 323	if (!state->ext_session_lock_v1) {
 324		wl_surface_commit(surface->surface);
 325	}
 326}
 327
 328static void initially_render_surface(struct swaylock_surface *surface) {
 329	swaylock_log(LOG_DEBUG, "Surface for output %s ready", surface->output_name);
 330	if (surface_is_opaque(surface) &&
 331			surface->state->args.mode != BACKGROUND_MODE_CENTER &&
 332			surface->state->args.mode != BACKGROUND_MODE_FIT) {
 333		struct wl_region *region =
 334			wl_compositor_create_region(surface->state->compositor);
 335		wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
 336		wl_surface_set_opaque_region(surface->surface, region);
 337		wl_region_destroy(region);
 338	}
 339
 340	if (!surface->state->ext_session_lock_v1) {
 341		render_frame_background(surface, true);
 342		render_frame(surface);
 343	}
 344}
 345
 346static void layer_surface_configure(void *data,
 347		struct zwlr_layer_surface_v1 *layer_surface,
 348		uint32_t serial, uint32_t width, uint32_t height) {
 349	swaylock_trace();
 350	struct swaylock_surface *surface = data;
 351	surface->width = width;
 352	surface->height = height;
 353	surface->indicator_width = 0;
 354	surface->indicator_height = 0;
 355	zwlr_layer_surface_v1_ack_configure(layer_surface, serial);
 356
 357	if (!surface->configured && --surface->events_pending == 0) {
 358		initially_render_surface(surface);
 359	}
 360	surface->configured = true;
 361}
 362
 363static void layer_surface_closed(void *data,
 364		struct zwlr_layer_surface_v1 *layer_surface) {
 365	swaylock_trace();
 366	struct swaylock_surface *surface = data;
 367	destroy_surface(surface);
 368}
 369
 370static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
 371	.configure = layer_surface_configure,
 372	.closed = layer_surface_closed,
 373};
 374
 375static struct swaylock_state state;
 376
 377static void ext_session_lock_surface_v1_handle_configure(void *data,
 378		struct ext_session_lock_surface_v1 *lock_surface, uint32_t serial,
 379		uint32_t width, uint32_t height) {
 380	struct swaylock_surface *surface = data;
 381	surface->width = width;
 382	surface->height = height;
 383	surface->indicator_width = 0;
 384	surface->indicator_height = 0;
 385	// Render before we send the ACK event, so that we minimize flickering
 386	// This means we cannot commit immediately after rendering -- we will have
 387	// to send the ACK first and then commit.
 388	render_frame_background(surface, false);
 389	ext_session_lock_surface_v1_ack_configure(lock_surface, serial);
 390	wl_surface_commit(surface->surface);
 391	if(!state.args.fade_in){
 392		render_frame(surface);
 393	}
 394}
 395
 396static const struct ext_session_lock_surface_v1_listener ext_session_lock_surface_v1_listener = {
 397	.configure = ext_session_lock_surface_v1_handle_configure,
 398};
 399
 400static const struct wl_callback_listener surface_frame_listener;
 401
 402static void surface_frame_handle_done(void *data, struct wl_callback *callback,
 403		uint32_t time) {
 404	struct swaylock_surface *surface = data;
 405
 406	wl_callback_destroy(callback);
 407	surface->frame_pending = false;
 408
 409	if (surface->dirty) {
 410		// Schedule a frame in case the surface is damaged again
 411		struct wl_callback *callback = wl_surface_frame(surface->surface);
 412		wl_callback_add_listener(callback, &surface_frame_listener, surface);
 413		surface->frame_pending = true;
 414		surface->dirty = false;
 415
 416		if (!fade_is_complete(&surface->fade)) {
 417			render_background_fade(surface, time);
 418			surface->dirty = true;
 419		}
 420
 421		render_frame(surface);
 422	}
 423}
 424
 425static const struct wl_callback_listener surface_frame_listener = {
 426	.done = surface_frame_handle_done,
 427};
 428
 429void damage_surface(struct swaylock_surface *surface) {
 430	if (surface->width == 0 || surface->height == 0) {
 431		// Not yet configured
 432		return;
 433	}
 434
 435	surface->dirty = true;
 436	if (surface->frame_pending) {
 437		return;
 438	}
 439
 440	struct wl_callback *callback = wl_surface_frame(surface->surface);
 441	wl_callback_add_listener(callback, &surface_frame_listener, surface);
 442	surface->frame_pending = true;
 443	wl_surface_commit(surface->surface);
 444}
 445
 446void damage_state(struct swaylock_state *state) {
 447	struct swaylock_surface *surface;
 448	wl_list_for_each(surface, &state->surfaces, link) {
 449		damage_surface(surface);
 450	}
 451}
 452
 453static void handle_wl_output_geometry(void *data, struct wl_output *wl_output,
 454		int32_t x, int32_t y, int32_t width_mm, int32_t height_mm,
 455		int32_t subpixel, const char *make, const char *model,
 456		int32_t transform) {
 457	swaylock_trace();
 458	struct swaylock_surface *surface = data;
 459	surface->subpixel = subpixel;
 460	surface->transform = transform;
 461	if (surface->state->run_display) {
 462		damage_surface(surface);
 463	}
 464}
 465
 466static void handle_wl_output_mode(void *data, struct wl_output *output,
 467		uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
 468	// Who cares
 469}
 470
 471static void handle_wl_output_scale(void *data, struct wl_output *output,
 472		int32_t factor) {
 473	swaylock_trace();
 474	struct swaylock_surface *surface = data;
 475	surface->scale = factor;
 476	if (surface->state->run_display) {
 477		damage_surface(surface);
 478	}
 479}
 480
 481static struct wl_buffer *create_shm_buffer(struct wl_shm *shm, enum wl_shm_format fmt,
 482		int width, int height, int stride, void **data_out) {
 483	int size = stride * height;
 484
 485	const char shm_name[] = "/swaylock-shm";
 486	int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
 487	if (fd < 0) {
 488		fprintf(stderr, "shm_open failed\n");
 489		return NULL;
 490	}
 491	shm_unlink(shm_name);
 492
 493	int ret;
 494	while ((ret = ftruncate(fd, size)) == EINTR) {
 495		// No-op
 496	}
 497	if (ret < 0) {
 498		close(fd);
 499		fprintf(stderr, "ftruncate failed\n");
 500		return NULL;
 501	}
 502
 503	void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 504	if (data == MAP_FAILED) {
 505		fprintf(stderr, "mmap failed: %m\n");
 506		close(fd);
 507		return NULL;
 508	}
 509
 510	struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
 511	close(fd);
 512	struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
 513		stride, fmt);
 514	wl_shm_pool_destroy(pool);
 515
 516	*data_out = data;
 517	return buffer;
 518}
 519
 520static cairo_surface_t *apply_effects(cairo_surface_t *image, struct swaylock_state *state, int scale) {
 521	if (state->args.effects_count == 0) {
 522		return image;
 523	}
 524
 525	if (state->args.time_effects) {
 526		return swaylock_effects_run_timed(
 527				image, scale,
 528				state->args.effects, state->args.effects_count);
 529	} else {
 530		return swaylock_effects_run(
 531				image, scale,
 532				state->args.effects, state->args.effects_count);
 533	}
 534}
 535
 536static void handle_screencopy_frame_buffer(void *data,
 537		struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width,
 538		uint32_t height, uint32_t stride) {
 539	swaylock_trace();
 540	struct swaylock_surface *surface = data;
 541
 542	struct swaylock_image *image = calloc(1, sizeof(struct swaylock_image));
 543	image->path = NULL;
 544	image->output_name = surface->output_name;
 545
 546	void *bufdata;
 547	struct wl_buffer *buf = create_shm_buffer(surface->state->shm, format, width, height, stride, &bufdata);
 548	if (buf == NULL) {
 549		free(image);
 550		return;
 551	}
 552
 553	surface->screencopy.format = format;
 554	surface->screencopy.width = width;
 555	surface->screencopy.height = height;
 556	surface->screencopy.stride = stride;
 557
 558	surface->screencopy.image = image;
 559	surface->screencopy.data = bufdata;
 560
 561	zwlr_screencopy_frame_v1_copy(frame, buf);
 562}
 563
 564static void handle_screencopy_frame_flags(void *data,
 565		struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
 566	swaylock_trace();
 567	struct swaylock_surface *surface = data;
 568
 569	// The transform affecting a screenshot consists of three parts:
 570	// Whether it's flipped vertically, whether it's flipped horizontally,
 571	// and the four rotation options (0, 90, 180, 270).
 572	// Any of the combinations of vertical flips, horizontal flips and rotation,
 573	// can be expressed in terms of only horizontal flips and rotation
 574	// (which is what the enum wl_output_transform encodes).
 575	// Therefore, instead of inverting the Y axis or keeping around the
 576	// "was it vertically flipped?" bit, we just map our state space onto the
 577	// state space encoded by wl_output_transform and let load_background_from_buffer
 578	// handle the rest.
 579	if (flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT) {
 580		switch (surface->transform) {
 581		case WL_OUTPUT_TRANSFORM_NORMAL:
 582			surface->screencopy.transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
 583			break;
 584		case WL_OUTPUT_TRANSFORM_90:
 585			surface->screencopy.transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
 586			break;
 587		case WL_OUTPUT_TRANSFORM_180:
 588			surface->screencopy.transform = WL_OUTPUT_TRANSFORM_FLIPPED;
 589			break;
 590		case WL_OUTPUT_TRANSFORM_270:
 591			surface->screencopy.transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
 592			break;
 593		case WL_OUTPUT_TRANSFORM_FLIPPED:
 594			surface->screencopy.transform = WL_OUTPUT_TRANSFORM_180;
 595			break;
 596		case WL_OUTPUT_TRANSFORM_FLIPPED_90:
 597			surface->screencopy.transform = WL_OUTPUT_TRANSFORM_90;
 598			break;
 599		case WL_OUTPUT_TRANSFORM_FLIPPED_180:
 600			surface->screencopy.transform = WL_OUTPUT_TRANSFORM_NORMAL;
 601			break;
 602		case WL_OUTPUT_TRANSFORM_FLIPPED_270:
 603			surface->screencopy.transform = WL_OUTPUT_TRANSFORM_270;
 604			break;
 605		}
 606	} else {
 607		surface->screencopy.transform = surface->transform;
 608	}
 609}
 610
 611static void handle_screencopy_frame_ready(void *data,
 612		struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi,
 613		uint32_t tv_sec_lo, uint32_t tv_nsec) {
 614	swaylock_trace();
 615	struct swaylock_surface *surface = data;
 616	struct swaylock_state *state = surface->state;
 617
 618	cairo_surface_t *image = load_background_from_buffer(
 619			surface->screencopy.data,
 620			surface->screencopy.format,
 621			surface->screencopy.width,
 622			surface->screencopy.height,
 623			surface->screencopy.stride,
 624			surface->screencopy.transform);
 625	if (image == NULL) {
 626		swaylock_log(LOG_ERROR, "Failed to create image from screenshot");
 627		state->args.screenshots = false;
 628		state->args.fade_in = 0; // Fade in is not possible without screenshot
 629	} else  {
 630		surface->screencopy.original_image = cairo_surface_duplicate(image);
 631		surface->screencopy.image->cairo_surface = image;
 632		if (state->args.screenshots) {
 633			swaylock_log(LOG_DEBUG, "Loaded screenshot for output %s", surface->output_name);
 634			wl_list_insert(&state->images, &surface->screencopy.image->link);
 635		}
 636	}
 637
 638	--surface->events_pending;
 639}
 640
 641static void handle_screencopy_frame_failed(void *data,
 642		struct zwlr_screencopy_frame_v1 *frame) {
 643	swaylock_trace();
 644	struct swaylock_surface *surface = data;
 645	swaylock_log(LOG_ERROR, "Screencopy failed");
 646	surface->state->args.screenshots = false;
 647	surface->state->args.fade_in = 0; // Fade in is not possible without screenshot
 648
 649	--surface->events_pending;
 650}
 651
 652static const struct zwlr_screencopy_frame_v1_listener screencopy_frame_listener = {
 653	.buffer = handle_screencopy_frame_buffer,
 654	.flags = handle_screencopy_frame_flags,
 655	.ready = handle_screencopy_frame_ready,
 656	.failed = handle_screencopy_frame_failed,
 657};
 658
 659static void handle_wl_output_name(void *data, struct wl_output *output,
 660		const char *name) {
 661	swaylock_trace();
 662	swaylock_log(LOG_DEBUG, "output name is %s", name);
 663	struct swaylock_surface *surface = data;
 664	surface->output_name = strdup(name);
 665}
 666
 667static void handle_wl_output_description(void *data, struct wl_output *output,
 668		const char *description) {
 669	// Who cares
 670}
 671
 672static void handle_wl_output_done(void *data, struct wl_output *output) {
 673	swaylock_trace();
 674	struct swaylock_surface *surface = data;
 675	struct swaylock_state *state = surface->state;
 676
 677	static bool has_printed_screencopy_error = false;
 678	if (state->screencopy_manager) {
 679		surface->screencopy_frame = zwlr_screencopy_manager_v1_capture_output(
 680				state->screencopy_manager, false, surface->output);
 681		zwlr_screencopy_frame_v1_add_listener(surface->screencopy_frame,
 682				&screencopy_frame_listener, surface);
 683		surface->events_pending += 1;
 684	} else if (!has_printed_screencopy_error) {
 685		swaylock_log(LOG_INFO, "Compositor does not support screencopy manager, "
 686				"screenshots / fade-in will not work");
 687		state->args.screenshots = false;
 688		state->args.fade_in = 0; // Fade in is not possible without screenshot
 689		has_printed_screencopy_error = true;
 690	}
 691
 692	--surface->events_pending;
 693}
 694
 695struct wl_output_listener _wl_output_listener = {
 696	.geometry = handle_wl_output_geometry,
 697	.mode = handle_wl_output_mode,
 698	.done = handle_wl_output_done,
 699	.scale = handle_wl_output_scale,
 700	.name = handle_wl_output_name,
 701	.description = handle_wl_output_description,
 702};
 703
 704static void ext_session_lock_v1_handle_locked(void *data, struct ext_session_lock_v1 *lock) {
 705	// Who cares
 706}
 707
 708static void ext_session_lock_v1_handle_finished(void *data, struct ext_session_lock_v1 *lock) {
 709	swaylock_log(LOG_ERROR, "Failed to lock session -- "
 710			"is another lockscreen running?");
 711	exit(2);
 712}
 713
 714static const struct ext_session_lock_v1_listener ext_session_lock_v1_listener = {
 715	.locked = ext_session_lock_v1_handle_locked,
 716	.finished = ext_session_lock_v1_handle_finished,
 717};
 718
 719static void handle_global(void *data, struct wl_registry *registry,
 720		uint32_t name, const char *interface, uint32_t version) {
 721
 722	struct swaylock_state *state = data;
 723	if (strcmp(interface, wl_compositor_interface.name) == 0) {
 724		state->compositor = wl_registry_bind(registry, name,
 725				&wl_compositor_interface, 4);
 726	} else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
 727		state->subcompositor = wl_registry_bind(registry, name,
 728				&wl_subcompositor_interface, 1);
 729	} else if (strcmp(interface, wl_shm_interface.name) == 0) {
 730		state->shm = wl_registry_bind(registry, name,
 731				&wl_shm_interface, 1);
 732	} else if (strcmp(interface, wl_seat_interface.name) == 0) {
 733		struct wl_seat *seat = wl_registry_bind(
 734				registry, name, &wl_seat_interface, 4);
 735		struct swaylock_seat *swaylock_seat =
 736			calloc(1, sizeof(struct swaylock_seat));
 737		swaylock_seat->state = state;
 738		wl_seat_add_listener(seat, &seat_listener, swaylock_seat);
 739	} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
 740		state->layer_shell = wl_registry_bind(
 741				registry, name, &zwlr_layer_shell_v1_interface, 1);
 742	} else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) {
 743		state->input_inhibit_manager = wl_registry_bind(
 744				registry, name, &zwlr_input_inhibit_manager_v1_interface, 1);
 745	} else if (strcmp(interface, wl_output_interface.name) == 0) {
 746		struct swaylock_surface *surface =
 747			calloc(1, sizeof(struct swaylock_surface));
 748		surface->state = state;
 749		surface->output = wl_registry_bind(registry, name,
 750				&wl_output_interface, 4);
 751		surface->output_global_name = name;
 752		wl_output_add_listener(surface->output, &_wl_output_listener, surface);
 753		wl_list_insert(&state->surfaces, &surface->link);
 754
 755		if (state->run_display) {
 756			create_surface(surface);
 757			wl_display_roundtrip(state->display);
 758		}
 759	} else if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
 760		state->screencopy_manager = wl_registry_bind(registry, name,
 761				&zwlr_screencopy_manager_v1_interface, 1);
 762	} else if (strcmp(interface, ext_session_lock_manager_v1_interface.name) == 0) {
 763		state->ext_session_lock_manager_v1 = wl_registry_bind(registry, name,
 764				&ext_session_lock_manager_v1_interface, 1);
 765	}
 766}
 767
 768static void handle_global_remove(void *data, struct wl_registry *registry,
 769		uint32_t name) {
 770	struct swaylock_state *state = data;
 771	struct swaylock_surface *surface;
 772	wl_list_for_each(surface, &state->surfaces, link) {
 773		if (surface->output_global_name == name) {
 774			destroy_surface(surface);
 775			break;
 776		}
 777	}
 778}
 779
 780static const struct wl_registry_listener registry_listener = {
 781	.global = handle_global,
 782	.global_remove = handle_global_remove,
 783};
 784
 785static int sigusr_fds[2] = {-1, -1};
 786
 787void do_sigusr(int sig) {
 788	(void)write(sigusr_fds[1], "1", 1);
 789}
 790
 791static cairo_surface_t *select_image(struct swaylock_state *state,
 792		struct swaylock_surface *surface) {
 793	struct swaylock_image *image;
 794	cairo_surface_t *default_image = NULL;
 795	wl_list_for_each(image, &state->images, link) {
 796		if (lenient_strcmp(image->output_name, surface->output_name) == 0) {
 797			return image->cairo_surface;
 798		} else if (!image->output_name) {
 799			default_image = image->cairo_surface;
 800		}
 801	}
 802	return default_image;
 803}
 804
 805static char *join_args(char **argv, int argc) {
 806	assert(argc > 0);
 807	int len = 0, i;
 808	for (i = 0; i < argc; ++i) {
 809		len += strlen(argv[i]) + 1;
 810	}
 811	char *res = malloc(len);
 812	len = 0;
 813	for (i = 0; i < argc; ++i) {
 814		strcpy(res + len, argv[i]);
 815		len += strlen(argv[i]);
 816		res[len++] = ' ';
 817	}
 818	res[len - 1] = '\0';
 819	return res;
 820}
 821
 822static void load_image(char *arg, struct swaylock_state *state) {
 823	// [[<output>]:]<path>
 824	struct swaylock_image *image = calloc(1, sizeof(struct swaylock_image));
 825	char *separator = strchr(arg, ':');
 826	if (separator) {
 827		*separator = '\0';
 828		image->output_name = separator == arg ? NULL : strdup(arg);
 829		image->path = strdup(separator + 1);
 830	} else {
 831		image->output_name = NULL;
 832		image->path = strdup(arg);
 833	}
 834
 835	struct swaylock_image *iter_image, *temp;
 836	wl_list_for_each_safe(iter_image, temp, &state->images, link) {
 837		if (lenient_strcmp(iter_image->output_name, image->output_name) == 0) {
 838			if (image->output_name) {
 839				swaylock_log(LOG_DEBUG,
 840						"Replacing image defined for output %s with %s",
 841						image->output_name, image->path);
 842			} else {
 843				swaylock_log(LOG_DEBUG, "Replacing default image with %s",
 844						image->path);
 845			}
 846			wl_list_remove(&iter_image->link);
 847			free(iter_image->cairo_surface);
 848			free(iter_image->output_name);
 849			free(iter_image->path);
 850			free(iter_image);
 851			break;
 852		}
 853	}
 854
 855	// The shell will not expand ~ to the value of $HOME when an output name is
 856	// given. Also, any image paths given in the config file need to have shell
 857	// expansions performed
 858	wordexp_t p;
 859	while (strstr(image->path, "  ")) {
 860		image->path = realloc(image->path, strlen(image->path) + 2);
 861		char *ptr = strstr(image->path, "  ") + 1;
 862		memmove(ptr + 1, ptr, strlen(ptr) + 1);
 863		*ptr = '\\';
 864	}
 865	if (wordexp(image->path, &p, 0) == 0) {
 866		free(image->path);
 867		image->path = join_args(p.we_wordv, p.we_wordc);
 868		wordfree(&p);
 869	}
 870
 871	// Load the actual image
 872	image->cairo_surface = load_background_image(image->path);
 873	if (!image->cairo_surface) {
 874		free(image);
 875		return;
 876	}
 877
 878	wl_list_insert(&state->images, &image->link);
 879	swaylock_log(LOG_DEBUG, "Loaded image %s for output %s", image->path,
 880			image->output_name ? image->output_name : "*");
 881}
 882
 883static void set_default_colors(struct swaylock_colors *colors) {
 884	colors->background = 0xFFFFFFFF;
 885	colors->bs_highlight = 0xDB3300FF;
 886	colors->key_highlight = 0x33DB00FF;
 887	colors->caps_lock_bs_highlight = 0xDB3300FF;
 888	colors->caps_lock_key_highlight = 0x33DB00FF;
 889	colors->separator = 0x000000FF;
 890	colors->layout_background = 0x000000C0;
 891	colors->layout_border = 0x00000000;
 892	colors->layout_text = 0xFFFFFFFF;
 893	colors->inside = (struct swaylock_colorset){
 894		.input = 0x000000C0,
 895		.cleared = 0xE5A445C0,
 896		.caps_lock = 0x000000C0,
 897		.verifying = 0x0072FFC0,
 898		.wrong = 0xFA0000C0,
 899	};
 900	colors->line = (struct swaylock_colorset){
 901		.input = 0x000000FF,
 902		.cleared = 0x000000FF,
 903		.caps_lock = 0x000000FF,
 904		.verifying = 0x000000FF,
 905		.wrong = 0x000000FF,
 906	};
 907	colors->ring = (struct swaylock_colorset){
 908		.input = 0x337D00FF,
 909		.cleared = 0xE5A445FF,
 910		.caps_lock = 0xE5A445FF,
 911		.verifying = 0x3300FFFF,
 912		.wrong = 0x7D3300FF,
 913	};
 914	colors->text = (struct swaylock_colorset){
 915		.input = 0xE5A445FF,
 916		.cleared = 0x000000FF,
 917		.caps_lock = 0xE5A445FF,
 918		.verifying = 0x000000FF,
 919		.wrong = 0x000000FF,
 920	};
 921}
 922
 923enum line_mode {
 924	LM_LINE,
 925	LM_INSIDE,
 926	LM_RING,
 927};
 928
 929static int parse_options(int argc, char **argv, struct swaylock_state *state,
 930		enum line_mode *line_mode, char **config_path) {
 931	enum long_option_codes {
 932		LO_BS_HL_COLOR = 256,
 933		LO_CAPS_LOCK_BS_HL_COLOR,
 934		LO_CAPS_LOCK_KEY_HL_COLOR,
 935		LO_FONT,
 936		LO_FONT_SIZE,
 937		LO_IND_IDLE_VISIBLE,
 938		LO_IND_RADIUS,
 939		LO_IND_X_POSITION,
 940		LO_IND_Y_POSITION,
 941		LO_IND_THICKNESS,
 942		LO_IND_IMAGE,
 943		LO_INSIDE_COLOR,
 944		LO_INSIDE_CLEAR_COLOR,
 945		LO_INSIDE_CAPS_LOCK_COLOR,
 946		LO_INSIDE_VER_COLOR,
 947		LO_INSIDE_WRONG_COLOR,
 948		LO_KEY_HL_COLOR,
 949		LO_LAYOUT_TXT_COLOR,
 950		LO_LAYOUT_BG_COLOR,
 951		LO_LAYOUT_BORDER_COLOR,
 952		LO_LINE_COLOR,
 953		LO_LINE_CLEAR_COLOR,
 954		LO_LINE_CAPS_LOCK_COLOR,
 955		LO_LINE_VER_COLOR,
 956		LO_LINE_WRONG_COLOR,
 957		LO_RING_COLOR,
 958		LO_RING_CLEAR_COLOR,
 959		LO_RING_CAPS_LOCK_COLOR,
 960		LO_RING_VER_COLOR,
 961		LO_RING_WRONG_COLOR,
 962		LO_SEP_COLOR,
 963		LO_TEXT_COLOR,
 964		LO_TEXT_CLEAR,
 965		LO_TEXT_CLEAR_COLOR,
 966		LO_TEXT_CAPS_LOCK,
 967		LO_TEXT_CAPS_LOCK_COLOR,
 968		LO_TEXT_VER,
 969		LO_TEXT_VER_COLOR,
 970		LO_TEXT_WRONG,
 971		LO_TEXT_WRONG_COLOR,
 972		LO_EFFECT_BLUR,
 973		LO_EFFECT_PIXELATE,
 974		LO_EFFECT_SCALE,
 975		LO_EFFECT_GREYSCALE,
 976		LO_EFFECT_VIGNETTE,
 977		LO_EFFECT_COMPOSE,
 978		LO_EFFECT_CUSTOM,
 979		LO_TIME_EFFECTS,
 980		LO_INDICATOR,
 981		LO_CLOCK,
 982		LO_TIMESTR,
 983		LO_DATESTR,
 984		LO_FADE_IN,
 985		LO_SUBMIT_ON_TOUCH,
 986		LO_GRACE,
 987		LO_GRACE_NO_MOUSE,
 988		LO_GRACE_NO_TOUCH,
 989	};
 990
 991	static struct option long_options[] = {
 992		{"config", required_argument, NULL, 'C'},
 993		{"color", required_argument, NULL, 'c'},
 994		{"debug", no_argument, NULL, 'd'},
 995		{"trace", no_argument, NULL, 't'},
 996		{"ignore-empty-password", no_argument, NULL, 'e'},
 997		{"daemonize", no_argument, NULL, 'f'},
 998		{"help", no_argument, NULL, 'h'},
 999		{"image", required_argument, NULL, 'i'},
1000		{"screenshots", no_argument, NULL, 'S'},
1001		{"disable-caps-lock-text", no_argument, NULL, 'L'},
1002		{"indicator-caps-lock", no_argument, NULL, 'l'},
1003		{"line-uses-inside", no_argument, NULL, 'n'},
1004		{"line-uses-ring", no_argument, NULL, 'r'},
1005		{"scaling", required_argument, NULL, 's'},
1006		{"tiling", no_argument, NULL, 'T'},
1007		{"no-unlock-indicator", no_argument, NULL, 'u'},
1008		{"show-keyboard-layout", no_argument, NULL, 'k'},
1009		{"hide-keyboard-layout", no_argument, NULL, 'K'},
1010		{"show-failed-attempts", no_argument, NULL, 'F'},
1011		{"version", no_argument, NULL, 'v'},
1012		{"bs-hl-color", required_argument, NULL, LO_BS_HL_COLOR},
1013		{"caps-lock-bs-hl-color", required_argument, NULL, LO_CAPS_LOCK_BS_HL_COLOR},
1014		{"caps-lock-key-hl-color", required_argument, NULL, LO_CAPS_LOCK_KEY_HL_COLOR},
1015		{"font", required_argument, NULL, LO_FONT},
1016		{"font-size", required_argument, NULL, LO_FONT_SIZE},
1017		{"indicator-idle-visible", no_argument, NULL, LO_IND_IDLE_VISIBLE},
1018		{"indicator-radius", required_argument, NULL, LO_IND_RADIUS},
1019		{"indicator-thickness", required_argument, NULL, LO_IND_THICKNESS},
1020		{"indicator-x-position", required_argument, NULL, LO_IND_X_POSITION},
1021		{"indicator-y-position", required_argument, NULL, LO_IND_Y_POSITION},
1022		{"indicator-image", required_argument, NULL, LO_IND_IMAGE},
1023		{"inside-color", required_argument, NULL, LO_INSIDE_COLOR},
1024		{"inside-clear-color", required_argument, NULL, LO_INSIDE_CLEAR_COLOR},
1025		{"inside-caps-lock-color", required_argument, NULL, LO_INSIDE_CAPS_LOCK_COLOR},
1026		{"inside-ver-color", required_argument, NULL, LO_INSIDE_VER_COLOR},
1027		{"inside-wrong-color", required_argument, NULL, LO_INSIDE_WRONG_COLOR},
1028		{"key-hl-color", required_argument, NULL, LO_KEY_HL_COLOR},
1029		{"layout-bg-color", required_argument, NULL, LO_LAYOUT_BG_COLOR},
1030		{"layout-border-color", required_argument, NULL, LO_LAYOUT_BORDER_COLOR},
1031		{"layout-text-color", required_argument, NULL, LO_LAYOUT_TXT_COLOR},
1032		{"line-color", required_argument, NULL, LO_LINE_COLOR},
1033		{"line-clear-color", required_argument, NULL, LO_LINE_CLEAR_COLOR},
1034		{"line-caps-lock-color", required_argument, NULL, LO_LINE_CAPS_LOCK_COLOR},
1035		{"line-ver-color", required_argument, NULL, LO_LINE_VER_COLOR},
1036		{"line-wrong-color", required_argument, NULL, LO_LINE_WRONG_COLOR},
1037		{"ring-color", required_argument, NULL, LO_RING_COLOR},
1038		{"ring-clear-color", required_argument, NULL, LO_RING_CLEAR_COLOR},
1039		{"ring-caps-lock-color", required_argument, NULL, LO_RING_CAPS_LOCK_COLOR},
1040		{"ring-ver-color", required_argument, NULL, LO_RING_VER_COLOR},
1041		{"ring-wrong-color", required_argument, NULL, LO_RING_WRONG_COLOR},
1042		{"separator-color", required_argument, NULL, LO_SEP_COLOR},
1043		{"text-color", required_argument, NULL, LO_TEXT_COLOR},
1044		{"text-clear", required_argument, NULL, LO_TEXT_CLEAR},
1045		{"text-clear-color", required_argument, NULL, LO_TEXT_CLEAR_COLOR},
1046		{"text-caps-lock", required_argument, NULL, LO_TEXT_CAPS_LOCK},
1047		{"text-caps-lock-color", required_argument, NULL, LO_TEXT_CAPS_LOCK_COLOR},
1048		{"text-ver", required_argument, NULL, LO_TEXT_VER},
1049		{"text-ver-color", required_argument, NULL, LO_TEXT_VER_COLOR},
1050		{"text-wrong", required_argument, NULL, LO_TEXT_WRONG},
1051		{"text-wrong-color", required_argument, NULL, LO_TEXT_WRONG_COLOR},
1052		{"effect-blur", required_argument, NULL, LO_EFFECT_BLUR},
1053		{"effect-pixelate", required_argument, NULL, LO_EFFECT_PIXELATE},
1054		{"effect-scale", required_argument, NULL, LO_EFFECT_SCALE},
1055		{"effect-greyscale", no_argument, NULL, LO_EFFECT_GREYSCALE},
1056		{"effect-vignette", required_argument, NULL, LO_EFFECT_VIGNETTE},
1057		{"effect-compose", required_argument, NULL, LO_EFFECT_COMPOSE},
1058		{"effect-custom", required_argument, NULL, LO_EFFECT_CUSTOM},
1059		{"time-effects", no_argument, NULL, LO_TIME_EFFECTS},
1060		{"indicator", no_argument, NULL, LO_INDICATOR},
1061		{"clock", no_argument, NULL, LO_CLOCK},
1062		{"timestr", required_argument, NULL, LO_TIMESTR},
1063		{"datestr", required_argument, NULL, LO_DATESTR},
1064		{"fade-in", required_argument, NULL, LO_FADE_IN},
1065		{"submit-on-touch", no_argument, NULL, LO_SUBMIT_ON_TOUCH},
1066		{"grace", required_argument, NULL, LO_GRACE},
1067		{"grace-no-mouse", no_argument, NULL, LO_GRACE_NO_MOUSE},
1068		{"grace-no-touch", no_argument, NULL, LO_GRACE_NO_TOUCH},
1069		{0, 0, 0, 0}
1070	};
1071
1072	const char usage[] =
1073		"Usage: swaylock [options...]\n"
1074		"\n"
1075		"  -C, --config <config_file>       "
1076			"Path to the config file.\n"
1077		"  -c, --color <color>              "
1078			"Turn the screen into the given color instead of white.\n"
1079		"  -d, --debug                      "
1080			"Enable debugging output.\n"
1081		"  -t, --trace                      "
1082			"Enable tracing output.\n"
1083		"  -e, --ignore-empty-password      "
1084			"When an empty password is provided, do not validate it.\n"
1085		"  -F, --show-failed-attempts       "
1086			"Show current count of failed authentication attempts.\n"
1087		"  -f, --daemonize                  "
1088			"Detach from the controlling terminal after locking.\n"
1089		"  --fade-in <seconds>              "
1090			"Make the lock screen fade in instead of just popping in.\n"
1091		"  --submit-on-touch                "
1092			"Submit password in response to a touch event.\n"
1093		"  --grace <seconds>                "
1094			"Password grace period. Don't require the password for the first N seconds.\n"
1095		"  --grace-no-mouse                 "
1096			"During the grace period, don't unlock on a mouse event.\n"
1097		"  --grace-no-touch                 "
1098			"During the grace period, don't unlock on a touch event.\n"
1099		"  -h, --help                       "
1100			"Show help message and quit.\n"
1101		"  -i, --image [[<output>]:]<path>  "
1102			"Display the given image, optionally only on the given output.\n"
1103		"  -S, --screenshots                "
1104			"Use a screenshots as the background image.\n"
1105		"  -k, --show-keyboard-layout       "
1106			"Display the current xkb layout while typing.\n"
1107		"  -K, --hide-keyboard-layout       "
1108			"Hide the current xkb layout while typing.\n"
1109		"  -L, --disable-caps-lock-text     "
1110			"Disable the Caps Lock text.\n"
1111		"  -l, --indicator-caps-lock        "
1112			"Show the current Caps Lock state also on the indicator.\n"
1113		"  -s, --scaling <mode>             "
1114			"Image scaling mode: stretch, fill, fit, center, tile, solid_color.\n"
1115		"  -T, --tiling                     "
1116			"Same as --scaling=tile.\n"
1117		"  -u, --no-unlock-indicator        "
1118			"Disable the unlock indicator.\n"
1119		"  --indicator                      "
1120			"Always show the indicator.\n"
1121		"  --clock                          "
1122			"Show time and date.\n"
1123		"  --timestr <format>               "
1124			"The format string for the time. Defaults to '%T'.\n"
1125		"  --datestr <format>               "
1126			"The format string for the date. Defaults to '%a, %x'.\n"
1127		"  -v, --version                    "
1128			"Show the version number and quit.\n"
1129		"  --bs-hl-color <color>            "
1130			"Sets the color of backspace highlight segments.\n"
1131		"  --caps-lock-bs-hl-color <color>  "
1132			"Sets the color of backspace highlight segments when Caps Lock "
1133			"is active.\n"
1134		"  --caps-lock-key-hl-color <color> "
1135			"Sets the color of the key press highlight segments when "
1136			"Caps Lock is active.\n"
1137		"  --font <font>                    "
1138			"Sets the font of the text.\n"
1139		"  --font-size <size>               "
1140			"Sets a fixed font size for the indicator text.\n"
1141		"  --indicator-idle-visible         "
1142			"Sets the indicator to show even if idle.\n"
1143		"  --indicator-radius <radius>      "
1144			"Sets the indicator radius.\n"
1145		"  --indicator-thickness <thick>    "
1146			"Sets the indicator thickness.\n"
1147		"  --indicator-x-position <x>       "
1148			"Sets the horizontal position of the indicator.\n"
1149		"  --indicator-y-position <y>       "
1150			"Sets the vertical position of the indicator.\n"
1151		"  --indicator-image <path>         "
1152			"Display the given image inside of the indicator.\n"
1153		"  --inside-color <color>           "
1154			"Sets the color of the inside of the indicator.\n"
1155		"  --inside-clear-color <color>     "
1156			"Sets the color of the inside of the indicator when cleared.\n"
1157		"  --inside-caps-lock-color <color> "
1158			"Sets the color of the inside of the indicator when Caps Lock "
1159			"is active.\n"
1160		"  --inside-ver-color <color>       "
1161			"Sets the color of the inside of the indicator when verifying.\n"
1162		"  --inside-wrong-color <color>     "
1163			"Sets the color of the inside of the indicator when invalid.\n"
1164		"  --key-hl-color <color>           "
1165			"Sets the color of the key press highlight segments.\n"
1166		"  --layout-bg-color <color>        "
1167			"Sets the background color of the box containing the layout text.\n"
1168		"  --layout-border-color <color>    "
1169			"Sets the color of the border of the box containing the layout text.\n"
1170		"  --layout-text-color <color>      "
1171			"Sets the color of the layout text.\n"
1172		"  --line-color <color>             "
1173			"Sets the color of the line between the inside and ring.\n"
1174		"  --line-clear-color <color>       "
1175			"Sets the color of the line between the inside and ring when "
1176			"cleared.\n"
1177		"  --line-caps-lock-color <color>   "
1178			"Sets the color of the line between the inside and ring when "
1179			"Caps Lock is active.\n"
1180		"  --line-ver-color <color>         "
1181			"Sets the color of the line between the inside and ring when "
1182			"verifying.\n"
1183		"  --line-wrong-color <color>       "
1184			"Sets the color of the line between the inside and ring when "
1185			"invalid.\n"
1186		"  -n, --line-uses-inside           "
1187			"Use the inside color for the line between the inside and ring.\n"
1188		"  -r, --line-uses-ring             "
1189			"Use the ring color for the line between the inside and ring.\n"
1190		"  --ring-color <color>             "
1191			"Sets the color of the ring of the indicator.\n"
1192		"  --ring-clear-color <color>       "
1193			"Sets the color of the ring of the indicator when cleared.\n"
1194		"  --ring-caps-lock-color <color>   "
1195			"Sets the color of the ring of the indicator when Caps Lock "
1196			"is active.\n"
1197		"  --ring-ver-color <color>         "
1198			"Sets the color of the ring of the indicator when verifying.\n"
1199		"  --ring-wrong-color <color>       "
1200			"Sets the color of the ring of the indicator when invalid.\n"
1201		"  --separator-color <color>        "
1202			"Sets the color of the lines that separate highlight segments.\n"
1203		"  --text-color <color>             "
1204			"Sets the color of the text.\n"
1205		"  --text-clear-color <color>       "
1206			"Sets the color of the text when cleared.\n"
1207		"  --text-caps-lock-color <color>   "
1208			"Sets the color of the text when Caps Lock is active.\n"
1209		"  --text-ver-color <color>         "
1210			"Sets the color of the text when verifying.\n"
1211		"  --text-wrong-color <color>       "
1212			"Sets the color of the text when invalid.\n"
1213		"  --effect-blur <radius>x<times>   "
1214			"Blur images.\n"
1215		"  --effect-pixelate <factor>       "
1216			"Pixelate images.\n"
1217		"  --effect-scale <scale>           "
1218			"Scale images.\n"
1219		"  --effect-greyscale               "
1220			"Make images greyscale.\n"
1221		"  --effect-vignette <base>:<factor>"
1222			"Apply a vignette effect to images. Base and factor should be numbers between 0 and 1.\n"
1223		"  --effect-custom <path>           "
1224			"Apply a custom effect from a shared object or C source file.\n"
1225		"  --time-effects                   "
1226			"Measure the time it takes to run each effect.\n"
1227		"\n"
1228		"All <color> options are of the form <rrggbb[aa]>.\n";
1229
1230	int c;
1231	optind = 1;
1232	while (1) {
1233		int opt_idx = 0;
1234		c = getopt_long(argc, argv, "c:deFfhi:SkKLlnrs:tuvC:", long_options,
1235				&opt_idx);
1236		if (c == -1) {
1237			break;
1238		}
1239		switch (c) {
1240		case 'C':
1241			if (config_path) {
1242				*config_path = strdup(optarg);
1243			}
1244			break;
1245		case 'c':
1246			if (state) {
1247				state->args.colors.background = parse_color(optarg);
1248			}
1249			break;
1250		case 'd':
1251			swaylock_log_init(LOG_DEBUG);
1252			break;
1253		case 't':
1254			swaylock_log_init(LOG_TRACE);
1255			break;
1256		case 'e':
1257			if (state) {
1258				state->args.ignore_empty = true;
1259			}
1260			break;
1261		case 'F':
1262			if (state) {
1263				state->args.show_failed_attempts = true;
1264			}
1265			break;
1266		case 'f':
1267			if (state) {
1268				state->args.daemonize = true;
1269			}
1270			break;
1271		case 'i':
1272			if (state) {
1273				load_image(optarg, state);
1274			}
1275			break;
1276		case 'S':
1277			if (state) {
1278				state->args.screenshots = true;
1279			}
1280			break;
1281		case 'k':
1282			if (state) {
1283				state->args.show_keyboard_layout = true;
1284			}
1285			break;
1286		case 'K':
1287			if (state) {
1288				state->args.hide_keyboard_layout = true;
1289			}
1290			break;
1291		case 'L':
1292			if (state) {
1293				state->args.show_caps_lock_text = false;
1294			}
1295			break;
1296		case 'l':
1297			if (state) {
1298				state->args.show_caps_lock_indicator = true;
1299			}
1300			break;
1301		case 'n':
1302			if (line_mode) {
1303				*line_mode = LM_INSIDE;
1304			}
1305			break;
1306		case 'r':
1307			if (line_mode) {
1308				*line_mode = LM_RING;
1309			}
1310			break;
1311		case 's':
1312			if (state) {
1313				state->args.mode = parse_background_mode(optarg);
1314				if (state->args.mode == BACKGROUND_MODE_INVALID) {
1315					return 1;
1316				}
1317			}
1318			break;
1319		case 'T':
1320			if (state) {
1321				state->args.mode = BACKGROUND_MODE_TILE;
1322			}
1323			break;
1324		case 'u':
1325			if (state) {
1326				state->args.show_indicator = false;
1327			}
1328			break;
1329		case 'v':
1330			fprintf(stdout, "swaylock version " SWAYLOCK_VERSION "\n");
1331			exit(EXIT_SUCCESS);
1332			break;
1333		case LO_BS_HL_COLOR:
1334			if (state) {
1335				state->args.colors.bs_highlight = parse_color(optarg);
1336			}
1337			break;
1338		case LO_CAPS_LOCK_BS_HL_COLOR:
1339			if (state) {
1340				state->args.colors.caps_lock_bs_highlight = parse_color(optarg);
1341			}
1342			break;
1343		case LO_CAPS_LOCK_KEY_HL_COLOR:
1344			if (state) {
1345				state->args.colors.caps_lock_key_highlight = parse_color(optarg);
1346			}
1347			break;
1348		case LO_FONT:
1349			if (state) {
1350				free(state->args.font);
1351				state->args.font = strdup(optarg);
1352			}
1353			break;
1354		case LO_FONT_SIZE:
1355			if (state) {
1356				state->args.font_size = atoi(optarg);
1357			}
1358			break;
1359		case LO_IND_IDLE_VISIBLE:
1360			if (state) {
1361				state->args.indicator_idle_visible = true;
1362			}
1363			break;
1364		case LO_IND_RADIUS:
1365			if (state) {
1366				state->args.radius = strtol(optarg, NULL, 0);
1367			}
1368			break;
1369		case LO_IND_THICKNESS:
1370			if (state) {
1371				state->args.thickness = strtol(optarg, NULL, 0);
1372			}
1373			break;
1374		case LO_IND_X_POSITION:
1375			if (state) {
1376				state->args.override_indicator_x_position = true;
1377				state->args.indicator_x_position = atoi(optarg);
1378			}
1379			break;
1380		case LO_IND_Y_POSITION:
1381			if (state) {
1382				state->args.override_indicator_y_position = true;
1383				state->args.indicator_y_position = atoi(optarg);
1384			}
1385			break;
1386		case LO_IND_IMAGE:
1387			if (state) {
1388				state->indicator_image = load_background_image(optarg);
1389			}
1390			break;
1391		case LO_INSIDE_COLOR:
1392			if (state) {
1393				state->args.colors.inside.input = parse_color(optarg);
1394			}
1395			break;
1396		case LO_INSIDE_CLEAR_COLOR:
1397			if (state) {
1398				state->args.colors.inside.cleared = parse_color(optarg);
1399			}
1400			break;
1401		case LO_INSIDE_CAPS_LOCK_COLOR:
1402			if (state) {
1403				state->args.colors.inside.caps_lock = parse_color(optarg);
1404			}
1405			break;
1406		case LO_INSIDE_VER_COLOR:
1407			if (state) {
1408				state->args.colors.inside.verifying = parse_color(optarg);
1409			}
1410			break;
1411		case LO_INSIDE_WRONG_COLOR:
1412			if (state) {
1413				state->args.colors.inside.wrong = parse_color(optarg);
1414			}
1415			break;
1416		case LO_KEY_HL_COLOR:
1417			if (state) {
1418				state->args.colors.key_highlight = parse_color(optarg);
1419			}
1420			break;
1421		case LO_LAYOUT_BG_COLOR:
1422			if (state) {
1423				state->args.colors.layout_background = parse_color(optarg);
1424			}
1425			break;
1426		case LO_LAYOUT_BORDER_COLOR:
1427			if (state) {
1428				state->args.colors.layout_border = parse_color(optarg);
1429			}
1430			break;
1431		case LO_LAYOUT_TXT_COLOR:
1432			if (state) {
1433				state->args.colors.layout_text = parse_color(optarg);
1434			}
1435			break;
1436		case LO_LINE_COLOR:
1437			if (state) {
1438				state->args.colors.line.input = parse_color(optarg);
1439			}
1440			break;
1441		case LO_LINE_CLEAR_COLOR:
1442			if (state) {
1443				state->args.colors.line.cleared = parse_color(optarg);
1444			}
1445			break;
1446		case LO_LINE_CAPS_LOCK_COLOR:
1447			if (state) {
1448				state->args.colors.line.caps_lock = parse_color(optarg);
1449			}
1450			break;
1451		case LO_LINE_VER_COLOR:
1452			if (state) {
1453				state->args.colors.line.verifying = parse_color(optarg);
1454			}
1455			break;
1456		case LO_LINE_WRONG_COLOR:
1457			if (state) {
1458				state->args.colors.line.wrong = parse_color(optarg);
1459			}
1460			break;
1461		case LO_RING_COLOR:
1462			if (state) {
1463				state->args.colors.ring.input = parse_color(optarg);
1464			}
1465			break;
1466		case LO_RING_CLEAR_COLOR:
1467			if (state) {
1468				state->args.colors.ring.cleared = parse_color(optarg);
1469			}
1470			break;
1471		case LO_RING_CAPS_LOCK_COLOR:
1472			if (state) {
1473				state->args.colors.ring.caps_lock = parse_color(optarg);
1474			}
1475			break;
1476		case LO_RING_VER_COLOR:
1477			if (state) {
1478				state->args.colors.ring.verifying = parse_color(optarg);
1479			}
1480			break;
1481		case LO_RING_WRONG_COLOR:
1482			if (state) {
1483				state->args.colors.ring.wrong = parse_color(optarg);
1484			}
1485			break;
1486		case LO_SEP_COLOR:
1487			if (state) {
1488				state->args.colors.separator = parse_color(optarg);
1489			}
1490			break;
1491		case LO_TEXT_COLOR:
1492			if (state) {
1493				state->args.colors.text.input = parse_color(optarg);
1494			}
1495			break;
1496		case LO_TEXT_CLEAR:
1497			if (state) {
1498				free(state->args.text_cleared);
1499				state->args.text_cleared = strdup(optarg);
1500			}
1501			break;
1502		case LO_TEXT_CLEAR_COLOR:
1503			if (state) {
1504				state->args.colors.text.cleared = parse_color(optarg);
1505			}
1506			break;
1507		case LO_TEXT_CAPS_LOCK:
1508			if (state) {
1509				free(state->args.text_caps_lock);
1510				state->args.text_caps_lock = strdup(optarg);
1511			}
1512			break;
1513		case LO_TEXT_CAPS_LOCK_COLOR:
1514			if (state) {
1515				state->args.colors.text.caps_lock = parse_color(optarg);
1516			}
1517			break;
1518		case LO_TEXT_VER:
1519			if (state) {
1520				free(state->args.text_verifying);
1521				state->args.text_verifying = strdup(optarg);
1522			}
1523			break;
1524		case LO_TEXT_VER_COLOR:
1525			if (state) {
1526				state->args.colors.text.verifying = parse_color(optarg);
1527			}
1528			break;
1529		case LO_TEXT_WRONG:
1530			if (state) {
1531				free(state->args.text_wrong);
1532				state->args.text_wrong = strdup(optarg);
1533			}
1534			break;
1535		case LO_TEXT_WRONG_COLOR:
1536			if (state) {
1537				state->args.colors.text.wrong = parse_color(optarg);
1538			}
1539			break;
1540		case LO_EFFECT_BLUR:
1541			if (state) {
1542				state->args.effects = realloc(state->args.effects,
1543						sizeof(*state->args.effects) * ++state->args.effects_count);
1544				struct swaylock_effect *effect = &state->args.effects[state->args.effects_count - 1];
1545				effect->tag = EFFECT_BLUR;
1546				if (sscanf(optarg, "%dx%d", &effect->e.blur.radius, &effect->e.blur.times) != 2) {
1547					swaylock_log(LOG_ERROR, "Invalid blur effect argument %s, ignoring", optarg);
1548					state->args.effects_count -= 1;
1549				}
1550			}
1551			break;
1552		case LO_EFFECT_PIXELATE:
1553			if (state) {
1554				state->args.effects = realloc(state->args.effects,
1555						sizeof(*state->args.effects) * ++state->args.effects_count);
1556				struct swaylock_effect *effect = &state->args.effects[state->args.effects_count - 1];
1557				effect->tag = EFFECT_PIXELATE;
1558				effect->e.pixelate.factor = atoi(optarg);
1559			}
1560			break;
1561		case LO_EFFECT_SCALE:
1562			if (state) {
1563				state->args.effects = realloc(state->args.effects,
1564						sizeof(*state->args.effects) * ++state->args.effects_count);
1565				struct swaylock_effect *effect = &state->args.effects[state->args.effects_count - 1];
1566				effect->tag = EFFECT_SCALE;
1567				if (sscanf(optarg, "%lf", &effect->e.scale) != 1) {
1568					swaylock_log(LOG_ERROR, "Invalid scale effect argument %s, ignoring", optarg);
1569					state->args.effects_count -= 1;
1570				}
1571			}
1572			break;
1573		case LO_EFFECT_GREYSCALE:
1574			if (state) {
1575				state->args.effects = realloc(state->args.effects,
1576						sizeof(*state->args.effects) * ++state->args.effects_count);
1577				struct swaylock_effect *effect = &state->args.effects[state->args.effects_count - 1];
1578				effect->tag = EFFECT_GREYSCALE;
1579			}
1580			break;
1581		case LO_EFFECT_VIGNETTE:
1582			if (state) {
1583				state->args.effects = realloc(state->args.effects,
1584						sizeof(*state->args.effects) * ++state->args.effects_count);
1585				struct swaylock_effect *effect = &state->args.effects[state->args.effects_count - 1];
1586				effect->tag = EFFECT_VIGNETTE;
1587				if (sscanf(optarg, "%lf:%lf", &effect->e.vignette.base, &effect->e.vignette.factor) != 2) {
1588					swaylock_log(LOG_ERROR, "Invalid factor effect argument %s, ignoring", optarg);
1589					state->args.effects_count -= 1;
1590				}
1591			}
1592			break;
1593		case LO_EFFECT_COMPOSE:
1594			if (state) {
1595				state->args.effects = realloc(state->args.effects,
1596						sizeof(*state->args.effects) * ++state->args.effects_count);
1597				struct swaylock_effect *effect = &state->args.effects[state->args.effects_count - 1];
1598				effect->tag = EFFECT_COMPOSE;
1599				parse_effect_compose(optarg, effect);
1600			}
1601			break;
1602		case LO_EFFECT_CUSTOM:
1603			if (state) {
1604				state->args.effects = realloc(state->args.effects,
1605						sizeof(*state->args.effects) * ++state->args.effects_count);
1606				struct swaylock_effect *effect = &state->args.effects[state->args.effects_count - 1];
1607				effect->tag = EFFECT_CUSTOM;
1608				effect->e.custom = strdup(optarg);
1609			}
1610			break;
1611		case LO_TIME_EFFECTS:
1612			if (state) {
1613				state->args.time_effects = true;
1614			}
1615			break;
1616		case LO_INDICATOR:
1617			if (state) {
1618				state->args.indicator = true;
1619			}
1620			break;
1621		case LO_CLOCK:
1622			if (state) {
1623				state->args.clock = true;
1624			}
1625			break;
1626		case LO_TIMESTR:
1627			if (state) {
1628				free(state->args.timestr);
1629				state->args.timestr = strdup(optarg);
1630			}
1631			break;
1632		case LO_DATESTR:
1633			if (state) {
1634				free(state->args.datestr);
1635				state->args.datestr = strdup(optarg);
1636			}
1637			break;
1638		case LO_FADE_IN:
1639			if (state) {
1640				state->args.fade_in = parse_seconds(optarg);
1641			}
1642			break;
1643		case LO_SUBMIT_ON_TOUCH:
1644			if (state) {
1645				state->args.password_submit_on_touch = true;
1646			}
1647			break;
1648		case LO_GRACE:
1649			if (state) {
1650				state->args.password_grace_period = parse_seconds(optarg);
1651			}
1652			break;
1653		case LO_GRACE_NO_MOUSE:
1654			if (state) {
1655				state->args.password_grace_no_mouse = true;
1656			}
1657			break;
1658		case LO_GRACE_NO_TOUCH:
1659			if (state) {
1660				state->args.password_grace_no_touch = true;
1661			}
1662			break;
1663		default:
1664			fprintf(stderr, "%s", usage);
1665			return 1;
1666		}
1667	}
1668
1669	return 0;
1670}
1671
1672static bool file_exists(const char *path) {
1673	return path && access(path, R_OK) != -1;
1674}
1675
1676static char *get_config_path(void) {
1677	static const char *config_paths[] = {
1678		"$HOME/.swaylock/config",
1679		"$XDG_CONFIG_HOME/swaylock/config",
1680		SYSCONFDIR "/swaylock/config",
1681	};
1682
1683	char *config_home = getenv("XDG_CONFIG_HOME");
1684	if (!config_home || config_home[0] == '\0') {
1685		config_paths[1] = "$HOME/.config/swaylock/config";
1686	}
1687
1688	wordexp_t p;
1689	char *path;
1690	for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) {
1691		if (wordexp(config_paths[i], &p, 0) == 0) {
1692			path = strdup(p.we_wordv[0]);
1693			wordfree(&p);
1694			if (file_exists(path)) {
1695				return path;
1696			}
1697			free(path);
1698		}
1699	}
1700
1701	return NULL;
1702}
1703
1704static int load_config(char *path, struct swaylock_state *state,
1705		enum line_mode *line_mode) {
1706	FILE *config = fopen(path, "r");
1707	if (!config) {
1708		swaylock_log(LOG_ERROR, "Failed to read config. Running without it.");
1709		return 0;
1710	}
1711	char *line = NULL;
1712	size_t line_size = 0;
1713	ssize_t nread;
1714	int line_number = 0;
1715	int result = 0;
1716	while ((nread = getline(&line, &line_size, config)) != -1) {
1717		line_number++;
1718
1719		if (line[nread - 1] == '\n') {
1720			line[--nread] = '\0';
1721		}
1722
1723		if (!*line || line[0] == '#') {
1724			continue;
1725		}
1726
1727		swaylock_log(LOG_DEBUG, "Config Line #%d: %s", line_number, line);
1728		char *flag = malloc(nread + 3);
1729		if (flag == NULL) {
1730			free(line);
1731			fclose(config);
1732			swaylock_log(LOG_ERROR, "Failed to allocate memory");
1733			return 0;
1734		}
1735		sprintf(flag, "--%s", line);
1736		char *argv[] = {"swaylock", flag};
1737		result = parse_options(2, argv, state, line_mode, NULL);
1738		free(flag);
1739		if (result != 0) {
1740			break;
1741		}
1742	}
1743	free(line);
1744	fclose(config);
1745	return 0;
1746}
1747
1748static void display_in(int fd, short mask, void *data) {
1749	if (wl_display_dispatch(state.display) == -1) {
1750		state.run_display = false;
1751	}
1752}
1753
1754static void end_allow_fade_period(void *data) {
1755	struct swaylock_state *state = data;
1756	if (state->args.allow_fade) {
1757		state->args.allow_fade = false;
1758	}
1759}
1760
1761static void end_grace_period(void *data) {
1762	struct swaylock_state *state = data;
1763	if (state->auth_state == AUTH_STATE_GRACE) {
1764		state->auth_state = AUTH_STATE_IDLE;
1765	}
1766}
1767
1768static void comm_in(int fd, short mask, void *data) {
1769	if (read_comm_reply()) {
1770		// Authentication succeeded
1771		state.run_display = false;
1772	} else {
1773		state.auth_state = AUTH_STATE_INVALID;
1774		schedule_indicator_clear(&state);
1775		++state.failed_attempts;
1776		damage_state(&state);
1777	}
1778}
1779
1780static void timer_render(void *data) {
1781	struct swaylock_state *state = (struct swaylock_state *)data;
1782	damage_state(state);
1783	loop_add_timer(state->eventloop, 1000, timer_render, state);
1784}
1785
1786static void term_in(int fd, short mask, void *data) {
1787	state.run_display = false;
1788}
1789
1790// Check for --debug 'early' we also apply the correct loglevel
1791// to the forked child, without having to first proces all of the
1792// configuration (including from file) before forking and (in the
1793// case of the shadow backend) dropping privileges
1794void log_init(int argc, char **argv) {
1795	static struct option long_options[] = {
1796		{"debug", no_argument, NULL, 'd'},
1797        {0, 0, 0, 0}
1798    };
1799    int c;
1800	optind = 1;
1801    while (1) {
1802		int opt_idx = 0;
1803		c = getopt_long(argc, argv, "-:d", long_options, &opt_idx);
1804		if (c == -1) {
1805			break;
1806		}
1807		switch (c) {
1808		case 'd':
1809			swaylock_log_init(LOG_DEBUG);
1810			return;
1811		}
1812	}
1813	swaylock_log_init(LOG_ERROR);
1814}
1815
1816int main(int argc, char **argv) {
1817	log_init(argc, argv);
1818	initialize_pw_backend(argc, argv);
1819	srand(time(NULL));
1820
1821	enum line_mode line_mode = LM_LINE;
1822	state.failed_attempts = 0;
1823	state.indicator_dirty = false;
1824	state.args = (struct swaylock_args){
1825		.mode = BACKGROUND_MODE_FILL,
1826		.font = strdup("sans-serif"),
1827		.font_size = 0,
1828		.radius = 75,
1829		.thickness = 10,
1830		.indicator_x_position = 0,
1831		.indicator_y_position = 0,
1832		.override_indicator_x_position = false,
1833		.override_indicator_y_position = false,
1834		.ignore_empty = false,
1835		.show_indicator = true,
1836		.show_caps_lock_indicator = false,
1837		.show_caps_lock_text = true,
1838		.show_keyboard_layout = false,
1839		.hide_keyboard_layout = false,
1840		.show_failed_attempts = false,
1841		.indicator_idle_visible = false,
1842
1843		.screenshots = false,
1844		.effects = NULL,
1845		.effects_count = 0,
1846		.indicator = false,
1847		.clock = false,
1848		.timestr = strdup("%T"),
1849		.datestr = strdup("%a, %x"),
1850		.allow_fade = true,
1851		.password_grace_period = 0,
1852
1853		.text_cleared = strdup("Cleared"),
1854		.text_caps_lock = strdup("Caps Lock"),
1855		.text_verifying = strdup("Verifying"),
1856		.text_wrong = strdup("Wrong"),
1857	};
1858	wl_list_init(&state.images);
1859	set_default_colors(&state.args.colors);
1860
1861	char *config_path = NULL;
1862	int result = parse_options(argc, argv, NULL, NULL, &config_path);
1863	if (result != 0) {
1864		free(config_path);
1865		return result;
1866	}
1867	if (!config_path) {
1868		config_path = get_config_path();
1869	}
1870
1871	if (config_path) {
1872		swaylock_log(LOG_DEBUG, "Found config at %s", config_path);
1873		int config_status = load_config(config_path, &state, &line_mode);
1874		free(config_path);
1875		if (config_status != 0) {
1876			free(state.args.font);
1877			return config_status;
1878		}
1879	}
1880
1881	if (argc > 1) {
1882		swaylock_log(LOG_DEBUG, "Parsing CLI Args");
1883		int result = parse_options(argc, argv, &state, &line_mode, NULL);
1884		if (result != 0) {
1885			free(state.args.font);
1886			return result;
1887		}
1888	}
1889
1890	if (line_mode == LM_INSIDE) {
1891		state.args.colors.line = state.args.colors.inside;
1892	} else if (line_mode == LM_RING) {
1893		state.args.colors.line = state.args.colors.ring;
1894	}
1895
1896	if (state.args.password_grace_period > 0) {
1897		state.auth_state = AUTH_STATE_GRACE;
1898	}
1899
1900	state.password.len = 0;
1901	state.password.buffer_len = 1024;
1902	state.password.buffer = password_buffer_create(state.password.buffer_len);
1903	if (!state.password.buffer) {
1904		return EXIT_FAILURE;
1905	}
1906
1907	if (pipe(sigusr_fds) != 0) {
1908		swaylock_log(LOG_ERROR, "Failed to pipe");
1909		return 1;
1910	}
1911
1912	wl_list_init(&state.surfaces);
1913	state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1914	state.display = wl_display_connect(NULL);
1915	if (!state.display) {
1916		free(state.args.font);
1917		swaylock_log(LOG_ERROR, "Unable to connect to the compositor. "
1918				"If your compositor is running, check or set the "
1919				"WAYLAND_DISPLAY environment variable.");
1920		return EXIT_FAILURE;
1921	}
1922
1923	struct wl_registry *registry = wl_display_get_registry(state.display);
1924	wl_registry_add_listener(registry, &registry_listener, &state);
1925	wl_display_roundtrip(state.display);
1926
1927	if (!state.compositor) {
1928		swaylock_log(LOG_ERROR, "Missing wl_compositor");
1929		return 1;
1930	}
1931
1932	if (!state.subcompositor) {
1933		swaylock_log(LOG_ERROR, "Missing wl_subcompositor");
1934		return 1;
1935	}
1936
1937	if (!state.shm) {
1938		swaylock_log(LOG_ERROR, "Missing wl_shm");
1939		return 1;
1940	}
1941
1942	struct swaylock_surface *surface;
1943	// Enumerate all outputs first so that screenshots can be obtained
1944	// before ext_session_lock_manager_v1_lock(). After the screen is locked,
1945	// no screenshot can be retrieved because normal rendering is blocked.
1946	wl_list_for_each(surface, &state.surfaces, link) {
1947		surface->events_pending += 1;
1948	};
1949
1950	wl_list_for_each(surface, &state.surfaces, link) {
1951		while (surface->events_pending > 0) {
1952			wl_display_roundtrip(state.display);
1953		}
1954	}
1955
1956	// Must daemonize before we run any effects, since effects use openmp
1957	int daemonfd;
1958	if (state.args.daemonize) {
1959		wl_display_roundtrip(state.display);
1960		daemonfd = daemonize_start();
1961	}
1962
1963	// Need to apply effects to all images *before* requesting ext_session_lock_v1
1964	// Otherwise, the screen would be blank while the effects are being applied.
1965	struct swaylock_image *iter_image, *temp;
1966	wl_list_for_each_safe(iter_image, temp, &state.images, link) {
1967		iter_image->cairo_surface = apply_effects(
1968				iter_image->cairo_surface, &state, 1);
1969	}
1970
1971	if (state.ext_session_lock_manager_v1) {
1972		swaylock_log(LOG_DEBUG, "Using ext-session-lock-v1");
1973		state.ext_session_lock_v1 = ext_session_lock_manager_v1_lock(state.ext_session_lock_manager_v1);
1974		ext_session_lock_v1_add_listener(state.ext_session_lock_v1,
1975				&ext_session_lock_v1_listener, &state);
1976	} else if (state.layer_shell && state.input_inhibit_manager) {
1977		swaylock_log(LOG_DEBUG, "Using wlr-layer-shell + wlr-input-inhibitor");
1978		zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager);
1979	} else {
1980		swaylock_log(LOG_ERROR, "Missing ext-session-lock-v1, wlr-layer-shell "
1981				"and wlr-input-inhibitor");
1982		return 1;
1983	}
1984
1985	if (wl_display_roundtrip(state.display) == -1) {
1986		free(state.args.font);
1987		if (state.input_inhibit_manager) {
1988			swaylock_log(LOG_ERROR, "Exiting - failed to inhibit input:"
1989					" is another lockscreen already running?");
1990			return 2;
1991		}
1992		return 1;
1993	}
1994
1995	wl_list_for_each(surface, &state.surfaces, link) {
1996		create_surface(surface);
1997	}
1998
1999	wl_list_for_each(surface, &state.surfaces, link) {
2000		while (surface->events_pending > 0) {
2001			wl_display_roundtrip(state.display);
2002		}
2003	}
2004
2005	state.eventloop = loop_create();
2006	loop_add_fd(state.eventloop, wl_display_get_fd(state.display), POLLIN,
2007			display_in, NULL);
2008
2009	loop_add_fd(state.eventloop, get_comm_reply_fd(), POLLIN, comm_in, NULL);
2010
2011	loop_add_fd(state.eventloop, sigusr_fds[0], POLLIN, term_in, NULL);
2012	signal(SIGUSR1, do_sigusr);
2013
2014	loop_add_timer(state.eventloop, 1000, timer_render, &state);
2015
2016	if (state.args.fade_in) {
2017		loop_add_timer(state.eventloop, state.args.fade_in, end_allow_fade_period, &state);
2018	}
2019
2020	if (state.args.daemonize && state.args.fade_in) {
2021		loop_add_timer(state.eventloop, state.args.fade_in + 500, daemonize_done, &daemonfd);
2022	} else if (state.args.daemonize) {
2023		daemonize_done(&daemonfd);
2024	}
2025
2026	if (state.args.password_grace_period > 0) {
2027		loop_add_timer(state.eventloop, state.args.password_grace_period, end_grace_period, &state);
2028	}
2029
2030	// Re-draw once to start the draw loop
2031	damage_state(&state);
2032
2033	state.run_display = true;
2034	while (state.run_display) {
2035		errno = 0;
2036		if (wl_display_flush(state.display) == -1 && errno != EAGAIN) {
2037			break;
2038		}
2039		loop_poll(state.eventloop);
2040	}
2041
2042	if (state.args.daemonize && state.args.fade_in) {
2043		daemonize_done(&daemonfd); // In case we exit before --fade-in timeout
2044	}
2045	if (state.ext_session_lock_v1) {
2046		ext_session_lock_v1_unlock_and_destroy(state.ext_session_lock_v1);
2047		wl_display_roundtrip(state.display);
2048	}
2049
2050	free(state.args.font);
2051	return 0;
2052}