diff options
author | Kevin Hamacher <kevin.hamacher@rub.de> | 2016-03-25 14:06:27 +0100 |
---|---|---|
committer | Kevin Hamacher <kevin.hamacher@rub.de> | 2016-03-25 15:24:41 +0100 |
commit | fb6dcce2adb16c302f637978d8c0b80df123802a (patch) | |
tree | 552fb9c745d8c5a851c774ef48e3fa685a13bef6 /swaylock | |
parent | Merge pull request #538 from nuew/display-images (diff) | |
download | sway-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.c | 290 |
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 | ||
18 | list_t *surfaces; | ||
19 | struct registry *registry; | 21 | struct registry *registry; |
20 | 22 | struct render_data render_data; | |
21 | enum scaling_mode { | 23 | |
22 | SCALING_MODE_STRETCH, | 24 | void 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 | ||
29 | void sway_terminate(int exit_code) { | 33 | void 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 | ||
102 | void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) { | 106 | void 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 | ||
230 | int main(int argc, char **argv) { | 260 | int 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 | |||
493 | void 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 | } | ||