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 <config.h> 12 13 #include <pthread.h> 14 #include <math.h> 15 16 #include <stdio.h> 17 #include <locale.h> 18 #include <stdlib.h> 19 #include <pwd.h> 20 #include <sys/types.h> 21 #include <dirent.h> 22 #include <unistd.h> 23 #include <stdbool.h> 24 #include <stdint.h> 25 #include <xcb/xcb.h> 26 #include <xcb/xkb.h> 27 #include <xcb/xproto.h> 28 #include <err.h> 29 #include <errno.h> 30 #include <assert.h> 31 #include <regex.h> 32 #ifdef __OpenBSD__ 33 #include <bsd_auth.h> 34 #else 35 #include <security/pam_appl.h> 36 #endif 37 #include <getopt.h> 38 #include <string.h> 39 #include <ev.h> 40 #include <sys/mman.h> 41 #include <xkbcommon/xkbcommon.h> 42 #if XKBCOMPOSE == 1 43 #include <xkbcommon/xkbcommon-compose.h> 44 #endif 45 #include <xkbcommon/xkbcommon-x11.h> 46 #include <cairo.h> 47 #include <cairo/cairo-xcb.h> 48 #ifdef HAVE_EXPLICIT_BZERO 49 #include <strings.h> /* explicit_bzero(3) */ 50 #endif 51 #include <xcb/xcb_aux.h> 52 #include <xcb/randr.h> 53 54 #include "i3lock.h" 55 #include "xcb.h" 56 #include "cursors.h" 57 #include "unlock_indicator.h" 58 #include "randr.h" 59 #include "dpi.h" 60 #include "blur.h" 61 #include "jpg.h" 62 #include "fonts.h" 63 64 #define TSTAMP_N_SECS(n) (n * 1.0) 65 #define TSTAMP_N_MINS(n) (60 * TSTAMP_N_SECS(n)) 66 #define START_TIMER(timer_obj, timeout, callback) \ 67 timer_obj = start_timer(timer_obj, timeout, callback) 68 #define STOP_TIMER(timer_obj) \ 69 timer_obj = stop_timer(timer_obj) 70 71 typedef void (*ev_callback_t)(EV_P_ ev_timer *w, int revents); 72 static void input_done(void); 73 74 char color[9] = "a3a3a3ff"; 75 76 /* options for unlock indicator colors */ 77 char insidevercolor[9] = "006effbf"; 78 char insidewrongcolor[9] = "fa0000bf"; 79 char insidecolor[9] = "000000bf"; 80 char ringvercolor[9] = "3300faff"; 81 char ringwrongcolor[9] = "7d3300ff"; 82 char ringcolor[9] = "337d00ff"; 83 char linecolor[9] = "000000ff"; 84 char verifcolor[9] = "000000ff"; 85 char wrongcolor[9] = "000000ff"; 86 char layoutcolor[9] = "000000ff"; 87 char timecolor[9] = "000000ff"; 88 char datecolor[9] = "000000ff"; 89 char modifcolor[9] = "000000ff"; 90 char keyhlcolor[9] = "33db00ff"; 91 char bshlcolor[9] = "db3300ff"; 92 char separatorcolor[9] = "000000ff"; 93 char greetercolor[9] = "000000ff"; 94 95 char verifoutlinecolor[9] = "00000000"; 96 char wrongoutlinecolor[9] = "00000000"; 97 char layoutoutlinecolor[9] = "00000000"; 98 char timeoutlinecolor[9] = "00000000"; 99 char dateoutlinecolor[9] = "00000000"; 100 char greeteroutlinecolor[9] = "00000000"; 101 char modifoutlinecolor[9] = "00000000"; 102 103 /* int defining which display the lock indicator should be shown on. If -1, then show on all displays.*/ 104 int screen_number = 0; 105 106 /* default is to use the supplied line color, 1 will be ring color, 2 will be to use the inside color for ver/wrong/etc */ 107 int internal_line_source = 0; 108 109 /* refresh rate in seconds, default to 1s */ 110 float refresh_rate = 1.0; 111 112 bool show_clock = false; 113 bool slideshow_enabled = false; 114 bool always_show_clock = false; 115 bool show_indicator = false; 116 bool show_modkey_text = true; 117 118 /* there's some issues with compositing - upstream removed support for this, but we'll allow people to supply an arg to enable it */ 119 bool composite = false; 120 /* time formatter strings for date/time 121 I picked 32-length char arrays because some people might want really funky time formatters. 122 Who am I to judge? 123 */ 124 /* 125 * 0 = center 126 * 1 = left 127 * 2 = right 128 */ 129 int verif_align = 0; 130 int wrong_align = 0; 131 int time_align = 0; 132 int date_align = 0; 133 int layout_align = 0; 134 int modif_align = 0; 135 int greeter_align = 0; 136 137 char time_format[32] = "%H:%M:%S\0"; 138 char date_format[32] = "%A, %m %Y\0"; 139 140 char verif_font[64] = "sans-serif\0"; 141 char wrong_font[64] = "sans-serif\0"; 142 char layout_font[64] = "sans-serif\0"; 143 char time_font[64] = "sans-serif\0"; 144 char date_font[64] = "sans-serif\0"; 145 char greeter_font[64] = "sans-serif\0"; 146 147 char* fonts[6] = { 148 verif_font, 149 wrong_font, 150 layout_font, 151 time_font, 152 date_font, 153 greeter_font 154 }; 155 156 char ind_x_expr[32] = "x + (w / 2)\0"; 157 char ind_y_expr[32] = "y + (h / 2)\0"; 158 char time_x_expr[32] = "ix\0"; 159 char time_y_expr[32] = "iy\0"; 160 char date_x_expr[32] = "tx\0"; 161 char date_y_expr[32] = "ty+30\0"; 162 char layout_x_expr[32] = "dx\0"; 163 char layout_y_expr[32] = "dy+30\0"; 164 char status_x_expr[32] = "ix\0"; 165 char status_y_expr[32] = "iy\0"; 166 char modif_x_expr[32] = "ix\0"; 167 char modif_y_expr[32] = "iy+28\0"; 168 char verif_x_expr[32] = "ix\0"; 169 char verif_y_expr[32] = "iy\0"; 170 char wrong_x_expr[32] = "ix\0"; 171 char wrong_y_expr[32] = "iy\0"; 172 char greeter_x_expr[32] = "ix\0"; 173 char greeter_y_expr[32] = "iy\0"; 174 175 double time_size = 32.0; 176 double date_size = 14.0; 177 double verif_size = 28.0; 178 double wrong_size = 28.0; 179 double modifier_size = 14.0; 180 double layout_size = 14.0; 181 double circle_radius = 90.0; 182 double ring_width = 7.0; 183 double greeter_size = 32.0; 184 185 double timeoutlinewidth = 0; 186 double dateoutlinewidth = 0; 187 double verifoutlinewidth = 0; 188 double wrongoutlinewidth = 0; 189 double modifieroutlinewidth = 0; 190 double layoutoutlinewidth = 0; 191 double greeteroutlinewidth = 0; 192 193 char* verif_text = "verifying…"; 194 char* wrong_text = "wrong!"; 195 char* noinput_text = "no input"; 196 char* lock_text = "locking…"; 197 char* lock_failed_text = "lock failed!"; 198 int keylayout_mode = -1; 199 char* layout_text = NULL; 200 char* greeter_text = ""; 201 202 /* opts for blurring */ 203 bool blur = false; 204 bool step_blur = false; 205 int blur_sigma = 5; 206 207 /* do not verify password */ 208 bool no_verify = false; 209 210 uint32_t last_resolution[2]; 211 xcb_window_t win; 212 static xcb_cursor_t cursor; 213 #ifndef __OpenBSD__ 214 static pam_handle_t *pam_handle; 215 static bool pam_cleanup; 216 #endif 217 int input_position = 0; 218 /* Holds the password you enter (in UTF-8). */ 219 static char password[512]; 220 static bool beep = false; 221 bool debug_mode = false; 222 bool unlock_indicator = true; 223 char *modifier_string = NULL; 224 static bool dont_fork = false; 225 struct ev_loop *main_loop; 226 static struct ev_timer *clear_auth_wrong_timeout; 227 static struct ev_timer *clear_indicator_timeout; 228 static struct ev_timer *discard_passwd_timeout; 229 extern unlock_state_t unlock_state; 230 extern auth_state_t auth_state; 231 int failed_attempts = 0; 232 bool show_failed_attempts = false; 233 bool retry_verification = false; 234 235 static struct xkb_state *xkb_state; 236 static struct xkb_context *xkb_context; 237 static struct xkb_keymap *xkb_keymap; 238 #if XKBCOMPOSE == 1 239 static struct xkb_compose_table *xkb_compose_table; 240 static struct xkb_compose_state *xkb_compose_state; 241 #endif 242 static uint8_t xkb_base_event; 243 static uint8_t xkb_base_error; 244 static int randr_base = -1; 245 246 char *image_path = NULL; 247 char *image_raw_format = NULL; 248 char *slideshow_path = NULL; 249 250 cairo_surface_t *img = NULL; 251 char *img_slideshow[256]; 252 cairo_surface_t *blur_bg_img = NULL; 253 int slideshow_image_count = 0; 254 int slideshow_interval = 10; 255 bool slideshow_random_selection = false; 256 257 background_type_t bg_type = NONE; 258 259 bool ignore_empty_password = false; 260 bool skip_repeated_empty_password = false; 261 bool pass_media_keys = false; 262 bool pass_screen_keys = false; 263 bool pass_power_keys = false; 264 bool pass_volume_keys = false; 265 266 bool hotkeys = false; 267 char* cmd_brightness_up = NULL; 268 char* cmd_brightness_down = NULL; 269 270 char* cmd_media_play = NULL; 271 char* cmd_media_pause = NULL; 272 char* cmd_media_stop = NULL; 273 char* cmd_media_next = NULL; 274 char* cmd_media_prev = NULL; 275 276 char* cmd_audio_mute = NULL; 277 char* cmd_volume_up = NULL; 278 char* cmd_volume_down = NULL; 279 char* cmd_mic_mute = NULL; 280 281 char* cmd_power_down = NULL; 282 char* cmd_power_off = NULL; 283 char* cmd_power_sleep = NULL; 284 285 // for the rendering thread, so we can clean it up 286 pthread_t draw_thread; 287 // main thread still sometimes calls redraw() 288 // allow you to disable. handy if you use bar with lots of crap. 289 bool redraw_thread = false; 290 291 // experimental bar stuff 292 #define BAR_VERT 0 293 #define BAR_FLAT 1 294 #define BAR_DEFAULT 0 295 #define BAR_REVERSED 1 296 #define BAR_BIDIRECTIONAL 2 297 #define MAX_BAR_COUNT 65535 298 #define MIN_BAR_COUNT 1 299 300 bool bar_enabled = false; 301 double *bar_heights = NULL; 302 double bar_step = 15; 303 double bar_base_height = 25; 304 double bar_periodic_step = 15; 305 double max_bar_height = 25; 306 int bar_count = 10; 307 int bar_orientation = BAR_FLAT; 308 309 char bar_base_color[9] = "000000ff"; 310 char bar_x_expr[32] = "0"; 311 char bar_y_expr[32] = ""; // empty string on y means use x as offset based on orientation 312 char bar_width_expr[32] = ""; // empty string means full width based on bar orientation 313 bool bar_bidirectional = false; 314 bool bar_reversed = false; 315 316 /* isutf, u8_dec © 2005 Jeff Bezanson, public domain */ 317 #define isutf(c) (((c)&0xC0) != 0x80) 318 319 /* 320 * Checks if the given path leads to an actual file or something else, e.g. a directory 321 */ 322 int is_directory(const char *path) { 323 struct stat path_stat; 324 stat(path, &path_stat); 325 return S_ISDIR(path_stat.st_mode); 326 } 327 328 /* 329 * Decrements i to point to the previous unicode glyph 330 * 331 */ 332 static void u8_dec(char *s, int *i) { 333 (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) || isutf(s[--(*i)]) || --(*i)); 334 } 335 336 /* 337 * fetches the keylayout name 338 * -1 (do not) 339 * arg: 0 (show full string returned) 340 * 1 (show the text, sans parenthesis) 341 * 2 (show just what's in the parenthesis) 342 * 343 * credit to the XKB/xcb implementation (no libx11) from https://gist.github.com/bluetech/6061368 344 * docs are really sparse, so finding some random implementation was nice 345 */ 346 static char* get_keylayoutname(int mode, xcb_connection_t* conn) { 347 if (mode < 0 || mode > 2) return NULL; 348 char *newans = NULL, *newans2 = NULL, *answer = xcb_get_key_group_names(conn); 349 int substringStart = 0, substringEnd = 0, size = 0; 350 DEBUG("keylayout answer is: [%s]\n", answer); 351 switch (mode) { 352 case 1: 353 // truncate the string at the first parens 354 for (int i = 0; answer[i] != '\0'; ++i) { 355 if (answer[i] == '(') { 356 if (i != 0 && answer[i - 1] == ' ') { 357 answer[i - 1] = '\0'; 358 break; 359 } else { 360 answer[i] = '\0'; 361 break; 362 } 363 } 364 } 365 break; 366 case 2: 367 for (int i = 0; answer[i] != '\0'; ++i) { 368 if (answer[i] == '(') { 369 newans = &answer[i + 1]; 370 substringStart = i + 1; 371 } else if (answer[i] == ')' && newans != NULL) { 372 answer[i] = '\0'; 373 substringEnd = i; 374 break; 375 } 376 } 377 if (newans != NULL) { 378 size = sizeof(char) * (substringEnd - substringStart + 1); 379 newans2 = malloc(size); 380 memcpy(newans2, newans, size); 381 free(answer); 382 answer = newans2; 383 } 384 break; 385 case 0: 386 // fall through 387 default: 388 break; 389 } 390 DEBUG("answer after mode parsing: [%s]\n", answer); 391 // Free symbolic names structures 392 return answer; 393 } 394 395 /* 396 * Loads the XKB keymap from the X11 server and feeds it to xkbcommon. 397 * Necessary so that we can properly let xkbcommon track the keyboard state and 398 * translate keypresses to utf-8. 399 * 400 */ 401 402 static bool load_keymap(void) { 403 if (xkb_context == NULL) { 404 if ((xkb_context = xkb_context_new(0)) == NULL) { 405 fprintf(stderr, "[i3lock] could not create xkbcommon context\n"); 406 return false; 407 } 408 } 409 410 xkb_keymap_unref(xkb_keymap); 411 412 int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn); 413 DEBUG("device = %d\n", device_id); 414 if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL) { 415 fprintf(stderr, "[i3lock] xkb_x11_keymap_new_from_device failed\n"); 416 return false; 417 } 418 419 struct xkb_state *new_state = 420 xkb_x11_state_new_from_device(xkb_keymap, conn, device_id); 421 if (new_state == NULL) { 422 fprintf(stderr, "[i3lock] xkb_x11_state_new_from_device failed\n"); 423 return false; 424 } 425 426 xkb_state_unref(xkb_state); 427 xkb_state = new_state; 428 429 return true; 430 } 431 432 #if XKBCOMPOSE == 1 433 /* 434 * Loads the XKB compose table from the given locale. 435 * 436 */ 437 static bool load_compose_table(const char *locale) { 438 xkb_compose_table_unref(xkb_compose_table); 439 440 if ((xkb_compose_table = xkb_compose_table_new_from_locale(xkb_context, locale, 0)) == NULL) { 441 fprintf(stderr, "[i3lock] xkb_compose_table_new_from_locale failed\n"); 442 return false; 443 } 444 445 struct xkb_compose_state *new_compose_state = xkb_compose_state_new(xkb_compose_table, 0); 446 if (new_compose_state == NULL) { 447 fprintf(stderr, "[i3lock] xkb_compose_state_new failed\n"); 448 return false; 449 } 450 451 xkb_compose_state_unref(xkb_compose_state); 452 xkb_compose_state = new_compose_state; 453 454 return true; 455 } 456 #endif /* XKBCOMPOSE */ 457 458 /* 459 * Clears the memory which stored the password to be a bit safer against 460 * cold-boot attacks. 461 * 462 */ 463 static void clear_password_memory(void) { 464 #ifdef HAVE_EXPLICIT_BZERO 465 /* Use explicit_bzero(3) which was explicitly designed not to be 466 * optimized out by the compiler. */ 467 explicit_bzero(password, strlen(password)); 468 #else 469 /* A volatile pointer to the password buffer to prevent the compiler from 470 * optimizing this out. */ 471 volatile char *vpassword = password; 472 for (size_t c = 0; c < sizeof(password); c++) 473 /* We store a non-random pattern which consists of the (irrelevant) 474 * index plus (!) the value of the beep variable. This prevents the 475 * compiler from optimizing the calls away, since the value of 'beep' 476 * is not known at compile-time. */ 477 vpassword[c] = c + (int)beep; 478 #endif 479 } 480 481 ev_timer *start_timer(ev_timer *timer_obj, ev_tstamp timeout, ev_callback_t callback) { 482 if (timer_obj) { 483 ev_timer_stop(main_loop, timer_obj); 484 ev_timer_set(timer_obj, timeout, 0.); 485 ev_timer_start(main_loop, timer_obj); 486 } else { 487 /* When there is no memory, we just don’t have a timeout. We cannot 488 * exit() here, since that would effectively unlock the screen. */ 489 timer_obj = calloc(sizeof(struct ev_timer), 1); 490 if (timer_obj) { 491 ev_timer_init(timer_obj, callback, timeout, 0.); 492 ev_timer_start(main_loop, timer_obj); 493 } 494 } 495 return timer_obj; 496 } 497 498 ev_timer *stop_timer(ev_timer *timer_obj) { 499 if (timer_obj) { 500 ev_timer_stop(main_loop, timer_obj); 501 free(timer_obj); 502 } 503 return NULL; 504 } 505 506 /* 507 * Neccessary calls after ending input via enter or others 508 * 509 */ 510 static void finish_input(void) { 511 password[input_position] = '\0'; 512 unlock_state = STATE_KEY_PRESSED; 513 redraw_screen(); 514 input_done(); 515 } 516 517 /* 518 * Resets auth_state to STATE_AUTH_IDLE 2 seconds after an unsuccessful 519 * authentication event. 520 * 521 */ 522 static void clear_auth_wrong(EV_P_ ev_timer *w, int revents) { 523 DEBUG("clearing auth wrong\n"); 524 auth_state = STATE_AUTH_IDLE; 525 redraw_screen(); 526 527 /* Clear modifier string. */ 528 if (modifier_string != NULL) { 529 free(modifier_string); 530 modifier_string = NULL; 531 } 532 533 /* Now free this timeout. */ 534 STOP_TIMER(clear_auth_wrong_timeout); 535 536 /* retry with input done during auth verification */ 537 if (retry_verification) { 538 retry_verification = false; 539 finish_input(); 540 } 541 } 542 543 static void clear_indicator_cb(EV_P_ ev_timer *w, int revents) { 544 clear_indicator(); 545 STOP_TIMER(clear_indicator_timeout); 546 } 547 548 static void clear_input(void) { 549 input_position = 0; 550 clear_password_memory(); 551 password[input_position] = '\0'; 552 } 553 554 static void discard_passwd_cb(EV_P_ ev_timer *w, int revents) { 555 clear_input(); 556 STOP_TIMER(discard_passwd_timeout); 557 } 558 559 static void input_done(void) { 560 STOP_TIMER(clear_auth_wrong_timeout); 561 auth_state = STATE_AUTH_VERIFY; 562 unlock_state = STATE_STARTED; 563 redraw_screen(); 564 565 if (no_verify) { 566 ev_break(EV_DEFAULT, EVBREAK_ALL); 567 return; 568 } 569 570 #ifdef __OpenBSD__ 571 struct passwd *pw; 572 573 if (!(pw = getpwuid(getuid()))) 574 errx(1, "unknown uid %u.", getuid()); 575 576 if (auth_userokay(pw->pw_name, NULL, NULL, password) != 0) { 577 DEBUG("successfully authenticated\n"); 578 clear_password_memory(); 579 580 ev_break(EV_DEFAULT, EVBREAK_ALL); 581 return; 582 } 583 #else 584 if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS) { 585 DEBUG("successfully authenticated\n"); 586 clear_password_memory(); 587 588 /* PAM credentials should be refreshed, this will for example update any kerberos tickets. 589 * Related to credentials pam_end() needs to be called to cleanup any temporary 590 * credentials like kerberos /tmp/krb5cc_pam_* files which may of been left behind if the 591 * refresh of the credentials failed. */ 592 pam_setcred(pam_handle, PAM_REFRESH_CRED); 593 pam_cleanup = true; 594 595 ev_break(EV_DEFAULT, EVBREAK_ALL); 596 return; 597 } 598 #endif 599 600 if (debug_mode) 601 fprintf(stderr, "Authentication failure\n"); 602 603 /* Get state of Caps and Num lock modifiers, to be displayed in 604 * STATE_AUTH_WRONG state */ 605 xkb_mod_index_t idx, num_mods; 606 const char *mod_name; 607 608 num_mods = xkb_keymap_num_mods(xkb_keymap); 609 610 for (idx = 0; idx < num_mods; idx++) { 611 if (!xkb_state_mod_index_is_active(xkb_state, idx, XKB_STATE_MODS_EFFECTIVE)) 612 continue; 613 614 mod_name = xkb_keymap_mod_get_name(xkb_keymap, idx); 615 if (mod_name == NULL) 616 continue; 617 618 /* Replace certain xkb names with nicer, human-readable ones. */ 619 if (strcmp(mod_name, XKB_MOD_NAME_CAPS) == 0) 620 mod_name = "Caps Lock"; 621 else if (strcmp(mod_name, XKB_MOD_NAME_ALT) == 0) 622 mod_name = "Alt"; 623 else if (strcmp(mod_name, XKB_MOD_NAME_NUM) == 0) 624 mod_name = "Num Lock"; 625 else if (strcmp(mod_name, XKB_MOD_NAME_LOGO) == 0) 626 mod_name = "Super"; 627 628 if (show_modkey_text) { 629 char *tmp; 630 if (modifier_string == NULL) { 631 if (asprintf(&tmp, "%s", mod_name) != -1) 632 modifier_string = tmp; 633 } else if (asprintf(&tmp, "%s, %s", modifier_string, mod_name) != -1) { 634 free(modifier_string); 635 modifier_string = tmp; 636 } 637 } 638 } 639 640 auth_state = STATE_AUTH_WRONG; 641 failed_attempts += 1; 642 clear_input(); 643 if (unlock_indicator) 644 redraw_screen(); 645 646 /* Clear this state after 2 seconds (unless the user enters another 647 * password during that time). */ 648 ev_now_update(main_loop); 649 START_TIMER(clear_auth_wrong_timeout, TSTAMP_N_SECS(2), clear_auth_wrong); 650 651 /* Cancel the clear_indicator_timeout, it would hide the unlock indicator 652 * too early. */ 653 STOP_TIMER(clear_indicator_timeout); 654 655 /* beep on authentication failure, if enabled */ 656 if (beep) { 657 xcb_bell(conn, 100); 658 xcb_flush(conn); 659 } 660 } 661 662 static void redraw_timeout(EV_P_ ev_timer *w, int revents) { 663 redraw_screen(); 664 STOP_TIMER(w); 665 } 666 667 static bool skip_without_validation(void) { 668 if (input_position != 0) 669 return false; 670 671 if (skip_repeated_empty_password || ignore_empty_password) 672 return true; 673 674 return false; 675 } 676 677 /* 678 * Handle key presses. Fixes state, then looks up the key symbol for the 679 * given keycode, then looks up the key symbol (as UCS-2), converts it to 680 * UTF-8 and stores it in the password array. 681 * 682 */ 683 static void handle_key_press(xcb_key_press_event_t *event) { 684 xkb_keysym_t ksym; 685 char buffer[128]; 686 int n; 687 bool ctrl; 688 #if XKBCOMPOSE == 1 689 bool composed = false; 690 #endif 691 692 ksym = xkb_state_key_get_one_sym(xkb_state, event->detail); 693 ctrl = xkb_state_mod_name_is_active(xkb_state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_DEPRESSED); 694 695 /* The buffer will be null-terminated, so n >= 2 for 1 actual character. */ 696 memset(buffer, '\0', sizeof(buffer)); 697 698 #if XKBCOMPOSE == 1 699 if (xkb_compose_state && xkb_compose_state_feed(xkb_compose_state, ksym) == XKB_COMPOSE_FEED_ACCEPTED) { 700 switch (xkb_compose_state_get_status(xkb_compose_state)) { 701 case XKB_COMPOSE_NOTHING: 702 break; 703 case XKB_COMPOSE_COMPOSING: 704 return; 705 case XKB_COMPOSE_COMPOSED: 706 /* xkb_compose_state_get_utf8 doesn't include the terminating byte in the return value 707 * as xkb_keysym_to_utf8 does. Adding one makes the variable n consistent. */ 708 n = xkb_compose_state_get_utf8(xkb_compose_state, buffer, sizeof(buffer)) + 1; 709 ksym = xkb_compose_state_get_one_sym(xkb_compose_state); 710 composed = true; 711 break; 712 case XKB_COMPOSE_CANCELLED: 713 xkb_compose_state_reset(xkb_compose_state); 714 return; 715 } 716 } 717 718 if (!composed) { 719 n = xkb_keysym_to_utf8(ksym, buffer, sizeof(buffer)); 720 } 721 #else 722 n = xkb_keysym_to_utf8(ksym, buffer, sizeof(buffer)); 723 #endif 724 725 //custom key commands 726 if (hotkeys) { 727 switch(ksym) { 728 case XKB_KEY_XF86MonBrightnessUp: 729 if (cmd_brightness_up) { 730 system(cmd_brightness_up); 731 return; 732 } 733 break; 734 case XKB_KEY_XF86MonBrightnessDown: 735 if (cmd_brightness_down) { 736 system(cmd_brightness_down); 737 return; 738 } 739 break; 740 case XKB_KEY_XF86AudioPlay: 741 if (cmd_media_play) { 742 system(cmd_media_play); 743 return; 744 } 745 break; 746 case XKB_KEY_XF86AudioPause: 747 if (cmd_media_pause) { 748 system(cmd_media_pause); 749 return; 750 } 751 break; 752 case XKB_KEY_XF86AudioStop: 753 if (cmd_media_stop) { 754 system(cmd_media_stop); 755 return; 756 } 757 break; 758 case XKB_KEY_XF86AudioPrev: 759 if (cmd_media_prev) { 760 system(cmd_media_prev); 761 return; 762 } 763 break; 764 case XKB_KEY_XF86AudioNext: 765 if (cmd_media_next) { 766 system(cmd_media_next); 767 return; 768 } 769 break; 770 case XKB_KEY_XF86AudioMute: 771 if (cmd_audio_mute) { 772 system(cmd_audio_mute); 773 return; 774 } 775 break; 776 case XKB_KEY_XF86AudioLowerVolume: 777 if (cmd_volume_down) { 778 system(cmd_volume_down); 779 return; 780 } 781 break; 782 case XKB_KEY_XF86AudioRaiseVolume: 783 if (cmd_volume_up) { 784 system(cmd_volume_up); 785 return; 786 } 787 break; 788 case XKB_KEY_XF86AudioMicMute: 789 if (cmd_mic_mute) { 790 system(cmd_mic_mute); 791 return; 792 } 793 break; 794 case XKB_KEY_XF86PowerDown: 795 if (cmd_power_down) { 796 system(cmd_power_down); 797 return; 798 } 799 break; 800 case XKB_KEY_XF86PowerOff: 801 if (cmd_power_off) { 802 system(cmd_power_off); 803 return; 804 } 805 break; 806 case XKB_KEY_XF86Sleep: 807 if (cmd_power_sleep) { 808 system(cmd_power_sleep); 809 return; 810 } 811 break; 812 } 813 } 814 815 // media keys 816 if (pass_media_keys) { 817 switch(ksym) { 818 case XKB_KEY_XF86AudioPlay: 819 case XKB_KEY_XF86AudioPause: 820 case XKB_KEY_XF86AudioStop: 821 case XKB_KEY_XF86AudioPrev: 822 case XKB_KEY_XF86AudioNext: 823 case XKB_KEY_XF86AudioMute: 824 case XKB_KEY_XF86AudioLowerVolume: 825 case XKB_KEY_XF86AudioRaiseVolume: 826 case XKB_KEY_XF86AudioMicMute: 827 xcb_send_event(conn, true, screen->root, XCB_EVENT_MASK_BUTTON_PRESS, (char *)event); 828 return; 829 } 830 } 831 832 // screen keys 833 if (pass_screen_keys) { 834 switch(ksym) { 835 case XKB_KEY_XF86MonBrightnessUp: 836 case XKB_KEY_XF86MonBrightnessDown: 837 xcb_send_event(conn, true, screen->root, XCB_EVENT_MASK_BUTTON_PRESS, (char *)event); 838 return; 839 } 840 } 841 842 // power keys 843 if (pass_power_keys) { 844 switch(ksym) { 845 case XKB_KEY_XF86PowerDown: 846 case XKB_KEY_XF86PowerOff: 847 case XKB_KEY_XF86Sleep: 848 xcb_send_event(conn, true, screen->root, XCB_EVENT_MASK_BUTTON_PRESS, (char *)event); 849 return; 850 } 851 } 852 853 // volume keys 854 if (pass_volume_keys) { 855 switch(ksym) { 856 case XKB_KEY_XF86AudioMute: 857 case XKB_KEY_XF86AudioLowerVolume: 858 case XKB_KEY_XF86AudioRaiseVolume: 859 xcb_send_event(conn, true, screen->root, XCB_EVENT_MASK_BUTTON_PRESS, (char *)event); 860 return; 861 } 862 } 863 864 // return/enter/etc 865 switch (ksym) { 866 case XKB_KEY_j: 867 case XKB_KEY_m: 868 case XKB_KEY_Return: 869 case XKB_KEY_KP_Enter: 870 case XKB_KEY_XF86ScreenSaver: 871 if ((ksym == XKB_KEY_j || ksym == XKB_KEY_m) && !ctrl) 872 break; 873 874 if (auth_state == STATE_AUTH_WRONG) { 875 retry_verification = true; 876 return; 877 } 878 879 if (skip_without_validation()) { 880 clear_input(); 881 return; 882 } 883 finish_input(); 884 skip_repeated_empty_password = true; 885 return; 886 default: 887 skip_repeated_empty_password = false; 888 // A new password is being entered, but a previous one is pending. 889 // Discard the old one and clear the retry_verification flag. 890 if (retry_verification) { 891 retry_verification = false; 892 clear_input(); 893 } 894 } 895 896 // backspace, esc, delete, etc 897 switch (ksym) { 898 case XKB_KEY_u: 899 case XKB_KEY_Escape: 900 if ((ksym == XKB_KEY_u && ctrl) || 901 ksym == XKB_KEY_Escape) { 902 DEBUG("C-u pressed\n"); 903 clear_input(); 904 /* Also hide the unlock indicator */ 905 if (unlock_indicator) 906 clear_indicator(); 907 return; 908 } 909 break; 910 911 case XKB_KEY_Delete: 912 case XKB_KEY_KP_Delete: 913 /* Deleting forward doesn’t make sense, as i3lock doesn’t allow you 914 * to move the cursor when entering a password. We need to eat this 915 * key press so that it won’t be treated as part of the password, 916 * see issue #50. */ 917 return; 918 919 case XKB_KEY_h: 920 case XKB_KEY_BackSpace: 921 if (ksym == XKB_KEY_h && !ctrl) 922 break; 923 924 if (input_position == 0) { 925 START_TIMER(clear_indicator_timeout, 1.0, clear_indicator_cb); 926 unlock_state = STATE_NOTHING_TO_DELETE; 927 redraw_screen(); 928 return; 929 } 930 931 /* decrement input_position to point to the previous glyph */ 932 u8_dec(password, &input_position); 933 password[input_position] = '\0'; 934 935 /* Hide the unlock indicator after a bit if the password buffer is 936 * empty. */ 937 START_TIMER(clear_indicator_timeout, 1.0, clear_indicator_cb); 938 unlock_state = STATE_BACKSPACE_ACTIVE; 939 redraw_screen(); 940 unlock_state = STATE_KEY_PRESSED; 941 return; 942 } 943 944 if ((input_position + 8) >= (int)sizeof(password)) 945 return; 946 947 #if 0 948 /* FIXME: handle all of these? */ 949 printf("is_keypad_key = %d\n", xcb_is_keypad_key(sym)); 950 printf("is_private_keypad_key = %d\n", xcb_is_private_keypad_key(sym)); 951 printf("xcb_is_cursor_key = %d\n", xcb_is_cursor_key(sym)); 952 printf("xcb_is_pf_key = %d\n", xcb_is_pf_key(sym)); 953 printf("xcb_is_function_key = %d\n", xcb_is_function_key(sym)); 954 printf("xcb_is_misc_function_key = %d\n", xcb_is_misc_function_key(sym)); 955 printf("xcb_is_modifier_key = %d\n", xcb_is_modifier_key(sym)); 956 #endif 957 958 if (n < 2) 959 return; 960 961 /* store it in the password array as UTF-8 */ 962 memcpy(password + input_position, buffer, n - 1); 963 input_position += n - 1; 964 DEBUG("current password = %.*s\n", input_position, password); 965 966 if (unlock_indicator) { 967 unlock_state = STATE_KEY_ACTIVE; 968 redraw_screen(); 969 unlock_state = STATE_KEY_PRESSED; 970 971 struct ev_timer *timeout = NULL; 972 START_TIMER(timeout, TSTAMP_N_SECS(0.25), redraw_timeout); 973 STOP_TIMER(clear_indicator_timeout); 974 } 975 976 START_TIMER(discard_passwd_timeout, TSTAMP_N_MINS(3), discard_passwd_cb); 977 } 978 979 /* 980 * A visibility notify event will be received when the visibility (= can the 981 * user view the complete window) changes, so for example when a popup overlays 982 * some area of the i3lock window. 983 * 984 * In this case, we raise our window on top so that the popup (or whatever is 985 * hiding us) gets hidden. 986 * 987 */ 988 static void handle_visibility_notify(xcb_connection_t *conn, 989 xcb_visibility_notify_event_t *event) { 990 if (event->state != XCB_VISIBILITY_UNOBSCURED) { 991 uint32_t values[] = {XCB_STACK_MODE_ABOVE}; 992 xcb_configure_window(conn, event->window, XCB_CONFIG_WINDOW_STACK_MODE, values); 993 xcb_flush(conn); 994 } 995 } 996 997 /* 998 * Called when the keyboard mapping changes. We update our symbols. 999 * 1000 * We ignore errors — if the new keymap cannot be loaded it’s better if the 1001 * screen stays locked and the user intervenes by using killall i3lock. 1002 * 1003 */ 1004 static void process_xkb_event(xcb_generic_event_t *gevent) { 1005 union xkb_event { 1006 struct { 1007 uint8_t response_type; 1008 uint8_t xkbType; 1009 uint16_t sequence; 1010 xcb_timestamp_t time; 1011 uint8_t deviceID; 1012 } any; 1013 xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify; 1014 xcb_xkb_map_notify_event_t map_notify; 1015 xcb_xkb_state_notify_event_t state_notify; 1016 } *event = (union xkb_event *)gevent; 1017 1018 DEBUG("process_xkb_event for device %d\n", event->any.deviceID); 1019 1020 if (event->any.deviceID != xkb_x11_get_core_keyboard_device_id(conn)) 1021 return; 1022 1023 /* 1024 * XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap 1025 * updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent 1026 * recompilations. 1027 */ 1028 switch (event->any.xkbType) { 1029 case XCB_XKB_NEW_KEYBOARD_NOTIFY: 1030 if (event->new_keyboard_notify.changed & XCB_XKB_NKN_DETAIL_KEYCODES) 1031 (void)load_keymap(); 1032 break; 1033 1034 case XCB_XKB_MAP_NOTIFY: 1035 (void)load_keymap(); 1036 break; 1037 1038 case XCB_XKB_STATE_NOTIFY: 1039 xkb_state_update_mask(xkb_state, 1040 event->state_notify.baseMods, 1041 event->state_notify.latchedMods, 1042 event->state_notify.lockedMods, 1043 event->state_notify.baseGroup, 1044 event->state_notify.latchedGroup, 1045 event->state_notify.lockedGroup); 1046 if (layout_text != NULL) { 1047 free(layout_text); 1048 layout_text = NULL; 1049 } 1050 layout_text = get_keylayoutname(keylayout_mode, conn); 1051 redraw_screen(); 1052 break; 1053 } 1054 } 1055 1056 /* 1057 * Called when the properties on the root window change, e.g. when the screen 1058 * resolution changes. If so we update the window to cover the whole screen 1059 * and also redraw the image, if any. 1060 * 1061 */ 1062 static void handle_screen_resize(void) { 1063 xcb_get_geometry_cookie_t geomc; 1064 xcb_get_geometry_reply_t *geom; 1065 geomc = xcb_get_geometry(conn, screen->root); 1066 if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL) 1067 return; 1068 1069 if (last_resolution[0] == geom->width && 1070 last_resolution[1] == geom->height) { 1071 free(geom); 1072 return; 1073 } 1074 1075 last_resolution[0] = geom->width; 1076 last_resolution[1] = geom->height; 1077 1078 free(geom); 1079 1080 redraw_screen(); 1081 1082 uint32_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; 1083 xcb_configure_window(conn, win, mask, last_resolution); 1084 xcb_flush(conn); 1085 1086 randr_query(screen->root); 1087 redraw_screen(); 1088 } 1089 1090 static ssize_t read_raw_image_native(uint32_t *dest, FILE *src, size_t width, size_t height, int pixstride) { 1091 ssize_t count = 0; 1092 for (size_t y = 0; y < height; y++) { 1093 size_t n = fread(&dest[y * pixstride], 1, width * 4, src); 1094 count += n; 1095 if (n < (size_t)(width * 4)) 1096 break; 1097 } 1098 1099 return count; 1100 } 1101 1102 struct raw_pixel_format { 1103 int bpp; 1104 int red; 1105 int green; 1106 int blue; 1107 }; 1108 1109 static ssize_t read_raw_image_fmt(uint32_t *dest, FILE *src, size_t width, size_t height, int pixstride, 1110 struct raw_pixel_format fmt) { 1111 unsigned char *buf = malloc(width * fmt.bpp); 1112 if (buf == NULL) 1113 return -1; 1114 1115 ssize_t count = 0; 1116 for (size_t y = 0; y < height; y++) { 1117 size_t n = fread(buf, 1, width * fmt.bpp, src); 1118 count += n; 1119 if (n < (size_t)(width * fmt.bpp)) 1120 break; 1121 1122 for (size_t x = 0; x < width; ++x) { 1123 int idx = x * fmt.bpp; 1124 dest[y * pixstride + x] = 0 | 1125 (buf[idx + fmt.red]) << 16 | 1126 (buf[idx + fmt.green]) << 8 | 1127 (buf[idx + fmt.blue]); 1128 } 1129 } 1130 1131 free(buf); 1132 return count; 1133 } 1134 1135 // Pre-defind pixel formats (<bytes per pixel>, <red pixel>, <green pixel>, <blue pixel>) 1136 static const struct raw_pixel_format raw_fmt_rgb = {3, 0, 1, 2}; 1137 static const struct raw_pixel_format raw_fmt_rgbx = {4, 0, 1, 2}; 1138 static const struct raw_pixel_format raw_fmt_xrgb = {4, 1, 2, 3}; 1139 static const struct raw_pixel_format raw_fmt_bgr = {3, 2, 1, 0}; 1140 static const struct raw_pixel_format raw_fmt_bgrx = {4, 2, 1, 0}; 1141 static const struct raw_pixel_format raw_fmt_xbgr = {4, 3, 2, 1}; 1142 1143 static cairo_surface_t *read_raw_image(const char *image_path, const char *image_raw_format) { 1144 cairo_surface_t *img; 1145 1146 #define RAW_PIXFMT_MAXLEN 6 1147 #define STRINGIFY1(x) #x 1148 #define STRINGIFY(x) STRINGIFY1(x) 1149 /* Parse format as <width>x<height>:<pixfmt> */ 1150 char pixfmt[RAW_PIXFMT_MAXLEN + 1]; 1151 size_t w, h; 1152 const char *fmt = "%zux%zu:%" STRINGIFY(RAW_PIXFMT_MAXLEN) "s"; 1153 if (sscanf(image_raw_format, fmt, &w, &h, pixfmt) != 3) { 1154 fprintf(stderr, "Invalid image format: \"%s\"\n", image_raw_format); 1155 return NULL; 1156 } 1157 #undef RAW_PIXFMT_MAXLEN 1158 #undef STRINGIFY1 1159 #undef STRINGIFY 1160 1161 /* Create image surface */ 1162 img = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h); 1163 if (cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) { 1164 fprintf(stderr, "Could not create surface: %s\n", 1165 cairo_status_to_string(cairo_surface_status(img))); 1166 return NULL; 1167 } 1168 cairo_surface_flush(img); 1169 1170 /* Use uint32_t* because cairo uses native endianness */ 1171 uint32_t *data = (uint32_t *)cairo_image_surface_get_data(img); 1172 const int pixstride = cairo_image_surface_get_stride(img) / 4; 1173 1174 FILE *f = fopen(image_path, "r"); 1175 if (f == NULL) { 1176 fprintf(stderr, "Could not open image \"%s\": %s\n", 1177 image_path, strerror(errno)); 1178 cairo_surface_destroy(img); 1179 return NULL; 1180 } 1181 1182 /* Read the image, respecting cairo's stride, according to the pixfmt */ 1183 ssize_t size, count; 1184 if (strcmp(pixfmt, "native") == 0) { 1185 /* If the pixfmt is 'native', just read each line directly into the buffer */ 1186 size = w * h * 4; 1187 count = read_raw_image_native(data, f, w, h, pixstride); 1188 } else { 1189 const struct raw_pixel_format *fmt = NULL; 1190 1191 if (strcmp(pixfmt, "rgb") == 0) 1192 fmt = &raw_fmt_rgb; 1193 else if (strcmp(pixfmt, "rgbx") == 0) 1194 fmt = &raw_fmt_rgbx; 1195 else if (strcmp(pixfmt, "xrgb") == 0) 1196 fmt = &raw_fmt_xrgb; 1197 else if (strcmp(pixfmt, "bgr") == 0) 1198 fmt = &raw_fmt_bgr; 1199 else if (strcmp(pixfmt, "bgrx") == 0) 1200 fmt = &raw_fmt_bgrx; 1201 else if (strcmp(pixfmt, "xbgr") == 0) 1202 fmt = &raw_fmt_xbgr; 1203 1204 if (fmt == NULL) { 1205 fprintf(stderr, "Unknown raw pixel format: %s\n", pixfmt); 1206 fclose(f); 1207 cairo_surface_destroy(img); 1208 return NULL; 1209 } 1210 1211 size = w * h * fmt->bpp; 1212 count = read_raw_image_fmt(data, f, w, h, pixstride, *fmt); 1213 } 1214 1215 cairo_surface_mark_dirty(img); 1216 1217 if (count < size) { 1218 if (count < 0 || ferror(f)) { 1219 fprintf(stderr, "Failed to read image \"%s\": %s\n", 1220 image_path, strerror(errno)); 1221 fclose(f); 1222 cairo_surface_destroy(img); 1223 return NULL; 1224 } else { 1225 /* Print a warning if the file contains less data than expected, 1226 * but don't abort. It's useful to see how the image looks even if it's wrong. */ 1227 fprintf(stderr, "Warning: expected to read %zi bytes from \"%s\", read %zi\n", 1228 size, image_path, count); 1229 } 1230 } 1231 1232 fclose(f); 1233 return img; 1234 } 1235 1236 static bool verify_png_image(const char *image_path) { 1237 if (!image_path) { 1238 return false; 1239 } 1240 1241 /* Check file exists and has correct PNG header */ 1242 FILE *png_file = fopen(image_path, "r"); 1243 if (png_file == NULL) { 1244 DEBUG("Image file path \"%s\" cannot be opened: %s\n", image_path, strerror(errno)); 1245 return false; 1246 } 1247 unsigned char png_header[8]; 1248 memset(png_header, '\0', sizeof(png_header)); 1249 int bytes_read = fread(png_header, 1, sizeof(png_header), png_file); 1250 fclose(png_file); 1251 if (bytes_read != sizeof(png_header)) { 1252 DEBUG("Could not read PNG header from \"%s\"\n", image_path); 1253 return false; 1254 } 1255 1256 // Check PNG header according to the specification, available at: 1257 // https://www.w3.org/TR/2003/REC-PNG-20031110/#5PNG-file-signature 1258 static unsigned char PNG_REFERENCE_HEADER[8] = {137, 80, 78, 71, 13, 10, 26, 10}; 1259 if (memcmp(PNG_REFERENCE_HEADER, png_header, sizeof(png_header)) != 0) { 1260 DEBUG("File \"%s\" does not start with a PNG header. i3lock currently only supports loading PNG files.\n", image_path); 1261 return false; 1262 } 1263 return true; 1264 } 1265 1266 #ifndef __OpenBSD__ 1267 /* 1268 * Callback function for PAM. We only react on password request callbacks. 1269 * 1270 */ 1271 static int conv_callback(int num_msg, const struct pam_message **msg, 1272 struct pam_response **resp, void *appdata_ptr) { 1273 if (num_msg == 0) 1274 return 1; 1275 1276 /* PAM expects an array of responses, one for each message */ 1277 if ((*resp = calloc(num_msg, sizeof(struct pam_response))) == NULL) { 1278 perror("calloc"); 1279 return 1; 1280 } 1281 1282 for (int c = 0; c < num_msg; c++) { 1283 if (msg[c]->msg_style != PAM_PROMPT_ECHO_OFF && 1284 msg[c]->msg_style != PAM_PROMPT_ECHO_ON) 1285 continue; 1286 1287 /* return code is currently not used but should be set to zero */ 1288 resp[c]->resp_retcode = 0; 1289 if ((resp[c]->resp = strdup(password)) == NULL) { 1290 perror("strdup"); 1291 return 1; 1292 } 1293 } 1294 1295 return 0; 1296 } 1297 #endif 1298 1299 /* 1300 * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb. 1301 * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop 1302 * 1303 */ 1304 static void xcb_got_event(EV_P_ struct ev_io *w, int revents) { 1305 /* empty, because xcb_prepare_cb and xcb_check_cb are used */ 1306 } 1307 1308 /* 1309 * Flush before blocking (and waiting for new events) 1310 * 1311 */ 1312 static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) { 1313 xcb_flush(conn); 1314 } 1315 1316 /* 1317 * Try closing logind sleep lock fd passed over from xss-lock, in case we're 1318 * being run from there. 1319 * 1320 */ 1321 static void maybe_close_sleep_lock_fd(void) { 1322 const char *sleep_lock_fd = getenv("XSS_SLEEP_LOCK_FD"); 1323 char *endptr; 1324 if (sleep_lock_fd && *sleep_lock_fd != 0) { 1325 long int fd = strtol(sleep_lock_fd, &endptr, 10); 1326 if (*endptr == 0) { 1327 close(fd); 1328 } 1329 } 1330 } 1331 1332 /* 1333 * Instead of polling the X connection socket we leave this to 1334 * xcb_poll_for_event() which knows better than we can ever know. 1335 * 1336 */ 1337 static void xcb_check_cb(EV_P_ ev_check *w, int revents) { 1338 xcb_generic_event_t *event; 1339 1340 if (xcb_connection_has_error(conn)) 1341 errx(EXIT_FAILURE, "X11 connection broke, did your server terminate?"); 1342 1343 while ((event = xcb_poll_for_event(conn)) != NULL) { 1344 if (event->response_type == 0) { 1345 xcb_generic_error_t *error = (xcb_generic_error_t *)event; 1346 if (debug_mode) 1347 fprintf(stderr, "X11 Error received! sequence 0x%x, error_code = %d\n", 1348 error->sequence, error->error_code); 1349 free(event); 1350 continue; 1351 } 1352 1353 /* Strip off the highest bit (set if the event is generated) */ 1354 int type = (event->response_type & 0x7F); 1355 1356 switch (type) { 1357 case XCB_KEY_PRESS: 1358 handle_key_press((xcb_key_press_event_t *)event); 1359 break; 1360 1361 case XCB_VISIBILITY_NOTIFY: 1362 handle_visibility_notify(conn, (xcb_visibility_notify_event_t *)event); 1363 break; 1364 1365 case XCB_MAP_NOTIFY: 1366 maybe_close_sleep_lock_fd(); 1367 if (!dont_fork) { 1368 /* After the first MapNotify, we never fork again. We don’t 1369 * expect to get another MapNotify, but better be sure… */ 1370 dont_fork = true; 1371 1372 /* In the parent process, we exit */ 1373 if (fork() != 0) 1374 exit(EXIT_SUCCESS); 1375 1376 ev_loop_fork(EV_DEFAULT); 1377 } 1378 break; 1379 1380 case XCB_CONFIGURE_NOTIFY: 1381 handle_screen_resize(); 1382 break; 1383 1384 default: 1385 if (type == xkb_base_event) { 1386 process_xkb_event(event); 1387 } 1388 if (randr_base > -1 && 1389 type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { 1390 randr_query(screen->root); 1391 handle_screen_resize(); 1392 } 1393 } 1394 1395 free(event); 1396 } 1397 } 1398 1399 /* 1400 * This function is called from a fork()ed child and will raise the i3lock 1401 * window when the window is obscured, even when the main i3lock process is 1402 * blocked due to the authentication backend. 1403 * 1404 */ 1405 static void raise_loop(xcb_window_t window) { 1406 xcb_connection_t *conn; 1407 xcb_generic_event_t *event; 1408 int screens; 1409 1410 if (xcb_connection_has_error((conn = xcb_connect(NULL, &screens))) > 0) 1411 errx(EXIT_FAILURE, "Cannot open display"); 1412 1413 /* We need to know about the window being obscured or getting destroyed. */ 1414 xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, 1415 (uint32_t[]){ 1416 XCB_EVENT_MASK_VISIBILITY_CHANGE | 1417 XCB_EVENT_MASK_STRUCTURE_NOTIFY}); 1418 xcb_flush(conn); 1419 1420 DEBUG("Watching window 0x%08x\n", window); 1421 while ((event = xcb_wait_for_event(conn)) != NULL) { 1422 if (event->response_type == 0) { 1423 xcb_generic_error_t *error = (xcb_generic_error_t *)event; 1424 DEBUG("X11 Error received! sequence 0x%x, error_code = %d\n", 1425 error->sequence, error->error_code); 1426 free(event); 1427 continue; 1428 } 1429 /* Strip off the highest bit (set if the event is generated) */ 1430 int type = (event->response_type & 0x7F); 1431 DEBUG("Read event of type %d\n", type); 1432 switch (type) { 1433 case XCB_VISIBILITY_NOTIFY: 1434 handle_visibility_notify(conn, (xcb_visibility_notify_event_t *)event); 1435 break; 1436 case XCB_UNMAP_NOTIFY: 1437 DEBUG("UnmapNotify for 0x%08x\n", (((xcb_unmap_notify_event_t *)event)->window)); 1438 if (((xcb_unmap_notify_event_t *)event)->window == window) 1439 exit(EXIT_SUCCESS); 1440 break; 1441 case XCB_DESTROY_NOTIFY: 1442 DEBUG("DestroyNotify for 0x%08x\n", (((xcb_destroy_notify_event_t *)event)->window)); 1443 if (((xcb_destroy_notify_event_t *)event)->window == window) 1444 exit(EXIT_SUCCESS); 1445 break; 1446 default: 1447 DEBUG("Unhandled event type %d\n", type); 1448 break; 1449 } 1450 free(event); 1451 } 1452 } 1453 1454 /* 1455 * Loads an image from the given path. Handles JPEG and PNG. Returns NULL in case of error. 1456 */ 1457 cairo_surface_t* load_image(char* image_path) { 1458 cairo_surface_t *img = NULL; 1459 JPEG_INFO jpg_info; 1460 1461 if (image_raw_format != NULL && image_path != NULL) { 1462 /* Read image. 'read_raw_image' returns NULL on error, 1463 * so we don't have to handle errors here. */ 1464 img = read_raw_image(image_path, image_raw_format); 1465 } else if (verify_png_image(image_path)) { 1466 /* Create a pixmap to render on, fill it with the background color */ 1467 img = cairo_image_surface_create_from_png(image_path); 1468 } else if (file_is_jpg(image_path)) { 1469 DEBUG("Image looks like a jpeg, decoding\n"); 1470 unsigned char* jpg_data = read_JPEG_file(image_path, &jpg_info); 1471 if (jpg_data != NULL) { 1472 img = cairo_image_surface_create_for_data(jpg_data, 1473 CAIRO_FORMAT_ARGB32, jpg_info.width, jpg_info.height, 1474 jpg_info.stride); 1475 } 1476 } 1477 1478 /* In case loading failed, we just pretend no -i was specified. */ 1479 if (img && cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) { 1480 fprintf(stderr, "Could not load image \"%s\": %s\n", 1481 image_path, cairo_status_to_string(cairo_surface_status(img))); 1482 img = NULL; 1483 } 1484 1485 return img; 1486 } 1487 1488 /* 1489 * Reads the provided directory and stores the images path in the pointer array 1490 * img_slideshow 1491 */ 1492 bool load_slideshow_images(const char *path) { 1493 slideshow_enabled = true; 1494 DIR *d; 1495 struct dirent *dir; 1496 int file_count = 0; 1497 slideshow_image_count = 0; 1498 1499 DEBUG("Loading slideshow images at \"%s\"\n", path); 1500 1501 d = opendir(path); 1502 if (d == NULL) { 1503 printf("Could not open directory: %s\n", path); 1504 return false; 1505 } 1506 1507 regex_t reg; 1508 1509 if (regcomp(®, ".*\\.(jpe?g|png)", REG_EXTENDED)) { 1510 printf("Could not compile regex\n"); 1511 return false; 1512 } 1513 1514 while ((dir = readdir(d)) != NULL) { 1515 if (file_count >= 256) break; 1516 int result = regexec(®, dir->d_name, 0, NULL, 0); 1517 if (result) continue; 1518 1519 char path_to_image[256]; 1520 strcpy(path_to_image, path); 1521 strcat(path_to_image, "/"); 1522 strcat(path_to_image, dir->d_name); 1523 1524 img_slideshow[file_count] = strdup(path_to_image); 1525 1526 if (img_slideshow[file_count] != NULL) { 1527 ++file_count; 1528 } 1529 } 1530 1531 slideshow_image_count = file_count; 1532 regfree(®); 1533 closedir(d); 1534 return true; 1535 } 1536 1537 int main(int argc, char *argv[]) { 1538 struct passwd *pw; 1539 char *username; 1540 #ifndef __OpenBSD__ 1541 int ret; 1542 struct pam_conv conv = {conv_callback, NULL}; 1543 #endif 1544 int curs_choice = CURS_NONE; 1545 int o; 1546 int longoptind = 0; 1547 struct option longopts[] = { 1548 {"version", no_argument, NULL, 'v'}, 1549 {"nofork", no_argument, NULL, 'n'}, 1550 {"beep", no_argument, NULL, 'b'}, 1551 {"dpms", no_argument, NULL, 'd'}, 1552 {"color", required_argument, NULL, 'c'}, 1553 {"pointer", required_argument, NULL, 'p'}, 1554 {"debug", no_argument, NULL, 999}, 1555 {"help", no_argument, NULL, 'h'}, 1556 {"no-unlock-indicator", no_argument, NULL, 'u'}, 1557 {"image", required_argument, NULL, 'i'}, 1558 {"raw", required_argument, NULL, 998}, 1559 {"tiling", no_argument, NULL, 't'}, 1560 {"centered", no_argument, NULL, 'C'}, 1561 {"fill", no_argument, NULL, 'F'}, 1562 {"scale", no_argument, NULL, 'L'}, 1563 {"max", no_argument, NULL, 'M'}, 1564 {"ignore-empty-password", no_argument, NULL, 'e'}, 1565 {"inactivity-timeout", required_argument, NULL, 'I'}, 1566 {"show-failed-attempts", no_argument, NULL, 'f'}, 1567 {"screen", required_argument, NULL, 'S'}, 1568 {"blur", required_argument, NULL, 'B'}, 1569 1570 // options for unlock indicator colors 1571 {"insidever-color", required_argument, NULL, 300}, 1572 {"insidewrong-color", required_argument, NULL, 301}, 1573 {"inside-color", required_argument, NULL, 302}, 1574 {"ringver-color", required_argument, NULL, 303}, 1575 {"ringwrong-color", required_argument, NULL, 304}, 1576 {"ring-color", required_argument, NULL, 305}, 1577 {"line-color", required_argument, NULL, 306}, 1578 {"verif-color", required_argument, NULL, 307}, 1579 {"wrong-color", required_argument, NULL, 308}, 1580 {"layout-color", required_argument, NULL, 309}, 1581 {"time-color", required_argument, NULL, 310}, 1582 {"date-color", required_argument, NULL, 311}, 1583 {"modif-color", required_argument, NULL, 322}, 1584 {"keyhl-color", required_argument, NULL, 312}, 1585 {"bshl-color", required_argument, NULL, 313}, 1586 {"separator-color", required_argument, NULL, 314}, 1587 {"greeter-color", required_argument, NULL, 315}, 1588 1589 // text outline colors 1590 {"verifoutline-color", required_argument, NULL, 316}, 1591 {"wrongoutline-color", required_argument, NULL, 317}, 1592 {"layoutoutline-color", required_argument, NULL, 318}, 1593 {"timeoutline-color", required_argument, NULL, 319}, 1594 {"dateoutline-color", required_argument, NULL, 320}, 1595 {"greeteroutline-color", required_argument, NULL, 321}, 1596 {"modifoutline-color", required_argument, NULL, 323}, 1597 1598 {"line-uses-ring", no_argument, NULL, 'r'}, 1599 {"line-uses-inside", no_argument, NULL, 's'}, 1600 1601 {"clock", no_argument, NULL, 'k'}, 1602 {"force-clock", no_argument, NULL, 400}, 1603 {"indicator", no_argument, NULL, 401}, 1604 {"radius", required_argument, NULL, 402}, 1605 {"ring-width", required_argument, NULL, 403}, 1606 1607 // alignment 1608 {"time-align", required_argument, NULL, 500}, 1609 {"date-align", required_argument, NULL, 501}, 1610 {"verif-align", required_argument, NULL, 502}, 1611 {"wrong-align", required_argument, NULL, 503}, 1612 {"layout-align", required_argument, NULL, 504}, 1613 {"modif-align", required_argument, NULL, 505}, 1614 {"greeter-align", required_argument, NULL, 506}, 1615 1616 // string stuff 1617 {"time-str", required_argument, NULL, 510}, 1618 {"date-str", required_argument, NULL, 511}, 1619 {"verif-text", required_argument, NULL, 512}, 1620 {"wrong-text", required_argument, NULL, 513}, 1621 {"keylayout", required_argument, NULL, 514}, 1622 {"noinput-text", required_argument, NULL, 515}, 1623 {"lock-text", required_argument, NULL, 516}, 1624 {"lockfailed-text", required_argument, NULL, 517}, 1625 {"greeter-text", required_argument, NULL, 518}, 1626 {"no-modkey-text", no_argument, NULL, 519}, 1627 1628 // fonts 1629 {"time-font", required_argument, NULL, 520}, 1630 {"date-font", required_argument, NULL, 521}, 1631 {"verif-font", required_argument, NULL, 522}, 1632 {"wrong-font", required_argument, NULL, 523}, 1633 {"layout-font", required_argument, NULL, 524}, 1634 {"greeter-font", required_argument, NULL, 525}, 1635 1636 // text size 1637 {"time-size", required_argument, NULL, 530}, 1638 {"date-size", required_argument, NULL, 531}, 1639 {"verif-size", required_argument, NULL, 532}, 1640 {"wrong-size", required_argument, NULL, 533}, 1641 {"layout-size", required_argument, NULL, 534}, 1642 {"modif-size", required_argument, NULL, 535}, 1643 {"greeter-size", required_argument, NULL, 536}, 1644 1645 // text/indicator positioning 1646 {"time-pos", required_argument, NULL, 540}, 1647 {"date-pos", required_argument, NULL, 541}, 1648 {"verif-pos", required_argument, NULL, 542}, 1649 {"wrong-pos", required_argument, NULL, 543}, 1650 {"layout-pos", required_argument, NULL, 544}, 1651 {"status-pos", required_argument, NULL, 545}, 1652 {"modif-pos", required_argument, NULL, 546}, 1653 {"ind-pos", required_argument, NULL, 547}, 1654 {"greeter-pos", required_argument, NULL, 548}, 1655 1656 // text outline width 1657 {"timeoutline-width", required_argument, NULL, 560}, 1658 {"dateoutline-width", required_argument, NULL, 561}, 1659 {"verifoutline-width", required_argument, NULL, 562}, 1660 {"wrongoutline-width", required_argument, NULL, 563}, 1661 {"modifieroutline-width", required_argument, NULL, 564}, 1662 {"layoutoutline-width", required_argument, NULL, 565}, 1663 {"greeteroutline-width", required_argument, NULL, 566}, 1664 1665 // pass keys 1666 {"pass-media-keys", no_argument, NULL, 601}, 1667 {"pass-screen-keys", no_argument, NULL, 602}, 1668 {"pass-power-keys", no_argument, NULL, 603}, 1669 {"pass-volume-keys", no_argument, NULL, 604}, 1670 1671 // custom commands for pass keys 1672 {"custom-key-commands", no_argument, NULL, 610}, 1673 {"cmd-brightness-up", required_argument, NULL, 620}, 1674 {"cmd-brightness-down", required_argument, NULL, 621}, 1675 1676 {"cmd-media-play", required_argument, NULL, 630}, 1677 {"cmd-media-pause", required_argument, NULL, 631}, 1678 {"cmd-media-stop", required_argument, NULL, 632}, 1679 {"cmd-media-next", required_argument, NULL, 633}, 1680 {"cmd-media-prev", required_argument, NULL, 634}, 1681 1682 {"cmd-audio-mute", required_argument, NULL, 640}, 1683 {"cmd-volume-up", required_argument, NULL, 641}, 1684 {"cmd-volume-down", required_argument, NULL, 642}, 1685 {"cmd-mic-mute", required_argument, NULL, 643}, 1686 1687 {"cmd-power-down", required_argument, NULL, 650}, 1688 {"cmd-power-off", required_argument, NULL, 651}, 1689 {"cmd-power-sleep", required_argument, NULL, 652}, 1690 1691 // bar indicator stuff 1692 {"bar-indicator", no_argument, NULL, 700}, 1693 {"bar-direction", required_argument, NULL, 701}, 1694 {"bar-orientation", required_argument, NULL, 703}, 1695 {"bar-step", required_argument, NULL, 704}, 1696 {"bar-max-height", required_argument, NULL, 705}, 1697 {"bar-base-width", required_argument, NULL, 706}, 1698 {"bar-color", required_argument, NULL, 707}, 1699 {"bar-periodic-step", required_argument, NULL, 708}, 1700 {"bar-pos", required_argument, NULL, 709}, 1701 {"bar-count", required_argument, NULL, 710}, 1702 {"bar-total-width", required_argument, NULL, 711}, 1703 1704 // misc. 1705 {"redraw-thread", no_argument, NULL, 900}, 1706 {"refresh-rate", required_argument, NULL, 901}, 1707 {"composite", no_argument, NULL, 902}, 1708 {"no-verify", no_argument, NULL, 905}, 1709 1710 // slideshow options 1711 {"slideshow-interval", required_argument, NULL, 903}, 1712 {"slideshow-random-selection", no_argument, NULL, 904}, 1713 1714 {NULL, no_argument, NULL, 0}}; 1715 1716 if ((pw = getpwuid(getuid())) == NULL) 1717 err(EXIT_FAILURE, "getpwuid() failed"); 1718 if ((username = pw->pw_name) == NULL) 1719 errx(EXIT_FAILURE, "pw->pw_name is NULL."); 1720 if (getenv("WAYLAND_DISPLAY") != NULL) 1721 errx(EXIT_FAILURE, "i3lock is a program for X11 and does not work on Wayland. Try https://github.com/swaywm/swaylock instead"); 1722 1723 char *optstring = "hvnbdc:p:ui:tCFLMeI:frsS:kB:m"; 1724 char *arg = NULL; 1725 int opt = 0; 1726 char padded[9] = "ffffffff"; \ 1727 1728 #define parse_color(acolor)\ 1729 arg = optarg;\ 1730 if (arg[0] == '#') arg++;\ 1731 if (strlen(arg) == 6) {\ 1732 /* If 6 digits given, assume RGB and pad 0xff for alpha */\ 1733 strncpy( padded, arg, 6 );\ 1734 arg = padded;\ 1735 }\ 1736 if (strlen(arg) != 8 || sscanf(arg, "%08[0-9a-fA-F]", acolor) != 1)\ 1737 errx(1, #acolor " is invalid, color must be given in 3 or 4-byte format: rrggbb[aa]\n"); 1738 1739 #define parse_outline_width(awidth)\ 1740 arg = optarg;\ 1741 if (sscanf(arg, "%lf", &awidth) != 1)\ 1742 errx(1, #awidth " must be a number\n");\ 1743 if (awidth < 0) {\ 1744 fprintf(stderr, #awidth " must be a positive double; ignoring...\n");\ 1745 awidth = 0;\ 1746 } 1747 1748 while ((o = getopt_long(argc, argv, optstring, longopts, &longoptind)) != -1) { 1749 switch (o) { 1750 case 'v': 1751 errx(EXIT_SUCCESS, "version " I3LOCK_VERSION " © 2010 Michael Stapelberg, © 2015 Cassandra Fox, © 2021 Raymond Li"); 1752 case 'n': 1753 dont_fork = true; 1754 break; 1755 case 'b': 1756 beep = true; 1757 break; 1758 case 'd': 1759 fprintf(stderr, "DPMS support has been removed from i3lock. Please see the manpage i3lock(1).\n"); 1760 break; 1761 case 'I': { 1762 fprintf(stderr, "Inactivity timeout only makes sense with DPMS, which was removed. Please see the manpage i3lock(1).\n"); 1763 break; 1764 } 1765 case 'u': 1766 unlock_indicator = false; 1767 break; 1768 case 'i': 1769 image_path = strdup(optarg); 1770 break; 1771 case 't': 1772 if(bg_type != NONE) { 1773 errx(EXIT_FAILURE, "i3lock-color: Only one background type can be used."); 1774 } 1775 bg_type = TILE; 1776 break; 1777 case 'C': 1778 if(bg_type != NONE) { 1779 errx(EXIT_FAILURE, "i3lock-color: Only one background type can be used."); 1780 } 1781 bg_type = CENTER; 1782 break; 1783 case 'F': 1784 if(bg_type != NONE) { 1785 errx(EXIT_FAILURE, "i3lock-color: Only one background type can be used."); 1786 } 1787 bg_type = FILL; 1788 break; 1789 case 'L': 1790 if(bg_type != NONE) { 1791 errx(EXIT_FAILURE, "i3lock-color: Only one background type can be used."); 1792 } 1793 bg_type = SCALE; 1794 break; 1795 case 'M': 1796 if(bg_type != NONE) { 1797 errx(EXIT_FAILURE, "i3lock-color: Only one background type can be used."); 1798 } 1799 bg_type = MAX; 1800 break; 1801 case 'p': 1802 if (!strcmp(optarg, "win")) { 1803 curs_choice = CURS_WIN; 1804 } else if (!strcmp(optarg, "default")) { 1805 curs_choice = CURS_DEFAULT; 1806 } else { 1807 errx(EXIT_FAILURE, "i3lock: Invalid pointer type given. Expected one of \"win\" or \"default\"."); 1808 } 1809 break; 1810 case 'e': 1811 ignore_empty_password = true; 1812 break; 1813 case 'f': 1814 show_failed_attempts = true; 1815 break; 1816 case 'r': 1817 if (internal_line_source != 0) { 1818 errx(EXIT_FAILURE, "i3lock-color: Options line-uses-ring and line-uses-inside conflict."); 1819 } 1820 internal_line_source = 1; //sets the line drawn inside to use the inside color when drawn 1821 break; 1822 case 's': 1823 if (internal_line_source != 0) { 1824 errx(EXIT_FAILURE, "i3lock-color: Options line-uses-ring and line-uses-inside conflict."); 1825 } 1826 internal_line_source = 2; 1827 break; 1828 case 'S': 1829 screen_number = atoi(optarg); 1830 break; 1831 1832 case 'k': 1833 show_clock = true; 1834 break; 1835 case 'B': 1836 blur = true; 1837 blur_sigma = atoi(optarg); 1838 break; 1839 1840 // Begin colors 1841 case 'c': 1842 parse_color(color); 1843 break; 1844 case 300: 1845 parse_color(insidevercolor); 1846 break; 1847 case 301: 1848 parse_color(insidewrongcolor); 1849 break; 1850 case 302: 1851 parse_color(insidecolor); 1852 break; 1853 case 303: 1854 parse_color(ringvercolor); 1855 break; 1856 case 304: 1857 parse_color(ringwrongcolor); 1858 break; 1859 case 305: 1860 parse_color(ringcolor); 1861 break; 1862 case 306: 1863 parse_color(linecolor); 1864 break; 1865 case 307: 1866 parse_color(verifcolor); 1867 break; 1868 case 308: 1869 parse_color(wrongcolor); 1870 break; 1871 case 309: 1872 parse_color(layoutcolor); 1873 break; 1874 case 310: 1875 parse_color(timecolor); 1876 break; 1877 case 311: 1878 parse_color(datecolor); 1879 break; 1880 case 312: 1881 parse_color(keyhlcolor); 1882 break; 1883 case 313: 1884 parse_color(bshlcolor); 1885 break; 1886 case 314: 1887 parse_color(separatorcolor); 1888 break; 1889 case 315: 1890 parse_color(greetercolor); 1891 break; 1892 case 316: 1893 parse_color(verifoutlinecolor); 1894 break; 1895 case 317: 1896 parse_color(wrongoutlinecolor); 1897 break; 1898 case 318: 1899 parse_color(layoutoutlinecolor); 1900 break; 1901 case 319: 1902 parse_color(timeoutlinecolor); 1903 break; 1904 case 320: 1905 parse_color(dateoutlinecolor); 1906 break; 1907 case 321: 1908 parse_color(greeteroutlinecolor); 1909 break; 1910 case 322: 1911 parse_color(modifcolor); 1912 break; 1913 case 323: 1914 parse_color(modifoutlinecolor); 1915 break; 1916 1917 1918 // General indicator opts 1919 case 400: 1920 show_clock = true; 1921 always_show_clock = true; 1922 break; 1923 case 401: 1924 show_indicator = true; 1925 break; 1926 case 402: 1927 arg = optarg; 1928 if (sscanf(arg, "%lf", &circle_radius) != 1) 1929 errx(1, "radius must be a number\n"); 1930 if (circle_radius < 1) { 1931 fprintf(stderr, "radius must be a positive integer; ignoring...\n"); 1932 circle_radius = 90.0; 1933 } 1934 break; 1935 case 403: 1936 arg = optarg; 1937 if (sscanf(arg, "%lf", &ring_width) != 1) 1938 errx(1, "ring-width must be a number\n"); 1939 if (ring_width < 1.0) { 1940 fprintf(stderr, "ring-width must be a positive float; ignoring...\n"); 1941 ring_width = 7.0; 1942 } 1943 break; 1944 1945 // Alignment stuff 1946 case 500: 1947 opt = atoi(optarg); 1948 if (opt < 0 || opt > 2) opt = 0; 1949 time_align = opt; 1950 break; 1951 case 501: 1952 opt = atoi(optarg); 1953 if (opt < 0 || opt > 2) opt = 0; 1954 date_align = opt; 1955 break; 1956 case 502: 1957 opt = atoi(optarg); 1958 if (opt < 0 || opt > 2) opt = 0; 1959 verif_align = opt; 1960 break; 1961 case 503: 1962 opt = atoi(optarg); 1963 if (opt < 0 || opt > 2) opt = 0; 1964 wrong_align = opt; 1965 break; 1966 case 504: 1967 opt = atoi(optarg); 1968 if (opt < 0 || opt > 2) opt = 0; 1969 layout_align = opt; 1970 break; 1971 case 505: 1972 opt = atoi(optarg); 1973 if (opt < 0 || opt > 2) opt = 0; 1974 modif_align = opt; 1975 break; 1976 case 506: 1977 opt = atoi(optarg); 1978 if (opt < 0 || opt > 2) opt = 0; 1979 greeter_align = opt; 1980 break; 1981 1982 // String stuff 1983 case 510: 1984 if (strlen(optarg) > 31) { 1985 errx(1, "time format string can be at most 31 characters\n"); 1986 } 1987 strcpy(time_format,optarg); 1988 break; 1989 case 511: 1990 if (strlen(optarg) > 31) { 1991 errx(1, "time format string can be at most 31 characters\n"); 1992 } 1993 strcpy(date_format,optarg); 1994 break; 1995 case 512: 1996 verif_text = optarg; 1997 break; 1998 case 513: 1999 wrong_text = optarg; 2000 break; 2001 case 514: 2002 // if layout is NULL, do nothing 2003 // if not NULL, attempt to display stuff 2004 // need to code some sane defaults for it 2005 keylayout_mode = atoi(optarg); 2006 break; 2007 case 515: 2008 noinput_text = optarg; 2009 break; 2010 case 516: 2011 lock_text = optarg; 2012 break; 2013 case 517: 2014 lock_failed_text = optarg; 2015 break; 2016 case 518: 2017 greeter_text = optarg; 2018 break; 2019 case 519: 2020 show_modkey_text = false; 2021 break; 2022 2023 // Font stuff 2024 case 520: 2025 if (strlen(optarg) > 63) { 2026 errx(1, "time font string can be at most 63 characters\n"); 2027 } 2028 strcpy(fonts[TIME_FONT],optarg); 2029 break; 2030 case 521: 2031 if (strlen(optarg) > 63) { 2032 errx(1, "date font string can be at most 63 characters\n"); 2033 } 2034 strcpy(fonts[DATE_FONT],optarg); 2035 break; 2036 case 522: 2037 if (strlen(optarg) > 63) { 2038 errx(1, "verif font string can be at most 63 " 2039 "characters\n"); 2040 } 2041 strcpy(fonts[VERIF_FONT],optarg); 2042 break; 2043 case 523: 2044 if (strlen(optarg) > 63) { 2045 errx(1, "wrong font string can be at most 63 " 2046 "characters\n"); 2047 } 2048 strcpy(fonts[WRONG_FONT],optarg); 2049 break; 2050 case 524: 2051 if (strlen(optarg) > 63) { 2052 errx(1, "layout font string can be at most 63 characters\n"); 2053 } 2054 strcpy(fonts[LAYOUT_FONT],optarg); 2055 break; 2056 case 525: 2057 if (strlen(optarg) > 63) { 2058 errx(1, "greeter font string can be at most 63 characters\n"); 2059 } 2060 strcpy(fonts[GREETER_FONT],optarg); 2061 break; 2062 2063 // Text size 2064 case 530: 2065 arg = optarg; 2066 if (sscanf(arg, "%lf", &time_size) != 1) 2067 errx(1, "timesize must be a number\n"); 2068 if (time_size < 1) 2069 errx(1, "timesize must be larger than 0\n"); 2070 break; 2071 case 531: 2072 arg = optarg; 2073 if (sscanf(arg, "%lf", &date_size) != 1) 2074 errx(1, "datesize must be a number\n"); 2075 if (date_size < 1) 2076 errx(1, "datesize must be larger than 0\n"); 2077 break; 2078 case 532: 2079 arg = optarg; 2080 if (sscanf(arg, "%lf", &verif_size) != 1) 2081 errx(1, "verifsize must be a number\n"); 2082 if (verif_size < 1) { 2083 fprintf(stderr, "verifsize must be a positive integer; ignoring...\n"); 2084 verif_size = 28.0; 2085 } 2086 break; 2087 case 533: 2088 arg = optarg; 2089 if (sscanf(arg, "%lf", &wrong_size) != 1) 2090 errx(1, "wrongsize must be a number\n"); 2091 if (wrong_size < 1) { 2092 fprintf(stderr, "wrongsize must be a positive integer; ignoring...\n"); 2093 wrong_size = 28.0; 2094 } 2095 break; 2096 case 534: 2097 arg = optarg; 2098 if (sscanf(arg, "%lf", &layout_size) != 1) 2099 errx(1, "layoutsize must be a number\n"); 2100 if (date_size < 1) 2101 errx(1, "layoutsize must be larger than 0\n"); 2102 break; 2103 case 535: 2104 arg = optarg; 2105 if (sscanf(arg, "%lf", &modifier_size) != 1) 2106 errx(1, "modsize must be a number\n"); 2107 if (modifier_size < 1) { 2108 fprintf(stderr, "modsize must be a positive integer; ignoring...\n"); 2109 modifier_size = 14.0; 2110 } 2111 break; 2112 case 536: 2113 arg = optarg; 2114 if (sscanf(arg, "%lf", &greeter_size) != 1) 2115 errx(1, "greetersize must be a number\n"); 2116 if (greeter_size < 1) { 2117 fprintf(stderr, "greetersize must be a positive integer; ignoring...\n"); 2118 greeter_size = 14.0; 2119 } 2120 break; 2121 2122 // Positions 2123 case 540: 2124 //read in to time_x_expr and time_y_expr 2125 if (strlen(optarg) > 31) { 2126 // this is overly restrictive since both the x and y string buffers have size 32, but it's easier to check. 2127 errx(1, "time position string can be at most 31 characters\n"); 2128 } 2129 arg = optarg; 2130 if (sscanf(arg, "%30[^:]:%30[^:]", time_x_expr, time_y_expr) != 2) { 2131 errx(1, "timepos must be of the form x:y\n"); 2132 } 2133 break; 2134 case 541: 2135 //read in to date_x_expr and date_y_expr 2136 if (strlen(optarg) > 31) { 2137 // this is overly restrictive since both the x and y string buffers have size 32, but it's easier to check. 2138 errx(1, "date position string can be at most 31 characters\n"); 2139 } 2140 arg = optarg; 2141 if (sscanf(arg, "%30[^:]:%30[^:]", date_x_expr, date_y_expr) != 2) { 2142 errx(1, "datepos must be of the form x:y\n"); 2143 } 2144 break; 2145 case 542: 2146 // read in to time_x_expr and time_y_expr 2147 if (strlen(optarg) > 31) { 2148 errx(1, "verif position string can be at most 31 characters\n"); 2149 } 2150 arg = optarg; 2151 if (sscanf(arg, "%30[^:]:%30[^:]", verif_x_expr, verif_y_expr) != 2) { 2152 errx(1, "verifpos must be of the form x:y\n"); 2153 } 2154 break; 2155 case 543: 2156 if (strlen(optarg) > 31) { 2157 errx(1, "\"wrong\" text position string can be at most 31 characters\n"); 2158 } 2159 arg = optarg; 2160 if (sscanf(arg, "%30[^:]:%30[^:]", wrong_x_expr, wrong_y_expr) != 2) { 2161 errx(1, "wrongpos must be of the form x:y\n"); 2162 } 2163 break; 2164 case 544: 2165 if (strlen(optarg) > 31) { 2166 errx(1, "layout position string can be at most 31 characters\n"); 2167 } 2168 arg = optarg; 2169 if (sscanf(arg, "%30[^:]:%30[^:]", layout_x_expr, layout_y_expr) != 2) { 2170 errx(1, "layoutpos must be of the form x:y\n"); 2171 } 2172 break; 2173 case 545: 2174 if (strlen(optarg) > 31) { 2175 // this is overly restrictive since both the x and y string buffers have size 32, but it's easier to check. 2176 errx(1, "status position string can be at most 31 characters\n"); 2177 } 2178 arg = optarg; 2179 if (sscanf(arg, "%30[^:]:%30[^:]", status_x_expr, status_y_expr) != 2) { 2180 errx(1, "statuspos must be of the form x:y\n"); 2181 } 2182 break; 2183 case 546: 2184 if (strlen(optarg) > 31) { 2185 // this is overly restrictive since both the x and y string buffers have size 32, but it's easier to check. 2186 errx(1, "modif position string can be at most 31 characters\n"); 2187 } 2188 arg = optarg; 2189 if (sscanf(arg, "%30[^:]:%30[^:]", modif_x_expr, modif_y_expr) != 2) { 2190 errx(1, "modifpos must be of the form x:y\n"); 2191 } 2192 break; 2193 case 547: 2194 if (strlen(optarg) > 31) { 2195 // this is overly restrictive since both the x and y string buffers have size 32, but it's easier to check. 2196 errx(1, "indicator position string can be at most 31 characters\n"); 2197 } 2198 arg = optarg; 2199 if (sscanf(arg, "%30[^:]:%30[^:]", ind_x_expr, ind_y_expr) != 2) { 2200 errx(1, "indpos must be of the form x:y\n"); 2201 } 2202 break; 2203 case 548: 2204 if (strlen(optarg) > 31) { 2205 // this is overly restrictive since both the x and y string buffers have size 32, but it's easier to check. 2206 errx(1, "greeter position string can be at most 31 characters\n"); 2207 } 2208 arg = optarg; 2209 if (sscanf(arg, "%30[^:]:%30[^:]", greeter_x_expr, greeter_y_expr) != 2) { 2210 errx(1, "greeterpos must be of the form x:y\n"); 2211 } 2212 break; 2213 2214 // text outline width 2215 case 560: 2216 parse_outline_width(timeoutlinewidth); 2217 break; 2218 case 561: 2219 parse_outline_width(dateoutlinewidth); 2220 break; 2221 case 562: 2222 parse_outline_width(verifoutlinewidth); 2223 break; 2224 case 563: 2225 parse_outline_width(wrongoutlinewidth); 2226 break; 2227 case 564: 2228 parse_outline_width(modifieroutlinewidth); 2229 break; 2230 case 565: 2231 parse_outline_width(layoutoutlinewidth); 2232 break; 2233 case 566: 2234 parse_outline_width(greeteroutlinewidth); 2235 break; 2236 2237 2238 // Pass keys 2239 case 601: 2240 pass_media_keys = true; 2241 break; 2242 case 602: 2243 pass_screen_keys = true; 2244 break; 2245 case 603: 2246 pass_power_keys = true; 2247 break; 2248 case 604: 2249 pass_volume_keys = true; 2250 break; 2251 2252 //custom key commands 2253 case 610: 2254 hotkeys = true; 2255 break; 2256 case 620: 2257 cmd_brightness_up = optarg; 2258 break; 2259 case 621: 2260 cmd_brightness_down = optarg; 2261 break; 2262 2263 case 630: 2264 cmd_media_play = optarg; 2265 break; 2266 case 631: 2267 cmd_media_pause = optarg; 2268 break; 2269 case 632: 2270 cmd_media_stop = optarg; 2271 break; 2272 case 633: 2273 cmd_media_next = optarg; 2274 break; 2275 case 634: 2276 cmd_media_prev = optarg; 2277 break; 2278 2279 case 640: 2280 cmd_audio_mute = optarg; 2281 break; 2282 case 641: 2283 cmd_volume_up = optarg; 2284 break; 2285 case 642: 2286 cmd_volume_down = optarg; 2287 break; 2288 case 643: 2289 cmd_mic_mute = optarg; 2290 break; 2291 2292 case 650: 2293 cmd_power_down = optarg; 2294 break; 2295 case 651: 2296 cmd_power_off = optarg; 2297 break; 2298 case 652: 2299 cmd_power_sleep = optarg; 2300 break; 2301 2302 // Bar indicator 2303 case 700: 2304 bar_enabled = true; 2305 break; 2306 case 701: 2307 opt = atoi(optarg); 2308 switch(opt) { 2309 case BAR_REVERSED: 2310 bar_reversed = true; 2311 break; 2312 case BAR_BIDIRECTIONAL: 2313 bar_bidirectional = true; 2314 break; 2315 case BAR_DEFAULT: 2316 default: 2317 break; 2318 } 2319 break; 2320 case 703: 2321 arg = optarg; 2322 if (strcmp(arg, "vertical") == 0) 2323 bar_orientation = BAR_VERT; 2324 else if (strcmp(arg, "horizontal") == 0) 2325 bar_orientation = BAR_FLAT; 2326 else 2327 errx(1, "bar orientation must be \"vertical\" or \"horizontal\"\n"); 2328 break; 2329 case 704: 2330 bar_step = atoi(optarg); 2331 if (bar_step < 1) bar_step = 15; 2332 break; 2333 case 705: 2334 max_bar_height = atoi(optarg); 2335 if (max_bar_height < 1) max_bar_height = 25; 2336 break; 2337 case 706: 2338 bar_base_height = atoi(optarg); 2339 if (bar_base_height < 1) bar_base_height = 25; 2340 break; 2341 case 707: 2342 parse_color(bar_base_color); 2343 break; 2344 case 708: 2345 opt = atoi(optarg); 2346 if (opt > 0) 2347 bar_periodic_step = opt; 2348 break; 2349 case 709: 2350 arg = optarg; 2351 if (sscanf(arg, "%31[^:]:%31[^:]", bar_x_expr, bar_y_expr) < 1) { 2352 errx(1, "bar-position must be a single number or of the form x:y with a max length of 31\n"); 2353 } 2354 break; 2355 case 710: 2356 bar_count = atoi(optarg); 2357 if (bar_count > MAX_BAR_COUNT || bar_count < MIN_BAR_COUNT) { 2358 errx(1, "bar-count must be between %d and %d\n", MIN_BAR_COUNT, MAX_BAR_COUNT); 2359 } 2360 break; 2361 case 711: 2362 arg = optarg; 2363 if (sscanf(arg, "%31s", bar_width_expr) != 1) { 2364 errx(1, "missing argument for bar-total-width\n"); 2365 } 2366 break; 2367 2368 // Misc 2369 case 900: 2370 redraw_thread = true; 2371 break; 2372 case 901: 2373 arg = optarg; 2374 refresh_rate = strtof(arg, NULL); 2375 if (refresh_rate < 0.0) { 2376 fprintf(stderr, "The given refresh rate of %fs is less than zero seconds and was ignored.\n", refresh_rate); 2377 refresh_rate = 1.0; 2378 } 2379 break; 2380 case 902: 2381 composite = true; 2382 break; 2383 case 903: 2384 slideshow_interval = atoi(optarg); 2385 2386 if (slideshow_interval < 0) { 2387 slideshow_interval = 10; 2388 } 2389 break; 2390 case 904: 2391 slideshow_random_selection = true; 2392 break; 2393 case 905: 2394 no_verify = true; 2395 break; 2396 case 998: 2397 image_raw_format = strdup(optarg); 2398 break; 2399 case 999: 2400 debug_mode = true; 2401 break; 2402 default: 2403 errx(EXIT_FAILURE, "Syntax: i3lock [-v] [-n] [-b] [-d] [-c color] [-u] [-p win|default]" 2404 " [-i image.png] [-t] [-e] [-f]\n" 2405 "Please see the manpage for a full list of arguments."); 2406 } 2407 } 2408 2409 /* We need (relatively) random numbers for highlighting a random part of 2410 * the unlock indicator upon keypresses. */ 2411 srand(time(NULL)); 2412 2413 #ifndef __OpenBSD__ 2414 /* Initialize PAM */ 2415 if ((ret = pam_start("i3lock", username, &conv, &pam_handle)) != PAM_SUCCESS) 2416 errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret)); 2417 2418 if ((ret = pam_set_item(pam_handle, PAM_TTY, getenv("DISPLAY"))) != PAM_SUCCESS) 2419 errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret)); 2420 #endif 2421 2422 /* Using mlock() as non-super-user seems only possible in Linux. 2423 * Users of other operating systems should use encrypted swap/no swap 2424 * (or remove the ifdef and run i3lock as super-user). 2425 * Alas, swap is encrypted by default on OpenBSD so swapping out 2426 * is not necessarily an issue. */ 2427 #if defined(__linux__) 2428 /* Lock the area where we store the password in memory, we don’t want it to 2429 * be swapped to disk. Since Linux 2.6.9, this does not require any 2430 * privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */ 2431 if (mlock(password, sizeof(password)) != 0) 2432 err(EXIT_FAILURE, "Could not lock page in memory, check RLIMIT_MEMLOCK"); 2433 #endif 2434 2435 /* Double checking that connection is good and operatable with xcb */ 2436 int screennr; 2437 if ((conn = xcb_connect(NULL, &screennr)) == NULL || 2438 xcb_connection_has_error(conn)) 2439 errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?"); 2440 2441 if (xkb_x11_setup_xkb_extension(conn, 2442 XKB_X11_MIN_MAJOR_XKB_VERSION, 2443 XKB_X11_MIN_MINOR_XKB_VERSION, 2444 0, 2445 NULL, 2446 NULL, 2447 &xkb_base_event, 2448 &xkb_base_error) != 1) 2449 errx(EXIT_FAILURE, "Could not setup XKB extension."); 2450 2451 layout_text = get_keylayoutname(keylayout_mode, conn); 2452 if (layout_text) 2453 show_clock = true; 2454 static const xcb_xkb_map_part_t required_map_parts = 2455 (XCB_XKB_MAP_PART_KEY_TYPES | 2456 XCB_XKB_MAP_PART_KEY_SYMS | 2457 XCB_XKB_MAP_PART_MODIFIER_MAP | 2458 XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | 2459 XCB_XKB_MAP_PART_KEY_ACTIONS | 2460 XCB_XKB_MAP_PART_VIRTUAL_MODS | 2461 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP); 2462 2463 static const xcb_xkb_event_type_t required_events = 2464 (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | 2465 XCB_XKB_EVENT_TYPE_MAP_NOTIFY | 2466 XCB_XKB_EVENT_TYPE_STATE_NOTIFY); 2467 2468 xcb_xkb_select_events( 2469 conn, 2470 xkb_x11_get_core_keyboard_device_id(conn), 2471 required_events, 2472 0, 2473 required_events, 2474 required_map_parts, 2475 required_map_parts, 2476 0); 2477 2478 /* When we cannot initially load the keymap, we better exit */ 2479 if (!load_keymap()) 2480 errx(EXIT_FAILURE, "Could not load keymap"); 2481 2482 2483 const char *locale = getenv("LC_ALL"); 2484 if (!locale || !*locale) 2485 locale = getenv("LC_TIME"); 2486 if (!locale || !*locale) 2487 locale = getenv("LC_CTYPE"); 2488 if (!locale || !*locale) 2489 locale = getenv("LANG"); 2490 if (!locale || !*locale) { 2491 if (debug_mode) 2492 fprintf(stderr, "Can't detect your locale, fallback to C\n"); 2493 locale = "C"; 2494 } 2495 2496 setlocale(LC_ALL, locale); 2497 2498 #if XKBCOMPOSE == 1 2499 load_compose_table(locale); 2500 #endif 2501 2502 2503 screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; 2504 2505 init_dpi(); 2506 2507 randr_init(&randr_base, screen->root); 2508 randr_query(screen->root); 2509 2510 last_resolution[0] = screen->width_in_pixels; 2511 last_resolution[1] = screen->height_in_pixels; 2512 2513 if (bar_enabled) { 2514 bar_heights = (double*) calloc(bar_count, sizeof(double)); 2515 } 2516 2517 xcb_change_window_attributes(conn, screen->root, XCB_CW_EVENT_MASK, 2518 (uint32_t[]){XCB_EVENT_MASK_STRUCTURE_NOTIFY}); 2519 2520 init_colors_once(); 2521 if (image_path != NULL) { 2522 if (!is_directory(image_path)) { 2523 img = load_image(image_path); 2524 } else { 2525 /* Path to a directory is provided -> use slideshow mode */ 2526 slideshow_path = strdup(image_path); 2527 if (!load_slideshow_images(slideshow_path)) exit(EXIT_FAILURE); 2528 img = load_image(img_slideshow[0]); 2529 } 2530 2531 free(image_path); 2532 } 2533 2534 free(image_raw_format); 2535 2536 if (blur) { 2537 xcb_pixmap_t bg_pixmap = capture_bg_pixmap(conn, screen, last_resolution); 2538 cairo_surface_t *xcb_img = cairo_xcb_surface_create(conn, bg_pixmap, get_root_visual_type(screen), last_resolution[0], last_resolution[1]); 2539 2540 blur_bg_img = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, last_resolution[0], last_resolution[1]); 2541 cairo_t *ctx = cairo_create(blur_bg_img); 2542 2543 cairo_set_source_surface(ctx, xcb_img, 0, 0); 2544 cairo_paint(ctx); 2545 blur_image_surface(blur_bg_img, blur_sigma); 2546 2547 cairo_destroy(ctx); 2548 cairo_surface_destroy(xcb_img); 2549 xcb_free_pixmap(conn, bg_pixmap); 2550 } 2551 2552 xcb_window_t stolen_focus = find_focused_window(conn, screen->root); 2553 2554 /* Open the fullscreen window, already with the correct pixmap in place */ 2555 win = open_fullscreen_window(conn, screen, color); 2556 2557 xcb_pixmap_t pixmap = create_bg_pixmap(conn, win, last_resolution, color); 2558 render_lock(last_resolution, pixmap); 2559 xcb_change_window_attributes(conn, win, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap}); 2560 xcb_free_pixmap(conn, pixmap); 2561 2562 cursor = create_cursor(conn, screen, win, curs_choice); 2563 2564 /* Display the "locking…" message while trying to grab the pointer/keyboard. */ 2565 auth_state = STATE_AUTH_LOCK; 2566 if (!grab_pointer_and_keyboard(conn, screen, cursor, 1000)) { 2567 DEBUG("stole focus from X11 window 0x%08x\n", stolen_focus); 2568 2569 /* Set the focus to i3lock, possibly closing context menus which would 2570 * otherwise prevent us from grabbing keyboard/pointer. 2571 * 2572 * We cannot use set_focused_window because _NET_ACTIVE_WINDOW only 2573 * works for managed windows, but i3lock uses an unmanaged window 2574 * (override_redirect=1). */ 2575 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT /* revert_to */, win, XCB_CURRENT_TIME); 2576 if (!grab_pointer_and_keyboard(conn, screen, cursor, 9000)) { 2577 auth_state = STATE_I3LOCK_LOCK_FAILED; 2578 redraw_screen(); 2579 sleep(1); 2580 errx(EXIT_FAILURE, "Cannot grab pointer/keyboard"); 2581 } 2582 } 2583 2584 pid_t pid = fork(); 2585 /* The pid == -1 case is intentionally ignored here: 2586 * While the child process is useful for preventing other windows from 2587 * popping up while i3lock blocks, it is not critical. */ 2588 if (pid == 0) { 2589 /* Child */ 2590 close(xcb_get_file_descriptor(conn)); 2591 maybe_close_sleep_lock_fd(); 2592 raise_loop(win); 2593 exit(EXIT_SUCCESS); 2594 } 2595 2596 /* Load the keymap again to sync the current modifier state. Since we first 2597 * loaded the keymap, there might have been changes, but starting from now, 2598 * we should get all key presses/releases due to having grabbed the 2599 * keyboard. */ 2600 (void)load_keymap(); 2601 2602 /* Initialize the libev event loop. */ 2603 main_loop = EV_DEFAULT; 2604 if (main_loop == NULL) 2605 errx(EXIT_FAILURE, "Could not initialize libev. Bad LIBEV_FLAGS?"); 2606 2607 /* Explicitly call the screen redraw in case "locking…" message was displayed */ 2608 auth_state = STATE_AUTH_IDLE; 2609 redraw_screen(); 2610 2611 struct ev_io *xcb_watcher = calloc(sizeof(struct ev_io), 1); 2612 struct ev_check *xcb_check = calloc(sizeof(struct ev_check), 1); 2613 struct ev_prepare *xcb_prepare = calloc(sizeof(struct ev_prepare), 1); 2614 2615 ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ); 2616 ev_io_start(main_loop, xcb_watcher); 2617 2618 ev_check_init(xcb_check, xcb_check_cb); 2619 ev_check_start(main_loop, xcb_check); 2620 2621 ev_prepare_init(xcb_prepare, xcb_prepare_cb); 2622 ev_prepare_start(main_loop, xcb_prepare); 2623 2624 /* Invoke the event callback once to catch all the events which were 2625 * received up until now. ev will only pick up new events (when the X11 2626 * file descriptor becomes readable). */ 2627 ev_invoke(main_loop, xcb_check, 0); 2628 2629 if (show_clock || bar_enabled || slideshow_enabled) { 2630 if (redraw_thread) { 2631 struct timespec ts; 2632 double s; 2633 double ns = modf(refresh_rate, &s); 2634 ts.tv_sec = (time_t) s; 2635 ts.tv_nsec = ns * NANOSECONDS_IN_SECOND; 2636 (void) pthread_create(&draw_thread, NULL, start_time_redraw_tick_pthread, (void*) &ts); 2637 } else { 2638 start_time_redraw_tick(main_loop); 2639 } 2640 } 2641 ev_loop(main_loop, 0); 2642 2643 #ifndef __OpenBSD__ 2644 if (pam_cleanup) { 2645 pam_end(pam_handle, PAM_SUCCESS); 2646 } 2647 #endif 2648 2649 if (stolen_focus == XCB_NONE) { 2650 return 0; 2651 } 2652 2653 DEBUG("restoring focus to X11 window 0x%08x\n", stolen_focus); 2654 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); 2655 xcb_ungrab_keyboard(conn, XCB_CURRENT_TIME); 2656 xcb_destroy_window(conn, win); 2657 set_focused_window(conn, screen->root, stolen_focus); 2658 xcb_aux_sync(conn); 2659 2660 return 0; 2661 }
