i3lock-color

The world's most popular non-default computer lockscreen.
Index Commits Files Refs README LICENSE
unlock_indicator.c (51360B)
   1 /*
   2  * vim:ts=4:sw=4:expandtab
   3  *
   4  * © 2010 Michael Stapelberg
   5  * © 2015 Cassandra Fox
   6  * © 2021 Raymond Li
   7  *
   8  * See LICENSE for licensing information
   9  *
  10  */
  11 #include <stdbool.h>
  12 #include <stdlib.h>
  13 #include <stdio.h>
  14 #include <string.h>
  15 #include <math.h>
  16 #include <xcb/xcb.h>
  17 #include <xcb/randr.h>
  18 #include <ev.h>
  19 #include <cairo.h>
  20 #include <cairo/cairo-xcb.h>
  21 
  22 #include "i3lock.h"
  23 #include "xcb.h"
  24 #include "unlock_indicator.h"
  25 #include "randr.h"
  26 #include "dpi.h"
  27 #include "tinyexpr.h"
  28 #include "fonts.h"
  29 
  30 /* clock stuff */
  31 #include <time.h>
  32 
  33 extern double circle_radius;
  34 extern double ring_width;
  35 
  36 #define BUTTON_RADIUS (circle_radius)
  37 #define RING_WIDTH (ring_width)
  38 #define BUTTON_SPACE (BUTTON_RADIUS + (RING_WIDTH / 2))
  39 #define BUTTON_DIAMETER (2 * BUTTON_SPACE)
  40 
  41 /*******************************************************************************
  42  * Variables defined in i3lock.c.
  43  ******************************************************************************/
  44 
  45 extern bool debug_mode;
  46 
  47 /* The current position in the input buffer. Useful to determine if any
  48  * characters of the password have already been entered or not. */
  49 extern int input_position;
  50 
  51 /* The lock window. */
  52 extern xcb_window_t win;
  53 
  54 /* The current resolution of the X11 root window. */
  55 extern uint32_t last_resolution[2];
  56 
  57 /* Whether the unlock indicator is enabled (defaults to true). */
  58 extern bool unlock_indicator;
  59 
  60 /* List of pressed modifiers, or NULL if none are pressed. */
  61 extern char *modifier_string;
  62 
  63 /* A Cairo surface containing the specified image (-i), if any. */
  64 extern cairo_surface_t *img;
  65 extern char *image_path;
  66 extern char *slideshow_path;
  67 extern char *img_slideshow[256];
  68 extern cairo_surface_t *blur_bg_img;
  69 extern int slideshow_image_count;
  70 extern int slideshow_interval;
  71 extern bool slideshow_random_selection;
  72 int slideshow_image_now = 0;
  73 
  74 unsigned long lastCheck;
  75 
  76 /* How the background image should be displayed */
  77 extern background_type_t bg_type;
  78 /* The background color to use (in hex). */
  79 extern char color[9];
  80 /* indicator color options */
  81 extern char insidevercolor[9];
  82 extern char insidewrongcolor[9];
  83 extern char insidecolor[9];
  84 extern char ringvercolor[9];
  85 extern char ringwrongcolor[9];
  86 extern char ringcolor[9];
  87 extern char linecolor[9];
  88 extern char verifcolor[9];
  89 extern char wrongcolor[9];
  90 extern char layoutcolor[9];
  91 extern char timecolor[9];
  92 extern char datecolor[9];
  93 extern char modifcolor[9];
  94 extern char keyhlcolor[9];
  95 extern char bshlcolor[9];
  96 extern char separatorcolor[9];
  97 extern char greetercolor[9];
  98 extern int internal_line_source;
  99 
 100 extern char verifoutlinecolor[9];
 101 extern char wrongoutlinecolor[9];
 102 extern char layoutoutlinecolor[9];
 103 extern char timeoutlinecolor[9];
 104 extern char dateoutlinecolor[9];
 105 extern char greeteroutlinecolor[9];
 106 extern char modifoutlinecolor[9];
 107 
 108 extern int screen_number;
 109 extern float refresh_rate;
 110 
 111 extern bool show_clock;
 112 extern bool always_show_clock;
 113 extern bool show_indicator;
 114 extern int verif_align;
 115 extern int wrong_align;
 116 extern int time_align;
 117 extern int date_align;
 118 extern int layout_align;
 119 extern int modif_align;
 120 extern int greeter_align;
 121 extern char time_format[32];
 122 extern char date_format[32];
 123 extern char *fonts[6];
 124 extern char ind_x_expr[32];
 125 extern char ind_y_expr[32];
 126 extern char time_x_expr[32];
 127 extern char time_y_expr[32];
 128 extern char date_x_expr[32];
 129 extern char date_y_expr[32];
 130 extern char layout_x_expr[32];
 131 extern char layout_y_expr[32];
 132 extern char status_x_expr[32];
 133 extern char status_y_expr[32];
 134 extern char verif_x_expr[32];
 135 extern char verif_y_expr[32];
 136 extern char wrong_x_expr[32];
 137 extern char wrong_y_expr[32];
 138 extern char modif_x_expr[32];
 139 extern char modif_y_expr[32];
 140 extern char greeter_x_expr[32];
 141 extern char greeter_y_expr[32];
 142 
 143 extern double time_size;
 144 extern double date_size;
 145 extern double verif_size;
 146 extern double wrong_size;
 147 extern double modifier_size;
 148 extern double layout_size;
 149 extern double greeter_size;
 150 
 151 extern double timeoutlinewidth;
 152 extern double dateoutlinewidth;
 153 extern double verifoutlinewidth;
 154 extern double wrongoutlinewidth;
 155 extern double modifieroutlinewidth;
 156 extern double layoutoutlinewidth;
 157 extern double greeteroutlinewidth;
 158 
 159 extern char *verif_text;
 160 extern char *wrong_text;
 161 extern char *noinput_text;
 162 extern char *lock_text;
 163 extern char *lock_failed_text;
 164 extern char *layout_text;
 165 extern char *greeter_text;
 166 
 167 bool load_slideshow_images(const char *path);
 168 cairo_surface_t* load_image(char* image_path);
 169 
 170 /* Whether the failed attempts should be displayed. */
 171 extern bool show_failed_attempts;
 172 /* Number of failed unlock attempts. */
 173 extern int failed_attempts;
 174 
 175 /*******************************************************************************
 176  * Variables defined in xcb.c.
 177  ******************************************************************************/
 178 
 179 /* The root screen, to determine the DPI. */
 180 extern xcb_screen_t *screen;
 181 
 182 /*******************************************************************************
 183  * Local variables.
 184  ******************************************************************************/
 185 
 186 /* time stuff */
 187 static struct ev_periodic *time_redraw_tick;
 188 
 189 /* Cache the screen’s visual, necessary for creating a Cairo context. */
 190 static xcb_visualtype_t *vistype;
 191 
 192 int current_slideshow_index = 0;
 193 
 194 /* Maintain the current unlock/PAM state to draw the appropriate unlock
 195  * indicator. */
 196 unlock_state_t unlock_state;
 197 auth_state_t auth_state;
 198 
 199 // color arrays
 200 rgba_t insidever16;
 201 rgba_t insidewrong16;
 202 rgba_t inside16;
 203 rgba_t ringver16;
 204 rgba_t ringwrong16;
 205 rgba_t ring16;
 206 rgba_t line16;
 207 rgba_t verif16;
 208 rgba_t wrong16;
 209 rgba_t layout16;
 210 rgba_t time16;
 211 rgba_t date16;
 212 rgba_t modif16;
 213 rgba_t keyhl16;
 214 rgba_t bshl16;
 215 rgba_t sep16;
 216 rgba_t bar16;
 217 rgba_t greeter16;
 218 rgba_t background;
 219 
 220 rgba_t verifoutline16;
 221 rgba_t wrongoutline16;
 222 rgba_t layoutoutline16;
 223 rgba_t timeoutline16;
 224 rgba_t dateoutline16;
 225 rgba_t greeteroutline16;
 226 rgba_t modifoutline16;
 227 
 228 // experimental bar stuff
 229 
 230 #define BAR_VERT 0
 231 #define BAR_FLAT 1
 232 // experimental bar stuff
 233 extern bool bar_enabled;
 234 extern double *bar_heights;
 235 extern double bar_step;
 236 extern double bar_base_height;
 237 extern double bar_periodic_step;
 238 extern double max_bar_height;
 239 extern double bar_position;
 240 extern int bar_count;
 241 extern int bar_orientation;
 242 
 243 extern char bar_base_color[9];
 244 extern char bar_x_expr[32];
 245 extern char bar_y_expr[32];
 246 extern char bar_width_expr[32];
 247 extern bool bar_bidirectional;
 248 extern bool bar_reversed;
 249 
 250 static cairo_font_face_t *font_faces[6] = {
 251     NULL,
 252     NULL,
 253     NULL,
 254     NULL,
 255     NULL,
 256     NULL,
 257 };
 258 
 259 static control_char_config_t control_characters[] = {
 260     {'\n', CC_POS_RESET, 0, CC_POS_CHANGE, 1},
 261     {'\b', CC_POS_CHANGE, -1, CC_POS_KEEP, 0},
 262     {'\r', CC_POS_RESET, 0, CC_POS_KEEP, 0},
 263     {'\t', CC_POS_TAB, 4, CC_POS_KEEP, 0},
 264 };
 265 size_t control_char_count = sizeof control_characters / sizeof(control_char_config_t);
 266 
 267 static cairo_font_face_t *get_font_face(int which) {
 268     if (font_faces[which]) {
 269         return font_faces[which];
 270     }
 271     const unsigned char *face_name = (const unsigned char *)fonts[which];
 272     FcResult result;
 273     /*
 274      * Loads the default config.
 275      * On successive calls, does no work and just returns true.
 276      */
 277     if (!FcInit()) {
 278         DEBUG("Fontconfig init failed. No text will be shown.\n");
 279         return NULL;
 280     }
 281 
 282     /*
 283      * converts a font face name to a pattern for that face name
 284      */
 285     FcPattern *pattern = FcNameParse(face_name);
 286     if (!pattern) {
 287         DEBUG("no sans-serif font available\n");
 288         return NULL;
 289     }
 290 
 291     /*
 292      * Gets the default font for our pattern. (Gets the default sans-serif font face)
 293      * Without these two calls, the FcFontMatch call will fail due to FcConfigGetCurrent()
 294      * not giving it a valid/useful config.
 295      */
 296     FcDefaultSubstitute(pattern);
 297     if (!FcConfigSubstitute(FcConfigGetCurrent(), pattern, FcMatchPattern)) {
 298         DEBUG("config sub failed?\n");
 299         return NULL;
 300     }
 301 
 302     /*
 303      * Looks up the font pattern and does some internal RenderPrepare work,
 304      * then returns the resulting pattern that's ready for rendering.
 305      */
 306     FcPattern *pattern_ready = FcFontMatch(FcConfigGetCurrent(), pattern, &result);
 307     FcPatternDestroy(pattern);
 308     pattern = NULL;
 309     if (!pattern_ready) {
 310         DEBUG("no sans-serif font available\n");
 311         return NULL;
 312     }
 313 
 314     /*
 315      * Passes the given pattern into cairo, which loads it into a cairo freetype font face.
 316      * Increment its reference count and cache it.
 317      */
 318     cairo_font_face_t *face = cairo_ft_font_face_create_for_pattern(pattern_ready);
 319     FcPatternDestroy(pattern_ready);
 320     font_faces[which] = cairo_font_face_reference(face);
 321     FcFini();
 322     return face;
 323 }
 324 
 325 /*
 326  * Splits the given text by "control chars",
 327  * And then draws the given text onto the cairo context.
 328  */
 329 static void draw_text_with_cc(cairo_t *ctx, text_t text, double start_x) {
 330     // get scaled_font
 331     cairo_scaled_font_t *sft;
 332     cairo_matrix_t fm, ctm;
 333     cairo_matrix_init_scale(&fm, text.size, text.size);
 334     cairo_get_matrix(ctx, &ctm);
 335     cairo_font_options_t *opts;
 336     opts = cairo_font_options_create();
 337     sft = cairo_scaled_font_create(text.font, &fm, &ctm, opts);
 338     cairo_font_options_destroy(opts);
 339     /* use `a` to represent common character width, using in `\t`  */
 340     cairo_text_extents_t te;
 341     cairo_text_extents(ctx, "a", &te);
 342 
 343     // convert text to glyphs.
 344     cairo_status_t status;
 345     cairo_glyph_t* glyphs;
 346     int nglyphs = 0,
 347         len = 0,
 348         start = 0,
 349         lineno = 0,
 350         x = start_x,
 351         y = text.y;
 352     size_t cur_cc;
 353 
 354     while (text.str[start + len] != '\0') {
 355         char is_cc = 0;
 356         do {
 357             for (cur_cc = 0; cur_cc < control_char_count; cur_cc++) {
 358                 if (text.str[start+len] == control_characters[cur_cc].character) {
 359                     is_cc = 1;
 360                     break;
 361                 }
 362             }
 363         } while (text.str[start+(len++)] != '\0' && !is_cc);
 364         if (len > is_cc) {
 365             status = cairo_scaled_font_text_to_glyphs(
 366                 sft, x, y, text.str + start, is_cc ? len - 1: len,
 367                 &glyphs, &nglyphs,
 368                 NULL, NULL, NULL
 369             );
 370             if (status == CAIRO_STATUS_SUCCESS) {
 371                 cairo_glyph_path(ctx, glyphs, nglyphs);
 372             } else {
 373                 DEBUG("draw %c failed\n", text.str[start]);
 374             }
 375         }
 376         if (is_cc && (cur_cc < control_char_count)) {
 377             if (control_characters[cur_cc].x_behavior == CC_POS_CHANGE) {
 378                 char x_offset = control_characters[cur_cc].x_behavior_arg;
 379                 if (x_offset < 0 && x_offset > -nglyphs) {
 380                     x = glyphs[nglyphs+x_offset].x;
 381                 } else if (x_offset > 0) {
 382                     if (nglyphs >= 1) { // the case is some leading control chars.(although there is none now)
 383                         x = glyphs[nglyphs - 1].x + x_offset * te.x_advance;
 384                     } else { // deal the leading control chars.
 385                         x += x_offset * te.x_advance;
 386                     }
 387                 }
 388             } else if (control_characters[cur_cc].x_behavior == CC_POS_RESET) {
 389                 x = start_x;
 390             } else if (control_characters[cur_cc].x_behavior == CC_POS_TAB) {
 391                 if (nglyphs > 0) { // there may be leading tab, such as '\t\t' or '\n\t'
 392                     int advance = control_characters[cur_cc].x_behavior_arg - ((nglyphs - 1) % control_characters[cur_cc].x_behavior_arg);
 393                     x = glyphs[nglyphs - 1].x + advance * te.x_advance;
 394                 } else { // deal the leading tab.
 395                     x += control_characters[cur_cc].x_behavior_arg * te.x_advance;
 396                 }
 397             }
 398             if (control_characters[cur_cc].y_behavior == CC_POS_CHANGE) {
 399                 lineno += control_characters[cur_cc].y_behavior_arg;
 400             } // CC_POS_KEEP is default for y
 401         }
 402         y = text.y + text.size * lineno;
 403         if (len > is_cc) {
 404             cairo_glyph_free(glyphs);
 405         }
 406         nglyphs = 0;
 407         start += len;
 408         len = 0;
 409     }
 410     cairo_scaled_font_destroy(sft);
 411 }
 412 
 413 /*
 414  * Draws the given text onto the cairo context
 415  */
 416 static void draw_text(cairo_t *ctx, text_t text) {
 417     if (!text.show)
 418         return;
 419     cairo_text_extents_t extents;
 420     cairo_set_font_face(ctx, text.font);
 421     cairo_set_font_size(ctx, text.size);
 422 
 423     cairo_text_extents(ctx, text.str, &extents);
 424 
 425     double x;
 426 
 427     switch (text.align) {
 428         case 1:
 429             x = text.x;
 430             break;
 431         case 2:
 432             x = text.x - (extents.width + extents.x_bearing);
 433             break;
 434         case 0:
 435         default:
 436             x = text.x - extents.x_advance / 2;
 437             break;
 438     }
 439 
 440     cairo_set_source_rgba(ctx, text.color.red, text.color.green, text.color.blue, text.color.alpha);
 441 
 442     draw_text_with_cc(ctx, text, x);
 443     cairo_fill_preserve(ctx);
 444 
 445     cairo_set_source_rgba(ctx, text.outline_color.red, text.outline_color.green, text.outline_color.blue, text.outline_color.alpha);
 446     cairo_set_line_width(ctx, text.outline_width);
 447     cairo_stroke(ctx);
 448 }
 449 
 450 static void draw_single_bar(cairo_t *ctx, double pos, double offset, double width, double height) {
 451     if (bar_reversed) {
 452         offset -= height;
 453     } else if (bar_bidirectional) {
 454         offset -= height / 2;
 455     }
 456 
 457     if (bar_orientation == BAR_VERT)
 458         cairo_rectangle(ctx, offset, pos, height, width);
 459     else
 460         cairo_rectangle(ctx, pos, offset, width, height);
 461     cairo_fill(ctx);
 462 }
 463 
 464 static void draw_bar(cairo_t *ctx, double bar_x, double bar_y, double bar_width, double screen_x, double screen_y) {
 465 
 466     cairo_save(ctx);
 467 
 468     switch (auth_state) {
 469         case STATE_AUTH_VERIFY:
 470         case STATE_AUTH_LOCK:
 471             cairo_set_source_rgba(ctx, ringver16.red, ringver16.green, ringver16.blue, ringver16.alpha);
 472             break;
 473         case STATE_AUTH_WRONG:
 474         case STATE_I3LOCK_LOCK_FAILED:
 475             cairo_set_source_rgba(ctx, ringwrong16.red, ringwrong16.green, ringwrong16.blue, ringwrong16.alpha);
 476             break;
 477         default:
 478             cairo_set_source_rgba(ctx, bar16.red, bar16.green, bar16.blue, bar16.alpha);
 479             break;
 480     }
 481 
 482     if (bar_orientation == BAR_VERT)
 483         draw_single_bar(ctx, bar_y, bar_x, bar_width, bar_base_height);
 484     else
 485         draw_single_bar(ctx, bar_x, bar_y, bar_width, bar_base_height);
 486 
 487     if (unlock_state == STATE_BACKSPACE_ACTIVE)
 488         cairo_set_source_rgba(ctx, bshl16.red, bshl16.green, bshl16.blue, bshl16.alpha);
 489     else
 490         cairo_set_source_rgba(ctx, keyhl16.red, keyhl16.green, keyhl16.blue, keyhl16.alpha);
 491 
 492     cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
 493 
 494     double base_width = bar_width / bar_count;
 495     double bar_pos, bar_offset;
 496     if (bar_orientation == BAR_VERT) {
 497         bar_pos = bar_y;
 498         bar_offset = bar_x;
 499     } else {
 500         bar_pos = bar_x;
 501         bar_offset = bar_y;
 502     }
 503 
 504     for (int i = 0; i < bar_count; ++i) {
 505         double bar_height = bar_heights[i];
 506         if (bar_bidirectional) bar_height *= 2;
 507         if (bar_height > 0) {
 508             draw_single_bar(ctx, bar_pos + i * base_width, bar_offset, base_width, bar_height);
 509         }
 510     }
 511 
 512     for (int i = 0; i < bar_count; ++i) {
 513         if (bar_heights[i] > 0) {
 514             bar_heights[i] -= bar_periodic_step;
 515         }
 516     }
 517 
 518     cairo_restore(ctx);
 519 }
 520 
 521 static void draw_indic(cairo_t *ctx, double ind_x, double ind_y) {
 522     if (unlock_indicator &&
 523         (unlock_state >= STATE_KEY_PRESSED || auth_state > STATE_AUTH_IDLE || show_indicator)) {
 524         /* Draw a (centered) circle with transparent background. */
 525         cairo_set_line_width(ctx, RING_WIDTH);
 526         cairo_arc(ctx, ind_x, ind_y, BUTTON_RADIUS, 0, 2 * M_PI);
 527 
 528         /* Use the appropriate color for the different PAM states
 529          * (currently verifying, wrong password, or default) */
 530         switch (auth_state) {
 531             case STATE_AUTH_VERIFY:
 532             case STATE_AUTH_LOCK:
 533                 cairo_set_source_rgba(ctx, insidever16.red, insidever16.green, insidever16.blue, insidever16.alpha);
 534                 break;
 535             case STATE_AUTH_WRONG:
 536             case STATE_I3LOCK_LOCK_FAILED:
 537                 cairo_set_source_rgba(ctx, insidewrong16.red, insidewrong16.green, insidewrong16.blue, insidewrong16.alpha);
 538                 break;
 539             default:
 540                 if (unlock_state == STATE_NOTHING_TO_DELETE) {
 541                     cairo_set_source_rgba(ctx, insidewrong16.red, insidewrong16.green, insidewrong16.blue, insidewrong16.alpha);
 542                     break;
 543                 }
 544                 cairo_set_source_rgba(ctx, inside16.red, inside16.green, inside16.blue, inside16.alpha);
 545                 break;
 546         }
 547         cairo_fill_preserve(ctx);
 548 
 549         switch (auth_state) {
 550             case STATE_AUTH_VERIFY:
 551             case STATE_AUTH_LOCK:
 552                 cairo_set_source_rgba(ctx, ringver16.red, ringver16.green, ringver16.blue, ringver16.alpha);
 553                 if (internal_line_source == 1) {
 554                     line16.red = ringver16.red;
 555                     line16.green = ringver16.green;
 556                     line16.blue = ringver16.blue;
 557                     line16.alpha = ringver16.alpha;
 558                 }
 559                 break;
 560             case STATE_AUTH_WRONG:
 561             case STATE_I3LOCK_LOCK_FAILED:
 562                 cairo_set_source_rgba(ctx, ringwrong16.red, ringwrong16.green, ringwrong16.blue, ringwrong16.alpha);
 563                 if (internal_line_source == 1) {
 564                     line16.red = ringwrong16.red;
 565                     line16.green = ringwrong16.green;
 566                     line16.blue = ringwrong16.blue;
 567                     line16.alpha = ringwrong16.alpha;
 568                 }
 569                 break;
 570             case STATE_AUTH_IDLE:
 571                 if (unlock_state == STATE_NOTHING_TO_DELETE) {
 572                     cairo_set_source_rgba(ctx, ringwrong16.red, ringwrong16.green, ringwrong16.blue, ringwrong16.alpha);
 573                     if (internal_line_source == 1) {
 574                         line16.red = ringwrong16.red;
 575                         line16.green = ringwrong16.green;
 576                         line16.blue = ringwrong16.blue;
 577                         line16.alpha = ringwrong16.alpha;
 578                     }
 579                     break;
 580                 }
 581                 cairo_set_source_rgba(ctx, ring16.red, ring16.green, ring16.blue, ring16.alpha);
 582                 if (internal_line_source == 1) {
 583                     line16.red = ring16.red;
 584                     line16.green = ring16.green;
 585                     line16.blue = ring16.blue;
 586                     line16.alpha = ring16.alpha;
 587                 }
 588                 break;
 589         }
 590         cairo_stroke(ctx);
 591 
 592         /* Draw an inner separator line. */
 593         if (internal_line_source != 2) {  //pretty sure this only needs drawn if it's being drawn over the inside?
 594             cairo_set_source_rgba(ctx, line16.red, line16.green, line16.blue, line16.alpha);
 595             cairo_set_line_width(ctx, 2.0);
 596             cairo_arc(ctx, ind_x, ind_y, BUTTON_RADIUS - 5, 0, 2 * M_PI);
 597             cairo_stroke(ctx);
 598         }
 599         if (unlock_state == STATE_KEY_ACTIVE || unlock_state == STATE_BACKSPACE_ACTIVE) {
 600             cairo_set_line_width(ctx, RING_WIDTH);
 601             cairo_new_sub_path(ctx);
 602             double highlight_start = (rand() % (int)(2 * M_PI * 100)) / 100.0;
 603             cairo_arc(ctx, ind_x, ind_y, BUTTON_RADIUS,
 604                       highlight_start, highlight_start + (M_PI / 3.0));
 605             if (unlock_state == STATE_KEY_ACTIVE) {
 606                 /* For normal keys, we use a lighter green. */
 607                 cairo_set_source_rgba(ctx, keyhl16.red, keyhl16.green, keyhl16.blue, keyhl16.alpha);
 608             } else {
 609                 /* For backspace, we use red. */
 610                 cairo_set_source_rgba(ctx, bshl16.red, bshl16.green, bshl16.blue, bshl16.alpha);
 611             }
 612 
 613             cairo_stroke(ctx);
 614 
 615             /* Draw two little separators for the highlighted part of the
 616              * unlock indicator. */
 617             cairo_set_source_rgba(ctx, sep16.red, sep16.green, sep16.blue, sep16.alpha);
 618             cairo_arc(ctx, ind_x, ind_y, BUTTON_RADIUS,
 619                       highlight_start, highlight_start + (M_PI / 128.0));
 620             cairo_stroke(ctx);
 621             cairo_arc(ctx, ind_x, ind_y, BUTTON_RADIUS,
 622                       (highlight_start + (M_PI / 3.0)) - (M_PI / 128.0),
 623                       highlight_start + (M_PI / 3.0));
 624             cairo_stroke(ctx);
 625         }
 626     }
 627 }
 628 
 629 /*
 630  * Initialize all the color arrays once.
 631  * Called once after options are parsed.
 632  */
 633 
 634 /*
 635     colorstring: 8-character RGBA string ("ff0000ff", "00000000", "ffffffff", etc)
 636     colorstring16: array of 4 integers (r, g, b, a).
 637     MAKE_COLORGROUPS(colorstring, colorstring16) =>
 638 
 639     char colorstring_tmparr[4][3] = {{colorstring[0], colorstring[1], '\0'},
 640                                      {colorstring[2], colorstring[3], '\0'},
 641                                      {colorstring[4], colorstring[5], '\0'},
 642                                      {colorstring[6], colorstring[7], '\0'}};
 643     uint32_t colorstring16[4] = {(strtol(colorstring_tmparr[0], NULL, 16)),
 644                                  (strtol(colorstring_tmparr[1], NULL, 16)),
 645                                  (strtol(colorstring_tmparr[2], NULL, 16)),
 646                                  (strtol(colorstring_tmparr[3], NULL, 16))};
 647  */
 648 
 649 static void set_color(char *dest, const char *src, int offset) {
 650     dest[0] = src[offset];
 651     dest[1] = src[offset + 1];
 652     dest[2] = '\0';
 653 }
 654 
 655 static void colorgen(rgba_str_t *tmp, const char *src, rgba_t *dest) {
 656     set_color(tmp->red, src, 0);
 657     set_color(tmp->green, src, 2);
 658     set_color(tmp->blue, src, 4);
 659     set_color(tmp->alpha, src, 6);
 660 
 661     dest->red = strtol(tmp->red, NULL, 16) / 255.0;
 662     dest->green = strtol(tmp->green, NULL, 16) / 255.0;
 663     dest->blue = strtol(tmp->blue, NULL, 16) / 255.0;
 664     dest->alpha = strtol(tmp->alpha, NULL, 16) / 255.0;
 665 }
 666 
 667 void init_colors_once(void) {
 668 
 669     /* initialize for slideshow time interval */
 670     lastCheck = (unsigned long)time(NULL);
 671 
 672     rgba_str_t tmp;
 673 
 674     /* build indicator color arrays */
 675     colorgen(&tmp, insidevercolor, &insidever16);
 676     colorgen(&tmp, insidewrongcolor, &insidewrong16);
 677     colorgen(&tmp, insidecolor, &inside16);
 678     colorgen(&tmp, ringvercolor, &ringver16);
 679     colorgen(&tmp, ringwrongcolor, &ringwrong16);
 680     colorgen(&tmp, ringcolor, &ring16);
 681     colorgen(&tmp, linecolor, &line16);
 682     colorgen(&tmp, verifcolor, &verif16);
 683     colorgen(&tmp, wrongcolor, &wrong16);
 684     colorgen(&tmp, layoutcolor, &layout16);
 685     colorgen(&tmp, timecolor, &time16);
 686     colorgen(&tmp, datecolor, &date16);
 687     colorgen(&tmp, modifcolor, &modif16);
 688     colorgen(&tmp, keyhlcolor, &keyhl16);
 689     colorgen(&tmp, bshlcolor, &bshl16);
 690     colorgen(&tmp, separatorcolor, &sep16);
 691     colorgen(&tmp, bar_base_color, &bar16);
 692     colorgen(&tmp, greetercolor, &greeter16);
 693     colorgen(&tmp, color, &background);
 694 
 695     colorgen(&tmp, verifoutlinecolor, &verifoutline16);
 696     colorgen(&tmp, wrongoutlinecolor, &wrongoutline16);
 697     colorgen(&tmp, layoutoutlinecolor, &layoutoutline16);
 698     colorgen(&tmp, timeoutlinecolor, &timeoutline16);
 699     colorgen(&tmp, dateoutlinecolor, &dateoutline16);
 700     colorgen(&tmp, greeteroutlinecolor, &greeteroutline16);
 701     colorgen(&tmp, modifoutlinecolor, &modifoutline16);
 702 }
 703 
 704 static te_expr *compile_expression(const char *const from, const char *expression, const te_variable *variables, int var_count) {
 705     int te_err = 0;
 706     te_expr *expr = te_compile(expression, variables, var_count, &te_err);
 707     if (te_err) {
 708         fprintf(stderr, "Failed to reason about '%s' given by '%s'\n", expression, from);
 709         exit(EXIT_FAILURE);
 710     }
 711     return expr;
 712 }
 713 
 714 static DrawData create_draw_data() {
 715     DrawData draw_data;
 716     memset(&draw_data, 0, sizeof(DrawData));
 717 
 718     return draw_data;
 719 }
 720 
 721 static void draw_elements(cairo_t *const ctx, DrawData const *const draw_data) {
 722     // indicator stuff
 723     if (!bar_enabled) {
 724         draw_indic(ctx, draw_data->indicator_x, draw_data->indicator_y);
 725     } else {
 726         if (unlock_state == STATE_KEY_ACTIVE ||
 727             unlock_state == STATE_BACKSPACE_ACTIVE) {
 728             // note: might be biased to cause more hits on lower indices
 729             // maybe see about doing ((double) rand() / RAND_MAX) * bar_count
 730             int index = rand() % bar_count;
 731             bar_heights[index] = max_bar_height;
 732             for (int i = 0; i < ((max_bar_height / bar_step) + 1); ++i) {
 733                 int low_ind = index - i;
 734                 while (low_ind < 0) {
 735                     low_ind += bar_count;
 736                 }
 737                 int high_ind = (index + i) % bar_count;
 738                 int tmp_height = max_bar_height - (bar_step * i);
 739                 if (tmp_height < 0)
 740                     tmp_height = 0;
 741                 if (bar_heights[low_ind] < tmp_height)
 742                     bar_heights[low_ind] = tmp_height;
 743                 if (bar_heights[high_ind] < tmp_height)
 744                     bar_heights[high_ind] = tmp_height;
 745                 if (tmp_height == 0)
 746                     break;
 747             }
 748         }
 749         draw_bar(ctx, draw_data->bar_x, draw_data->bar_y, draw_data->bar_width, draw_data->screen_x, draw_data->screen_y);
 750     }
 751 
 752     draw_text(ctx, draw_data->status_text);
 753     draw_text(ctx, draw_data->keylayout_text);
 754     draw_text(ctx, draw_data->mod_text);
 755     draw_text(ctx, draw_data->time_text);
 756     draw_text(ctx, draw_data->date_text);
 757     draw_text(ctx, draw_data->greeter_text);
 758 }
 759 
 760 /*
 761  * Renders the lock screen on the provided drawable with the given resolution.
 762  */
 763 void render_lock(uint32_t *resolution, xcb_drawable_t drawable) {
 764     const double scaling_factor = get_dpi_value() / 96.0;
 765     int button_diameter_physical = ceil(scaling_factor * BUTTON_DIAMETER);
 766     DEBUG("scaling_factor is %.f, physical diameter is %d px\n",
 767         scaling_factor, button_diameter_physical);
 768 
 769     if (!vistype)
 770         vistype = get_visualtype_by_depth(32, screen);
 771     /* Initialize cairo: Create one in-memory surface to render the unlock
 772      * indicator on, create one XCB surface to actually draw (one or more,
 773      * depending on the amount of screens) unlock indicators on.
 774      * create two more surfaces for time and date display
 775      */
 776     cairo_surface_t *output = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, resolution[0], resolution[1]);
 777     cairo_t *ctx = cairo_create(output);
 778     cairo_scale(ctx, scaling_factor, scaling_factor);
 779 
 780     //    cairo_set_font_face(ctx, get_font_face(0));
 781 
 782     cairo_surface_t *xcb_output = cairo_xcb_surface_create(conn, drawable, vistype, resolution[0], resolution[1]);
 783     cairo_t *xcb_ctx = cairo_create(xcb_output);
 784 
 785     /*update image according to the slideshow_interval*/
 786     if (slideshow_image_count > 0) {
 787         unsigned long now = (unsigned long)time(NULL);
 788         if (img == NULL || now - lastCheck >= slideshow_interval) {
 789             if (slideshow_random_selection) {
 790                 img = load_image(img_slideshow[rand() % slideshow_image_count]);
 791             } else {
 792                 img = load_image(img_slideshow[current_slideshow_index]);
 793             }
 794             current_slideshow_index++;
 795             if (current_slideshow_index >= slideshow_image_count) {
 796                 current_slideshow_index = 0;
 797                 load_slideshow_images(slideshow_path);
 798             }
 799             lastCheck = now;
 800         }
 801     }
 802 
 803     if (blur_bg_img) {
 804         cairo_set_source_surface(xcb_ctx, blur_bg_img, 0, 0);
 805         cairo_paint(xcb_ctx);
 806     } else {
 807         cairo_set_source_rgba(xcb_ctx, background.red, background.green, background.blue, background.alpha);
 808         cairo_rectangle(xcb_ctx, 0, 0, resolution[0], resolution[1]);
 809         cairo_fill(xcb_ctx);
 810     }
 811 
 812     if (img) {
 813         draw_image(resolution, img, xcb_ctx);
 814     }
 815 
 816     /*
 817      * gen text
 818      * calc vars
 819      * process if keystroke or not
 820      * draw indicator
 821      * draw text
 822      */
 823     DrawData draw_data = create_draw_data();
 824 
 825     if (unlock_indicator &&
 826         (unlock_state >= STATE_KEY_PRESSED || auth_state > STATE_AUTH_IDLE || show_indicator)) {
 827         switch (auth_state) {
 828             case STATE_AUTH_VERIFY:
 829                 draw_data.status_text.show = true;
 830                 strncpy(draw_data.status_text.str, verif_text, sizeof(draw_data.status_text.str) - 1);
 831                 draw_data.status_text.font = get_font_face(VERIF_FONT);
 832                 draw_data.status_text.color = verif16;
 833                 draw_data.status_text.outline_color = verifoutline16;
 834                 draw_data.status_text.size = verif_size;
 835                 draw_data.status_text.outline_width = verifoutlinewidth;
 836                 draw_data.status_text.align = verif_align;
 837                 break;
 838             case STATE_AUTH_LOCK:
 839                 draw_data.status_text.show = true;
 840                 strncpy(draw_data.status_text.str, lock_text, sizeof(draw_data.status_text.str) - 1);
 841                 draw_data.status_text.font = get_font_face(VERIF_FONT);
 842                 draw_data.status_text.color = verif16;
 843                 draw_data.status_text.outline_color = verifoutline16;
 844                 draw_data.status_text.size = verif_size;
 845                 draw_data.status_text.outline_width = verifoutlinewidth;
 846                 draw_data.status_text.align = verif_align;
 847                 break;
 848             case STATE_AUTH_WRONG:
 849                 draw_data.status_text.show = true;
 850                 strncpy(draw_data.status_text.str, wrong_text, sizeof(draw_data.status_text.str) - 1);
 851                 draw_data.status_text.font = get_font_face(WRONG_FONT);
 852                 draw_data.status_text.color = wrong16;
 853                 draw_data.status_text.outline_color = wrongoutline16;
 854                 draw_data.status_text.size = wrong_size;
 855                 draw_data.status_text.outline_width = wrongoutlinewidth;
 856                 draw_data.status_text.align = wrong_align;
 857                 break;
 858             case STATE_I3LOCK_LOCK_FAILED:
 859                 draw_data.status_text.show = true;
 860                 strncpy(draw_data.status_text.str, lock_failed_text, sizeof(draw_data.status_text.str) - 1);
 861                 draw_data.status_text.font = get_font_face(WRONG_FONT);
 862                 draw_data.status_text.color = wrong16;
 863                 draw_data.status_text.outline_color = wrongoutline16;
 864                 draw_data.status_text.size = wrong_size;
 865                 draw_data.status_text.outline_width = wrongoutlinewidth;
 866                 draw_data.status_text.align = wrong_align;
 867                 break;
 868             default:
 869                 if (unlock_state == STATE_NOTHING_TO_DELETE) {
 870                     draw_data.status_text.show = true;
 871                     strncpy(draw_data.status_text.str, noinput_text, sizeof(draw_data.status_text.str) - 1);
 872                     draw_data.status_text.font = get_font_face(WRONG_FONT);
 873                     draw_data.status_text.color = wrong16;
 874                     draw_data.status_text.outline_color = wrongoutline16;
 875                     draw_data.status_text.size = wrong_size;
 876                     draw_data.status_text.outline_width = wrongoutlinewidth;
 877                     draw_data.status_text.align = wrong_align;
 878                     break;
 879                 }
 880                 if (show_failed_attempts && failed_attempts > 0) {
 881                     draw_data.status_text.show = true;
 882                     draw_data.status_text.font = get_font_face(WRONG_FONT);
 883                     draw_data.status_text.color = wrong16;
 884                     draw_data.status_text.outline_color = wrongoutline16;
 885                     draw_data.status_text.size = wrong_size;
 886                     draw_data.status_text.outline_width = wrongoutlinewidth;
 887                     draw_data.status_text.align = wrong_align;
 888                     // TODO: variable for this
 889                     draw_data.status_text.size = 32.0;
 890                     if (failed_attempts > 999) {
 891                         strncpy(draw_data.status_text.str, "> 999", sizeof(draw_data.status_text.str));
 892                     } else {
 893                         snprintf(draw_data.status_text.str, sizeof(draw_data.status_text.str), "%d", failed_attempts);
 894                     }
 895                 }
 896                 break;
 897         }
 898     }
 899 
 900     if (modifier_string) {
 901         draw_data.mod_text.show = true;
 902         strncpy(draw_data.mod_text.str, modifier_string, sizeof(draw_data.mod_text.str) - 1);
 903         draw_data.mod_text.size = modifier_size;
 904         draw_data.mod_text.outline_width = modifieroutlinewidth;
 905         draw_data.mod_text.font = get_font_face(WRONG_FONT);
 906         draw_data.mod_text.align = modif_align;
 907         draw_data.mod_text.color = modif16;
 908         draw_data.mod_text.outline_color = modifoutline16;
 909     }
 910 
 911     if (layout_text) {
 912         draw_data.keylayout_text.show = true;
 913         strncpy(draw_data.keylayout_text.str, layout_text, sizeof(draw_data.keylayout_text.str) - 1);
 914         draw_data.keylayout_text.size = layout_size;
 915         draw_data.keylayout_text.outline_width = layoutoutlinewidth;
 916         draw_data.keylayout_text.font = get_font_face(LAYOUT_FONT);
 917         draw_data.keylayout_text.color = layout16;
 918         draw_data.keylayout_text.outline_color = layoutoutline16;
 919         draw_data.keylayout_text.align = layout_align;
 920     }
 921 
 922     if (greeter_text) {
 923         draw_data.greeter_text.show = true;
 924         strncpy(draw_data.greeter_text.str, greeter_text, sizeof(draw_data.greeter_text.str) - 1);
 925         draw_data.greeter_text.size = greeter_size;
 926         draw_data.greeter_text.outline_width = greeteroutlinewidth;
 927         draw_data.greeter_text.font = get_font_face(GREETER_FONT);
 928         draw_data.greeter_text.color = greeter16;
 929         draw_data.greeter_text.outline_color = greeteroutline16;
 930         draw_data.greeter_text.align = greeter_align;
 931     }
 932 
 933     if (show_clock && (!draw_data.status_text.show || always_show_clock)) {
 934         time_t rawtime;
 935         struct tm *timeinfo;
 936         time(&rawtime);
 937         timeinfo = localtime(&rawtime);
 938 
 939         strftime(draw_data.time_text.str, 40, time_format, timeinfo);
 940         if (*draw_data.time_text.str) {
 941             draw_data.time_text.show = true;
 942             draw_data.time_text.size = time_size;
 943             draw_data.time_text.outline_width = timeoutlinewidth;
 944             draw_data.time_text.color = time16;
 945             draw_data.time_text.outline_color = timeoutline16;
 946             draw_data.time_text.font = get_font_face(TIME_FONT);
 947             draw_data.time_text.align = time_align;
 948         }
 949         strftime(draw_data.date_text.str, 40, date_format, timeinfo);
 950         if (*draw_data.date_text.str) {
 951             draw_data.date_text.show = true;
 952             draw_data.date_text.size = date_size;
 953             draw_data.date_text.outline_width = dateoutlinewidth;
 954             draw_data.date_text.color = date16;
 955             draw_data.date_text.outline_color = dateoutline16;
 956             draw_data.date_text.font = get_font_face(DATE_FONT);
 957             draw_data.date_text.align = date_align;
 958         }
 959 
 960         if (*draw_data.greeter_text.str) {
 961             draw_data.greeter_text.show = true;
 962             draw_data.greeter_text.size = greeter_size;
 963             draw_data.greeter_text.outline_width = greeteroutlinewidth;
 964             draw_data.greeter_text.color = greeter16;
 965             draw_data.greeter_text.outline_color = greeteroutline16;
 966             draw_data.greeter_text.font = get_font_face(GREETER_FONT);
 967             draw_data.greeter_text.align = greeter_align;
 968         }
 969     }
 970 
 971     // initialize positioning vars
 972     double screen_x = 0, screen_y = 0,
 973            width = 0, height = 0;
 974 
 975     double radius = (circle_radius + ring_width);
 976     DEBUG("scaling_factor is %f, physical diameter is %d px\n",
 977           scaling_factor, button_diameter_physical);
 978 
 979     // variable mapping for evaluating the clock position expression
 980     const unsigned int vars_size = 14;
 981     te_variable vars[] =
 982         {{"w", &width},
 983          {"h", &height},
 984          {"x", &screen_x},
 985          {"y", &screen_y},
 986          {"ix", &draw_data.indicator_x},
 987          {"iy", &draw_data.indicator_y},
 988          {"tx", &draw_data.time_text.x},
 989          {"ty", &draw_data.time_text.y},
 990          {"dx", &draw_data.date_text.x},
 991          {"dy", &draw_data.date_text.y},
 992          {"bw", &draw_data.bar_width},
 993          {"bx", &draw_data.bar_x},
 994          {"by", &draw_data.bar_y},
 995          {"r", &radius}};
 996 
 997     te_expr *te_ind_x_expr = compile_expression("--indpos", ind_x_expr, vars, vars_size);
 998     te_expr *te_ind_y_expr = compile_expression("--indpos", ind_y_expr, vars, vars_size);
 999     te_expr *te_time_x_expr = compile_expression("--timepos", time_x_expr, vars, vars_size);
