+static void clear_pam_wrong(EV_P_ ev_timer *w, int revents) {
+ DEBUG("clearing pam wrong\n");
+ pam_state = STATE_PAM_IDLE;
+ unlock_state = STATE_STARTED;
+ redraw_screen();
+
+ /* Now free this timeout. */
+ ev_timer_stop(main_loop, clear_pam_wrong_timeout);
+ free(clear_pam_wrong_timeout);
+ clear_pam_wrong_timeout = NULL;
+}
+
+static void input_done() {
+ if (input_position == 0)
+ return;
+
+ if (clear_pam_wrong_timeout) {
+ ev_timer_stop(main_loop, clear_pam_wrong_timeout);
+ free(clear_pam_wrong_timeout);
+ clear_pam_wrong_timeout = NULL;
+ }
+
+ pam_state = STATE_PAM_VERIFY;
+ redraw_screen();
+
+ if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS) {
+ DEBUG("successfully authenticated\n");
+ clear_password_memory();
+ exit(0);
+ }
+
+ if (debug_mode)
+ fprintf(stderr, "Authentication failure\n");
+
+ pam_state = STATE_PAM_WRONG;
+ redraw_screen();
+
+ /* Clear this state after 2 seconds (unless the user enters another
+ * password during that time). */
+ ev_now_update(main_loop);
+ if ((clear_pam_wrong_timeout = calloc(sizeof(struct ev_timer), 1))) {
+ ev_timer_init(clear_pam_wrong_timeout, clear_pam_wrong, 2.0, 0.);
+ ev_timer_start(main_loop, clear_pam_wrong_timeout);
+ }
+
+ /* Cancel the clear_indicator_timeout, it would hide the unlock indicator
+ * too early. */
+ stop_clear_indicator_timeout();
+
+ /* beep on authentication failure, if enabled */
+ if (beep) {
+ xcb_bell(conn, 100);
+ xcb_flush(conn);
+ }
+}
+
+/*
+ * Called when the user releases a key. We need to leave the Mode_switch
+ * state when the user releases the Mode_switch key.
+ *
+ */
+static void handle_key_release(xcb_key_release_event_t *event) {
+ DEBUG("releasing key %d, state raw = %d, modeswitch_active = %d, iso_level3_shift_active = %d, iso_level5_shift_active = %d\n",
+ event->detail, event->state, modeswitch_active, iso_level3_shift_active, iso_level5_shift_active);
+
+ /* We don’t care about the column here and just use the first symbol. Since
+ * we only check for Mode_switch and ISO_Level3_Shift, this *should* work.
+ * Also, if we would use the current column, we would look in the wrong
+ * place. */
+ xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, 0);
+ if (sym == XK_Mode_switch) {
+ //printf("Mode switch disabled\n");
+ modeswitch_active = false;
+ } else if (sym == XK_ISO_Level3_Shift) {
+ iso_level3_shift_active = false;
+ } else if (sym == XK_ISO_Level5_Shift) {
+ iso_level5_shift_active = false;
+ }
+ DEBUG("release done. modeswitch_active = %d, iso_level3_shift_active = %d, iso_level5_shift_active = %d\n",
+ modeswitch_active, iso_level3_shift_active, iso_level5_shift_active);
+}
+
+static void redraw_timeout(EV_P_ ev_timer *w, int revents) {
+ redraw_screen();
+
+ ev_timer_stop(main_loop, w);
+ free(w);
+}
+
+/*
+ * Handle key presses. Fixes state, then looks up the key symbol for the
+ * given keycode, then looks up the key symbol (as UCS-2), converts it to
+ * UTF-8 and stores it in the password array.
+ *
+ */
+static void handle_key_press(xcb_key_press_event_t *event) {
+ DEBUG("keypress %d, state raw = %d, modeswitch_active = %d, iso_level3_shift_active = %d\n",
+ event->detail, event->state, modeswitch_active, iso_level3_shift_active);
+
+ xcb_keysym_t sym0, sym1, sym;
+ /* For each keycode, there is a list of symbols. The list could look like this:
+ * $ xmodmap -pke | grep 'keycode 38'
+ * keycode 38 = a A adiaeresis Adiaeresis o O
+ * In non-X11 terminology, the symbols for the keycode 38 (the key labeled
+ * with "a" on my keyboard) are "a A ä Ä o O".
+ * Another form to display the same information is using xkbcomp:
+ * $ xkbcomp $DISPLAY /tmp/xkb.dump
+ * Then open /tmp/xkb.dump and search for '\<a\>' (in VIM regexp-language):
+ *
+ * symbols[Group1]= [ a, A, o, O ],
+ * symbols[Group2]= [ adiaeresis, Adiaeresis ]
+ *
+ * So there are two *groups*, one containing 'a A' and one containing 'ä
+ * Ä'. You can use Mode_switch to switch between these groups. You can use
+ * ISO_Level3_Shift to reach the 'o O' part of the first group (it’s the
+ * same group, just an even higher shift level).
+ *
+ * So, using the "logical" XKB information, the following lookup will be
+ * performed:
+ *
+ * Neither Mode_switch nor ISO_Level3_Shift active: group 1, column 0 and 1
+ * Mode_switch active: group 2, column 0 and 1
+ * ISO_Level3_Shift active: group 1, column 2 and 3
+ *
+ * Using the column index which xcb_key_press_lookup_keysym uses (and
+ * xmodmap prints out), the following lookup will be performed:
+ *
+ * Neither Mode_switch nor ISO_Level3_Shift active: column 0 and 1
+ * Mode_switch active: column 2 and 3
+ * ISO_Level3_Shift active: column 4 and 5
+ */
+ int base_column = 0;
+ if (modeswitch_active)
+ base_column = 2;
+ if (iso_level3_shift_active)
+ base_column = 4;
+ if (iso_level5_shift_active)
+ base_column = 6;
+ sym0 = xcb_key_press_lookup_keysym(symbols, event, base_column);
+ sym1 = xcb_key_press_lookup_keysym(symbols, event, base_column + 1);
+ switch (sym0) {
+ case XK_Mode_switch:
+ DEBUG("Mode switch enabled\n");
+ modeswitch_active = true;
+ return;
+ case XK_ISO_Level3_Shift:
+ DEBUG("ISO_Level3_Shift enabled\n");
+ iso_level3_shift_active = true;
+ return;
+ case XK_ISO_Level5_Shift:
+ DEBUG("ISO_Level5_Shift enabled\n");
+ iso_level5_shift_active = true;
+ return;
+ case XK_Return:
+ case XK_KP_Enter:
+ input_done();
+ case XK_Escape:
+ input_position = 0;
+ clear_password_memory();
+ password[input_position] = '\0';
+
+ /* Hide the unlock indicator after a bit if the password buffer is
+ * empty. */
+ start_clear_indicator_timeout();
+ unlock_state = STATE_BACKSPACE_ACTIVE;
+ redraw_screen();
+ unlock_state = STATE_KEY_PRESSED;
+ return;
+
+ case XK_BackSpace:
+ if (input_position == 0)
+ return;
+
+ /* decrement input_position to point to the previous glyph */
+ u8_dec(password, &input_position);
+ password[input_position] = '\0';
+
+ /* Hide the unlock indicator after a bit if the password buffer is
+ * empty. */
+ start_clear_indicator_timeout();
+ unlock_state = STATE_BACKSPACE_ACTIVE;
+ redraw_screen();
+ unlock_state = STATE_KEY_PRESSED;
+ return;
+ }
+
+ if ((input_position + 8) >= sizeof(password))
+ return;
+
+ /* Whether the user currently holds down the shift key. */
+ bool shift = (event->state & XCB_MOD_MASK_SHIFT);
+
+ /* Whether Caps Lock (all lowercase alphabetic keys will be replaced by
+ * their uppercase variant) is active at the moment. */
+ bool capslock = (event->state & capslockmask);
+
+ /* Whether Shift Lock (shift state is reversed) is active at the moment. */
+ bool shiftlock = (event->state & shiftlockmask);
+
+ /* Whether Caps Lock or Shift Lock is active at the moment. */
+ bool lock = (capslock || shiftlock);
+
+ DEBUG("shift = %d, lock = %d, capslock = %d, shiftlock = %d\n",
+ shift, lock, capslock, shiftlock);
+
+ if ((event->state & numlockmask) && xcb_is_keypad_key(sym1)) {
+ /* this key was a keypad key */
+ if (shift || shiftlock)
+ sym = sym0;
+ else sym = sym1;
+ } else {
+ xcb_keysym_t upper, lower;
+ XConvertCase(sym0, (KeySym*)&lower, (KeySym*)&upper);
+ DEBUG("sym0 = %c (%d), sym1 = %c (%d), lower = %c (%d), upper = %c (%d)\n",
+ sym0, sym0, sym1, sym1, lower, lower, upper, upper);
+ /* If there is no difference between the uppercase and lowercase
+ * variant of this key, we consider Caps Lock off — it is only relevant
+ * for alphabetic keys, unlike Shift Lock. */
+ if (lower == upper) {
+ capslock = false;
+ lock = (capslock || shiftlock);
+ DEBUG("lower == upper, now shift = %d, lock = %d, capslock = %d, shiftlock = %d\n",
+ shift, lock, capslock, shiftlock);