/*
* vim:ts=4:sw=4:expandtab
*
- * © 2010-2012 Michael Stapelberg
+ * © 2010-2013 Michael Stapelberg
*
* See LICENSE for licensing information
*
#include <string.h>
#include <ev.h>
#include <sys/mman.h>
+#include <sys/wait.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBfile.h>
#include <xkbcommon/xkbcommon.h>
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)
* 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;
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;
clear_pam_wrong_timeout = NULL;
}
-static void input_done(void) {
- if (clear_pam_wrong_timeout) {
- ev_timer_stop(main_loop, clear_pam_wrong_timeout);
- free(clear_pam_wrong_timeout);
- clear_pam_wrong_timeout = NULL;
- }
+static void clear_input(void) {
+ input_position = 0;
+ clear_password_memory();
+ password[input_position] = '\0';
- pam_state = STATE_PAM_VERIFY;
+ /* 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;
+}
- if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS) {
- DEBUG("successfully authenticated\n");
- clear_password_memory();
- exit(0);
- }
-
+static void auth_failed(void) {
if (debug_mode)
fprintf(stderr, "Authentication failure\n");
pam_state = STATE_PAM_WRONG;
+ clear_input();
redraw_screen();
/* Clear this state after 2 seconds (unless the user enters another
}
}
+static void child_cb(EV_P_ ev_child *child_watcher, int revents) {
+ if (child_watcher->rstatus != 0) {
+ DEBUG("Authentication successfull\n");
+ clear_password_memory();
+
+ exit(0);
+ } else {
+ auth_failed();
+ }
+ ev_child_stop(main_loop, child_watcher);
+ free(child_watcher);
+}
+
+static void input_done(void) {
+ if (pam_state == STATE_PAM_VERIFY) {
+ 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();
+
+ /* fork to unblock pam_authenticate */
+ pid_t cpid = fork();
+ if (cpid == 0) {
+ exit(pam_authenticate(pam_handle, 0) == PAM_SUCCESS);
+ } else if (cpid > 0) {
+ struct ev_child *child_watcher = calloc(sizeof(struct ev_io), 1);
+ ev_child_init(child_watcher, child_cb, cpid, 0);
+ ev_child_set(child_watcher, cpid, 0);
+ ev_child_start(EV_DEFAULT_ child_watcher);
+ } else if (cpid < 0) {
+ DEBUG("Could not fork");
+ if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS) {
+ DEBUG("successfully authenticated\n");
+ clear_password_memory();
+ exit(0);
+ }
+ auth_failed();
+ }
+}
+
/*
* Called when the user releases a key. We need to leave the Mode_switch
* state when the user releases the Mode_switch key.
case XKB_KEY_Return:
case XKB_KEY_KP_Enter:
case XKB_KEY_XF86ScreenSaver:
+ if (ignore_empty_password && input_position == 0) {
+ clear_input();
+ return;
+ }
password[input_position] = '\0';
unlock_state = STATE_KEY_PRESSED;
redraw_screen();
input_done();
+ return;
+
case XKB_KEY_u:
- if (!ctrl)
- break;
- case XKB_KEY_Escape:
- input_position = 0;
- clear_password_memory();
- password[input_position] = '\0';
+ if (ctrl) {
+ DEBUG("C-u pressed\n");
+ clear_input();
+ return;
+ }
+ break;
- /* 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;
+ case XKB_KEY_Escape:
+ clear_input();
return;
case XKB_KEY_BackSpace:
{"no-unlock-indicator", no_argument, NULL, 'u'},
{"image", required_argument, NULL, 'i'},
{"tiling", no_argument, NULL, 't'},
+ {"ignore-empty-password", no_argument, NULL, 'e'},
{NULL, no_argument, NULL, 0}
};
if ((username = getenv("USER")) == NULL)
errx(1, "USER environment variable not set, please set it.\n");
- while ((o = getopt_long(argc, argv, "hvnbdc:p:ui:t", longopts, &optind)) != -1) {
+ while ((o = getopt_long(argc, argv, "hvnbdc:p:ui:te", longopts, &optind)) != -1) {
switch (o) {
case 'v':
errx(EXIT_SUCCESS, "version " VERSION " © 2010-2012 Michael Stapelberg");
errx(1, "i3lock: Invalid pointer type given. Expected one of \"win\" or \"default\".\n");
}
break;
+ case 'e':
+ ignore_empty_password = true;
+ break;
case 0:
if (strcmp(longopts[optind].name, "debug") == 0)
debug_mode = true;
break;
default:
errx(1, "Syntax: i3lock [-v] [-n] [-b] [-d] [-c color] [-u] [-p win|default]"
- " [-i image.png] [-t]"
+ " [-i image.png] [-t] [-e]"
);
}
}
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\": cairo surface status %d\n",
- image_path, cairo_surface_status(img));
+ fprintf(stderr, "Could not load image \"%s\": %s\n",
+ image_path, cairo_status_to_string(cairo_surface_status(img)));
img = NULL;
}
}
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();
if (dpms)
dpms_turn_off_screen(conn);