summaryrefslogtreecommitdiffstats
path: root/swaylock
diff options
context:
space:
mode:
authorLibravatar Kevin Hamacher <kevin.hamacher@rub.de>2016-03-25 14:06:27 +0100
committerLibravatar Kevin Hamacher <kevin.hamacher@rub.de>2016-03-25 15:24:41 +0100
commitfb6dcce2adb16c302f637978d8c0b80df123802a (patch)
tree552fb9c745d8c5a851c774ef48e3fa685a13bef6 /swaylock
parentMerge pull request #538 from nuew/display-images (diff)
downloadsway-fb6dcce2adb16c302f637978d8c0b80df123802a.tar.gz
sway-fb6dcce2adb16c302f637978d8c0b80df123802a.tar.zst
sway-fb6dcce2adb16c302f637978d8c0b80df123802a.zip
Add graphical feedback to swaylock (#526)
Diffstat (limited to 'swaylock')
-rw-r--r--swaylock/main.c290
1 files changed, 216 insertions, 74 deletions
diff --git a/swaylock/main.c b/swaylock/main.c
index 8f0eddb3..848188e2 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -9,30 +9,34 @@
9#include <sys/types.h> 9#include <sys/types.h>
10#include <pwd.h> 10#include <pwd.h>
11#include <getopt.h> 11#include <getopt.h>
12#include <signal.h>
13
12#include "client/window.h" 14#include "client/window.h"
13#include "client/registry.h" 15#include "client/registry.h"
14#include "client/cairo.h" 16#include "client/cairo.h"
15#include "ipc-client.h" 17#include "ipc-client.h"
16#include "log.h" 18#include "log.h"
19#include "lock/lock.h"
17 20
18list_t *surfaces;
19struct registry *registry; 21struct registry *registry;
20 22struct render_data render_data;
21enum scaling_mode { 23
22 SCALING_MODE_STRETCH, 24void sigalarm_handler(int sig) {
23 SCALING_MODE_FILL, 25 signal(SIGALRM, SIG_IGN);
24 SCALING_MODE_FIT, 26 // Hide typing indicator
25 SCALING_MODE_CENTER, 27 render_data.auth_state = AUTH_STATE_IDLE;
26 SCALING_MODE_TILE, 28 render(&render_data);
27}; 29 wl_display_flush(registry->display);
30 signal(SIGALRM, sigalarm_handler);
31}
28 32
29void sway_terminate(int exit_code) { 33void sway_terminate(int exit_code) {
30 int i; 34 int i;
31 for (i = 0; i < surfaces->length; ++i) { 35 for (i = 0; i < render_data.surfaces->length; ++i) {
32 struct window *window = surfaces->items[i]; 36 struct window *window = render_data.surfaces->items[i];
33 window_teardown(window); 37 window_teardown(window);
34 } 38 }
35 list_free(surfaces); 39 list_free(render_data.surfaces);
36 if (registry) { 40 if (registry) {
37 registry_teardown(registry); 41 registry_teardown(registry);
38 } 42 }
@@ -100,12 +104,29 @@ bool verify_password() {
100} 104}
101 105
102void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) { 106void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) {
107 int i;
108 int redraw_screen = 0;
103 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { 109 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
104 switch (sym) { 110 switch (sym) {
105 case XKB_KEY_Return: 111 case XKB_KEY_Return:
112 render_data.auth_state = AUTH_STATE_VALIDATING;
113
114 render(&render_data);
115 // Make sure our render call will actually be displayed on the screen
116 wl_display_flush(registry->display);
117
118 // However, this is not how it should be done.
119 for (i = 0; i < registry->outputs->length; ++i) {
120 if (wl_display_dispatch(registry->display) == -1) {
121 exit(0);
122 }
123 }
106 if (verify_password()) { 124 if (verify_password()) {
107 exit(0); 125 exit(0);
108 } 126 }
127 render_data.auth_state = AUTH_STATE_INVALID;
128 redraw_screen = 1;
129
109 password_size = 1024; 130 password_size = 1024;
110 password = malloc(password_size); 131 password = malloc(password_size);
111 password[0] = '\0'; 132 password[0] = '\0';
@@ -115,11 +136,15 @@ void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t cod
115 int i = strlen(password); 136 int i = strlen(password);
116 if (i > 0) { 137 if (i > 0) {
117 password[i - 1] = '\0'; 138 password[i - 1] = '\0';
139 render_data.auth_state = AUTH_STATE_BACKSPACE;
140 redraw_screen = 1;
118 } 141 }
119 break; 142 break;
120 } 143 }
121 default: 144 default:
122 { 145 {
146 render_data.auth_state = AUTH_STATE_INPUT;
147 redraw_screen = 1;
123 int i = strlen(password); 148 int i = strlen(password);
124 if (i + 1 == password_size) { 149 if (i + 1 == password_size) {
125 password_size += 1024; 150 password_size += 1024;
@@ -130,6 +155,11 @@ void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t cod
130 break; 155 break;
131 } 156 }
132 } 157 }
158 if (redraw_screen) {
159 render(&render_data);
160 // Hide the indicator after a couple of seconds
161 alarm(5);
162 }
133 } 163 }
134} 164}
135 165
@@ -228,12 +258,18 @@ cairo_surface_t *load_image(char *image_path) {
228} 258}
229 259
230int main(int argc, char **argv) { 260int main(int argc, char **argv) {
231 char *scaling_mode_str = "fit", *socket_path = NULL; 261 const char *scaling_mode_str = "fit", *socket_path = NULL;
232 int i, num_images = 0, color_set = 0; 262 int i;
233 uint32_t color = 0xFFFFFFFF;
234 void *images; 263 void *images;
235 264
265 render_data.num_images = 0;
266 render_data.color_set = 0;
267 render_data.color = 0xFFFFFFFF;
268 render_data.auth_state = AUTH_STATE_IDLE;
269
236 init_log(L_INFO); 270 init_log(L_INFO);
271 // Install SIGALARM handler (for hiding the typing indicator)
272 signal(SIGALRM, sigalarm_handler);
237 273
238 static struct option long_options[] = { 274 static struct option long_options[] = {
239 {"help", no_argument, NULL, 'h'}, 275 {"help", no_argument, NULL, 'h'},
@@ -274,38 +310,39 @@ int main(int argc, char **argv) {
274 sway_log(L_ERROR, "color must be specified in 3 or 4 byte format, e.g. ff0000 or ff0000ff"); 310 sway_log(L_ERROR, "color must be specified in 3 or 4 byte format, e.g. ff0000 or ff0000ff");
275 exit(EXIT_FAILURE); 311 exit(EXIT_FAILURE);
276 } 312 }
277 color = strtol(optarg, NULL, 16); 313 render_data.color = strtol(optarg, NULL, 16);
278 color_set = 1; 314 render_data.color_set = 1;
279 315
280 if (colorlen == 6) { 316 if (colorlen == 6) {
281 color <<= 8; 317 render_data.color <<= 8;
282 color |= 0xFF; 318 render_data.color |= 0xFF;
283 } 319 }
284 sway_log(L_DEBUG, "color: 0x%x", color);
285 break; 320 break;
286 } 321 }
287 case 'i': 322 case 'i':
288 { 323 {
289 char *image_path = strchr(optarg, ':'); 324 char *image_path = strchr(optarg, ':');
290 if (image_path == NULL) { 325 if (image_path == NULL) {
291 if (num_images == 0) { 326 if (render_data.num_images == 0) {
292 images = load_image(optarg); 327 // Provided image without output
293 num_images = -1; 328 render_data.image = load_image(optarg);
329 render_data.num_images = -1;
294 } else { 330 } else {
295 sway_log(L_ERROR, "output must be defined for all --images or no --images"); 331 sway_log(L_ERROR, "output must be defined for all --images or no --images");
296 exit(EXIT_FAILURE); 332 exit(EXIT_FAILURE);
297 } 333 }
298 } else { 334 } else {
299 if (num_images == 0) { 335 // Provided image for all outputs
336 if (render_data.num_images == 0) {
300 images = calloc(registry->outputs->length, sizeof(char*) * 2); 337 images = calloc(registry->outputs->length, sizeof(char*) * 2);
301 } else if (num_images == -1) { 338 } else if (render_data.num_images == -1) {
302 sway_log(L_ERROR, "output must be defined for all --images or no --images"); 339 sway_log(L_ERROR, "output must be defined for all --images or no --images");
303 exit(EXIT_FAILURE); 340 exit(EXIT_FAILURE);
304 } 341 }
305 342
306 image_path[0] = '\0'; 343 image_path[0] = '\0';
307 ((char**) images)[num_images * 2] = optarg; 344 ((char**) images)[render_data.num_images * 2] = optarg;
308 ((char**) images)[num_images++ * 2 + 1] = ++image_path; 345 ((char**) images)[render_data.num_images++ * 2 + 1] = ++image_path;
309 } 346 }
310 break; 347 break;
311 } 348 }
@@ -332,17 +369,17 @@ int main(int argc, char **argv) {
332 } 369 }
333 } 370 }
334 371
335 enum scaling_mode scaling_mode = SCALING_MODE_STRETCH; 372 render_data.scaling_mode = SCALING_MODE_STRETCH;
336 if (strcmp(scaling_mode_str, "stretch") == 0) { 373 if (strcmp(scaling_mode_str, "stretch") == 0) {
337 scaling_mode = SCALING_MODE_STRETCH; 374 render_data.scaling_mode = SCALING_MODE_STRETCH;
338 } else if (strcmp(scaling_mode_str, "fill") == 0) { 375 } else if (strcmp(scaling_mode_str, "fill") == 0) {
339 scaling_mode = SCALING_MODE_FILL; 376 render_data.scaling_mode = SCALING_MODE_FILL;
340 } else if (strcmp(scaling_mode_str, "fit") == 0) { 377 } else if (strcmp(scaling_mode_str, "fit") == 0) {
341 scaling_mode = SCALING_MODE_FIT; 378 render_data.scaling_mode = SCALING_MODE_FIT;
342 } else if (strcmp(scaling_mode_str, "center") == 0) { 379 } else if (strcmp(scaling_mode_str, "center") == 0) {
343 scaling_mode = SCALING_MODE_CENTER; 380 render_data.scaling_mode = SCALING_MODE_CENTER;
344 } else if (strcmp(scaling_mode_str, "tile") == 0) { 381 } else if (strcmp(scaling_mode_str, "tile") == 0) {
345 scaling_mode = SCALING_MODE_TILE; 382 render_data.scaling_mode = SCALING_MODE_TILE;
346 } else { 383 } else {
347 sway_abort("Unsupported scaling mode: %s", scaling_mode_str); 384 sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
348 } 385 }
@@ -350,7 +387,7 @@ int main(int argc, char **argv) {
350 password_size = 1024; 387 password_size = 1024;
351 password = malloc(password_size); 388 password = malloc(password_size);
352 password[0] = '\0'; 389 password[0] = '\0';
353 surfaces = create_list(); 390 render_data.surfaces = create_list();
354 if (!socket_path) { 391 if (!socket_path) {
355 socket_path = get_socketpath(); 392 socket_path = get_socketpath();
356 if (!socket_path) { 393 if (!socket_path) {
@@ -378,14 +415,15 @@ int main(int argc, char **argv) {
378 if (!window) { 415 if (!window) {
379 sway_abort("Failed to create surfaces."); 416 sway_abort("Failed to create surfaces.");
380 } 417 }
381 list_add(surfaces, window); 418 list_add(render_data.surfaces, window);
382 } 419 }
383 420
384 registry->input->notify = notify_key; 421 registry->input->notify = notify_key;
385 422
386 if (num_images >= 1) { 423 // Different background for the output
424 if (render_data.num_images >= 1) {
387 char **displays_paths = images; 425 char **displays_paths = images;
388 images = calloc(registry->outputs->length, sizeof(cairo_surface_t*)); 426 render_data.images = calloc(registry->outputs->length, sizeof(cairo_surface_t*));
389 427
390 int socketfd = ipc_open_socket(socket_path); 428 int socketfd = ipc_open_socket(socket_path);
391 uint32_t len = 0; 429 uint32_t len = 0;
@@ -405,7 +443,7 @@ int main(int argc, char **argv) {
405 sway_abort("output doesn't have a name field"); 443 sway_abort("output doesn't have a name field");
406 } 444 }
407 if (!strcmp(displays_paths[i * 2], json_object_get_string(dsp_name))) { 445 if (!strcmp(displays_paths[i * 2], json_object_get_string(dsp_name))) {
408 ((cairo_surface_t**) images)[j] = load_image(displays_paths[i * 2 + 1]); 446 render_data.images[j] = load_image(displays_paths[i * 2 + 1]);
409 break; 447 break;
410 } 448 }
411 } 449 }
@@ -417,56 +455,160 @@ int main(int argc, char **argv) {
417 free(displays_paths); 455 free(displays_paths);
418 } 456 }
419 457
420 for (i = 0; i < surfaces->length; ++i) { 458 render(&render_data);
421 struct window *window = surfaces->items[i];
422 if (!window_prerender(window) || !window->cairo) {
423 continue;
424 }
425
426 if (num_images == 0 || color_set) {
427 render_color(window, color);
428 }
429
430 if (num_images == -1) {
431 render_image(window, images, scaling_mode);
432 } else if (num_images >= 1) {
433 if (((cairo_surface_t**) images)[i] != NULL) {
434 render_image(window, ((cairo_surface_t**) images)[i], scaling_mode);
435 }
436 }
437
438 window_render(window);
439 }
440
441 if (num_images == -1) {
442 cairo_surface_destroy((cairo_surface_t*) images);
443 } else if (num_images >= 1) {
444 for (i = 0; i < registry->outputs->length; ++i) {
445 if (((cairo_surface_t**) images)[i] != NULL) {
446 cairo_surface_destroy(((cairo_surface_t**) images)[i]);
447 }
448 }
449 free(images);
450 }
451
452 bool locked = false; 459 bool locked = false;
453 while (wl_display_dispatch(registry->display) != -1) { 460 while (wl_display_dispatch(registry->display) != -1) {
454 if (!locked) { 461 if (!locked) {
455 for (i = 0; i < registry->outputs->length; ++i) { 462 for (i = 0; i < registry->outputs->length; ++i) {
456 struct output_state *output = registry->outputs->items[i]; 463 struct output_state *output = registry->outputs->items[i];
457 struct window *window = surfaces->items[i]; 464 struct window *window = render_data.surfaces->items[i];
458 lock_set_lock_surface(registry->swaylock, output->output, window->surface); 465 lock_set_lock_surface(registry->swaylock, output->output, window->surface);
459 } 466 }
460 locked = true; 467 locked = true;
461 } 468 }
462 } 469 }
463 470
464 for (i = 0; i < surfaces->length; ++i) { 471 // Free surfaces
465 struct window *window = surfaces->items[i]; 472 if (render_data.num_images == -1) {
473 cairo_surface_destroy(render_data.image);
474 } else if (render_data.num_images >= 1) {
475 for (i = 0; i < registry->outputs->length; ++i) {
476 if (render_data.images[i] != NULL) {
477 cairo_surface_destroy(render_data.images[i]);
478 }
479 }
480 free(render_data.images);
481 }
482
483 for (i = 0; i < render_data.surfaces->length; ++i) {
484 struct window *window = render_data.surfaces->items[i];
466 window_teardown(window); 485 window_teardown(window);
467 } 486 }
468 list_free(surfaces); 487 list_free(render_data.surfaces);
469 registry_teardown(registry); 488 registry_teardown(registry);
470 489
471 return 0; 490 return 0;
472} 491}
492
493void render(struct render_data *render_data) {
494 int i;
495 for (i = 0; i < render_data->surfaces->length; ++i) {
496 sway_log(L_DEBUG, "Render surface %d of %d", i, render_data->surfaces->length);
497 struct window *window = render_data->surfaces->items[i];
498 if (!window_prerender(window) || !window->cairo) {
499 continue;
500 }
501
502 // Reset the transformation matrix
503 cairo_identity_matrix(window->cairo);
504
505 if (render_data->num_images == 0 || render_data->color_set) {
506 render_color(window, render_data->color);
507 }
508
509 if (render_data->num_images == -1) {
510 // One background for all
511 render_image(window, render_data->image, render_data->scaling_mode);
512 } else if (render_data->num_images >= 1) {
513 // Different backgrounds
514 if (render_data->images[i] != NULL) {
515 render_image(window, render_data->images[i], render_data->scaling_mode);
516 }
517 }
518
519 // Reset the transformation matrix again
520 cairo_identity_matrix(window->cairo);
521
522 // Draw specific values (copied from i3)
523 const int ARC_RADIUS = 50;
524 const int ARC_THICKNESS = 10;
525 const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;
526 const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
527
528 // Add visual indicator
529 if (render_data->auth_state != AUTH_STATE_IDLE) {
530 // Draw circle
531 cairo_set_line_width(window->cairo, ARC_THICKNESS);
532 cairo_arc(window->cairo, window->width/2, window->height/2, ARC_RADIUS, 0, 2 * M_PI);
533 switch (render_data->auth_state) {
534 case AUTH_STATE_INPUT:
535 case AUTH_STATE_BACKSPACE: {
536 cairo_set_source_rgba(window->cairo, 0, 0, 0, 0.75);
537 cairo_fill_preserve(window->cairo);
538 cairo_set_source_rgb(window->cairo, 51.0 / 255, 125.0 / 255, 0);
539 cairo_stroke(window->cairo);
540 } break;
541 case AUTH_STATE_VALIDATING: {
542 cairo_set_source_rgba(window->cairo, 0, 114.0 / 255, 255.0 / 255, 0.75);
543 cairo_fill_preserve(window->cairo);
544 cairo_set_source_rgb(window->cairo, 51.0 / 255, 0, 250.0 / 255);
545 cairo_stroke(window->cairo);
546 } break;
547 case AUTH_STATE_INVALID: {
548 cairo_set_source_rgba(window->cairo, 250.0 / 255, 0, 0, 0.75);
549 cairo_fill_preserve(window->cairo);
550 cairo_set_source_rgb(window->cairo, 125.0 / 255, 51.0 / 255, 0);
551 cairo_stroke(window->cairo);
552 } break;
553 default: break;
554 }
555
556 // Draw a message
557 char *text = NULL;
558 cairo_set_source_rgb(window->cairo, 0, 0, 0);
559 cairo_set_font_size(window->cairo, ARC_RADIUS/3.0f);
560 switch (render_data->auth_state) {
561 case AUTH_STATE_VALIDATING:
562 text = "verifying";
563 break;
564 case AUTH_STATE_INVALID:
565 text = "wrong";
566 break;
567 default: break;
568 }
569
570 if (text) {
571 cairo_text_extents_t extents;
572 double x, y;
573
574 cairo_text_extents(window->cairo, text, &extents);
575 x = window->width/2 - ((extents.width/2) + extents.x_bearing);
576 y = window->height/2 - ((extents.height/2) + extents.y_bearing);
577
578 cairo_move_to(window->cairo, x, y);
579 cairo_show_text(window->cairo, text);
580 cairo_close_path(window->cairo);
581 cairo_new_sub_path(window->cairo);
582 }
583
584 // Typing indicator: Highlight random part on keypress
585 if (render_data->auth_state == AUTH_STATE_INPUT || render_data->auth_state == AUTH_STATE_BACKSPACE) {
586 double highlight_start = (rand() % (int)(2 * M_PI * 100)) / 100.0;
587 cairo_arc(window->cairo, window->width/2, window->height/2, ARC_RADIUS, highlight_start, highlight_start + TYPE_INDICATOR_RANGE);
588 if (render_data->auth_state == AUTH_STATE_INPUT) {
589 cairo_set_source_rgb(window->cairo, 51.0 / 255, 219.0 / 255, 0);
590 } else {
591 cairo_set_source_rgb(window->cairo, 219.0 / 255, 51.0 / 255, 0);
592 }
593 cairo_stroke(window->cairo);
594
595 // Draw borders
596 cairo_set_source_rgb(window->cairo, 0, 0, 0);
597 cairo_arc(window->cairo, window->width/2, window->height/2, ARC_RADIUS, highlight_start, highlight_start + TYPE_INDICATOR_BORDER_THICKNESS);
598 cairo_stroke(window->cairo);
599
600 cairo_arc(window->cairo, window->width/2, window->height/2, ARC_RADIUS, highlight_start + TYPE_INDICATOR_RANGE, (highlight_start + TYPE_INDICATOR_RANGE) + TYPE_INDICATOR_BORDER_THICKNESS);
601 cairo_stroke(window->cairo);
602 }
603
604 // Draw inner + outer border of the circle
605 cairo_set_source_rgb(window->cairo, 0, 0, 0);
606 cairo_set_line_width(window->cairo, 2.0);
607 cairo_arc(window->cairo, window->width/2, window->height/2, ARC_RADIUS - ARC_THICKNESS/2, 0, 2*M_PI);
608 cairo_stroke(window->cairo);
609 cairo_arc(window->cairo, window->width/2, window->height/2, ARC_RADIUS + ARC_THICKNESS/2, 0, 2*M_PI);
610 cairo_stroke(window->cairo);
611 }
612 window_render(window);
613 }
614}