main
44ceea7 ยท 1 month ago 7 commits
  1#include <assert.h>
  2#include <errno.h>
  3#include <pwd.h>
  4#include <stdlib.h>
  5#include <string.h>
  6#include <unistd.h>
  7#include <xkbcommon/xkbcommon.h>
  8#include "comm.h"
  9#include "log.h"
 10#include "loop.h"
 11#include "seat.h"
 12#include "swaylock.h"
 13#include "unicode.h"
 14
 15void clear_buffer(char *buf, size_t size) {
 16	// Use volatile keyword so so compiler can't optimize this out.
 17	volatile char *buffer = buf;
 18	volatile char zero = '\0';
 19	for (size_t i = 0; i < size; ++i) {
 20		buffer[i] = zero;
 21	}
 22}
 23
 24void clear_password_buffer(struct swaylock_password *pw) {
 25	clear_buffer(pw->buffer, pw->buffer_len);
 26	pw->len = 0;
 27}
 28
 29static bool backspace(struct swaylock_password *pw) {
 30	if (pw->len != 0) {
 31		pw->len -= utf8_last_size(pw->buffer);
 32		pw->buffer[pw->len] = 0;
 33		return true;
 34	}
 35	return false;
 36}
 37
 38static void append_ch(struct swaylock_password *pw, uint32_t codepoint) {
 39	size_t utf8_size = utf8_chsize(codepoint);
 40	if (pw->len + utf8_size + 1 >= pw->buffer_len) {
 41		// TODO: Display error
 42		return;
 43	}
 44	utf8_encode(&pw->buffer[pw->len], codepoint);
 45	pw->buffer[pw->len + utf8_size] = 0;
 46	pw->len += utf8_size;
 47}
 48
 49static void clear_indicator(void *data) {
 50	struct swaylock_state *state = data;
 51	state->clear_indicator_timer = NULL;
 52	state->auth_state = AUTH_STATE_IDLE;
 53	damage_state(state);
 54}
 55
 56void schedule_indicator_clear(struct swaylock_state *state) {
 57	if (state->clear_indicator_timer) {
 58		loop_remove_timer(state->eventloop, state->clear_indicator_timer);
 59	}
 60	state->clear_indicator_timer = loop_add_timer(
 61			state->eventloop, 3000, clear_indicator, state);
 62}
 63
 64static void clear_password(void *data) {
 65	struct swaylock_state *state = data;
 66	state->clear_password_timer = NULL;
 67	state->auth_state = AUTH_STATE_CLEAR;
 68	clear_password_buffer(&state->password);
 69	damage_state(state);
 70	schedule_indicator_clear(state);
 71}
 72
 73static void schedule_password_clear(struct swaylock_state *state) {
 74	if (state->clear_password_timer) {
 75		loop_remove_timer(state->eventloop, state->clear_password_timer);
 76	}
 77	state->clear_password_timer = loop_add_timer(
 78			state->eventloop, 10000, clear_password, state);
 79}
 80
 81static void submit_password(struct swaylock_state *state) {
 82	if (state->args.ignore_empty && state->password.len == 0) {
 83		return;
 84	}
 85
 86	state->auth_state = AUTH_STATE_VALIDATING;
 87
 88	if (!write_comm_request(&state->password)) {
 89		state->auth_state = AUTH_STATE_INVALID;
 90		schedule_indicator_clear(state);
 91	}
 92
 93	damage_state(state);
 94}
 95
 96void swaylock_handle_mouse(struct swaylock_state *state) {
 97	if (state->auth_state == AUTH_STATE_GRACE && !state->args.password_grace_no_mouse) {
 98		state->run_display = false;
 99	}
100}
101
102void swaylock_handle_touch(struct swaylock_state *state) {
103	if (state->auth_state == AUTH_STATE_GRACE && !state->args.password_grace_no_touch) {
104		state->run_display = false;
105	} else if (state->auth_state != AUTH_STATE_VALIDATING && state->args.password_submit_on_touch) {
106		submit_password(state);
107	}
108}
109
110void swaylock_handle_key(struct swaylock_state *state,
111		xkb_keysym_t keysym, uint32_t codepoint) {
112	// Authentication not needed
113	if (state->auth_state == AUTH_STATE_GRACE) {
114		state->run_display = false;
115		return;
116	}
117	// Ignore input events if validating
118	if (state->auth_state == AUTH_STATE_VALIDATING) {
119		return;
120	}
121
122	switch (keysym) {
123	case XKB_KEY_KP_Enter: /* fallthrough */
124	case XKB_KEY_Return:
125		submit_password(state);
126		break;
127	case XKB_KEY_Delete:
128	case XKB_KEY_BackSpace:
129		if (backspace(&state->password)) {
130			state->auth_state = AUTH_STATE_BACKSPACE;
131		} else {
132			state->auth_state = AUTH_STATE_CLEAR;
133		}
134		state->indicator_dirty = true;
135		damage_state(state);
136		schedule_indicator_clear(state);
137		schedule_password_clear(state);
138		break;
139	case XKB_KEY_Escape:
140		clear_password_buffer(&state->password);
141		state->auth_state = AUTH_STATE_CLEAR;
142		state->indicator_dirty = true;
143		schedule_indicator_clear(state);
144		break;
145	case XKB_KEY_Caps_Lock:
146	case XKB_KEY_Shift_L:
147	case XKB_KEY_Shift_R:
148	case XKB_KEY_Control_L:
149	case XKB_KEY_Control_R:
150	case XKB_KEY_Meta_L:
151	case XKB_KEY_Meta_R:
152	case XKB_KEY_Alt_L:
153	case XKB_KEY_Alt_R:
154	case XKB_KEY_Super_L:
155	case XKB_KEY_Super_R:
156		state->auth_state = AUTH_STATE_INPUT_NOP;
157		damage_state(state);
158		schedule_indicator_clear(state);
159		schedule_password_clear(state);
160		break;
161	case XKB_KEY_m: /* fallthrough */
162	case XKB_KEY_d:
163	case XKB_KEY_j:
164		if (state->xkb.control) {
165			submit_password(state);
166			break;
167		}
168		// fallthrough
169	case XKB_KEY_c: /* fallthrough */
170	case XKB_KEY_u:
171		if (state->xkb.control) {
172			clear_password_buffer(&state->password);
173			state->auth_state = AUTH_STATE_CLEAR;
174			damage_state(state);
175			schedule_indicator_clear(state);
176			break;
177		}
178		// fallthrough
179	default:
180		if (codepoint) {
181			append_ch(&state->password, codepoint);
182			state->auth_state = AUTH_STATE_INPUT;
183			state->indicator_dirty = true;
184			damage_state(state);
185			schedule_indicator_clear(state);
186			schedule_password_clear(state);
187		}
188		break;
189	}
190}