+#include <X11/Xlib-xcb.h>
+#include <getopt.h>
+#include <string.h>
+#include <ev.h>
+#include <sys/mman.h>
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKBfile.h>
+#include <xkbcommon/xkbcommon.h>
+#include <cairo.h>
+#include <cairo/cairo-xcb.h>
+
+#include "i3lock.h"
+#include "xcb.h"
+#include "cursors.h"
+#include "unlock_indicator.h"
+#include "xinerama.h"
+
+/* We need this for libxkbfile */
+static Display *display;
+char color[7] = "ffffff";
+uint32_t last_resolution[2];
+xcb_window_t win;
+static xcb_cursor_t cursor;
+static pam_handle_t *pam_handle;
+int input_position = 0;
+/* Holds the password you enter (in UTF-8). */
+static char password[512];
+static bool beep = false;
+bool debug_mode = false;
+static bool dpms = false;
+bool unlock_indicator = true;
+static bool dont_fork = false;
+struct ev_loop *main_loop;
+static struct ev_timer *clear_pam_wrong_timeout;
+extern unlock_state_t unlock_state;
+extern pam_state_t pam_state;
+
+static struct xkb_state *xkb_state;
+static struct xkb_context *xkb_context;
+static struct xkb_keymap *xkb_keymap;
+
+cairo_surface_t *img = NULL;
+bool tile = false;
+bool ignore_empty_password = false;
+
+/* isutf, u8_dec © 2005 Jeff Bezanson, public domain */
+#define isutf(c) (((c) & 0xC0) != 0x80)
+
+/*
+ * Decrements i to point to the previous unicode glyph
+ *
+ */
+void u8_dec(char *s, int *i) {
+ (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) || isutf(s[--(*i)]) || --(*i));
+}
+
+static void turn_monitors_on(void) {
+ if (dpms)
+ dpms_set_mode(conn, XCB_DPMS_DPMS_MODE_ON);
+}
+
+static void turn_monitors_off(void) {
+ if (dpms)
+ dpms_set_mode(conn, XCB_DPMS_DPMS_MODE_OFF);
+}
+
+/*
+ * Loads the XKB keymap from the X11 server and feeds it to xkbcommon.
+ * Necessary so that we can properly let xkbcommon track the keyboard state and
+ * translate keypresses to utf-8.
+ *
+ * Ideally, xkbcommon would ship something like this itself, but as of now
+ * (version 0.2.0), it doesn’t.
+ *
+ * TODO: Once xcb-xkb is enabled by default and released, we should port this
+ * code to xcb-xkb. See also https://github.com/xkbcommon/libxkbcommon/issues/1
+ *
+ */
+static bool load_keymap(void) {
+ bool ret = false;
+ XkbFileInfo result;
+ memset(&result, '\0', sizeof(result));
+ result.xkb = XkbGetKeyboard(display, XkbAllMapComponentsMask, XkbUseCoreKbd);
+ if (result.xkb == NULL) {
+ fprintf(stderr, "[i3lock] XKB: XkbGetKeyboard failed\n");
+ return false;
+ }
+
+ FILE *temp = tmpfile();
+ if (temp == NULL) {
+ fprintf(stderr, "[i3lock] could not create tempfile\n");
+ return false;
+ }
+
+ bool ok = XkbWriteXKBKeymap(temp, &result, false, false, NULL, NULL);
+ if (!ok) {
+ fprintf(stderr, "[i3lock] XkbWriteXKBKeymap failed\n");
+ goto out;
+ }
+
+ rewind(temp);
+
+ if (xkb_context == NULL) {
+ if ((xkb_context = xkb_context_new(0)) == NULL) {
+ fprintf(stderr, "[i3lock] could not create xkbcommon context\n");
+ goto out;
+ }
+ }
+
+ if (xkb_keymap != NULL)
+ xkb_keymap_unref(xkb_keymap);
+
+ if ((xkb_keymap = xkb_keymap_new_from_file(xkb_context, temp, XKB_KEYMAP_FORMAT_TEXT_V1, 0)) == NULL) {
+ fprintf(stderr, "[i3lock] xkb_keymap_new_from_file failed\n");
+ goto out;
+ }
+
+ struct xkb_state *new_state = xkb_state_new(xkb_keymap);
+ if (new_state == NULL) {
+ fprintf(stderr, "[i3lock] xkb_state_new failed\n");
+ goto out;
+ }
+
+ /* Get the initial modifier state to be in sync with the X server.
+ * See https://github.com/xkbcommon/libxkbcommon/issues/1 for why we ignore
+ * the base and latched fields. */
+ XkbStateRec state_rec;
+ XkbGetState(display, XkbUseCoreKbd, &state_rec);
+
+ xkb_state_update_mask(new_state,
+ 0, 0, state_rec.locked_mods,
+ 0, 0, state_rec.locked_group);
+
+ if (xkb_state != NULL)
+ xkb_state_unref(xkb_state);
+ xkb_state = new_state;
+
+ ret = true;
+out:
+ XkbFreeKeyboard(result.xkb, XkbAllComponentsMask, true);
+ fclose(temp);
+ return ret;
+}
+
+/*
+ * Clears the memory which stored the password to be a bit safer against
+ * cold-boot attacks.
+ *
+ */
+static void clear_password_memory(void) {
+ /* A volatile pointer to the password buffer to prevent the compiler from
+ * optimizing this out. */
+ volatile char *vpassword = password;
+ for (int c = 0; c < sizeof(password); c++)
+ /* We store a non-random pattern which consists of the (irrelevant)
+ * index plus (!) the value of the beep variable. This prevents the
+ * compiler from optimizing the calls away, since the value of 'beep'
+ * is not known at compile-time. */
+ vpassword[c] = c + (int)beep;
+}
+