diff options
Diffstat (limited to 'swaylock')
-rw-r--r-- | swaylock/main.c | 1057 | ||||
-rw-r--r-- | swaylock/meson.build | 55 | ||||
-rw-r--r-- | swaylock/pam.c | 62 | ||||
-rw-r--r-- | swaylock/pam/swaylock.freebsd | 6 | ||||
-rw-r--r-- | swaylock/pam/swaylock.linux | 6 | ||||
-rw-r--r-- | swaylock/password.c | 190 | ||||
-rw-r--r-- | swaylock/render.c | 189 | ||||
-rw-r--r-- | swaylock/seat.c | 178 | ||||
-rw-r--r-- | swaylock/shadow.c | 155 | ||||
-rw-r--r-- | swaylock/swaylock.1.scd | 167 |
10 files changed, 0 insertions, 2065 deletions
diff --git a/swaylock/main.c b/swaylock/main.c deleted file mode 100644 index 0b167da1..00000000 --- a/swaylock/main.c +++ /dev/null | |||
@@ -1,1057 +0,0 @@ | |||
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 <stdbool.h> | ||
9 | #include <stdio.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <string.h> | ||
12 | #include <sys/mman.h> | ||
13 | #include <sys/stat.h> | ||
14 | #include <time.h> | ||
15 | #include <unistd.h> | ||
16 | #include <wayland-client.h> | ||
17 | #include <wordexp.h> | ||
18 | #include <wlr/util/log.h> | ||
19 | #include "swaylock/seat.h" | ||
20 | #include "swaylock/swaylock.h" | ||
21 | #include "background-image.h" | ||
22 | #include "pool-buffer.h" | ||
23 | #include "cairo.h" | ||
24 | #include "log.h" | ||
25 | #include "loop.h" | ||
26 | #include "stringop.h" | ||
27 | #include "util.h" | ||
28 | #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" | ||
29 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | ||
30 | #include "xdg-output-unstable-v1-client-protocol.h" | ||
31 | |||
32 | void sway_terminate(int exit_code) { | ||
33 | exit(exit_code); | ||
34 | } | ||
35 | |||
36 | static void daemonize(void) { | ||
37 | int fds[2]; | ||
38 | if (pipe(fds) != 0) { | ||
39 | wlr_log(WLR_ERROR, "Failed to pipe"); | ||
40 | exit(1); | ||
41 | } | ||
42 | if (fork() == 0) { | ||
43 | setsid(); | ||
44 | close(fds[0]); | ||
45 | int devnull = open("/dev/null", O_RDWR); | ||
46 | dup2(STDOUT_FILENO, devnull); | ||
47 | dup2(STDERR_FILENO, devnull); | ||
48 | close(devnull); | ||
49 | uint8_t success = 0; | ||
50 | if (chdir("/") != 0) { | ||
51 | write(fds[1], &success, 1); | ||
52 | exit(1); | ||
53 | } | ||
54 | success = 1; | ||
55 | if (write(fds[1], &success, 1) != 1) { | ||
56 | exit(1); | ||
57 | } | ||
58 | close(fds[1]); | ||
59 | } else { | ||
60 | close(fds[1]); | ||
61 | uint8_t success; | ||
62 | if (read(fds[0], &success, 1) != 1 || !success) { | ||
63 | wlr_log(WLR_ERROR, "Failed to daemonize"); | ||
64 | exit(1); | ||
65 | } | ||
66 | close(fds[0]); | ||
67 | exit(0); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static void destroy_surface(struct swaylock_surface *surface) { | ||
72 | wl_list_remove(&surface->link); | ||
73 | if (surface->layer_surface != NULL) { | ||
74 | zwlr_layer_surface_v1_destroy(surface->layer_surface); | ||
75 | } | ||
76 | if (surface->surface != NULL) { | ||
77 | wl_surface_destroy(surface->surface); | ||
78 | } | ||
79 | destroy_buffer(&surface->buffers[0]); | ||
80 | destroy_buffer(&surface->buffers[1]); | ||
81 | wl_output_destroy(surface->output); | ||
82 | free(surface); | ||
83 | } | ||
84 | |||
85 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener; | ||
86 | |||
87 | static cairo_surface_t *select_image(struct swaylock_state *state, | ||
88 | struct swaylock_surface *surface); | ||
89 | |||
90 | static bool surface_is_opaque(struct swaylock_surface *surface) { | ||
91 | if (surface->image) { | ||
92 | return cairo_surface_get_content(surface->image) == CAIRO_CONTENT_COLOR; | ||
93 | } | ||
94 | return (surface->state->args.colors.background & 0xff) == 0xff; | ||
95 | } | ||
96 | |||
97 | static void create_layer_surface(struct swaylock_surface *surface) { | ||
98 | struct swaylock_state *state = surface->state; | ||
99 | |||
100 | surface->image = select_image(state, surface); | ||
101 | |||
102 | surface->surface = wl_compositor_create_surface(state->compositor); | ||
103 | assert(surface->surface); | ||
104 | |||
105 | surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface( | ||
106 | state->layer_shell, surface->surface, surface->output, | ||
107 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen"); | ||
108 | assert(surface->layer_surface); | ||
109 | |||
110 | zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0); | ||
111 | zwlr_layer_surface_v1_set_anchor(surface->layer_surface, | ||
112 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
113 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
114 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | | ||
115 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); | ||
116 | zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1); | ||
117 | zwlr_layer_surface_v1_set_keyboard_interactivity( | ||
118 | surface->layer_surface, true); | ||
119 | zwlr_layer_surface_v1_add_listener(surface->layer_surface, | ||
120 | &layer_surface_listener, surface); | ||
121 | |||
122 | if (surface_is_opaque(surface) && | ||
123 | surface->state->args.mode != BACKGROUND_MODE_CENTER && | ||
124 | surface->state->args.mode != BACKGROUND_MODE_FIT) { | ||
125 | struct wl_region *region = | ||
126 | wl_compositor_create_region(surface->state->compositor); | ||
127 | wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); | ||
128 | wl_surface_set_opaque_region(surface->surface, region); | ||
129 | wl_region_destroy(region); | ||
130 | } | ||
131 | |||
132 | wl_surface_commit(surface->surface); | ||
133 | } | ||
134 | |||
135 | static void layer_surface_configure(void *data, | ||
136 | struct zwlr_layer_surface_v1 *layer_surface, | ||
137 | uint32_t serial, uint32_t width, uint32_t height) { | ||
138 | struct swaylock_surface *surface = data; | ||
139 | surface->width = width; | ||
140 | surface->height = height; | ||
141 | zwlr_layer_surface_v1_ack_configure(layer_surface, serial); | ||
142 | render_frame(surface); | ||
143 | } | ||
144 | |||
145 | static void layer_surface_closed(void *data, | ||
146 | struct zwlr_layer_surface_v1 *layer_surface) { | ||
147 | struct swaylock_surface *surface = data; | ||
148 | destroy_surface(surface); | ||
149 | } | ||
150 | |||
151 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { | ||
152 | .configure = layer_surface_configure, | ||
153 | .closed = layer_surface_closed, | ||
154 | }; | ||
155 | |||
156 | static const struct wl_callback_listener surface_frame_listener; | ||
157 | |||
158 | static void surface_frame_handle_done(void *data, struct wl_callback *callback, | ||
159 | uint32_t time) { | ||
160 | struct swaylock_surface *surface = data; | ||
161 | |||
162 | wl_callback_destroy(callback); | ||
163 | surface->frame_pending = false; | ||
164 | |||
165 | if (surface->dirty) { | ||
166 | // Schedule a frame in case the surface is damaged again | ||
167 | struct wl_callback *callback = wl_surface_frame(surface->surface); | ||
168 | wl_callback_add_listener(callback, &surface_frame_listener, surface); | ||
169 | surface->frame_pending = true; | ||
170 | |||
171 | render_frame(surface); | ||
172 | surface->dirty = false; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | static const struct wl_callback_listener surface_frame_listener = { | ||
177 | .done = surface_frame_handle_done, | ||
178 | }; | ||
179 | |||
180 | void damage_surface(struct swaylock_surface *surface) { | ||
181 | surface->dirty = true; | ||
182 | if (surface->frame_pending) { | ||
183 | return; | ||
184 | } | ||
185 | |||
186 | struct wl_callback *callback = wl_surface_frame(surface->surface); | ||
187 | wl_callback_add_listener(callback, &surface_frame_listener, surface); | ||
188 | surface->frame_pending = true; | ||
189 | wl_surface_commit(surface->surface); | ||
190 | } | ||
191 | |||
192 | void damage_state(struct swaylock_state *state) { | ||
193 | struct swaylock_surface *surface; | ||
194 | wl_list_for_each(surface, &state->surfaces, link) { | ||
195 | damage_surface(surface); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static void handle_wl_output_geometry(void *data, struct wl_output *wl_output, | ||
200 | int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, | ||
201 | int32_t subpixel, const char *make, const char *model, | ||
202 | int32_t transform) { | ||
203 | struct swaylock_surface *surface = data; | ||
204 | surface->subpixel = subpixel; | ||
205 | if (surface->state->run_display) { | ||
206 | damage_surface(surface); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | static void handle_wl_output_mode(void *data, struct wl_output *output, | ||
211 | uint32_t flags, int32_t width, int32_t height, int32_t refresh) { | ||
212 | // Who cares | ||
213 | } | ||
214 | |||
215 | static void handle_wl_output_done(void *data, struct wl_output *output) { | ||
216 | // Who cares | ||
217 | } | ||
218 | |||
219 | static void handle_wl_output_scale(void *data, struct wl_output *output, | ||
220 | int32_t factor) { | ||
221 | struct swaylock_surface *surface = data; | ||
222 | surface->scale = factor; | ||
223 | if (surface->state->run_display) { | ||
224 | damage_surface(surface); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | struct wl_output_listener _wl_output_listener = { | ||
229 | .geometry = handle_wl_output_geometry, | ||
230 | .mode = handle_wl_output_mode, | ||
231 | .done = handle_wl_output_done, | ||
232 | .scale = handle_wl_output_scale, | ||
233 | }; | ||
234 | |||
235 | static void handle_xdg_output_logical_size(void *data, struct zxdg_output_v1 *output, | ||
236 | int width, int height) { | ||
237 | // Who cares | ||
238 | } | ||
239 | |||
240 | static void handle_xdg_output_logical_position(void *data, | ||
241 | struct zxdg_output_v1 *output, int x, int y) { | ||
242 | // Who cares | ||
243 | } | ||
244 | |||
245 | static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *output, | ||
246 | const char *name) { | ||
247 | wlr_log(WLR_DEBUG, "output name is %s", name); | ||
248 | struct swaylock_surface *surface = data; | ||
249 | surface->xdg_output = output; | ||
250 | surface->output_name = strdup(name); | ||
251 | } | ||
252 | |||
253 | static void handle_xdg_output_description(void *data, struct zxdg_output_v1 *output, | ||
254 | const char *description) { | ||
255 | // Who cares | ||
256 | } | ||
257 | |||
258 | static void handle_xdg_output_done(void *data, struct zxdg_output_v1 *output) { | ||
259 | // Who cares | ||
260 | } | ||
261 | |||
262 | struct zxdg_output_v1_listener _xdg_output_listener = { | ||
263 | .logical_position = handle_xdg_output_logical_position, | ||
264 | .logical_size = handle_xdg_output_logical_size, | ||
265 | .done = handle_xdg_output_done, | ||
266 | .name = handle_xdg_output_name, | ||
267 | .description = handle_xdg_output_description, | ||
268 | }; | ||
269 | |||
270 | static void handle_global(void *data, struct wl_registry *registry, | ||
271 | uint32_t name, const char *interface, uint32_t version) { | ||
272 | struct swaylock_state *state = data; | ||
273 | if (strcmp(interface, wl_compositor_interface.name) == 0) { | ||
274 | state->compositor = wl_registry_bind(registry, name, | ||
275 | &wl_compositor_interface, 3); | ||
276 | } else if (strcmp(interface, wl_shm_interface.name) == 0) { | ||
277 | state->shm = wl_registry_bind(registry, name, | ||
278 | &wl_shm_interface, 1); | ||
279 | } else if (strcmp(interface, wl_seat_interface.name) == 0) { | ||
280 | struct wl_seat *seat = wl_registry_bind( | ||
281 | registry, name, &wl_seat_interface, 3); | ||
282 | struct swaylock_seat *swaylock_seat = | ||
283 | calloc(1, sizeof(struct swaylock_seat)); | ||
284 | swaylock_seat->state = state; | ||
285 | wl_seat_add_listener(seat, &seat_listener, swaylock_seat); | ||
286 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { | ||
287 | state->layer_shell = wl_registry_bind( | ||
288 | registry, name, &zwlr_layer_shell_v1_interface, 1); | ||
289 | } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) { | ||
290 | state->input_inhibit_manager = wl_registry_bind( | ||
291 | registry, name, &zwlr_input_inhibit_manager_v1_interface, 1); | ||
292 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { | ||
293 | state->zxdg_output_manager = wl_registry_bind( | ||
294 | registry, name, &zxdg_output_manager_v1_interface, 2); | ||
295 | } else if (strcmp(interface, wl_output_interface.name) == 0) { | ||
296 | struct swaylock_surface *surface = | ||
297 | calloc(1, sizeof(struct swaylock_surface)); | ||
298 | surface->state = state; | ||
299 | surface->output = wl_registry_bind(registry, name, | ||
300 | &wl_output_interface, 3); | ||
301 | surface->output_global_name = name; | ||
302 | wl_output_add_listener(surface->output, &_wl_output_listener, surface); | ||
303 | wl_list_insert(&state->surfaces, &surface->link); | ||
304 | |||
305 | if (state->run_display) { | ||
306 | create_layer_surface(surface); | ||
307 | wl_display_roundtrip(state->display); | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | |||
312 | static void handle_global_remove(void *data, struct wl_registry *registry, | ||
313 | uint32_t name) { | ||
314 | struct swaylock_state *state = data; | ||
315 | struct swaylock_surface *surface; | ||
316 | wl_list_for_each(surface, &state->surfaces, link) { | ||
317 | if (surface->output_global_name == name) { | ||
318 | destroy_surface(surface); | ||
319 | break; | ||
320 | } | ||
321 | } | ||
322 | } | ||
323 | |||
324 | static const struct wl_registry_listener registry_listener = { | ||
325 | .global = handle_global, | ||
326 | .global_remove = handle_global_remove, | ||
327 | }; | ||
328 | |||
329 | static cairo_surface_t *select_image(struct swaylock_state *state, | ||
330 | struct swaylock_surface *surface) { | ||
331 | struct swaylock_image *image; | ||
332 | cairo_surface_t *default_image = NULL; | ||
333 | wl_list_for_each(image, &state->images, link) { | ||
334 | if (lenient_strcmp(image->output_name, surface->output_name) == 0) { | ||
335 | return image->cairo_surface; | ||
336 | } else if (!image->output_name) { | ||
337 | default_image = image->cairo_surface; | ||
338 | } | ||
339 | } | ||
340 | return default_image; | ||
341 | } | ||
342 | |||
343 | static void load_image(char *arg, struct swaylock_state *state) { | ||
344 | // [<output>:]<path> | ||
345 | struct swaylock_image *image = calloc(1, sizeof(struct swaylock_image)); | ||
346 | char *separator = strchr(arg, ':'); | ||
347 | if (separator) { | ||
348 | *separator = '\0'; | ||
349 | image->output_name = strdup(arg); | ||
350 | image->path = strdup(separator + 1); | ||
351 | } else { | ||
352 | image->output_name = NULL; | ||
353 | image->path = strdup(arg); | ||
354 | } | ||
355 | |||
356 | struct swaylock_image *iter_image, *temp; | ||
357 | wl_list_for_each_safe(iter_image, temp, &state->images, link) { | ||
358 | if (lenient_strcmp(iter_image->output_name, image->output_name) == 0) { | ||
359 | if (image->output_name) { | ||
360 | wlr_log(WLR_DEBUG, | ||
361 | "Replacing image defined for output %s with %s", | ||
362 | image->output_name, image->path); | ||
363 | } else { | ||
364 | wlr_log(WLR_DEBUG, "Replacing default image with %s", | ||
365 | image->path); | ||
366 | } | ||
367 | wl_list_remove(&iter_image->link); | ||
368 | free(iter_image->cairo_surface); | ||
369 | free(iter_image->output_name); | ||
370 | free(iter_image->path); | ||
371 | free(iter_image); | ||
372 | break; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | // Bash doesn't replace the ~ with $HOME if the output name is supplied | ||
377 | wordexp_t p; | ||
378 | if (wordexp(image->path, &p, 0) == 0) { | ||
379 | free(image->path); | ||
380 | image->path = strdup(p.we_wordv[0]); | ||
381 | wordfree(&p); | ||
382 | } | ||
383 | |||
384 | // Load the actual image | ||
385 | image->cairo_surface = load_background_image(image->path); | ||
386 | if (!image->cairo_surface) { | ||
387 | free(image); | ||
388 | return; | ||
389 | } | ||
390 | wl_list_insert(&state->images, &image->link); | ||
391 | wlr_log(WLR_DEBUG, "Loaded image %s for output %s", | ||
392 | image->path, image->output_name ? image->output_name : "*"); | ||
393 | } | ||
394 | |||
395 | static void set_default_colors(struct swaylock_colors *colors) { | ||
396 | colors->background = 0xFFFFFFFF; | ||
397 | colors->bs_highlight = 0xDB3300FF; | ||
398 | colors->key_highlight = 0x33DB00FF; | ||
399 | colors->caps_lock_bs_highlight = 0xDB3300FF; | ||
400 | colors->caps_lock_key_highlight = 0x33DB00FF; | ||
401 | colors->separator = 0x000000FF; | ||
402 | colors->inside = (struct swaylock_colorset){ | ||
403 | .input = 0x000000C0, | ||
404 | .cleared = 0xE5A445C0, | ||
405 | .caps_lock = 0x000000C0, | ||
406 | .verifying = 0x0072FFC0, | ||
407 | .wrong = 0xFA0000C0, | ||
408 | }; | ||
409 | colors->line = (struct swaylock_colorset){ | ||
410 | .input = 0x000000FF, | ||
411 | .cleared = 0x000000FF, | ||
412 | .caps_lock = 0x000000FF, | ||
413 | .verifying = 0x000000FF, | ||
414 | .wrong = 0x000000FF, | ||
415 | }; | ||
416 | colors->ring = (struct swaylock_colorset){ | ||
417 | .input = 0x337D00FF, | ||
418 | .cleared = 0xE5A445FF, | ||
419 | .caps_lock = 0xE5A445FF, | ||
420 | .verifying = 0x3300FFFF, | ||
421 | .wrong = 0x7D3300FF, | ||
422 | }; | ||
423 | colors->text = (struct swaylock_colorset){ | ||
424 | .input = 0xE5A445FF, | ||
425 | .cleared = 0x000000FF, | ||
426 | .caps_lock = 0xE5A445FF, | ||
427 | .verifying = 0x000000FF, | ||
428 | .wrong = 0x000000FF, | ||
429 | }; | ||
430 | } | ||
431 | |||
432 | enum line_mode { | ||
433 | LM_LINE, | ||
434 | LM_INSIDE, | ||
435 | LM_RING, | ||
436 | }; | ||
437 | |||
438 | static int parse_options(int argc, char **argv, struct swaylock_state *state, | ||
439 | enum line_mode *line_mode, char **config_path) { | ||
440 | enum long_option_codes { | ||
441 | LO_BS_HL_COLOR = 256, | ||
442 | LO_CAPS_LOCK_BS_HL_COLOR, | ||
443 | LO_CAPS_LOCK_KEY_HL_COLOR, | ||
444 | LO_FONT, | ||
445 | LO_IND_RADIUS, | ||
446 | LO_IND_THICKNESS, | ||
447 | LO_INSIDE_COLOR, | ||
448 | LO_INSIDE_CLEAR_COLOR, | ||
449 | LO_INSIDE_CAPS_LOCK_COLOR, | ||
450 | LO_INSIDE_VER_COLOR, | ||
451 | LO_INSIDE_WRONG_COLOR, | ||
452 | LO_KEY_HL_COLOR, | ||
453 | LO_LINE_COLOR, | ||
454 | LO_LINE_CLEAR_COLOR, | ||
455 | LO_LINE_CAPS_LOCK_COLOR, | ||
456 | LO_LINE_VER_COLOR, | ||
457 | LO_LINE_WRONG_COLOR, | ||
458 | LO_RING_COLOR, | ||
459 | LO_RING_CLEAR_COLOR, | ||
460 | LO_RING_CAPS_LOCK_COLOR, | ||
461 | LO_RING_VER_COLOR, | ||
462 | LO_RING_WRONG_COLOR, | ||
463 | LO_SEP_COLOR, | ||
464 | LO_TEXT_COLOR, | ||
465 | LO_TEXT_CLEAR_COLOR, | ||
466 | LO_TEXT_CAPS_LOCK_COLOR, | ||
467 | LO_TEXT_VER_COLOR, | ||
468 | LO_TEXT_WRONG_COLOR, | ||
469 | }; | ||
470 | |||
471 | static struct option long_options[] = { | ||
472 | {"config", required_argument, NULL, 'C'}, | ||
473 | {"color", required_argument, NULL, 'c'}, | ||
474 | {"ignore-empty-password", no_argument, NULL, 'e'}, | ||
475 | {"daemonize", no_argument, NULL, 'f'}, | ||
476 | {"help", no_argument, NULL, 'h'}, | ||
477 | {"image", required_argument, NULL, 'i'}, | ||
478 | {"disable-caps-lock-text", no_argument, NULL, 'L'}, | ||
479 | {"indicator-caps-lock", no_argument, NULL, 'l'}, | ||
480 | {"line-uses-inside", no_argument, NULL, 'n'}, | ||
481 | {"socket", required_argument, NULL, 'p'}, | ||
482 | {"line-uses-ring", no_argument, NULL, 'r'}, | ||
483 | {"scaling", required_argument, NULL, 's'}, | ||
484 | {"tiling", no_argument, NULL, 't'}, | ||
485 | {"no-unlock-indicator", no_argument, NULL, 'u'}, | ||
486 | {"version", no_argument, NULL, 'v'}, | ||
487 | {"bs-hl-color", required_argument, NULL, LO_BS_HL_COLOR}, | ||
488 | {"caps-lock-bs-hl-color", required_argument, NULL, LO_CAPS_LOCK_BS_HL_COLOR}, | ||
489 | {"caps-lock-key-hl-color", required_argument, NULL, LO_CAPS_LOCK_KEY_HL_COLOR}, | ||
490 | {"font", required_argument, NULL, LO_FONT}, | ||
491 | {"indicator-radius", required_argument, NULL, LO_IND_RADIUS}, | ||
492 | {"indicator-thickness", required_argument, NULL, LO_IND_THICKNESS}, | ||
493 | {"inside-color", required_argument, NULL, LO_INSIDE_COLOR}, | ||
494 | {"inside-clear-color", required_argument, NULL, LO_INSIDE_CLEAR_COLOR}, | ||
495 | {"inside-caps-lock-color", required_argument, NULL, LO_INSIDE_CAPS_LOCK_COLOR}, | ||
496 | {"inside-ver-color", required_argument, NULL, LO_INSIDE_VER_COLOR}, | ||
497 | {"inside-wrong-color", required_argument, NULL, LO_INSIDE_WRONG_COLOR}, | ||
498 | {"key-hl-color", required_argument, NULL, LO_KEY_HL_COLOR}, | ||
499 | {"line-color", required_argument, NULL, LO_LINE_COLOR}, | ||
500 | {"line-clear-color", required_argument, NULL, LO_LINE_CLEAR_COLOR}, | ||
501 | {"line-caps-lock-color", required_argument, NULL, LO_LINE_CAPS_LOCK_COLOR}, | ||
502 | {"line-ver-color", required_argument, NULL, LO_LINE_VER_COLOR}, | ||
503 | {"line-wrong-color", required_argument, NULL, LO_LINE_WRONG_COLOR}, | ||
504 | {"ring-color", required_argument, NULL, LO_RING_COLOR}, | ||
505 | {"ring-clear-color", required_argument, NULL, LO_RING_CLEAR_COLOR}, | ||
506 | {"ring-caps-lock-color", required_argument, NULL, LO_RING_CAPS_LOCK_COLOR}, | ||
507 | {"ring-ver-color", required_argument, NULL, LO_RING_VER_COLOR}, | ||
508 | {"ring-wrong-color", required_argument, NULL, LO_RING_WRONG_COLOR}, | ||
509 | {"separator-color", required_argument, NULL, LO_SEP_COLOR}, | ||
510 | {"text-color", required_argument, NULL, LO_TEXT_COLOR}, | ||
511 | {"text-clear-color", required_argument, NULL, LO_TEXT_CLEAR_COLOR}, | ||
512 | {"text-caps-lock-color", required_argument, NULL, LO_TEXT_CAPS_LOCK_COLOR}, | ||
513 | {"text-ver-color", required_argument, NULL, LO_TEXT_VER_COLOR}, | ||
514 | {"text-wrong-color", required_argument, NULL, LO_TEXT_WRONG_COLOR}, | ||
515 | {0, 0, 0, 0} | ||
516 | }; | ||
517 | |||
518 | const char usage[] = | ||
519 | "Usage: swaylock [options...]\n" | ||
520 | "\n" | ||
521 | " -C, --config <config_file> " | ||
522 | "Path to the config file.\n" | ||
523 | " -c, --color <color> " | ||
524 | "Turn the screen into the given color instead of white.\n" | ||
525 | " -e, --ignore-empty-password " | ||
526 | "When an empty password is provided, do not validate it.\n" | ||
527 | " -f, --daemonize " | ||
528 | "Detach from the controlling terminal after locking.\n" | ||
529 | " -h, --help " | ||
530 | "Show help message and quit.\n" | ||
531 | " -i, --image [<output>:]<path> " | ||
532 | "Display the given image.\n" | ||
533 | " -L, --disable-caps-lock-text " | ||
534 | "Disable the Caps Lock text.\n" | ||
535 | " -l, --indicator-caps-lock " | ||
536 | "Show the current Caps Lock state also on the indicator.\n" | ||
537 | " -s, --scaling <mode> " | ||
538 | "Scaling mode: stretch, fill, fit, center, tile.\n" | ||
539 | " -t, --tiling " | ||
540 | "Same as --scaling=tile.\n" | ||
541 | " -u, --no-unlock-indicator " | ||
542 | "Disable the unlock indicator.\n" | ||
543 | " -v, --version " | ||
544 | "Show the version number and quit.\n" | ||
545 | " --bs-hl-color <color> " | ||
546 | "Sets the color of backspace highlight segments.\n" | ||
547 | " --caps-lock-bs-hl-color <color> " | ||
548 | "Sets the color of backspace highlight segments when Caps Lock " | ||
549 | "is active.\n" | ||
550 | " --caps-lock-key-hl-color <color> " | ||
551 | "Sets the color of the key press highlight segments when " | ||
552 | "Caps Lock is active.\n" | ||
553 | " --font <font> " | ||
554 | "Sets the font of the text.\n" | ||
555 | " --indicator-radius <radius> " | ||
556 | "Sets the indicator radius.\n" | ||
557 | " --indicator-thickness <thick> " | ||
558 | "Sets the indicator thickness.\n" | ||
559 | " --inside-color <color> " | ||
560 | "Sets the color of the inside of the indicator.\n" | ||
561 | " --inside-clear-color <color> " | ||
562 | "Sets the color of the inside of the indicator when cleared.\n" | ||
563 | " --inside-caps-lock-color <color> " | ||
564 | "Sets the color of the inside of the indicator when Caps Lock " | ||
565 | "is active.\n" | ||
566 | " --inside-ver-color <color> " | ||
567 | "Sets the color of the inside of the indicator when verifying.\n" | ||
568 | " --inside-wrong-color <color> " | ||
569 | "Sets the color of the inside of the indicator when invalid.\n" | ||
570 | " --key-hl-color <color> " | ||
571 | "Sets the color of the key press highlight segments.\n" | ||
572 | " --line-color <color> " | ||
573 | "Sets the color of the line between the inside and ring.\n" | ||
574 | " --line-clear-color <color> " | ||
575 | "Sets the color of the line between the inside and ring when " | ||
576 | "cleared.\n" | ||
577 | " --line-caps-lock-color <color> " | ||
578 | "Sets the color of the line between the inside and ring when " | ||
579 | "Caps Lock is active.\n" | ||
580 | " --line-ver-color <color> " | ||
581 | "Sets the color of the line between the inside and ring when " | ||
582 | "verifying.\n" | ||
583 | " --line-wrong-color <color> " | ||
584 | "Sets the color of the line between the inside and ring when " | ||
585 | "invalid.\n" | ||
586 | " -n, --line-uses-inside " | ||
587 | "Use the inside color for the line between the inside and ring.\n" | ||
588 | " -r, --line-uses-ring " | ||
589 | "Use the ring color for the line between the inside and ring.\n" | ||
590 | " --ring-color <color> " | ||
591 | "Sets the color of the ring of the indicator.\n" | ||
592 | " --ring-clear-color <color> " | ||
593 | "Sets the color of the ring of the indicator when cleared.\n" | ||
594 | " --ring-caps-lock-color <color> " | ||
595 | "Sets the color of the ring of the indicator when Caps Lock " | ||
596 | "is active.\n" | ||
597 | " --ring-ver-color <color> " | ||
598 | "Sets the color of the ring of the indicator when verifying.\n" | ||
599 | " --ring-wrong-color <color> " | ||
600 | "Sets the color of the ring of the indicator when invalid.\n" | ||
601 | " --separator-color <color> " | ||
602 | "Sets the color of the lines that separate highlight segments.\n" | ||
603 | " --text-color <color> " | ||
604 | "Sets the color of the text.\n" | ||
605 | " --text-clear-color <color> " | ||
606 | "Sets the color of the text when cleared.\n" | ||
607 | " --text-caps-lock-color <color> " | ||
608 | "Sets the color of the text when Caps Lock is active.\n" | ||
609 | " --text-ver-color <color> " | ||
610 | "Sets the color of the text when verifying.\n" | ||
611 | " --text-wrong-color <color> " | ||
612 | "Sets the color of the text when invalid.\n" | ||
613 | "\n" | ||
614 | "All <color> options are of the form <rrggbb[aa]>.\n"; | ||
615 | |||
616 | int c; | ||
617 | optind = 1; | ||
618 | while (1) { | ||
619 | int opt_idx = 0; | ||
620 | c = getopt_long(argc, argv, "c:efhi:Llnrs:tuvC:", long_options, &opt_idx); | ||
621 | if (c == -1) { | ||
622 | break; | ||
623 | } | ||
624 | switch (c) { | ||
625 | case 'C': | ||
626 | if (config_path) { | ||
627 | *config_path = strdup(optarg); | ||
628 | } | ||
629 | break; | ||
630 | case 'c': | ||
631 | if (state) { | ||
632 | state->args.colors.background = parse_color(optarg); | ||
633 | state->args.mode = BACKGROUND_MODE_SOLID_COLOR; | ||
634 | } | ||
635 | break; | ||
636 | case 'e': | ||
637 | if (state) { | ||
638 | state->args.ignore_empty = true; | ||
639 | } | ||
640 | break; | ||
641 | case 'f': | ||
642 | if (state) { | ||
643 | state->args.daemonize = true; | ||
644 | } | ||
645 | break; | ||
646 | case 'i': | ||
647 | if (state) { | ||
648 | load_image(optarg, state); | ||
649 | } | ||
650 | break; | ||
651 | case 'L': | ||
652 | if (state) { | ||
653 | state->args.show_caps_lock_text = false; | ||
654 | } | ||
655 | break; | ||
656 | case 'l': | ||
657 | if (state) { | ||
658 | state->args.show_caps_lock_indicator = true; | ||
659 | } | ||
660 | break; | ||
661 | case 'n': | ||
662 | if (line_mode) { | ||
663 | *line_mode = LM_INSIDE; | ||
664 | } | ||
665 | break; | ||
666 | case 'r': | ||
667 | if (line_mode) { | ||
668 | *line_mode = LM_RING; | ||
669 | } | ||
670 | break; | ||
671 | case 's': | ||
672 | if (state) { | ||
673 | state->args.mode = parse_background_mode(optarg); | ||
674 | if (state->args.mode == BACKGROUND_MODE_INVALID) { | ||
675 | return 1; | ||
676 | } | ||
677 | } | ||
678 | break; | ||
679 | case 't': | ||
680 | if (state) { | ||
681 | state->args.mode = BACKGROUND_MODE_TILE; | ||
682 | } | ||
683 | break; | ||
684 | case 'u': | ||
685 | if (state) { | ||
686 | state->args.show_indicator = false; | ||
687 | } | ||
688 | break; | ||
689 | case 'v': | ||
690 | fprintf(stdout, "swaylock version " SWAY_VERSION "\n"); | ||
691 | exit(EXIT_SUCCESS); | ||
692 | break; | ||
693 | case LO_BS_HL_COLOR: | ||
694 | if (state) { | ||
695 | state->args.colors.bs_highlight = parse_color(optarg); | ||
696 | } | ||
697 | break; | ||
698 | case LO_CAPS_LOCK_BS_HL_COLOR: | ||
699 | if (state) { | ||
700 | state->args.colors.caps_lock_bs_highlight = parse_color(optarg); | ||
701 | } | ||
702 | break; | ||
703 | case LO_CAPS_LOCK_KEY_HL_COLOR: | ||
704 | if (state) { | ||
705 | state->args.colors.caps_lock_key_highlight = parse_color(optarg); | ||
706 | } | ||
707 | break; | ||
708 | case LO_FONT: | ||
709 | if (state) { | ||
710 | free(state->args.font); | ||
711 | state->args.font = strdup(optarg); | ||
712 | } | ||
713 | break; | ||
714 | case LO_IND_RADIUS: | ||
715 | if (state) { | ||
716 | state->args.radius = strtol(optarg, NULL, 0); | ||
717 | } | ||
718 | break; | ||
719 | case LO_IND_THICKNESS: | ||
720 | if (state) { | ||
721 | state->args.thickness = strtol(optarg, NULL, 0); | ||
722 | } | ||
723 | break; | ||
724 | case LO_INSIDE_COLOR: | ||
725 | if (state) { | ||
726 | state->args.colors.inside.input = parse_color(optarg); | ||
727 | } | ||
728 | break; | ||
729 | case LO_INSIDE_CLEAR_COLOR: | ||
730 | if (state) { | ||
731 | state->args.colors.inside.cleared = parse_color(optarg); | ||
732 | } | ||
733 | break; | ||
734 | case LO_INSIDE_CAPS_LOCK_COLOR: | ||
735 | if (state) { | ||
736 | state->args.colors.inside.caps_lock = parse_color(optarg); | ||
737 | } | ||
738 | break; | ||
739 | case LO_INSIDE_VER_COLOR: | ||
740 | if (state) { | ||
741 | state->args.colors.inside.verifying = parse_color(optarg); | ||
742 | } | ||
743 | break; | ||
744 | case LO_INSIDE_WRONG_COLOR: | ||
745 | if (state) { | ||
746 | state->args.colors.inside.wrong = parse_color(optarg); | ||
747 | } | ||
748 | break; | ||
749 | case LO_KEY_HL_COLOR: | ||
750 | if (state) { | ||
751 | state->args.colors.key_highlight = parse_color(optarg); | ||
752 | } | ||
753 | break; | ||
754 | case LO_LINE_COLOR: | ||
755 | if (state) { | ||
756 | state->args.colors.line.input = parse_color(optarg); | ||
757 | } | ||
758 | break; | ||
759 | case LO_LINE_CLEAR_COLOR: | ||
760 | if (state) { | ||
761 | state->args.colors.line.cleared = parse_color(optarg); | ||
762 | } | ||
763 | break; | ||
764 | case LO_LINE_CAPS_LOCK_COLOR: | ||
765 | if (state) { | ||
766 | state->args.colors.line.caps_lock = parse_color(optarg); | ||
767 | } | ||
768 | break; | ||
769 | case LO_LINE_VER_COLOR: | ||
770 | if (state) { | ||
771 | state->args.colors.line.verifying = parse_color(optarg); | ||
772 | } | ||
773 | break; | ||
774 | case LO_LINE_WRONG_COLOR: | ||
775 | if (state) { | ||
776 | state->args.colors.line.wrong = parse_color(optarg); | ||
777 | } | ||
778 | break; | ||
779 | case LO_RING_COLOR: | ||
780 | if (state) { | ||
781 | state->args.colors.ring.input = parse_color(optarg); | ||
782 | } | ||
783 | break; | ||
784 | case LO_RING_CLEAR_COLOR: | ||
785 | if (state) { | ||
786 | state->args.colors.ring.cleared = parse_color(optarg); | ||
787 | } | ||
788 | break; | ||
789 | case LO_RING_CAPS_LOCK_COLOR: | ||
790 | if (state) { | ||
791 | state->args.colors.ring.caps_lock = parse_color(optarg); | ||
792 | } | ||
793 | break; | ||
794 | case LO_RING_VER_COLOR: | ||
795 | if (state) { | ||
796 | state->args.colors.ring.verifying = parse_color(optarg); | ||
797 | } | ||
798 | break; | ||
799 | case LO_RING_WRONG_COLOR: | ||
800 | if (state) { | ||
801 | state->args.colors.ring.wrong = parse_color(optarg); | ||
802 | } | ||
803 | break; | ||
804 | case LO_SEP_COLOR: | ||
805 | if (state) { | ||
806 | state->args.colors.separator = parse_color(optarg); | ||
807 | } | ||
808 | break; | ||
809 | case LO_TEXT_COLOR: | ||
810 | if (state) { | ||
811 | state->args.colors.text.input = parse_color(optarg); | ||
812 | } | ||
813 | break; | ||
814 | case LO_TEXT_CLEAR_COLOR: | ||
815 | if (state) { | ||
816 | state->args.colors.text.cleared = parse_color(optarg); | ||
817 | } | ||
818 | break; | ||
819 | case LO_TEXT_CAPS_LOCK_COLOR: | ||
820 | if (state) { | ||
821 | state->args.colors.text.caps_lock = parse_color(optarg); | ||
822 | } | ||
823 | break; | ||
824 | case LO_TEXT_VER_COLOR: | ||
825 | if (state) { | ||
826 | state->args.colors.text.verifying = parse_color(optarg); | ||
827 | } | ||
828 | break; | ||
829 | case LO_TEXT_WRONG_COLOR: | ||
830 | if (state) { | ||
831 | state->args.colors.text.wrong = parse_color(optarg); | ||
832 | } | ||
833 | break; | ||
834 | default: | ||
835 | fprintf(stderr, "%s", usage); | ||
836 | return 1; | ||
837 | } | ||
838 | } | ||
839 | |||
840 | return 0; | ||
841 | } | ||
842 | |||
843 | static bool file_exists(const char *path) { | ||
844 | return path && access(path, R_OK) != -1; | ||
845 | } | ||
846 | |||
847 | static char *get_config_path(void) { | ||
848 | static const char *config_paths[] = { | ||
849 | "$HOME/.swaylock/config", | ||
850 | "$XDG_CONFIG_HOME/swaylock/config", | ||
851 | SYSCONFDIR "/swaylock/config", | ||
852 | }; | ||
853 | |||
854 | if (!getenv("XDG_CONFIG_HOME")) { | ||
855 | char *home = getenv("HOME"); | ||
856 | char *config_home = malloc(strlen(home) + strlen("/.config") + 1); | ||
857 | if (!config_home) { | ||
858 | wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); | ||
859 | } else { | ||
860 | strcpy(config_home, home); | ||
861 | strcat(config_home, "/.config"); | ||
862 | setenv("XDG_CONFIG_HOME", config_home, 1); | ||
863 | wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); | ||
864 | free(config_home); | ||
865 | } | ||
866 | } | ||
867 | |||
868 | wordexp_t p; | ||
869 | char *path; | ||
870 | for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { | ||
871 | if (wordexp(config_paths[i], &p, 0) == 0) { | ||
872 | path = strdup(p.we_wordv[0]); | ||
873 | wordfree(&p); | ||
874 | if (file_exists(path)) { | ||
875 | return path; | ||
876 | } | ||
877 | free(path); | ||
878 | } | ||
879 | } | ||
880 | |||
881 | return NULL; | ||
882 | } | ||
883 | |||
884 | static int load_config(char *path, struct swaylock_state *state, | ||
885 | enum line_mode *line_mode) { | ||
886 | FILE *config = fopen(path, "r"); | ||
887 | if (!config) { | ||
888 | wlr_log(WLR_ERROR, "Failed to read config. Running without it."); | ||
889 | return 0; | ||
890 | } | ||
891 | char *line = NULL; | ||
892 | size_t line_size = 0; | ||
893 | ssize_t nread; | ||
894 | int line_number = 0; | ||
895 | int result = 0; | ||
896 | while ((nread = getline(&line, &line_size, config)) != -1) { | ||
897 | line_number++; | ||
898 | |||
899 | if (line[nread - 1] == '\n') { | ||
900 | line[--nread] = '\0'; | ||
901 | } | ||
902 | |||
903 | if (!*line || line[0] == '#') { | ||
904 | continue; | ||
905 | } | ||
906 | |||
907 | wlr_log(WLR_DEBUG, "Config Line #%d: %s", line_number, line); | ||
908 | char flag[nread + 3]; | ||
909 | sprintf(flag, "--%s", line); | ||
910 | char *argv[] = {"swaylock", flag}; | ||
911 | result = parse_options(2, argv, state, line_mode, NULL); | ||
912 | if (result != 0) { | ||
913 | break; | ||
914 | } | ||
915 | } | ||
916 | free(line); | ||
917 | fclose(config); | ||
918 | return 0; | ||
919 | } | ||
920 | |||
921 | static struct swaylock_state state; | ||
922 | |||
923 | static void display_in(int fd, short mask, void *data) { | ||
924 | if (wl_display_dispatch(state.display) == -1) { | ||
925 | state.run_display = false; | ||
926 | } | ||
927 | } | ||
928 | |||
929 | int main(int argc, char **argv) { | ||
930 | wlr_log_init(WLR_DEBUG, NULL); | ||
931 | initialize_pw_backend(); | ||
932 | |||
933 | enum line_mode line_mode = LM_LINE; | ||
934 | state.args = (struct swaylock_args){ | ||
935 | .mode = BACKGROUND_MODE_FILL, | ||
936 | .font = strdup("sans-serif"), | ||
937 | .radius = 50, | ||
938 | .thickness = 10, | ||
939 | .ignore_empty = false, | ||
940 | .show_indicator = true, | ||
941 | .show_caps_lock_indicator = false, | ||
942 | .show_caps_lock_text = true | ||
943 | }; | ||
944 | wl_list_init(&state.images); | ||
945 | set_default_colors(&state.args.colors); | ||
946 | |||
947 | char *config_path = NULL; | ||
948 | int result = parse_options(argc, argv, NULL, NULL, &config_path); | ||
949 | if (result != 0) { | ||
950 | free(config_path); | ||
951 | return result; | ||
952 | } | ||
953 | if (!config_path) { | ||
954 | config_path = get_config_path(); | ||
955 | } | ||
956 | |||
957 | if (config_path) { | ||
958 | wlr_log(WLR_DEBUG, "Found config at %s", config_path); | ||
959 | int config_status = load_config(config_path, &state, &line_mode); | ||
960 | free(config_path); | ||
961 | if (config_status != 0) { | ||
962 | return config_status; | ||
963 | } | ||
964 | } | ||
965 | |||
966 | if (argc > 1) { | ||
967 | wlr_log(WLR_DEBUG, "Parsing CLI Args"); | ||
968 | int result = parse_options(argc, argv, &state, &line_mode, NULL); | ||
969 | if (result != 0) { | ||
970 | return result; | ||
971 | } | ||
972 | } | ||
973 | |||
974 | if (line_mode == LM_INSIDE) { | ||
975 | state.args.colors.line = state.args.colors.inside; | ||
976 | } else if (line_mode == LM_RING) { | ||
977 | state.args.colors.line = state.args.colors.ring; | ||
978 | } | ||
979 | |||
980 | #ifdef __linux__ | ||
981 | // Most non-linux platforms require root to mlock() | ||
982 | if (mlock(state.password.buffer, sizeof(state.password.buffer)) != 0) { | ||
983 | sway_abort("Unable to mlock() password memory."); | ||
984 | } | ||
985 | #endif | ||
986 | |||
987 | wl_list_init(&state.surfaces); | ||
988 | state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | ||
989 | state.display = wl_display_connect(NULL); | ||
990 | if (!state.display) { | ||
991 | sway_abort("Unable to connect to the compositor. " | ||
992 | "If your compositor is running, check or set the " | ||
993 | "WAYLAND_DISPLAY environment variable."); | ||
994 | } | ||
995 | |||
996 | struct wl_registry *registry = wl_display_get_registry(state.display); | ||
997 | wl_registry_add_listener(registry, ®istry_listener, &state); | ||
998 | wl_display_roundtrip(state.display); | ||
999 | assert(state.compositor && state.layer_shell && state.shm); | ||
1000 | if (!state.input_inhibit_manager) { | ||
1001 | wlr_log(WLR_ERROR, "Compositor does not support the input inhibitor " | ||
1002 | "protocol, refusing to run insecurely"); | ||
1003 | return 1; | ||
1004 | } | ||
1005 | |||
1006 | if (wl_list_empty(&state.surfaces)) { | ||
1007 | wlr_log(WLR_DEBUG, "Exiting - no outputs to show on."); | ||
1008 | return 0; | ||
1009 | } | ||
1010 | |||
1011 | zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager); | ||
1012 | if (wl_display_roundtrip(state.display) == -1) { | ||
1013 | wlr_log(WLR_ERROR, "Exiting - failed to inhibit input:" | ||
1014 | " is another lockscreen already running?"); | ||
1015 | return 2; | ||
1016 | } | ||
1017 | |||
1018 | if (state.zxdg_output_manager) { | ||
1019 | struct swaylock_surface *surface; | ||
1020 | wl_list_for_each(surface, &state.surfaces, link) { | ||
1021 | surface->xdg_output = zxdg_output_manager_v1_get_xdg_output( | ||
1022 | state.zxdg_output_manager, surface->output); | ||
1023 | zxdg_output_v1_add_listener( | ||
1024 | surface->xdg_output, &_xdg_output_listener, surface); | ||
1025 | } | ||
1026 | wl_display_roundtrip(state.display); | ||
1027 | } else { | ||
1028 | wlr_log(WLR_INFO, "Compositor does not support zxdg output manager, " | ||
1029 | "images assigned to named outputs will not work"); | ||
1030 | } | ||
1031 | |||
1032 | struct swaylock_surface *surface; | ||
1033 | wl_list_for_each(surface, &state.surfaces, link) { | ||
1034 | create_layer_surface(surface); | ||
1035 | } | ||
1036 | |||
1037 | if (state.args.daemonize) { | ||
1038 | wl_display_roundtrip(state.display); | ||
1039 | daemonize(); | ||
1040 | } | ||
1041 | |||
1042 | state.eventloop = loop_create(); | ||
1043 | loop_add_fd(state.eventloop, wl_display_get_fd(state.display), POLLIN, | ||
1044 | display_in, NULL); | ||
1045 | |||
1046 | state.run_display = true; | ||
1047 | while (state.run_display) { | ||
1048 | errno = 0; | ||
1049 | if (wl_display_flush(state.display) == -1 && errno != EAGAIN) { | ||
1050 | break; | ||
1051 | } | ||
1052 | loop_poll(state.eventloop); | ||
1053 | } | ||
1054 | |||
1055 | free(state.args.font); | ||
1056 | return 0; | ||
1057 | } | ||
diff --git a/swaylock/meson.build b/swaylock/meson.build deleted file mode 100644 index f3321a78..00000000 --- a/swaylock/meson.build +++ /dev/null | |||
@@ -1,55 +0,0 @@ | |||
1 | sysconfdir = get_option('sysconfdir') | ||
2 | |||
3 | dependencies = [ | ||
4 | cairo, | ||
5 | client_protos, | ||
6 | gdk_pixbuf, | ||
7 | math, | ||
8 | pango, | ||
9 | pangocairo, | ||
10 | xkbcommon, | ||
11 | wayland_client, | ||
12 | wlroots, | ||
13 | ] | ||
14 | |||
15 | sources = [ | ||
16 | 'main.c', | ||
17 | 'password.c', | ||
18 | 'render.c', | ||
19 | 'seat.c' | ||
20 | ] | ||
21 | |||
22 | if libpam.found() | ||
23 | sources += ['pam.c'] | ||
24 | dependencies += [libpam] | ||
25 | else | ||
26 | warning('The swaylock binary must be setuid when compiled without libpam') | ||
27 | warning('You must do this manually post-install: chmod a+s /path/to/swaylock') | ||
28 | sources += ['shadow.c'] | ||
29 | if crypt.found() | ||
30 | dependencies += [crypt] | ||
31 | endif | ||
32 | endif | ||
33 | |||
34 | executable('swaylock', | ||
35 | sources, | ||
36 | include_directories: [sway_inc], | ||
37 | dependencies: dependencies, | ||
38 | link_with: [lib_sway_common, lib_sway_client], | ||
39 | install_rpath : rpathdir, | ||
40 | install: true | ||
41 | ) | ||
42 | |||
43 | if is_freebsd | ||
44 | install_data( | ||
45 | 'pam/swaylock.freebsd', | ||
46 | install_dir: sysconfdir + '/pam.d/', | ||
47 | rename: 'swaylock' | ||
48 | ) | ||
49 | else | ||
50 | install_data( | ||
51 | 'pam/swaylock.linux', | ||
52 | install_dir: sysconfdir + '/pam.d/', | ||
53 | rename: 'swaylock' | ||
54 | ) | ||
55 | endif | ||
diff --git a/swaylock/pam.c b/swaylock/pam.c deleted file mode 100644 index b90d9e87..00000000 --- a/swaylock/pam.c +++ /dev/null | |||
@@ -1,62 +0,0 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <pwd.h> | ||
3 | #include <security/pam_appl.h> | ||
4 | #include <stdbool.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | #include <unistd.h> | ||
8 | #include <wlr/util/log.h> | ||
9 | #include "swaylock/swaylock.h" | ||
10 | |||
11 | void initialize_pw_backend(void) { | ||
12 | // TODO: only call pam_start once. keep the same handle the whole time | ||
13 | } | ||
14 | |||
15 | static int function_conversation(int num_msg, const struct pam_message **msg, | ||
16 | struct pam_response **resp, void *data) { | ||
17 | struct swaylock_password *pw = data; | ||
18 | /* PAM expects an array of responses, one for each message */ | ||
19 | struct pam_response *pam_reply = calloc( | ||
20 | num_msg, sizeof(struct pam_response)); | ||
21 | *resp = pam_reply; | ||
22 | for (int i = 0; i < num_msg; ++i) { | ||
23 | switch (msg[i]->msg_style) { | ||
24 | case PAM_PROMPT_ECHO_OFF: | ||
25 | case PAM_PROMPT_ECHO_ON: | ||
26 | pam_reply[i].resp = strdup(pw->buffer); // PAM clears and frees this | ||
27 | break; | ||
28 | case PAM_ERROR_MSG: | ||
29 | case PAM_TEXT_INFO: | ||
30 | break; | ||
31 | } | ||
32 | } | ||
33 | return PAM_SUCCESS; | ||
34 | } | ||
35 | |||
36 | bool attempt_password(struct swaylock_password *pw) { | ||
37 | struct passwd *passwd = getpwuid(getuid()); | ||
38 | char *username = passwd->pw_name; | ||
39 | const struct pam_conv local_conversation = { | ||
40 | function_conversation, pw | ||
41 | }; | ||
42 | pam_handle_t *local_auth_handle = NULL; | ||
43 | int pam_err; | ||
44 | if ((pam_err = pam_start("swaylock", username, | ||
45 | &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { | ||
46 | wlr_log(WLR_ERROR, "PAM returned error %d", pam_err); | ||
47 | } | ||
48 | if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { | ||
49 | wlr_log(WLR_ERROR, "pam_authenticate failed"); | ||
50 | goto fail; | ||
51 | } | ||
52 | // TODO: only call pam_end once we succeed at authing. refresh tokens beforehand | ||
53 | if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { | ||
54 | wlr_log(WLR_ERROR, "pam_end failed"); | ||
55 | goto fail; | ||
56 | } | ||
57 | clear_password_buffer(pw); | ||
58 | return true; | ||
59 | fail: | ||
60 | clear_password_buffer(pw); | ||
61 | return false; | ||
62 | } | ||
diff --git a/swaylock/pam/swaylock.freebsd b/swaylock/pam/swaylock.freebsd deleted file mode 100644 index 603fc185..00000000 --- a/swaylock/pam/swaylock.freebsd +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | # | ||
2 | # PAM configuration file for the swaylock screen locker. By default, it includes | ||
3 | # the 'passwd' configuration file (see /etc/pam.d/passwd) | ||
4 | # | ||
5 | |||
6 | auth include passwd | ||
diff --git a/swaylock/pam/swaylock.linux b/swaylock/pam/swaylock.linux deleted file mode 100644 index 6a36b0d6..00000000 --- a/swaylock/pam/swaylock.linux +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | # | ||
2 | # PAM configuration file for the swaylock screen locker. By default, it includes | ||
3 | # the 'login' configuration file (see /etc/pam.d/login) | ||
4 | # | ||
5 | |||
6 | auth include login | ||
diff --git a/swaylock/password.c b/swaylock/password.c deleted file mode 100644 index 3bd113ad..00000000 --- a/swaylock/password.c +++ /dev/null | |||
@@ -1,190 +0,0 @@ | |||
1 | #include <assert.h> | ||
2 | #include <errno.h> | ||
3 | #include <pwd.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | #include <unistd.h> | ||
7 | #include <wlr/util/log.h> | ||
8 | #include <xkbcommon/xkbcommon.h> | ||
9 | #include "swaylock/swaylock.h" | ||
10 | #include "swaylock/seat.h" | ||
11 | #include "loop.h" | ||
12 | #include "unicode.h" | ||
13 | |||
14 | void clear_password_buffer(struct swaylock_password *pw) { | ||
15 | // Use volatile keyword so so compiler can't optimize this out. | ||
16 | volatile char *buffer = pw->buffer; | ||
17 | volatile char zero = '\0'; | ||
18 | for (size_t i = 0; i < sizeof(pw->buffer); ++i) { | ||
19 | buffer[i] = zero; | ||
20 | } | ||
21 | pw->len = 0; | ||
22 | } | ||
23 | |||
24 | static bool backspace(struct swaylock_password *pw) { | ||
25 | if (pw->len != 0) { | ||
26 | pw->buffer[--pw->len] = 0; | ||
27 | return true; | ||
28 | } | ||
29 | return false; | ||
30 | } | ||
31 | |||
32 | static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { | ||
33 | size_t utf8_size = utf8_chsize(codepoint); | ||
34 | if (pw->len + utf8_size + 1 >= sizeof(pw->buffer)) { | ||
35 | // TODO: Display error | ||
36 | return; | ||
37 | } | ||
38 | utf8_encode(&pw->buffer[pw->len], codepoint); | ||
39 | pw->buffer[pw->len + utf8_size] = 0; | ||
40 | pw->len += utf8_size; | ||
41 | } | ||
42 | |||
43 | static void clear_indicator(void *data) { | ||
44 | struct swaylock_state *state = data; | ||
45 | state->clear_indicator_timer = NULL; | ||
46 | state->auth_state = AUTH_STATE_IDLE; | ||
47 | damage_state(state); | ||
48 | } | ||
49 | |||
50 | static void schedule_indicator_clear(struct swaylock_state *state) { | ||
51 | if (state->clear_indicator_timer) { | ||
52 | loop_remove_timer(state->eventloop, state->clear_indicator_timer); | ||
53 | } | ||
54 | state->clear_indicator_timer = loop_add_timer( | ||
55 | state->eventloop, 3000, clear_indicator, state); | ||
56 | } | ||
57 | |||
58 | static void clear_password(void *data) { | ||
59 | struct swaylock_state *state = data; | ||
60 | state->clear_password_timer = NULL; | ||
61 | state->auth_state = AUTH_STATE_CLEAR; | ||
62 | clear_password_buffer(&state->password); | ||
63 | damage_state(state); | ||
64 | schedule_indicator_clear(state); | ||
65 | } | ||
66 | |||
67 | static void schedule_password_clear(struct swaylock_state *state) { | ||
68 | if (state->clear_password_timer) { | ||
69 | loop_remove_timer(state->eventloop, state->clear_password_timer); | ||
70 | } | ||
71 | state->clear_password_timer = loop_add_timer( | ||
72 | state->eventloop, 10000, clear_password, state); | ||
73 | } | ||
74 | |||
75 | static void handle_preverify_timeout(void *data) { | ||
76 | struct swaylock_state *state = data; | ||
77 | state->verify_password_timer = NULL; | ||
78 | } | ||
79 | |||
80 | static void submit_password(struct swaylock_state *state) { | ||
81 | if (state->args.ignore_empty && state->password.len == 0) { | ||
82 | return; | ||
83 | } | ||
84 | |||
85 | state->auth_state = AUTH_STATE_VALIDATING; | ||
86 | damage_state(state); | ||
87 | |||
88 | // We generally want to wait until all surfaces are showing the | ||
89 | // "verifying" state before we go and verify the password, because | ||
90 | // verifying it is a blocking operation. However, if the surface is on | ||
91 | // an output with DPMS off then it won't update, so we set a timer. | ||
92 | state->verify_password_timer = loop_add_timer( | ||
93 | state->eventloop, 50, handle_preverify_timeout, state); | ||
94 | |||
95 | while (state->run_display && state->verify_password_timer) { | ||
96 | errno = 0; | ||
97 | if (wl_display_flush(state->display) == -1 && errno != EAGAIN) { | ||
98 | break; | ||
99 | } | ||
100 | loop_poll(state->eventloop); | ||
101 | |||
102 | bool ok = 1; | ||
103 | struct swaylock_surface *surface; | ||
104 | wl_list_for_each(surface, &state->surfaces, link) { | ||
105 | if (surface->dirty) { | ||
106 | ok = 0; | ||
107 | } | ||
108 | } | ||
109 | if (ok) { | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | wl_display_flush(state->display); | ||
114 | |||
115 | if (attempt_password(&state->password)) { | ||
116 | state->run_display = false; | ||
117 | return; | ||
118 | } | ||
119 | state->auth_state = AUTH_STATE_INVALID; | ||
120 | damage_state(state); | ||
121 | schedule_indicator_clear(state); | ||
122 | } | ||
123 | |||
124 | void swaylock_handle_key(struct swaylock_state *state, | ||
125 | xkb_keysym_t keysym, uint32_t codepoint) { | ||
126 | switch (keysym) { | ||
127 | case XKB_KEY_KP_Enter: /* fallthrough */ | ||
128 | case XKB_KEY_Return: | ||
129 | submit_password(state); | ||
130 | break; | ||
131 | case XKB_KEY_Delete: | ||
132 | case XKB_KEY_BackSpace: | ||
133 | if (backspace(&state->password)) { | ||
134 | state->auth_state = AUTH_STATE_BACKSPACE; | ||
135 | } else { | ||
136 | state->auth_state = AUTH_STATE_CLEAR; | ||
137 | } | ||
138 | damage_state(state); | ||
139 | schedule_indicator_clear(state); | ||
140 | schedule_password_clear(state); | ||
141 | break; | ||
142 | case XKB_KEY_Escape: | ||
143 | clear_password_buffer(&state->password); | ||
144 | state->auth_state = AUTH_STATE_CLEAR; | ||
145 | damage_state(state); | ||
146 | schedule_indicator_clear(state); | ||
147 | break; | ||
148 | case XKB_KEY_Caps_Lock: | ||
149 | case XKB_KEY_Shift_L: | ||
150 | case XKB_KEY_Shift_R: | ||
151 | case XKB_KEY_Control_L: | ||
152 | case XKB_KEY_Control_R: | ||
153 | case XKB_KEY_Meta_L: | ||
154 | case XKB_KEY_Meta_R: | ||
155 | case XKB_KEY_Alt_L: | ||
156 | case XKB_KEY_Alt_R: | ||
157 | case XKB_KEY_Super_L: | ||
158 | case XKB_KEY_Super_R: | ||
159 | state->auth_state = AUTH_STATE_INPUT_NOP; | ||
160 | damage_state(state); | ||
161 | schedule_indicator_clear(state); | ||
162 | schedule_password_clear(state); | ||
163 | break; | ||
164 | case XKB_KEY_d: | ||
165 | if (state->xkb.control) { | ||
166 | submit_password(state); | ||
167 | break; | ||
168 | } | ||
169 | // fallthrough | ||
170 | case XKB_KEY_c: /* fallthrough */ | ||
171 | case XKB_KEY_u: | ||
172 | if (state->xkb.control) { | ||
173 | clear_password_buffer(&state->password); | ||
174 | state->auth_state = AUTH_STATE_CLEAR; | ||
175 | damage_state(state); | ||
176 | schedule_indicator_clear(state); | ||
177 | break; | ||
178 | } | ||
179 | // fallthrough | ||
180 | default: | ||
181 | if (codepoint) { | ||
182 | append_ch(&state->password, codepoint); | ||
183 | state->auth_state = AUTH_STATE_INPUT; | ||
184 | damage_state(state); | ||
185 | schedule_indicator_clear(state); | ||
186 | schedule_password_clear(state); | ||
187 | } | ||
188 | break; | ||
189 | } | ||
190 | } | ||
diff --git a/swaylock/render.c b/swaylock/render.c deleted file mode 100644 index 5aedaad5..00000000 --- a/swaylock/render.c +++ /dev/null | |||
@@ -1,189 +0,0 @@ | |||
1 | #include <math.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <wayland-client.h> | ||
4 | #include "cairo.h" | ||
5 | #include "background-image.h" | ||
6 | #include "swaylock/swaylock.h" | ||
7 | |||
8 | #define M_PI 3.14159265358979323846 | ||
9 | const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; | ||
10 | const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; | ||
11 | |||
12 | static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state, | ||
13 | struct swaylock_colorset *colorset) { | ||
14 | if (state->auth_state == AUTH_STATE_VALIDATING) { | ||
15 | cairo_set_source_u32(cairo, colorset->verifying); | ||
16 | } else if (state->auth_state == AUTH_STATE_INVALID) { | ||
17 | cairo_set_source_u32(cairo, colorset->wrong); | ||
18 | } else if (state->auth_state == AUTH_STATE_CLEAR) { | ||
19 | cairo_set_source_u32(cairo, colorset->cleared); | ||
20 | } else { | ||
21 | if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) { | ||
22 | cairo_set_source_u32(cairo, colorset->caps_lock); | ||
23 | } else if (state->xkb.caps_lock && !state->args.show_caps_lock_indicator && | ||
24 | state->args.show_caps_lock_text) { | ||
25 | uint32_t inputtextcolor = state->args.colors.text.input; | ||
26 | state->args.colors.text.input = state->args.colors.text.caps_lock; | ||
27 | cairo_set_source_u32(cairo, colorset->input); | ||
28 | state->args.colors.text.input = inputtextcolor; | ||
29 | } else { | ||
30 | cairo_set_source_u32(cairo, colorset->input); | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | void render_frame(struct swaylock_surface *surface) { | ||
36 | struct swaylock_state *state = surface->state; | ||
37 | |||
38 | int buffer_width = surface->width * surface->scale; | ||
39 | int buffer_height = surface->height * surface->scale; | ||
40 | if (buffer_width == 0 || buffer_height == 0) { | ||
41 | return; // not yet configured | ||
42 | } | ||
43 | |||
44 | surface->current_buffer = get_next_buffer(state->shm, | ||
45 | surface->buffers, buffer_width, buffer_height); | ||
46 | if (surface->current_buffer == NULL) { | ||
47 | return; | ||
48 | } | ||
49 | |||
50 | cairo_t *cairo = surface->current_buffer->cairo; | ||
51 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); | ||
52 | cairo_font_options_t *fo = cairo_font_options_create(); | ||
53 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); | ||
54 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); | ||
55 | cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(surface->subpixel)); | ||
56 | cairo_set_font_options(cairo, fo); | ||
57 | cairo_font_options_destroy(fo); | ||
58 | cairo_identity_matrix(cairo); | ||
59 | |||
60 | cairo_save(cairo); | ||
61 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
62 | if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) { | ||
63 | cairo_set_source_u32(cairo, state->args.colors.background); | ||
64 | cairo_paint(cairo); | ||
65 | } else { | ||
66 | render_background_image(cairo, surface->image, | ||
67 | state->args.mode, buffer_width, buffer_height); | ||
68 | } | ||
69 | cairo_restore(cairo); | ||
70 | cairo_identity_matrix(cairo); | ||
71 | |||
72 | int arc_radius = state->args.radius * surface->scale; | ||
73 | int arc_thickness = state->args.thickness * surface->scale; | ||
74 | float type_indicator_border_thickness = | ||
75 | TYPE_INDICATOR_BORDER_THICKNESS * surface->scale; | ||
76 | |||
77 | if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) { | ||
78 | // Draw circle | ||
79 | cairo_set_line_width(cairo, arc_thickness); | ||
80 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, | ||
81 | 0, 2 * M_PI); | ||
82 | set_color_for_state(cairo, state, &state->args.colors.inside); | ||
83 | cairo_fill_preserve(cairo); | ||
84 | set_color_for_state(cairo, state, &state->args.colors.ring); | ||
85 | cairo_stroke(cairo); | ||
86 | |||
87 | // Draw a message | ||
88 | char *text = NULL; | ||
89 | set_color_for_state(cairo, state, &state->args.colors.text); | ||
90 | cairo_select_font_face(cairo, state->args.font, | ||
91 | CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); | ||
92 | cairo_set_font_size(cairo, arc_radius / 3.0f); | ||
93 | switch (state->auth_state) { | ||
94 | case AUTH_STATE_VALIDATING: | ||
95 | text = "verifying"; | ||
96 | break; | ||
97 | case AUTH_STATE_INVALID: | ||
98 | text = "wrong"; | ||
99 | break; | ||
100 | case AUTH_STATE_CLEAR: | ||
101 | text = "cleared"; | ||
102 | break; | ||
103 | case AUTH_STATE_INPUT: | ||
104 | case AUTH_STATE_INPUT_NOP: | ||
105 | case AUTH_STATE_BACKSPACE: | ||
106 | if (state->xkb.caps_lock && state->args.show_caps_lock_text) { | ||
107 | text = "Caps Lock"; | ||
108 | } | ||
109 | break; | ||
110 | default: | ||
111 | break; | ||
112 | } | ||
113 | |||
114 | if (text) { | ||
115 | cairo_text_extents_t extents; | ||
116 | double x, y; | ||
117 | cairo_text_extents(cairo, text, &extents); | ||
118 | x = (buffer_width / 2) - | ||
119 | (extents.width / 2 + extents.x_bearing); | ||
120 | y = (buffer_height / 2) - | ||
121 | (extents.height / 2 + extents.y_bearing); | ||
122 | |||
123 | cairo_move_to(cairo, x, y); | ||
124 | cairo_show_text(cairo, text); | ||
125 | cairo_close_path(cairo); | ||
126 | cairo_new_sub_path(cairo); | ||
127 | } | ||
128 | |||
129 | // Typing indicator: Highlight random part on keypress | ||
130 | if (state->auth_state == AUTH_STATE_INPUT | ||
131 | || state->auth_state == AUTH_STATE_BACKSPACE) { | ||
132 | static double highlight_start = 0; | ||
133 | highlight_start += | ||
134 | (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; | ||
135 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
136 | arc_radius, highlight_start, | ||
137 | highlight_start + TYPE_INDICATOR_RANGE); | ||
138 | if (state->auth_state == AUTH_STATE_INPUT) { | ||
139 | if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) { | ||
140 | cairo_set_source_u32(cairo, state->args.colors.caps_lock_key_highlight); | ||
141 | } else { | ||
142 | cairo_set_source_u32(cairo, state->args.colors.key_highlight); | ||
143 | } | ||
144 | } else { | ||
145 | if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) { | ||
146 | cairo_set_source_u32(cairo, state->args.colors.caps_lock_bs_highlight); | ||
147 | } else { | ||
148 | cairo_set_source_u32(cairo, state->args.colors.bs_highlight); | ||
149 | } | ||
150 | } | ||
151 | cairo_stroke(cairo); | ||
152 | |||
153 | // Draw borders | ||
154 | cairo_set_source_u32(cairo, state->args.colors.separator); | ||
155 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
156 | arc_radius, highlight_start, | ||
157 | highlight_start + type_indicator_border_thickness); | ||
158 | cairo_stroke(cairo); | ||
159 | |||
160 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
161 | arc_radius, highlight_start + TYPE_INDICATOR_RANGE, | ||
162 | highlight_start + TYPE_INDICATOR_RANGE + | ||
163 | type_indicator_border_thickness); | ||
164 | cairo_stroke(cairo); | ||
165 | } | ||
166 | |||
167 | // Draw inner + outer border of the circle | ||
168 | set_color_for_state(cairo, state, &state->args.colors.line); | ||
169 | cairo_set_line_width(cairo, 2.0 * surface->scale); | ||
170 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
171 | arc_radius - arc_thickness / 2, 0, 2 * M_PI); | ||
172 | cairo_stroke(cairo); | ||
173 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
174 | arc_radius + arc_thickness / 2, 0, 2 * M_PI); | ||
175 | cairo_stroke(cairo); | ||
176 | } | ||
177 | |||
178 | wl_surface_set_buffer_scale(surface->surface, surface->scale); | ||
179 | wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0); | ||
180 | wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height); | ||
181 | wl_surface_commit(surface->surface); | ||
182 | } | ||
183 | |||
184 | void render_frames(struct swaylock_state *state) { | ||
185 | struct swaylock_surface *surface; | ||
186 | wl_list_for_each(surface, &state->surfaces, link) { | ||
187 | render_frame(surface); | ||
188 | } | ||
189 | } | ||
diff --git a/swaylock/seat.c b/swaylock/seat.c deleted file mode 100644 index f0b1385e..00000000 --- a/swaylock/seat.c +++ /dev/null | |||
@@ -1,178 +0,0 @@ | |||
1 | #include <assert.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <sys/mman.h> | ||
4 | #include <unistd.h> | ||
5 | #include <wlr/util/log.h> | ||
6 | #include <xkbcommon/xkbcommon.h> | ||
7 | #include "swaylock/swaylock.h" | ||
8 | #include "swaylock/seat.h" | ||
9 | |||
10 | static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, | ||
11 | uint32_t format, int32_t fd, uint32_t size) { | ||
12 | struct swaylock_state *state = data; | ||
13 | if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { | ||
14 | close(fd); | ||
15 | wlr_log(WLR_ERROR, "Unknown keymap format %d, aborting", format); | ||
16 | exit(1); | ||
17 | } | ||
18 | char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); | ||
19 | if (map_shm == MAP_FAILED) { | ||
20 | close(fd); | ||
21 | wlr_log(WLR_ERROR, "Unable to initialize keymap shm, aborting"); | ||
22 | exit(1); | ||
23 | } | ||
24 | struct xkb_keymap *keymap = xkb_keymap_new_from_string( | ||
25 | state->xkb.context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0); | ||
26 | munmap(map_shm, size); | ||
27 | close(fd); | ||
28 | assert(keymap); | ||
29 | struct xkb_state *xkb_state = xkb_state_new(keymap); | ||
30 | assert(xkb_state); | ||
31 | xkb_keymap_unref(state->xkb.keymap); | ||
32 | xkb_state_unref(state->xkb.state); | ||
33 | state->xkb.keymap = keymap; | ||
34 | state->xkb.state = xkb_state; | ||
35 | } | ||
36 | |||
37 | static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, | ||
38 | uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { | ||
39 | // Who cares | ||
40 | } | ||
41 | |||
42 | static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, | ||
43 | uint32_t serial, struct wl_surface *surface) { | ||
44 | // Who cares | ||
45 | } | ||
46 | |||
47 | static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, | ||
48 | uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) { | ||
49 | struct swaylock_state *state = data; | ||
50 | enum wl_keyboard_key_state key_state = _key_state; | ||
51 | xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb.state, key + 8); | ||
52 | uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ? | ||
53 | key + 8 : 0; | ||
54 | uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode); | ||
55 | if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED) { | ||
56 | swaylock_handle_key(state, sym, codepoint); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, | ||
61 | uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, | ||
62 | uint32_t mods_locked, uint32_t group) { | ||
63 | struct swaylock_state *state = data; | ||
64 | xkb_state_update_mask(state->xkb.state, | ||
65 | mods_depressed, mods_latched, mods_locked, 0, 0, group); | ||
66 | int caps_lock = xkb_state_mod_name_is_active(state->xkb.state, | ||
67 | XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED); | ||
68 | if (caps_lock != state->xkb.caps_lock) { | ||
69 | state->xkb.caps_lock = caps_lock; | ||
70 | damage_state(state); | ||
71 | } | ||
72 | state->xkb.control = xkb_state_mod_name_is_active(state->xkb.state, | ||
73 | XKB_MOD_NAME_CTRL, | ||
74 | XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); | ||
75 | |||
76 | } | ||
77 | |||
78 | static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, | ||
79 | int32_t rate, int32_t delay) { | ||
80 | // TODO | ||
81 | } | ||
82 | |||
83 | static const struct wl_keyboard_listener keyboard_listener = { | ||
84 | .keymap = keyboard_keymap, | ||
85 | .enter = keyboard_enter, | ||
86 | .leave = keyboard_leave, | ||
87 | .key = keyboard_key, | ||
88 | .modifiers = keyboard_modifiers, | ||
89 | .repeat_info = keyboard_repeat_info, | ||
90 | }; | ||
91 | |||
92 | static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | ||
93 | uint32_t serial, struct wl_surface *surface, | ||
94 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
95 | wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0); | ||
96 | } | ||
97 | |||
98 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, | ||
99 | uint32_t serial, struct wl_surface *surface) { | ||
100 | // Who cares | ||
101 | } | ||
102 | |||
103 | static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, | ||
104 | uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
105 | // Who cares | ||
106 | } | ||
107 | |||
108 | static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | ||
109 | uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { | ||
110 | // Who cares | ||
111 | } | ||
112 | |||
113 | static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, | ||
114 | uint32_t time, uint32_t axis, wl_fixed_t value) { | ||
115 | // Who cares | ||
116 | } | ||
117 | |||
118 | static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { | ||
119 | // Who cares | ||
120 | } | ||
121 | |||
122 | static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, | ||
123 | uint32_t axis_source) { | ||
124 | // Who cares | ||
125 | } | ||
126 | |||
127 | static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, | ||
128 | uint32_t time, uint32_t axis) { | ||
129 | // Who cares | ||
130 | } | ||
131 | |||
132 | static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, | ||
133 | uint32_t axis, int32_t discrete) { | ||
134 | // Who cares | ||
135 | } | ||
136 | |||
137 | static const struct wl_pointer_listener pointer_listener = { | ||
138 | .enter = wl_pointer_enter, | ||
139 | .leave = wl_pointer_leave, | ||
140 | .motion = wl_pointer_motion, | ||
141 | .button = wl_pointer_button, | ||
142 | .axis = wl_pointer_axis, | ||
143 | .frame = wl_pointer_frame, | ||
144 | .axis_source = wl_pointer_axis_source, | ||
145 | .axis_stop = wl_pointer_axis_stop, | ||
146 | .axis_discrete = wl_pointer_axis_discrete, | ||
147 | }; | ||
148 | |||
149 | static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, | ||
150 | enum wl_seat_capability caps) { | ||
151 | struct swaylock_seat *seat = data; | ||
152 | if (seat->pointer) { | ||
153 | wl_pointer_release(seat->pointer); | ||
154 | seat->pointer = NULL; | ||
155 | } | ||
156 | if (seat->keyboard) { | ||
157 | wl_keyboard_release(seat->keyboard); | ||
158 | seat->keyboard = NULL; | ||
159 | } | ||
160 | if ((caps & WL_SEAT_CAPABILITY_POINTER)) { | ||
161 | seat->pointer = wl_seat_get_pointer(wl_seat); | ||
162 | wl_pointer_add_listener(seat->pointer, &pointer_listener, NULL); | ||
163 | } | ||
164 | if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { | ||
165 | seat->keyboard = wl_seat_get_keyboard(wl_seat); | ||
166 | wl_keyboard_add_listener(seat->keyboard, &keyboard_listener, seat->state); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | static void seat_handle_name(void *data, struct wl_seat *wl_seat, | ||
171 | const char *name) { | ||
172 | // Who cares | ||
173 | } | ||
174 | |||
175 | const struct wl_seat_listener seat_listener = { | ||
176 | .capabilities = seat_handle_capabilities, | ||
177 | .name = seat_handle_name, | ||
178 | }; | ||
diff --git a/swaylock/shadow.c b/swaylock/shadow.c deleted file mode 100644 index b7b10a67..00000000 --- a/swaylock/shadow.c +++ /dev/null | |||
@@ -1,155 +0,0 @@ | |||
1 | #define _XOPEN_SOURCE // for crypt | ||
2 | #include <pwd.h> | ||
3 | #include <shadow.h> | ||
4 | #include <stdbool.h> | ||
5 | #include <sys/types.h> | ||
6 | #include <unistd.h> | ||
7 | #include <wlr/util/log.h> | ||
8 | #include "swaylock/swaylock.h" | ||
9 | #ifdef __GLIBC__ | ||
10 | // GNU, you damn slimy bastard | ||
11 | #include <crypt.h> | ||
12 | #endif | ||
13 | |||
14 | static int comm[2][2]; | ||
15 | |||
16 | static void clear_buffer(void *buf, size_t bytes) { | ||
17 | volatile char *buffer = buf; | ||
18 | volatile char zero = '\0'; | ||
19 | for (size_t i = 0; i < bytes; ++i) { | ||
20 | buffer[i] = zero; | ||
21 | } | ||
22 | } | ||
23 | |||
24 | void run_child(void) { | ||
25 | /* This code runs as root */ | ||
26 | struct passwd *pwent = getpwuid(getuid()); | ||
27 | if (!pwent) { | ||
28 | wlr_log_errno(WLR_ERROR, "failed to getpwuid"); | ||
29 | exit(EXIT_FAILURE); | ||
30 | } | ||
31 | char *encpw = pwent->pw_passwd; | ||
32 | if (strcmp(encpw, "x") == 0) { | ||
33 | struct spwd *swent = getspnam(pwent->pw_name); | ||
34 | if (!swent) { | ||
35 | wlr_log_errno(WLR_ERROR, "failed to getspnam"); | ||
36 | exit(EXIT_FAILURE); | ||
37 | } | ||
38 | encpw = swent->sp_pwdp; | ||
39 | } | ||
40 | |||
41 | /* We don't need any additional logging here because the parent process will | ||
42 | * also fail here and will handle logging for us. */ | ||
43 | if (setgid(getgid()) != 0) { | ||
44 | exit(EXIT_FAILURE); | ||
45 | } | ||
46 | if (setuid(getuid()) != 0) { | ||
47 | exit(EXIT_FAILURE); | ||
48 | } | ||
49 | |||
50 | /* This code does not run as root */ | ||
51 | wlr_log(WLR_DEBUG, "prepared to authorize user %s", pwent->pw_name); | ||
52 | |||
53 | size_t size; | ||
54 | char *buf; | ||
55 | while (1) { | ||
56 | ssize_t amt; | ||
57 | amt = read(comm[0][0], &size, sizeof(size)); | ||
58 | if (amt == 0) { | ||
59 | break; | ||
60 | } else if (amt < 0) { | ||
61 | wlr_log_errno(WLR_ERROR, "read pw request"); | ||
62 | } | ||
63 | wlr_log(WLR_DEBUG, "received pw check request"); | ||
64 | buf = malloc(size); | ||
65 | if (!buf) { | ||
66 | wlr_log_errno(WLR_ERROR, "failed to malloc pw buffer"); | ||
67 | exit(EXIT_FAILURE); | ||
68 | } | ||
69 | size_t offs = 0; | ||
70 | do { | ||
71 | amt = read(comm[0][0], &buf[offs], size - offs); | ||
72 | if (amt <= 0) { | ||
73 | wlr_log_errno(WLR_ERROR, "failed to read pw"); | ||
74 | exit(EXIT_FAILURE); | ||
75 | } | ||
76 | offs += (size_t)amt; | ||
77 | } while (offs < size); | ||
78 | bool result = false; | ||
79 | char *c = crypt(buf, encpw); | ||
80 | if (c == NULL) { | ||
81 | wlr_log_errno(WLR_ERROR, "crypt"); | ||
82 | } | ||
83 | result = strcmp(c, encpw) == 0; | ||
84 | if (write(comm[1][1], &result, sizeof(result)) != sizeof(result)) { | ||
85 | wlr_log_errno(WLR_ERROR, "failed to write pw check result"); | ||
86 | clear_buffer(buf, size); | ||
87 | exit(EXIT_FAILURE); | ||
88 | } | ||
89 | clear_buffer(buf, size); | ||
90 | free(buf); | ||
91 | } | ||
92 | |||
93 | clear_buffer(encpw, strlen(encpw)); | ||
94 | exit(EXIT_SUCCESS); | ||
95 | } | ||
96 | |||
97 | void initialize_pw_backend(void) { | ||
98 | if (geteuid() != 0) { | ||
99 | wlr_log(WLR_ERROR, "swaylock needs to be setuid to read /etc/shadow"); | ||
100 | exit(EXIT_FAILURE); | ||
101 | } | ||
102 | if (pipe(comm[0]) != 0) { | ||
103 | wlr_log_errno(WLR_ERROR, "failed to create pipe"); | ||
104 | exit(EXIT_FAILURE); | ||
105 | } | ||
106 | if (pipe(comm[1]) != 0) { | ||
107 | wlr_log_errno(WLR_ERROR, "failed to create pipe"); | ||
108 | exit(EXIT_FAILURE); | ||
109 | } | ||
110 | pid_t child = fork(); | ||
111 | if (child == 0) { | ||
112 | close(comm[0][1]); | ||
113 | close(comm[1][0]); | ||
114 | run_child(); | ||
115 | } else if (child < 0) { | ||
116 | wlr_log_errno(WLR_ERROR, "failed to fork"); | ||
117 | exit(EXIT_FAILURE); | ||
118 | } | ||
119 | close(comm[0][0]); | ||
120 | close(comm[1][1]); | ||
121 | if (setgid(getgid()) != 0) { | ||
122 | wlr_log_errno(WLR_ERROR, "Unable to drop root"); | ||
123 | exit(EXIT_FAILURE); | ||
124 | } | ||
125 | if (setuid(getuid()) != 0) { | ||
126 | wlr_log_errno(WLR_ERROR, "Unable to drop root"); | ||
127 | exit(EXIT_FAILURE); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | bool attempt_password(struct swaylock_password *pw) { | ||
132 | bool result = false; | ||
133 | size_t len = pw->len + 1; | ||
134 | size_t offs = 0; | ||
135 | if (write(comm[0][1], &len, sizeof(len)) < 0) { | ||
136 | wlr_log_errno(WLR_ERROR, "Failed to request pw check"); | ||
137 | goto ret; | ||
138 | } | ||
139 | do { | ||
140 | ssize_t amt = write(comm[0][1], &pw->buffer[offs], len - offs); | ||
141 | if (amt < 0) { | ||
142 | wlr_log_errno(WLR_ERROR, "Failed to write pw buffer"); | ||
143 | goto ret; | ||
144 | } | ||
145 | offs += amt; | ||
146 | } while (offs < len); | ||
147 | if (read(comm[1][0], &result, sizeof(result)) != sizeof(result)) { | ||
148 | wlr_log_errno(WLR_ERROR, "Failed to read pw result"); | ||
149 | goto ret; | ||
150 | } | ||
151 | wlr_log(WLR_DEBUG, "pw result: %d", result); | ||
152 | ret: | ||
153 | clear_password_buffer(pw); | ||
154 | return result; | ||
155 | } | ||
diff --git a/swaylock/swaylock.1.scd b/swaylock/swaylock.1.scd deleted file mode 100644 index 2c7979be..00000000 --- a/swaylock/swaylock.1.scd +++ /dev/null | |||
@@ -1,167 +0,0 @@ | |||
1 | swaylock(1) | ||
2 | |||
3 | # NAME | ||
4 | |||
5 | swaylock - Screen locker for Wayland | ||
6 | |||
7 | # SYNOPSIS | ||
8 | |||
9 | _swaylock_ [options...] | ||
10 | |||
11 | Locks your Wayland session. | ||
12 | |||
13 | # OPTIONS | ||
14 | |||
15 | *-C, --config* <path> | ||
16 | The config file to use. By default, the following paths are checked: | ||
17 | _$HOME/.swaylock/config_, _$XDG\_CONFIG\_HOME/swaylock/config_, and | ||
18 | _SYSCONFDIR/swaylock/config_. All flags aside from this one are valid | ||
19 | options in the configuration file using the format _long-option=value_. | ||
20 | For options such as _ignore-empty-password_, just supply the _long-option_. | ||
21 | All leading dashes should be omitted and the equals sign is required for | ||
22 | flags that take an argument. | ||
23 | |||
24 | *-e, --ignore-empty-password* | ||
25 | When an empty password is provided by the user, do not validate it. | ||
26 | |||
27 | *-f, --daemonize* | ||
28 | Detach from the controlling terminal after locking. | ||
29 | |||
30 | Note: this is the default behavior of i3lock. | ||
31 | |||
32 | *-h, --help* | ||
33 | Show help message and quit. | ||
34 | |||
35 | *-v, --version* | ||
36 | Show the version number and quit. | ||
37 | |||
38 | # APPEARANCE | ||
39 | |||
40 | *-u, --no-unlock-indicator* | ||
41 | Disable the unlock indicator. | ||
42 | |||
43 | *-i, --image* [<output>:]<path> | ||
44 | Display the given image, optionally only on the given output. Use -c to set | ||
45 | a background color. | ||
46 | |||
47 | *-L, --disable-caps-lock-text* | ||
48 | Disable the Caps Lock Text. | ||
49 | |||
50 | *-l, --indicator-caps-lock* | ||
51 | Show the current Caps Lock state also on the indicator. | ||
52 | |||
53 | *-s, --scaling* | ||
54 | Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_. | ||
55 | |||
56 | *-t, --tiling* | ||
57 | Same as --scaling=tile. | ||
58 | |||
59 | *-c, --color* <rrggbb[aa]> | ||
60 | Turn the screen into the given color. If -i is used, this sets the | ||
61 | background of the image to the given color. Defaults to white (FFFFFF), or | ||
62 | transparent (00000000) if an image is in use. | ||
63 | |||
64 | *--bs-hl-color* <rrggbb[aa]> | ||
65 | Sets the color of backspace highlight segments. | ||
66 | |||
67 | *--caps-lock-bs-hl-color* <rrggbb[aa]> | ||
68 | Sets the color of backspace highlight segments when Caps Lock is active. | ||
69 | |||
70 | *--caps-lock-bs-hl-color* <rrggbb[aa]> | ||
71 | Sets the color of the key press highlight segments when Caps Lock is active. | ||
72 | |||
73 | *--font* <font> | ||
74 | Sets the font of the text inside the indicator. | ||
75 | |||
76 | *--indicator-radius* <radius> | ||
77 | Sets the radius of the indicator to _radius_ pixels. The default value is | ||
78 | 50. | ||
79 | |||
80 | *--indicator-thickness* <thickness> | ||
81 | Sets the thickness of the indicator to _thickness_ pixels. The default value | ||
82 | is 10. | ||
83 | |||
84 | *--inside-color* <rrggbb[aa]> | ||
85 | Sets the color of the inside of the indicator when typing or idle. | ||
86 | |||
87 | *--inside-clear-color* <rrggbb[aa]> | ||
88 | Sets the color of the inside of the indicator when cleared. | ||
89 | |||
90 | *--inside-caps-lock-color* <rrggbb[aa]> | ||
91 | Sets the color of the inside of the indicator when Caps Lock is active. | ||
92 | |||
93 | *--inside-ver-color* <rrggbb[aa]> | ||
94 | Sets the color of the inside of the indicator when verifying. | ||
95 | |||
96 | *--inside-wrong-color* <rrggbb[aa]> | ||
97 | Sets the color of the inside of the indicator when invalid. | ||
98 | |||
99 | *--key-hl-color* <rrggbb[aa]> | ||
100 | Sets the color of key press highlight segments. | ||
101 | |||
102 | *--line-color* <rrggbb[aa]> | ||
103 | Sets the color of the lines that separate the inside and outside of the | ||
104 | indicator when typing or idle. | ||
105 | |||
106 | *--line-clear-color* <rrggbb[aa]> | ||
107 | Sets the color of the lines that separate the inside and outside of the | ||
108 | indicator when cleared. | ||
109 | |||
110 | *--line-caps-lock-color* <rrggbb[aa]> | ||
111 | Sets the color of the line between the inside and ring when Caps Lock | ||
112 | is active. | ||
113 | |||
114 | *--line-ver-color* <rrggbb[aa]> | ||
115 | Sets the color of the lines that separate the inside and outside of the | ||
116 | indicator when verifying. | ||
117 | |||
118 | *--line-wrong-color* <rrggbb[aa]> | ||
119 | Sets the color of the lines that separate the inside and outside of the | ||
120 | indicator when invalid. | ||
121 | |||
122 | *-n, --line-uses-inside* | ||
123 | Use the color of the inside of the indicator for the line separating the | ||
124 | inside and outside of the indicator. | ||
125 | |||
126 | *-r, --line-uses-ring* | ||
127 | Use the outer ring's color for the line separating the inside and outside of | ||
128 | the indicator. | ||
129 | |||
130 | *--ring-color* <rrggbb[aa]> | ||
131 | Sets the color of the outside of the indicator when typing or idle. | ||
132 | |||
133 | *--ring-clear-color* <rrggbb[aa]> | ||
134 | Sets the color of the outside of the indicator when cleared. | ||
135 | |||
136 | *--ring-caps-lock-color* <rrggbb[aa]> | ||
137 | Sets the color of the ring of the indicator when Caps Lock is active. | ||
138 | |||
139 | *--ring-ver-color* <rrggbb[aa]> | ||
140 | Sets the color of the outside of the indicator when verifying. | ||
141 | |||
142 | *--ring-wrong-color* <rrggbb[aa]> | ||
143 | Sets the color of the outside of the indicator when invalid. | ||
144 | |||
145 | *--separator-color* <rrggbb[aa]> | ||
146 | Sets the color of the lines that separate highlight segments. | ||
147 | |||
148 | *--text-color* <rrggbb[aa]> | ||
149 | Sets the color of the text inside the indicator when typing or idle. | ||
150 | |||
151 | *--text-clear-color* <rrggbb[aa]> | ||
152 | Sets the color of the text inside the indicator when cleared. | ||
153 | |||
154 | *--text-caps-lock-color* <rrggbb[aa]> | ||
155 | Sets the color of the text when Caps Lock is active. | ||
156 | |||
157 | *--text-ver-color* <rrggbb[aa]> | ||
158 | Sets the color of the text inside the indicator when verifying. | ||
159 | |||
160 | *--text-wrong-color* <rrggbb[aa]> | ||
161 | Sets the color of the text inside the indicator when invalid. | ||
162 | |||
163 | # AUTHORS | ||
164 | |||
165 | Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open | ||
166 | source contributors. For more information about sway development, see | ||
167 | https://github.com/swaywm/sway. | ||