Separate input and auth state
This commit establishes separate state machines for auth state (whether the password submitted is being verified or is wrong) and input state (typing indicators and clear message -- things relevant to the state of the password being typed in, before it is submitted.) This makes it possible to display the auth state while updating the input state (for example, show that the previously submitted password is 'verifying' or 'wrong' while typing another.) The two state machines interact only when submitting a password. There is some interference with the rendering code -- a 'cleared' message from the input state machine supersedes verifying/wrong messages from the auth state machine; although since the 'clear' state has a shorter timeout than the auth 'invalid' state, this is unlikely to hide the 'wrong' message.
This commit is contained in:
parent
876965f944
commit
31ebd85fe0
@ -8,14 +8,20 @@
|
||||
#include "pool-buffer.h"
|
||||
#include "seat.h"
|
||||
|
||||
// Indicator state: status of authentication attempt
|
||||
enum auth_state {
|
||||
AUTH_STATE_IDLE,
|
||||
AUTH_STATE_CLEAR,
|
||||
AUTH_STATE_INPUT,
|
||||
AUTH_STATE_INPUT_NOP,
|
||||
AUTH_STATE_BACKSPACE,
|
||||
AUTH_STATE_VALIDATING,
|
||||
AUTH_STATE_INVALID,
|
||||
AUTH_STATE_IDLE, // nothing happening
|
||||
AUTH_STATE_VALIDATING, // currently validating password
|
||||
AUTH_STATE_INVALID, // displaying message: password was wrong
|
||||
};
|
||||
|
||||
// Indicator state: status of password buffer / typing letters
|
||||
enum input_state {
|
||||
INPUT_STATE_IDLE, // nothing happening; other states decay to this after time
|
||||
INPUT_STATE_CLEAR, // displaying message: password buffer was cleared
|
||||
INPUT_STATE_LETTER, // pressed a key that input a letter
|
||||
INPUT_STATE_BACKSPACE, // pressed backspace and removed a letter
|
||||
INPUT_STATE_NEUTRAL, // pressed a key (like Ctrl) that did nothing
|
||||
};
|
||||
|
||||
struct swaylock_colorset {
|
||||
@ -73,7 +79,8 @@ struct swaylock_password {
|
||||
|
||||
struct swaylock_state {
|
||||
struct loop *eventloop;
|
||||
struct loop_timer *clear_indicator_timer; // clears the indicator
|
||||
struct loop_timer *input_idle_timer; // timer to reset input state to IDLE
|
||||
struct loop_timer *auth_idle_timer; // timer to stop displaying AUTH_STATE_INVALID
|
||||
struct loop_timer *clear_password_timer; // clears the password buffer
|
||||
struct wl_display *display;
|
||||
struct wl_compositor *compositor;
|
||||
@ -86,7 +93,8 @@ struct swaylock_state {
|
||||
struct swaylock_xkb xkb;
|
||||
cairo_surface_t *test_surface;
|
||||
cairo_t *test_cairo; // used to estimate font/text sizes
|
||||
enum auth_state auth_state;
|
||||
enum auth_state auth_state; // state of the authentication attempt
|
||||
enum input_state input_state; // state of the password buffer and key inputs
|
||||
uint32_t highlight_start; // position of highlight; 2048 = 1 full turn
|
||||
int failed_attempts;
|
||||
bool run_display, locked;
|
||||
@ -129,7 +137,7 @@ void render_frame(struct swaylock_surface *surface);
|
||||
void damage_surface(struct swaylock_surface *surface);
|
||||
void damage_state(struct swaylock_state *state);
|
||||
void clear_password_buffer(struct swaylock_password *pw);
|
||||
void schedule_indicator_clear(struct swaylock_state *state);
|
||||
void schedule_auth_idle(struct swaylock_state *state);
|
||||
|
||||
void initialize_pw_backend(int argc, char **argv);
|
||||
void run_pw_backend_child(void);
|
||||
|
2
main.c
2
main.c
@ -1072,7 +1072,7 @@ static void comm_in(int fd, short mask, void *data) {
|
||||
state.run_display = false;
|
||||
} else {
|
||||
state.auth_state = AUTH_STATE_INVALID;
|
||||
schedule_indicator_clear(&state);
|
||||
schedule_auth_idle(&state);
|
||||
++state.failed_attempts;
|
||||
damage_state(&state);
|
||||
}
|
||||
|
83
password.c
83
password.c
@ -46,28 +46,50 @@ static void append_ch(struct swaylock_password *pw, uint32_t codepoint) {
|
||||
pw->len += utf8_size;
|
||||
}
|
||||
|
||||
static void clear_indicator(void *data) {
|
||||
static void set_input_idle(void *data) {
|
||||
struct swaylock_state *state = data;
|
||||
state->clear_indicator_timer = NULL;
|
||||
state->input_idle_timer = NULL;
|
||||
state->input_state = INPUT_STATE_IDLE;
|
||||
damage_state(state);
|
||||
}
|
||||
|
||||
static void set_auth_idle(void *data) {
|
||||
struct swaylock_state *state = data;
|
||||
state->auth_idle_timer = NULL;
|
||||
state->auth_state = AUTH_STATE_IDLE;
|
||||
damage_state(state);
|
||||
}
|
||||
|
||||
void schedule_indicator_clear(struct swaylock_state *state) {
|
||||
if (state->clear_indicator_timer) {
|
||||
loop_remove_timer(state->eventloop, state->clear_indicator_timer);
|
||||
static void schedule_input_idle(struct swaylock_state *state) {
|
||||
if (state->input_idle_timer) {
|
||||
loop_remove_timer(state->eventloop, state->input_idle_timer);
|
||||
}
|
||||
state->clear_indicator_timer = loop_add_timer(
|
||||
state->eventloop, 3000, clear_indicator, state);
|
||||
state->input_idle_timer = loop_add_timer(
|
||||
state->eventloop, 1500, set_input_idle, state);
|
||||
}
|
||||
|
||||
static void cancel_input_idle(struct swaylock_state *state) {
|
||||
if (state->input_idle_timer) {
|
||||
loop_remove_timer(state->eventloop, state->input_idle_timer);
|
||||
state->input_idle_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void schedule_auth_idle(struct swaylock_state *state) {
|
||||
if (state->auth_idle_timer) {
|
||||
loop_remove_timer(state->eventloop, state->auth_idle_timer);
|
||||
}
|
||||
state->auth_idle_timer = loop_add_timer(
|
||||
state->eventloop, 3000, set_auth_idle, state);
|
||||
}
|
||||
|
||||
static void clear_password(void *data) {
|
||||
struct swaylock_state *state = data;
|
||||
state->clear_password_timer = NULL;
|
||||
state->auth_state = AUTH_STATE_CLEAR;
|
||||
state->input_state = INPUT_STATE_CLEAR;
|
||||
schedule_input_idle(state);
|
||||
clear_password_buffer(&state->password);
|
||||
damage_state(state);
|
||||
schedule_indicator_clear(state);
|
||||
}
|
||||
|
||||
static void schedule_password_clear(struct swaylock_state *state) {
|
||||
@ -78,16 +100,26 @@ static void schedule_password_clear(struct swaylock_state *state) {
|
||||
state->eventloop, 10000, clear_password, state);
|
||||
}
|
||||
|
||||
static void cancel_password_clear(struct swaylock_state *state) {
|
||||
if (state->clear_password_timer) {
|
||||
loop_remove_timer(state->eventloop, state->clear_password_timer);
|
||||
state->clear_password_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void submit_password(struct swaylock_state *state) {
|
||||
if (state->args.ignore_empty && state->password.len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->input_state = INPUT_STATE_IDLE;
|
||||
state->auth_state = AUTH_STATE_VALIDATING;
|
||||
cancel_password_clear(state);
|
||||
cancel_input_idle(state);
|
||||
|
||||
if (!write_comm_request(&state->password)) {
|
||||
state->auth_state = AUTH_STATE_INVALID;
|
||||
schedule_indicator_clear(state);
|
||||
schedule_auth_idle(state);
|
||||
}
|
||||
|
||||
damage_state(state);
|
||||
@ -110,20 +142,22 @@ void swaylock_handle_key(struct swaylock_state *state,
|
||||
case XKB_KEY_Delete:
|
||||
case XKB_KEY_BackSpace:
|
||||
if (backspace(&state->password)) {
|
||||
state->auth_state = AUTH_STATE_BACKSPACE;
|
||||
state->input_state = INPUT_STATE_BACKSPACE;
|
||||
schedule_password_clear(state);
|
||||
update_highlight(state);
|
||||
} else {
|
||||
state->auth_state = AUTH_STATE_CLEAR;
|
||||
state->input_state = INPUT_STATE_CLEAR;
|
||||
cancel_password_clear(state);
|
||||
}
|
||||
schedule_input_idle(state);
|
||||
damage_state(state);
|
||||
schedule_indicator_clear(state);
|
||||
schedule_password_clear(state);
|
||||
break;
|
||||
case XKB_KEY_Escape:
|
||||
clear_password_buffer(&state->password);
|
||||
state->auth_state = AUTH_STATE_CLEAR;
|
||||
state->input_state = INPUT_STATE_CLEAR;
|
||||
cancel_password_clear(state);
|
||||
schedule_input_idle(state);
|
||||
damage_state(state);
|
||||
schedule_indicator_clear(state);
|
||||
break;
|
||||
case XKB_KEY_Caps_Lock:
|
||||
case XKB_KEY_Shift_L:
|
||||
@ -136,10 +170,10 @@ void swaylock_handle_key(struct swaylock_state *state,
|
||||
case XKB_KEY_Alt_R:
|
||||
case XKB_KEY_Super_L:
|
||||
case XKB_KEY_Super_R:
|
||||
state->auth_state = AUTH_STATE_INPUT_NOP;
|
||||
damage_state(state);
|
||||
schedule_indicator_clear(state);
|
||||
state->input_state = INPUT_STATE_NEUTRAL;
|
||||
schedule_password_clear(state);
|
||||
schedule_input_idle(state);
|
||||
damage_state(state);
|
||||
break;
|
||||
case XKB_KEY_m: /* fallthrough */
|
||||
case XKB_KEY_d:
|
||||
@ -153,20 +187,21 @@ void swaylock_handle_key(struct swaylock_state *state,
|
||||
case XKB_KEY_u:
|
||||
if (state->xkb.control) {
|
||||
clear_password_buffer(&state->password);
|
||||
state->auth_state = AUTH_STATE_CLEAR;
|
||||
state->input_state = INPUT_STATE_CLEAR;
|
||||
cancel_password_clear(state);
|
||||
schedule_input_idle(state);
|
||||
damage_state(state);
|
||||
schedule_indicator_clear(state);
|
||||
break;
|
||||
}
|
||||
// fallthrough
|
||||
default:
|
||||
if (codepoint) {
|
||||
append_ch(&state->password, codepoint);
|
||||
state->auth_state = AUTH_STATE_INPUT;
|
||||
state->input_state = INPUT_STATE_LETTER;
|
||||
schedule_password_clear(state);
|
||||
schedule_input_idle(state);
|
||||
update_highlight(state);
|
||||
damage_state(state);
|
||||
schedule_indicator_clear(state);
|
||||
schedule_password_clear(state);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
44
render.c
44
render.c
@ -12,12 +12,12 @@ const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
|
||||
|
||||
static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state,
|
||||
struct swaylock_colorset *colorset) {
|
||||
if (state->auth_state == AUTH_STATE_VALIDATING) {
|
||||
if (state->input_state == INPUT_STATE_CLEAR) {
|
||||
cairo_set_source_u32(cairo, colorset->cleared);
|
||||
} else if (state->auth_state == AUTH_STATE_VALIDATING) {
|
||||
cairo_set_source_u32(cairo, colorset->verifying);
|
||||
} else if (state->auth_state == AUTH_STATE_INVALID) {
|
||||
cairo_set_source_u32(cairo, colorset->wrong);
|
||||
} else if (state->auth_state == AUTH_STATE_CLEAR) {
|
||||
cairo_set_source_u32(cairo, colorset->cleared);
|
||||
} else {
|
||||
if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) {
|
||||
cairo_set_source_u32(cairo, colorset->caps_lock);
|
||||
@ -107,20 +107,20 @@ void render_frame(struct swaylock_surface *surface) {
|
||||
char *text = NULL;
|
||||
const char *layout_text = NULL;
|
||||
|
||||
if (state->args.show_indicator) {
|
||||
switch (state->auth_state) {
|
||||
case AUTH_STATE_VALIDATING:
|
||||
text = "Verifying";
|
||||
break;
|
||||
case AUTH_STATE_INVALID:
|
||||
text = "Wrong";
|
||||
break;
|
||||
case AUTH_STATE_CLEAR:
|
||||
bool draw_indicator = state->args.show_indicator &&
|
||||
(state->auth_state != AUTH_STATE_IDLE ||
|
||||
state->input_state != INPUT_STATE_IDLE ||
|
||||
state->args.indicator_idle_visible);
|
||||
|
||||
if (draw_indicator) {
|
||||
if (state->input_state == INPUT_STATE_CLEAR) {
|
||||
// This message has highest priority
|
||||
text = "Cleared";
|
||||
break;
|
||||
case AUTH_STATE_INPUT:
|
||||
case AUTH_STATE_INPUT_NOP:
|
||||
case AUTH_STATE_BACKSPACE:
|
||||
} else if (state->auth_state == AUTH_STATE_VALIDATING) {
|
||||
text = "Verifying";
|
||||
} else if (state->auth_state == AUTH_STATE_INVALID) {
|
||||
text = "Wrong";
|
||||
} else {
|
||||
// Caps Lock has higher priority
|
||||
if (state->xkb.caps_lock && state->args.show_caps_lock_text) {
|
||||
text = "Caps Lock";
|
||||
@ -148,9 +148,6 @@ void render_frame(struct swaylock_surface *surface) {
|
||||
// will handle invalid index if none are active
|
||||
layout_text = xkb_keymap_layout_get_name(state->xkb.keymap, curr_layout);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,8 +227,7 @@ void render_frame(struct swaylock_surface *surface) {
|
||||
float type_indicator_border_thickness =
|
||||
TYPE_INDICATOR_BORDER_THICKNESS * surface->scale;
|
||||
|
||||
if (state->args.show_indicator && (state->auth_state != AUTH_STATE_IDLE ||
|
||||
state->args.indicator_idle_visible)) {
|
||||
if (draw_indicator) {
|
||||
// Fill inner circle
|
||||
cairo_set_line_width(cairo, 0);
|
||||
cairo_arc(cairo, buffer_width / 2, buffer_diameter / 2,
|
||||
@ -269,13 +265,13 @@ void render_frame(struct swaylock_surface *surface) {
|
||||
}
|
||||
|
||||
// Typing indicator: Highlight random part on keypress
|
||||
if (state->auth_state == AUTH_STATE_INPUT
|
||||
|| state->auth_state == AUTH_STATE_BACKSPACE) {
|
||||
if (state->input_state == INPUT_STATE_LETTER ||
|
||||
state->input_state == INPUT_STATE_BACKSPACE) {
|
||||
double highlight_start = state->highlight_start * (M_PI / 1024.0);
|
||||
cairo_arc(cairo, buffer_width / 2, buffer_diameter / 2,
|
||||
arc_radius, highlight_start,
|
||||
highlight_start + TYPE_INDICATOR_RANGE);
|
||||
if (state->auth_state == AUTH_STATE_INPUT) {
|
||||
if (state->input_state == INPUT_STATE_LETTER) {
|
||||
if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) {
|
||||
cairo_set_source_u32(cairo, state->args.colors.caps_lock_key_highlight);
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user