+ }
+
+ if (!composed) {
+ n = xkb_keysym_to_utf8(ksym, buffer, sizeof(buffer));
+ }
+
+ switch (ksym) {
+ case XKB_KEY_j:
+ case XKB_KEY_m:
+ case XKB_KEY_Return:
+ case XKB_KEY_KP_Enter:
+ case XKB_KEY_XF86ScreenSaver:
+ if ((ksym == XKB_KEY_j || ksym == XKB_KEY_m) && !ctrl)
+ break;
+
+ if (auth_state == STATE_AUTH_WRONG) {
+ retry_verification = true;
+ return;
+ }
+
+ if (skip_without_validation()) {
+ clear_input();
+ return;
+ }
+ finish_input();
+ skip_repeated_empty_password = true;
+ return;
+ default:
+ skip_repeated_empty_password = false;
+ }
+
+ switch (ksym) {
+ case XKB_KEY_u:
+ case XKB_KEY_Escape:
+ if ((ksym == XKB_KEY_u && ctrl) ||
+ ksym == XKB_KEY_Escape) {
+ DEBUG("C-u pressed\n");
+ clear_input();
+ /* Hide the unlock indicator after a bit if the password buffer is
+ * empty. */
+ if (unlock_indicator) {
+ START_TIMER(clear_indicator_timeout, 1.0, clear_indicator_cb);
+ unlock_state = STATE_BACKSPACE_ACTIVE;
+ redraw_screen();
+ unlock_state = STATE_KEY_PRESSED;
+ }
+ return;
+ }
+ break;
+
+ case XKB_KEY_Delete:
+ case XKB_KEY_KP_Delete:
+ /* Deleting forward doesn’t make sense, as i3lock doesn’t allow you
+ * to move the cursor when entering a password. We need to eat this
+ * key press so that it won’t be treated as part of the password,
+ * see issue #50. */
+ return;
+
+ case XKB_KEY_h:
+ case XKB_KEY_BackSpace:
+ if (ksym == XKB_KEY_h && !ctrl)
+ break;
+
+ 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_TIMER(clear_indicator_timeout, 1.0, clear_indicator_cb);
+ unlock_state = STATE_BACKSPACE_ACTIVE;
+ redraw_screen();
+ unlock_state = STATE_KEY_PRESSED;
+ return;
+ }
+
+ if ((input_position + 8) >= sizeof(password))
+ return;
+
+#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 (n < 2)
+ return;
+
+ /* store it in the password array as UTF-8 */
+ memcpy(password + input_position, buffer, n - 1);
+ input_position += n - 1;
+ DEBUG("current password = %.*s\n", input_position, password);
+
+ if (unlock_indicator) {
+ unlock_state = STATE_KEY_ACTIVE;
+ redraw_screen();
+ unlock_state = STATE_KEY_PRESSED;
+
+ struct ev_timer *timeout = NULL;
+ START_TIMER(timeout, TSTAMP_N_SECS(0.25), redraw_timeout);
+ STOP_TIMER(clear_indicator_timeout);
+ }
+
+ START_TIMER(discard_passwd_timeout, TSTAMP_N_MINS(3), discard_passwd_cb);
+}
+
+/*
+ * 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.
+ *
+ * In this case, we raise our window on top so that the popup (or whatever is
+ * hiding us) gets hidden.
+ *
+ */
+static void handle_visibility_notify(xcb_connection_t *conn,
+ xcb_visibility_notify_event_t *event) {
+ if (event->state != XCB_VISIBILITY_UNOBSCURED) {
+ uint32_t values[] = {XCB_STACK_MODE_ABOVE};
+ xcb_configure_window(conn, event->window, XCB_CONFIG_WINDOW_STACK_MODE, values);
+ xcb_flush(conn);
+ }
+}
+
+/*
+ * Called when the keyboard mapping changes. We update our symbols.
+ *
+ * We ignore errors — if the new keymap cannot be loaded it’s better if the
+ * screen stays locked and the user intervenes by using killall i3lock.
+ *
+ */
+static void process_xkb_event(xcb_generic_event_t *gevent) {
+ union xkb_event {
+ struct {
+ uint8_t response_type;
+ uint8_t xkbType;
+ uint16_t sequence;
+ xcb_timestamp_t time;
+ uint8_t deviceID;
+ } any;
+ xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify;
+ xcb_xkb_map_notify_event_t map_notify;
+ xcb_xkb_state_notify_event_t state_notify;
+ } *event = (union xkb_event *)gevent;
+
+ DEBUG("process_xkb_event for device %d\n", event->any.deviceID);
+
+ if (event->any.deviceID != xkb_x11_get_core_keyboard_device_id(conn))
+ return;
+
+ /*
+ * XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap
+ * updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent
+ * recompilations.
+ */
+ switch (event->any.xkbType) {
+ case XCB_XKB_NEW_KEYBOARD_NOTIFY:
+ if (event->new_keyboard_notify.changed & XCB_XKB_NKN_DETAIL_KEYCODES)
+ (void)load_keymap();
+ break;
+
+ case XCB_XKB_MAP_NOTIFY:
+ (void)load_keymap();
+ break;
+
+ case XCB_XKB_STATE_NOTIFY:
+ xkb_state_update_mask(xkb_state,
+ event->state_notify.baseMods,
+ event->state_notify.latchedMods,
+ event->state_notify.lockedMods,
+ event->state_notify.baseGroup,
+ event->state_notify.latchedGroup,
+ event->state_notify.lockedGroup);
+ break;
+ }