i3 - improved tiling WM


Authentication in different process

Patch status: superseded

Patch by Philippe Virouleau

Long description:

Add the possibility to buffer keystrokes when a process is running authentication
Fix #1090.

To apply this patch, use:
curl http://cr.i3wm.org/patch/300/raw.patch | git am

b/i3lock.c

15
@@ -63,6 +63,18 @@ cairo_surface_t *img = NULL;
16
 bool tile = false;
17
 bool ignore_empty_password = false;
18
 
19
+typedef struct ringbuffer_t {
20
+    int head;
21
+    int tail;
22
+    int size;
23
+    xcb_generic_event_t *buf[512];
24
+} ringbuffer_t;
25
+/* Size of the ringbuffer */
26
+const int ringbuffer_max = 512;
27
+
28
+/* Ring buffer where keystrokes are stored during authentication */
29
+static ringbuffer_t buffered_events = { 0, 0, 0 };
30
+
31
 /* isutf, u8_dec © 2005 Jeff Bezanson, public domain */
32
 #define isutf(c) (((c) & 0xC0) != 0x80)
33
 
34
@@ -75,6 +87,39 @@ void u8_dec(char *s, int *i) {
35
 }
36
 
37
 /*
38
+ * Add an event to the ring buffer.
39
+ *
40
+ * Return true if the event was added, false if there was an error
41
+ * (no space available or null buffer).
42
+ *
43
+ */
44
+static bool ringbuffer_add(ringbuffer_t *bufring, xcb_generic_event_t *event) {
45
+    if (bufring && bufring->size < ringbuffer_max) {
46
+        bufring->buf[bufring->head++] = event;
47
+        bufring->head %= ringbuffer_max;
48
+        bufring->size++;
49
+        return true;
50
+    }
51
+    return false;
52
+}
53
+
54
+/*
55
+ * Remove an event from the ring buffer.
56
+ *
57
+ * Return null if there was an error (empty or null buffer).
58
+ *
59
+ */
60
+static xcb_generic_event_t *ringbuffer_remove(ringbuffer_t *bufring) {
61
+    xcb_generic_event_t *ret_val = NULL;
62
+    if (bufring && bufring->size > 0) {
63
+        ret_val = bufring->buf[bufring->tail++];
64
+        bufring->tail %= ringbuffer_max;
65
+        bufring->size--;
66
+    }
67
+    return ret_val;
68
+}
69
+
70
+/*
71
  * Loads the XKB keymap from the X11 server and feeds it to xkbcommon.
72
  * Necessary so that we can properly let xkbcommon track the keyboard state and
73
  * translate keypresses to utf-8.
74
@@ -187,10 +232,14 @@ static void clear_pam_wrong(EV_P_ ev_timer *w, int revents) {
75
     clear_pam_wrong_timeout = NULL;
76
 }
77
 
78
-static void clear_input(void) {
79
+static void reset_input(void) {
80
     input_position = 0;
81
     clear_password_memory();
82
     password[input_position] = '\0';
83
+}
84
+
85
+static void clear_input(void) {
86
+    reset_input();
87
 
88
     /* Hide the unlock indicator after a bit if the password buffer is
89
      * empty. */
90
@@ -205,7 +254,6 @@ static void auth_failed(void) {
91
         fprintf(stderr, "Authentication failure\n");
92
 
93
     pam_state = STATE_PAM_WRONG;
94
-    clear_input();
95
     redraw_screen();
96
 
97
     /* Clear this state after 2 seconds (unless the user enters another
98
@@ -227,6 +275,9 @@ static void auth_failed(void) {
99
     }
100
 }
101
 
102
+static void handle_key_press(xcb_key_press_event_t *event);
103
+static void handle_key_release(xcb_key_release_event_t *event);
104
+
105
 static void child_cb(EV_P_ ev_child *child_watcher, int revents) {
106
     if (child_watcher->rstatus != 0) {
107
         DEBUG("Authentication successfull\n");
108
@@ -235,6 +286,17 @@ static void child_cb(EV_P_ ev_child *child_watcher, int revents) {
109
         exit(0);
110
     } else {
111
         auth_failed();
112
+        xcb_generic_event_t *event;
113
+        while (pam_state != STATE_PAM_VERIFY &&
114
+            (event = ringbuffer_remove(&buffered_events))) {
115
+            /* Strip off the highest bit (set if the event is generated) */
116
+            int type = (event->response_type & 0x7F);
117
+            if (type == XCB_KEY_PRESS)
118
+                handle_key_press((xcb_key_press_event_t*)event);
119
+            else if (type == XCB_KEY_RELEASE)
120
+                handle_key_release((xcb_key_release_event_t*)event);
121
+            free(event);
122
+        }
123
     }
124
     ev_child_stop(main_loop, child_watcher);
125
     free(child_watcher);
126
@@ -259,7 +321,8 @@ static void input_done(void) {
127
     if (cpid == 0) {
128
         exit(pam_authenticate(pam_handle, 0) == PAM_SUCCESS);
129
     } else if (cpid > 0) {
130
-        struct ev_child *child_watcher = calloc(sizeof(struct ev_io), 1);
131
+        reset_input();
132
+        struct ev_child *child_watcher = calloc(sizeof(struct ev_child), 1);
133
         ev_child_init(child_watcher, child_cb, cpid, 0);
134
         ev_child_set(child_watcher, cpid, 0);
135
         ev_child_start(EV_DEFAULT_ child_watcher);
136
@@ -281,6 +344,11 @@ static void input_done(void) {
137
  */
138
 static void handle_key_release(xcb_key_release_event_t *event) {
139
     xkb_state_update_key(xkb_state, event->detail, XKB_KEY_UP);
140
+
141
+    /* If this was the backspace or escape key we are back at an
142
+     * empty input, so turn off the screen if DPMS is enabled */
143
+    if (dpms && input_position == 0)
144
+        dpms_turn_off_screen(conn);
145
 }
146
 
147
 static void redraw_timeout(EV_P_ ev_timer *w, int revents) {
148
@@ -518,6 +586,14 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
149
 
150
         /* Strip off the highest bit (set if the event is generated) */
151
         int type = (event->response_type & 0x7F);
152
+        if (pam_state == STATE_PAM_VERIFY &&
153
+            (type == XCB_KEY_PRESS || type == XCB_KEY_RELEASE)) {
154
+            /* Don’t process KeyPress/KeyRelease events when a verification is
155
+             * in progress. Try to buffer them or drop them if the buffer is full. */
156
+            if (!ringbuffer_add(&buffered_events, event))
157
+                free(event);
158
+            continue;
159
+        }
160
         switch (type) {
161
             case XCB_KEY_PRESS:
162
                 handle_key_press((xcb_key_press_event_t*)event);
163
@@ -525,12 +601,6 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
164
 
165
             case XCB_KEY_RELEASE:
166
                 handle_key_release((xcb_key_release_event_t*)event);
167
-
168
-                /* If this was the backspace or escape key we are back at an
169
-                 * empty input, so turn off the screen if DPMS is enabled */
170
-                if (dpms && input_position == 0)
171
-                    dpms_turn_off_screen(conn);
172
-
173
                 break;
174
 
175
             case XCB_VISIBILITY_NOTIFY: