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, ®istry_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}