-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.c
309 lines (272 loc) · 9.9 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#define _POSIX_C_SOURCE 200809L
#include "client_state.h"
#include "config.h"
#include "freetype.h"
#include "log.h"
#include "pam.h"
#include "protocols/src/xdg-shell-client-protocol.c"
#include "protocols/xdg-shell-client-protocol.h"
#include "session_lock_handle.h"
#include "shaders.h"
#include "shared_mem_handle.h"
#include "wl_buffer_handle.h"
#include "wl_keyboard_handle.h"
#include "wl_output_handle.h"
#include "wl_pointer_handle.h"
#include "wl_registry_handle.h"
#include "wl_seat_handle.h"
#include "xdg_surface_handle.h"
#include "xdg_wm_base_handle.h"
/**********************************************
* @HOW ANVILOCK WORKS
**********************************************
*
* $ THIS IS OUTDATED WILL UPDATE SOON
*
* This program implements a simple screen lock feature using the Wayland
* protocol and the XDG Shell extension. It connects to a Wayland server,
* creates a window for the lock screen, and handles keyboard input for
* user authentication.
*
* @STRUCTURES AND LISTENERS:
*
* 1. **client_state**: A structure that holds the state of the client,
* including:
* - Wayland display, registry, compositor, and other protocol objects.
* - XDG surfaces for the lock screen and top-level windows.
* - Pointer and keyboard objects.
* - Buffer for rendering the lock screen.
* - Current user input data (username and password) and authentication status.
*
* 2. **Listeners**: Functions that respond to various Wayland events:
* - **wl_registry_listener**: Listens for global objects added or
* removed (e.g., compositor, SHM).
* - **wl_seat_listener**: Listens for capabilities of input devices
* (e.g., pointer, keyboard).
* - **wl_keyboard_listener**: Listens for keyboard events (key presses,
* keymap updates, etc.) and handles user input for unlocking.
*
* @FLOW OF THE PROGRAM:
*
* 1. **Initialization**:
* - The program starts by connecting to the Wayland display server and
* obtaining the registry.
* - The registry listener is added to receive global objects, which are
* necessary for rendering the lock screen.
* - A round trip to the display server is performed to get the global
* objects needed for window management and input handling.
*
* 2. **Creating the Lock Screen Surface**:
* - A Wayland surface is created through the compositor for the lock screen.
* - An XDG surface is obtained from the XDG shell base, allowing for proper
* management of the lock screen window.
* - The lock screen is configured with a title and is committed to the
* compositor for display.
*
* 3. **Event Loop**:
* - The program enters an event loop where it dispatches Wayland events.
* This enables the client to respond to keyboard input and manage the
* lock screen display.
*
* 4. **Keyboard Input Handling**:
* - The keyboard listener captures key presses and releases.
* - When the user types their password, the input is collected into a
* buffer (`client_state->password`), with backspace handling to allow
* for corrections.
* - Special key handling for the Return key triggers authentication logic:
* - If the password is non-empty, the program attempts to authenticate
* the user against a PAM (Pluggable Authentication Module) service.
* - On successful authentication, the program logs the event and
* updates the client's state to indicate that the user is authenticated.
* - On failure, the password buffer is cleared, and a failure message
* is displayed on the lock screen.
*
* 5. **Buffer Management**:
* - A shared memory buffer is created to store pixel data for drawing the
* lock screen.
* - The `draw_lock_screen` function is called to render the lock screen
* content, including messages for authentication success or failure.
* - The buffer is attached to the surface and committed to be displayed
* on the screen, ensuring the lock screen reflects the current state
* of user input.
*
* 6. **User Interaction and Feedback**:
* - The lock screen provides visual feedback based on user interactions,
* updating in real-time as the user types or if authentication attempts
* are made.
* - The program manages input states to handle situations where the
* backspace key is held down, allowing for continuous deletion until
* released.
*
* @CONCLUSION:
*
* This program serves as a basic example of how to create a Wayland client
* for a screen lock feature. It handles user input, manages authentication
* through PAM, and provides visual feedback using shared memory. This example
* demonstrates the structure and interaction of a screen lock application
* within the Wayland ecosystem, showcasing the use of various protocols and
* listeners for input handling and rendering.
**********************************************/
static int initialize_wayland(struct client_state* state)
{
state->wl_display = wl_display_connect(NULL);
if (!state->wl_display)
{
log_message(LOG_LEVEL_ERROR, "Failed to connect to Wayland display\n");
return -1;
}
state->wl_registry = wl_display_get_registry(state->wl_display);
wl_registry_add_listener(state->wl_registry, &wl_registry_listener, state);
wl_display_roundtrip(state->wl_display); // Get Wayland objects
state->wl_surface = wl_compositor_create_surface(state->wl_compositor);
state->xdg_surface = xdg_wm_base_get_xdg_surface(state->xdg_wm_base, state->wl_surface);
xdg_surface_add_listener(state->xdg_surface, &xdg_surface_listener, state);
state->xdg_toplevel = xdg_surface_get_toplevel(state->xdg_surface);
xdg_toplevel_set_title(state->xdg_toplevel, "Anvilock");
return 0;
}
static int initialize_xkb(struct client_state* state)
{
state->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!state->xkb_context)
{
log_message(LOG_LEVEL_ERROR, "Failed to initialize XKB context\n");
return -1;
}
state->xkb_keymap =
xkb_keymap_new_from_names(state->xkb_context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!state->xkb_keymap)
{
log_message(LOG_LEVEL_ERROR, "Failed to create XKB keymap\n");
return -1;
}
state->xkb_state = xkb_state_new(state->xkb_keymap);
if (!state->xkb_state)
{
log_message(LOG_LEVEL_ERROR, "Failed to create XKB state\n");
return -1;
}
return 0;
}
static int initialize_freetype(struct client_state* state)
{
int ft = init_freetype();
if (ft != 0)
{
return 0;
}
log_message(LOG_LEVEL_ERROR, "Initializing FreeType2 was unsuccessful.");
return -1;
}
static int initialize_configs(struct client_state* state)
{
const char* background_path =
get_bg_path(); // Get the background path from the loaded TOML config
if (!background_path || background_path == NULL)
{
log_message(LOG_LEVEL_ERROR, "Background path not found in config");
return -1;
}
// Check if the file exists and can be accessed
FILE* file = fopen(background_path, "r");
if (!file)
{
log_message(LOG_LEVEL_ERROR, "Background file does not exist or cannot be accessed: %s",
background_path);
return -1;
}
// Close the file after checking
fclose(file);
log_message(LOG_LEVEL_TRACE, "Found bg path through config.toml ==> %s", background_path);
state->user_configs.background_path = strdup(background_path);
const char* debug_option_str = get_debug_log_option();
if (!debug_option_str)
{
log_message(LOG_LEVEL_ERROR, "Debug option not set");
return -1;
}
state->user_configs.debug_log_option = strdup(debug_option_str);
return 0;
}
// Check if the shader file exists
static void shader_exist(const char* filepath)
{
FILE* file = fopen(filepath, "r");
if (!file)
{
log_message(LOG_LEVEL_ERROR, "[SHADERS] Failed to open shader file: %s", filepath);
exit(EXIT_FAILURE);
}
}
// Initialize shaders by checking all of them
static void initialize_shaders()
{
// Iterate over all shader paths and check if they exist
#define X(name, path) \
log_message(LOG_LEVEL_DEBUG, "[SHADERS] Loading shader %s.", path); \
shader_exist(path);
SHADER_PATHS // Expands to all shaders
#undef X
log_message(LOG_LEVEL_TRACE, "Found all shader files.");
}
static void cleanup(struct client_state* state)
{
unlock_and_destroy_session_lock(state);
eglDestroySurface(state->egl_display, state->egl_surface);
eglDestroyContext(state->egl_display, state->egl_context);
eglTerminate(state->egl_display);
wl_display_roundtrip(state->wl_display);
wl_display_disconnect(state->wl_display);
FT_Done_Face(ft_face);
FT_Done_FreeType(ft_library);
log_message(LOG_LEVEL_TRACE, "Anvilock resources cleanup completed. Exiting...");
}
int main(int argc, char* argv[])
{
struct client_state state = {0};
// Initialize logging
state.pam.username = getlogin();
log_message(LOG_LEVEL_TRACE, "Session found for user @ [%s]", state.pam.username);
// Initialize Wayland
if (initialize_wayland(&state) != 0)
{
cleanup(&state);
return 1;
}
// Initialize XKB for keyboard input
if (initialize_xkb(&state) != 0)
{
cleanup(&state);
return 1;
}
// Initialize FreeType for font rendering
if (initialize_freetype(&state) != 0)
{
cleanup(&state);
return 1;
}
if (initialize_configs(&state) != 0)
{
cleanup(&state);
return 1;
}
init_debug(&state);
initialize_shaders();
// Commit the surface to make it visible
wl_surface_commit(state.wl_surface);
// Event loop to handle input and manage session state
state.pam.auth_state.auth_success = false;
while (!state.pam.auth_state.auth_success && wl_display_dispatch(state.wl_display) != -1)
{
state.pam.auth_state.auth_failed = false;
if (!state.session_lock.surface_created)
{
initiate_session_lock(&state);
}
render_lock_screen(&state);
}
// Cleanup after exiting the event loop
cleanup(&state);
log_message(LOG_LEVEL_SUCCESS, "Unlocking...");
return 0;
}