1000     te_expr *te_time_y_expr = compile_expression("--timepos", time_y_expr, vars, vars_size);
1001     te_expr *te_date_x_expr = compile_expression("--datepos", date_x_expr, vars, vars_size);
1002     te_expr *te_date_y_expr = compile_expression("--datepos", date_y_expr, vars, vars_size);
1003     te_expr *te_layout_x_expr = compile_expression("--layoutpos", layout_x_expr, vars, vars_size);
1004     te_expr *te_layout_y_expr = compile_expression("--layoutpos", layout_y_expr, vars, vars_size);
1005     te_expr *te_status_x_expr = compile_expression("--statuspos", status_x_expr, vars, vars_size);
1006     te_expr *te_status_y_expr = compile_expression("--statuspos", status_y_expr, vars, vars_size);
1007     te_expr *te_verif_x_expr = compile_expression("--verifpos", verif_x_expr, vars, vars_size);
1008     te_expr *te_verif_y_expr = compile_expression("--verifpos", verif_y_expr, vars, vars_size);
1009     te_expr *te_wrong_x_expr = compile_expression("--wrongpos", wrong_x_expr, vars, vars_size);
1010     te_expr *te_wrong_y_expr = compile_expression("--wrongpos", wrong_y_expr, vars, vars_size);
1011     te_expr *te_modif_x_expr = compile_expression("--modifpos", modif_x_expr, vars, vars_size);
1012     te_expr *te_modif_y_expr = compile_expression("--modifpos", modif_y_expr, vars, vars_size);
1013     te_expr *te_bar_x_expr = compile_expression("--bar-position", bar_x_expr, vars, vars_size);
1014     te_expr *te_bar_y_expr = strlen(bar_y_expr) ? compile_expression("--bar-position", bar_y_expr, vars, vars_size) : NULL;
1015     te_expr *te_bar_width_expr = strlen(bar_width_expr) ? compile_expression("--bar-width", bar_width_expr, vars, vars_size) : NULL;
1016 
1017     te_expr *te_greeter_x_expr = compile_expression("--greeterpos", greeter_x_expr, vars, vars_size);
1018     te_expr *te_greeter_y_expr = compile_expression("--greeterpos", greeter_y_expr, vars, vars_size);
1019 
1020     if (xr_screens > 0) {
1021         if (screen_number < 0 || screen_number > xr_screens) {
1022             screen_number = 0;
1023         }
1024 
1025         DEBUG("Drawing indicator on %d screens\n", screen_number);
1026 
1027         int current_screen = screen_number == 0 ? 0 : screen_number - 1;
1028         const int end_screen = screen_number == 0 ? xr_screens : screen_number;
1029         for (; current_screen < end_screen; current_screen++) {
1030             draw_data.indicator_x = 0;
1031             draw_data.indicator_y = 0;
1032             draw_data.time_text.x = 0;
1033             draw_data.time_text.y = 0;
1034             draw_data.date_text.x = 0;
1035             draw_data.date_text.y = 0;
1036             draw_data.greeter_text.x = 0;
1037             draw_data.greeter_text.y = 0;
1038 
1039             width = xr_resolutions[current_screen].width / scaling_factor;
1040             height = xr_resolutions[current_screen].height / scaling_factor;
1041             screen_x = xr_resolutions[current_screen].x / scaling_factor;
1042             screen_y = xr_resolutions[current_screen].y / scaling_factor;
1043             draw_data.screen_x = screen_x;
1044             draw_data.screen_y = screen_y;
1045             draw_data.indicator_x = te_eval(te_ind_x_expr);
1046             draw_data.indicator_y = te_eval(te_ind_y_expr);
1047             draw_data.time_text.x = te_eval(te_time_x_expr);
1048             draw_data.time_text.y = te_eval(te_time_y_expr);
1049             draw_data.date_text.x = te_eval(te_date_x_expr);
1050             draw_data.date_text.y = te_eval(te_date_y_expr);
1051             draw_data.keylayout_text.x = te_eval(te_layout_x_expr);
1052             draw_data.keylayout_text.y = te_eval(te_layout_y_expr);
1053             draw_data.greeter_text.x = te_eval(te_greeter_x_expr);
1054             draw_data.greeter_text.y = te_eval(te_greeter_y_expr);
1055 
1056             switch (auth_state) {
1057                 case STATE_AUTH_VERIFY:
1058                 case STATE_AUTH_LOCK:
1059                     draw_data.status_text.x = te_eval(te_verif_x_expr);
1060                     draw_data.status_text.y = te_eval(te_verif_y_expr);
1061                     break;
1062                 case STATE_AUTH_WRONG:
1063                 case STATE_I3LOCK_LOCK_FAILED:
1064                     draw_data.status_text.x = te_eval(te_wrong_x_expr);
1065                     draw_data.status_text.y = te_eval(te_wrong_y_expr);
1066                     break;
1067                 default:
1068                     draw_data.status_text.x = te_eval(te_status_x_expr);
1069                     draw_data.status_text.y = te_eval(te_status_y_expr);
1070                     break;
1071             }
1072 
1073             draw_data.mod_text.x = te_eval(te_modif_x_expr);
1074             draw_data.mod_text.y = te_eval(te_modif_y_expr);
1075 
1076             if (te_bar_y_expr) {
1077                 draw_data.bar_x = te_eval(te_bar_x_expr);
1078                 draw_data.bar_y = te_eval(te_bar_y_expr);
1079             } else {
1080                 double bar_offset = te_eval(te_bar_x_expr);
1081                 if (bar_orientation == BAR_VERT) {
1082                     draw_data.bar_x = bar_offset;
1083                     draw_data.bar_y = screen_y;
1084                 } else {
1085                     draw_data.bar_x = screen_x;
1086                     draw_data.bar_y = bar_offset;
1087                 }
1088             }
1089             if (te_bar_width_expr)
1090                 draw_data.bar_width = te_eval(te_bar_width_expr);
1091             else if (bar_orientation == BAR_VERT)
1092                 draw_data.bar_width = height;
1093             else
1094                 draw_data.bar_width = width;
1095 
1096 
1097             DEBUG("Indicator at %fx%f on screen %d\n", draw_data.indicator_x, draw_data.indicator_y, current_screen + 1);
1098             DEBUG("Bar at %fx%f with width %f on screen %d\n", draw_data.bar_x, draw_data.bar_y, draw_data.bar_width, current_screen + 1);
1099             DEBUG("Time at %fx%f on screen %d\n", draw_data.time_text.x, draw_data.time_text.y, current_screen + 1);
1100             DEBUG("Date at %fx%f on screen %d\n", draw_data.date_text.x, draw_data.date_text.y, current_screen + 1);
1101             DEBUG("Layout at %fx%f on screen %d\n", draw_data.keylayout_text.x, draw_data.keylayout_text.y, current_screen + 1);
1102             DEBUG("Status at %fx%f on screen %d\n", draw_data.status_text.x, draw_data.status_text.y, current_screen + 1);
1103             DEBUG("Mod at %fx%f on screen %d\n", draw_data.mod_text.x, draw_data.mod_text.y, current_screen + 1);
1104             // scale_draw_data(&draw_data, scaling_factor);
1105             draw_elements(ctx, &draw_data);
1106         }
1107     } else {
1108         /* We have no information about the screen sizes/positions, so we just
1109          * place the unlock indicator in the middle of the X root window and
1110          * hope for the best. */
1111         width = last_resolution[0] / scaling_factor;
1112         height = last_resolution[1] / scaling_factor;
1113         draw_data.screen_x = 0;
1114         draw_data.screen_y = 0;
1115         draw_data.indicator_x = width / 2;
1116         draw_data.indicator_y = height / 2;
1117 
1118         draw_data.time_text.x = te_eval(te_time_x_expr);
1119         draw_data.time_text.y = te_eval(te_time_y_expr);
1120         draw_data.date_text.x = te_eval(te_date_x_expr);
1121         draw_data.date_text.y = te_eval(te_date_y_expr);
1122         draw_data.keylayout_text.x = te_eval(te_layout_x_expr);
1123         draw_data.keylayout_text.y = te_eval(te_layout_y_expr);
1124         draw_data.greeter_text.x = te_eval(te_greeter_x_expr);
1125         draw_data.greeter_text.y = te_eval(te_greeter_y_expr);
1126         switch (auth_state) {
1127             case STATE_AUTH_VERIFY:
1128             case STATE_AUTH_LOCK:
1129                 draw_data.status_text.x = te_eval(te_verif_x_expr);
1130                 draw_data.status_text.y = te_eval(te_verif_y_expr);
1131                 break;
1132             case STATE_AUTH_WRONG:
1133             case STATE_I3LOCK_LOCK_FAILED:
1134                 draw_data.status_text.x = te_eval(te_wrong_x_expr);
1135                 draw_data.status_text.y = te_eval(te_wrong_y_expr);
1136                 break;
1137             default:
1138                 draw_data.status_text.x = te_eval(te_status_x_expr);
1139                 draw_data.status_text.y = te_eval(te_status_y_expr);
1140                 break;
1141         }
1142         draw_data.mod_text.x = te_eval(te_modif_x_expr);
1143         draw_data.mod_text.y = te_eval(te_modif_y_expr);
1144 
1145         if (te_bar_y_expr) {
1146             draw_data.bar_x = te_eval(te_bar_x_expr);
1147             draw_data.bar_y = te_eval(te_bar_y_expr);
1148         } else {
1149             double bar_offset = te_eval(te_bar_x_expr);
1150             if (bar_orientation == BAR_VERT) {
1151                 draw_data.bar_x = bar_offset;
1152                 draw_data.bar_y = screen_y;
1153             } else {
1154                 draw_data.bar_x = screen_x;
1155                 draw_data.bar_y = bar_offset;
1156             }
1157         }
1158         if (te_bar_width_expr)
1159             draw_data.bar_width = te_eval(te_bar_width_expr);
1160         else if (bar_orientation == BAR_VERT)
1161             draw_data.bar_width = height;
1162         else
1163             draw_data.bar_width = width;
1164 
1165         DEBUG("Indicator at %fx%f\n", draw_data.indicator_x, draw_data.indicator_y);
1166         DEBUG("Bar at %fx%f with width %f\n", draw_data.bar_x, draw_data.bar_y, draw_data.bar_width);
1167         DEBUG("Time at %fx%f\n", draw_data.time_text.x, draw_data.time_text.y);
1168         DEBUG("Date at %fx%f\n", draw_data.date_text.x, draw_data.date_text.y);
1169         DEBUG("Layout at %fx%f\n", draw_data.keylayout_text.x, draw_data.keylayout_text.y);
1170         DEBUG("Status at %fx%f\n", draw_data.status_text.x, draw_data.status_text.y);
1171         DEBUG("Mod at %fx%f\n", draw_data.mod_text.x, draw_data.mod_text.y);
1172 
1173         draw_elements(ctx, &draw_data);
1174     }
1175 
1176     te_free(te_ind_x_expr);
1177     te_free(te_ind_y_expr);
1178     te_free(te_time_x_expr);
1179     te_free(te_time_y_expr);
1180     te_free(te_date_x_expr);
1181     te_free(te_date_y_expr);
1182     te_free(te_layout_x_expr);
1183     te_free(te_layout_y_expr);
1184     te_free(te_status_x_expr);
1185     te_free(te_status_y_expr);
1186     te_free(te_verif_x_expr);
1187     te_free(te_verif_y_expr);
1188     te_free(te_wrong_x_expr);
1189     te_free(te_wrong_y_expr);
1190     te_free(te_modif_x_expr);
1191     te_free(te_modif_y_expr);
1192     te_free(te_bar_x_expr);
1193     te_free(te_bar_y_expr);
1194     te_free(te_bar_width_expr);
1195     te_free(te_greeter_x_expr);
1196     te_free(te_greeter_y_expr);
1197 
1198     cairo_set_source_surface(xcb_ctx, output, 0, 0);
1199     cairo_rectangle(xcb_ctx, 0, 0, resolution[0], resolution[1]);
1200     cairo_fill(xcb_ctx);
1201 
1202     cairo_surface_destroy(xcb_output);
1203     cairo_surface_destroy(output);
1204     cairo_destroy(ctx);
1205     cairo_destroy(xcb_ctx);
1206 }
1207 
1208 /**
1209  * Draws the configured image on the provided context. The image is drawn centered on all monitors, tiled, or just
1210  * painted starting from 0,0. It is also scaled if bg_type is FILL, MAX, or SCALE.
1211  */
1212 void draw_image(uint32_t* root_resolution, cairo_surface_t *img, cairo_t* xcb_ctx) {
1213 
1214     if (bg_type == NONE) {
1215         // Don't do any image manipulation
1216         cairo_set_source_surface(xcb_ctx, img, 0, 0);
1217         cairo_paint(xcb_ctx);
1218         return;
1219     }
1220 
1221     cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img);
1222     cairo_pattern_set_extend(pattern, bg_type == TILE ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE);
1223     cairo_set_source(xcb_ctx, pattern);
1224 
1225     double image_width = cairo_image_surface_get_width(img);
1226     double image_height = cairo_image_surface_get_height(img);
1227 
1228     for (int i = 0; i < xr_screens; i++) {
1229         // Find out scaling factors using bg_type and aspect ratios
1230         double scale_x = 1, scale_y = 1;
1231         if (bg_type == SCALE) {
1232             scale_x = xr_resolutions[i].width / image_width;
1233             scale_y = xr_resolutions[i].height / image_height;
1234 
1235         } else if (bg_type == MAX || bg_type == FILL) {
1236             double aspect_diff = (double) xr_resolutions[i].height / xr_resolutions[i].width - image_height / image_width;
1237             if((bg_type == MAX && aspect_diff >= 0) || (bg_type == FILL && aspect_diff <= 0)) {
1238                 scale_x = scale_y = xr_resolutions[i].width / image_width;
1239             } else if ((bg_type == MAX && aspect_diff < 0) || (bg_type == FILL && aspect_diff > 0)) {
1240                 scale_x = scale_y = xr_resolutions[i].height / image_height;
1241             }
1242         }
1243 
1244         // Scale and translate the pattern
1245         cairo_matrix_t matrix;
1246         cairo_matrix_init_scale(&matrix, 1/scale_x, 1/scale_y);
1247 
1248         if (bg_type == TILE) {
1249             // Start image from top-left corner
1250             cairo_matrix_translate(&matrix, -xr_resolutions[i].x, -xr_resolutions[i].y);
1251         } else {
1252             // Draw image in the center of the screen
1253             cairo_matrix_translate(&matrix,
1254                 (image_width  * scale_x - xr_resolutions[i].width ) / 2 - xr_resolutions[i].x,
1255                 (image_height * scale_y - xr_resolutions[i].height) / 2 - xr_resolutions[i].y);
1256         }
1257 
1258         cairo_pattern_set_matrix(pattern, &matrix);
1259 
1260         // Draw to screen
1261         cairo_rectangle(xcb_ctx, xr_resolutions[i].x, xr_resolutions[i].y, xr_resolutions[i].width, xr_resolutions[i].height);
1262         cairo_fill(xcb_ctx);
1263     }
1264 
1265     cairo_pattern_destroy(pattern);
1266 }
1267 
1268 /*
1269  * Calls render_lock on a new pixmap and swaps that with the current pixmap
1270  *
1271  */
1272 void redraw_screen(void) {
1273     DEBUG("redraw_screen(unlock_state = %d, auth_state = %d) @ [%lu]\n", unlock_state, auth_state, (unsigned long)time(NULL));
1274     xcb_pixmap_t pixmap = create_bg_pixmap(conn, win, last_resolution, color);
1275     render_lock(last_resolution, pixmap);
1276     xcb_change_window_attributes(conn, win, XCB_CW_BACK_PIXMAP, (uint32_t[1]){pixmap});
1277     xcb_clear_area(conn, 0, win, 0, 0, last_resolution[0], last_resolution[1]);
1278     xcb_free_pixmap(conn, pixmap);
1279     xcb_flush(conn);
1280 }
1281 
1282 /*
1283  * Hides the unlock indicator completely when there is no content in the
1284  * password buffer.
1285  *
1286  */
1287 void clear_indicator(void) {
1288     if (input_position == 0) {
1289         unlock_state = STATE_STARTED;
1290     } else
1291         unlock_state = STATE_KEY_PRESSED;
1292     redraw_screen();
1293 }
1294 
1295 void *start_time_redraw_tick_pthread(void *arg) {
1296     struct timespec *ts = (struct timespec *)arg;
1297     while (1) {
1298         nanosleep(ts, NULL);
1299         redraw_screen();
1300     }
1301     return NULL;
1302 }
1303 
1304 static void time_redraw_cb(struct ev_loop *loop, ev_periodic *w, int revents) {
1305     redraw_screen();
1306 }
1307 
1308 void start_time_redraw_tick(struct ev_loop *main_loop) {
1309     if (time_redraw_tick) {
1310         ev_periodic_set(time_redraw_tick, 0., refresh_rate, 0);
1311         ev_periodic_again(main_loop, time_redraw_tick);
1312     } else {
1313         if (!(time_redraw_tick = calloc(sizeof(struct ev_periodic), 1))) {
1314             return;
1315         }
1316         ev_periodic_init(time_redraw_tick, time_redraw_cb, 0., refresh_rate, 0);
1317         ev_periodic_start(main_loop, time_redraw_tick);
1318     }
1319 }