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}