+ */
+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);
+ }
+
+ /* In two different cases we need to use the uppercase keysym:
+ * 1) The user holds shift, no lock is active.
+ * 2) Any of the two locks is active.
+ */
+ if ((shift && !lock) || (!shift && lock))
+ sym = sym1;
+ else sym = sym0;
+ }
+
+#if 0
+ /* FIXME: handle all of these? */
+ printf("is_keypad_key = %d\n", xcb_is_keypad_key(sym));
+ printf("is_private_keypad_key = %d\n", xcb_is_private_keypad_key(sym));
+ printf("xcb_is_cursor_key = %d\n", xcb_is_cursor_key(sym));
+ printf("xcb_is_pf_key = %d\n", xcb_is_pf_key(sym));
+ printf("xcb_is_function_key = %d\n", xcb_is_function_key(sym));
+ printf("xcb_is_misc_function_key = %d\n", xcb_is_misc_function_key(sym));
+ printf("xcb_is_modifier_key = %d\n", xcb_is_modifier_key(sym));
+#endif
+
+ if (xcb_is_modifier_key(sym) || xcb_is_cursor_key(sym))
+ return;
+
+ DEBUG("resolved to keysym = %c (%d)\n", sym, sym);
+
+ /* convert the keysym to UCS */
+ uint16_t ucs = keysym2ucs(sym);
+ if ((int16_t)ucs == -1) {
+ if (debug_mode)
+ fprintf(stderr, "Keysym could not be converted to UCS, skipping\n");
+ return;
+ }
+
+ /* store the UCS in a string to convert it */
+ uint8_t inp[3] = {(ucs & 0xFF00) >> 8, (ucs & 0xFF), 0};
+ DEBUG("input part = %s\n", inp);
+
+ /* store it in the password array as UTF-8 */
+ input_position += convert_ucs_to_utf8((char*)inp, password + input_position);
+ password[input_position] = '\0';
+ DEBUG("current password = %s\n", password);
+
+ unlock_state = STATE_KEY_ACTIVE;
+ redraw_screen();
+ unlock_state = STATE_KEY_PRESSED;
+
+ struct ev_timer *timeout = calloc(sizeof(struct ev_timer), 1);
+ if (timeout) {
+ ev_timer_init(timeout, redraw_timeout, 0.25, 0.);
+ ev_timer_start(main_loop, timeout);
+ }
+
+ stop_clear_indicator_timeout();
+}
+
+/*
+ * A visibility notify event will be received when the visibility (= can the
+ * user view the complete window) changes, so for example when a popup overlays
+ * some area of the i3lock window.