+ }
+
+ /* We need (relatively) random numbers for highlighting a random part of
+ * the unlock indicator upon keypresses. */
+ srand(time(NULL));
+
+ /* Initialize PAM */
+ ret = pam_start("i3lock", username, &conv, &pam_handle);
+ if (ret != PAM_SUCCESS)
+ errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret));
+
+/* Using mlock() as non-super-user seems only possible in Linux. Users of other
+ * operating systems should use encrypted swap/no swap (or remove the ifdef and
+ * run i3lock as super-user). */
+#if defined(__linux__)
+ /* Lock the area where we store the password in memory, we don’t want it to
+ * be swapped to disk. Since Linux 2.6.9, this does not require any
+ * privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */
+ if (mlock(password, sizeof(password)) != 0)
+ err(EXIT_FAILURE, "Could not lock page in memory, check RLIMIT_MEMLOCK");
+#endif
+
+ /* Initialize connection to X11 */
+ if ((display = XOpenDisplay(NULL)) == NULL)
+ errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?");
+ XSetEventQueueOwner(display, XCBOwnsEventQueue);
+ conn = XGetXCBConnection(display);
+
+ /* Double checking that connection is good and operatable with xcb */
+ if (xcb_connection_has_error(conn))
+ errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?");
+
+ /* When we cannot initially load the keymap, we better exit */
+ if (!load_keymap())
+ errx(EXIT_FAILURE, "Could not load keymap");
+
+ xinerama_init();
+ xinerama_query_screens();
+
+ /* if DPMS is enabled, check if the X server really supports it */
+ if (dpms) {
+ xcb_dpms_capable_cookie_t dpmsc = xcb_dpms_capable(conn);
+ xcb_dpms_capable_reply_t *dpmsr;
+ if ((dpmsr = xcb_dpms_capable_reply(conn, dpmsc, NULL))) {
+ if (!dpmsr->capable) {
+ if (debug_mode)
+ fprintf(stderr, "Disabling DPMS, X server not DPMS capable\n");
+ dpms = false;
+ }
+ free(dpmsr);
+ }
+ }
+
+ screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
+
+ last_resolution[0] = screen->width_in_pixels;
+ last_resolution[1] = screen->height_in_pixels;
+
+ xcb_change_window_attributes(conn, screen->root, XCB_CW_EVENT_MASK,
+ (uint32_t[]){ XCB_EVENT_MASK_STRUCTURE_NOTIFY });
+
+ if (image_path) {
+ /* Create a pixmap to render on, fill it with the background color */
+ img = cairo_image_surface_create_from_png(image_path);
+ /* In case loading failed, we just pretend no -i was specified. */
+ if (cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) {
+ fprintf(stderr, "Could not load image \"%s\": %s\n",
+ image_path, cairo_status_to_string(cairo_surface_status(img)));
+ img = NULL;
+ }
+ }
+
+ /* Pixmap on which the image is rendered to (if any) */
+ xcb_pixmap_t bg_pixmap = draw_image(last_resolution);
+
+ /* open the fullscreen window, already with the correct pixmap in place */
+ win = open_fullscreen_window(conn, screen, color, bg_pixmap);
+ xcb_free_pixmap(conn, bg_pixmap);
+
+ pid_t pid = fork();
+ /* The pid == -1 case is intentionally ignored here:
+ * While the child process is useful for preventing other windows from
+ * popping up while i3lock blocks, it is not critical. */
+ if (pid == 0) {
+ /* Child */
+ close(xcb_get_file_descriptor(conn));
+ raise_loop(win);
+ exit(EXIT_SUCCESS);
+ }
+
+ cursor = create_cursor(conn, screen, win, curs_choice);
+
+ grab_pointer_and_keyboard(conn, screen, cursor);
+ /* Load the keymap again to sync the current modifier state. Since we first
+ * loaded the keymap, there might have been changes, but starting from now,
+ * we should get all key presses/releases due to having grabbed the
+ * keyboard. */
+ (void)load_keymap();
+
+ turn_monitors_off();
+
+ /* Initialize the libev event loop. */
+ main_loop = EV_DEFAULT;
+ if (main_loop == NULL)
+ errx(EXIT_FAILURE, "Could not initialize libev. Bad LIBEV_FLAGS?\n");
+
+ struct ev_io *xcb_watcher = calloc(sizeof(struct ev_io), 1);
+ struct ev_check *xcb_check = calloc(sizeof(struct ev_check), 1);
+ struct ev_prepare *xcb_prepare = calloc(sizeof(struct ev_prepare), 1);
+
+ ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
+ ev_io_start(main_loop, xcb_watcher);
+
+ ev_check_init(xcb_check, xcb_check_cb);
+ ev_check_start(main_loop, xcb_check);
+
+ ev_prepare_init(xcb_prepare, xcb_prepare_cb);
+ ev_prepare_start(main_loop, xcb_prepare);
+
+ /* Invoke the event callback once to catch all the events which were
+ * received up until now. ev will only pick up new events (when the X11
+ * file descriptor becomes readable). */
+ ev_invoke(main_loop, xcb_check, 0);
+ ev_loop(main_loop, 0);