AtlatestRepositorysigil-wm

sigil-wm / tree / testlock-client.c

1/* SPDX-License-Identifier: BSD-3-Clause */
2/* Protocol fixture for isolated test compositors. It does not authenticate.
3 * stdin: u unlocks, d sends destroy, q disconnects. Modes: draw, blank,
4 * early-unlock, destroy-locked; globals inventories session input globals.
5 * Never use this fixture to lock a session. */
6#include <errno.h>
7#include <poll.h>
8#include <stdbool.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/mman.h>
14#include <time.h>
15#include <unistd.h>
16#include <wayland-client.h>
17#include "ext-session-lock-v1-client-protocol.h"
19struct client;
20struct output {
21 struct client *client;
22 uint32_t name;
23 struct wl_output *output;
24 struct wl_surface *surface;
25 struct ext_session_lock_surface_v1 *lock_surface;
26 struct wl_buffer *buffer;
27};
29struct client {
30 struct wl_display *display;
31 struct wl_compositor *compositor;
32 struct wl_shm *shm;
33 struct wl_seat *seat;
34 struct wl_keyboard *keyboard;
35 struct ext_session_lock_manager_v1 *manager;
36 struct ext_session_lock_v1 *lock;
37 struct output outputs[16];
38 struct timespec start;
39 const char *mode;
40 bool finished, unlocked, running;
41 bool virtual_keyboard, virtual_pointer;
42};
44static long elapsed_ms(struct client *client) {
45 struct timespec now;
46 clock_gettime(CLOCK_MONOTONIC, &now);
47 return (now.tv_sec - client->start.tv_sec) * 1000 +
48 (now.tv_nsec - client->start.tv_nsec) / 1000000;
51static void configure(void *data, struct ext_session_lock_surface_v1 *surface,
52 uint32_t serial, uint32_t width, uint32_t height) {
53 struct output *output = data;
54 struct client *client = output->client;
55 if (!width || !height || width > 4096 || height > 4096) exit(4);
56 printf("LOCK_CONFIGURE=%u,%u,%u\n", output->name, width, height);
57 ext_session_lock_surface_v1_ack_configure(surface, serial);
58 char path[256];
59 snprintf(path, sizeof(path), "%s/lock-pixels.XXXXXX", getenv("XDG_RUNTIME_DIR"));
60 int fd = mkstemp(path);
61 if (fd < 0) exit(5);
62 unlink(path);
63 size_t size = (size_t)width * height * 4;
64 if (ftruncate(fd, size) < 0) exit(6);
65 uint32_t *pixels = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
66 if (pixels == MAP_FAILED) exit(7);
67 for (size_t i = 0; i < size / 4; i++) pixels[i] = 0xff1155bb;
68 struct wl_shm_pool *pool = wl_shm_create_pool(client->shm, fd, size);
69 struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
70 width * 4, WL_SHM_FORMAT_XRGB8888);
71 wl_shm_pool_destroy(pool);
72 if (output->buffer) wl_buffer_destroy(output->buffer);
73 output->buffer = buffer;
74 munmap(pixels, size);
75 close(fd);
76 wl_surface_attach(output->surface, buffer, 0, 0);
77 wl_surface_damage(output->surface, 0, 0, width, height);
78 wl_surface_commit(output->surface);
79 printf("LOCK_DRAW=%u\n", output->name);
82static const struct ext_session_lock_surface_v1_listener surface_listener = {
83 .configure = configure,
84};
86static void add_surface(struct output *output) {
87 struct client *client = output->client;
88 if (!client->lock || client->finished || output->surface ||
89 strcmp(client->mode, "draw")) return;
90 output->surface = wl_compositor_create_surface(client->compositor);
91 output->lock_surface = ext_session_lock_v1_get_lock_surface(client->lock,
92 output->surface, output->output);
93 ext_session_lock_surface_v1_add_listener(output->lock_surface, &surface_listener, output);
96/* Deliberately invalid protocol requests must keep their local proxy until
97 * the error arrives, so libwayland can attribute it to this interface/id.
98 * The generated destructor would discard that metadata before dispatch. */
99static void invalid_request(struct ext_session_lock_v1 *lock, uint32_t opcode) {
100 struct wl_proxy *proxy = (struct wl_proxy *)lock;
101 printf("LOCK_INVALID_REQUEST opcode=%u object=%u\n", opcode, wl_proxy_get_id(proxy));
102 wl_proxy_marshal_flags(proxy, opcode, NULL, wl_proxy_get_version(proxy), 0);
105static void locked(void *data, struct ext_session_lock_v1 *lock) {
106 struct client *client = data;
107 printf("LOCKED elapsed-ms=%ld\n", elapsed_ms(client));
108 if (!strcmp(client->mode, "destroy-locked")) invalid_request(lock, EXT_SESSION_LOCK_V1_DESTROY);
111static void finished(void *data, struct ext_session_lock_v1 *lock) {
112 struct client *client = data;
113 client->finished = true;
114 client->running = false;
115 puts("LOCK_FINISHED");
116 ext_session_lock_v1_destroy(lock);
119static const struct ext_session_lock_v1_listener lock_listener = {
120 .locked = locked, .finished = finished,
121};
123static void keymap(void *data, struct wl_keyboard *keyboard, uint32_t format,
124 int32_t fd, uint32_t size) {
125 close(fd);
126 (void)data; (void)keyboard; (void)format; (void)size;
129static void enter(void *data, struct wl_keyboard *keyboard, uint32_t serial,
130 struct wl_surface *surface, struct wl_array *keys) {
131 printf("LOCK_ENTER keys=%zu\n", keys->size / sizeof(uint32_t));
132 (void)data; (void)keyboard; (void)serial; (void)surface;
135static void leave(void *data, struct wl_keyboard *keyboard, uint32_t serial,
136 struct wl_surface *surface) {
137 puts("LOCK_LEAVE");
138 (void)data; (void)keyboard; (void)serial; (void)surface;
141static void key(void *data, struct wl_keyboard *keyboard, uint32_t serial,
142 uint32_t time, uint32_t keycode, uint32_t state) {
143 printf("LOCK_KEY=%u,%u\n", keycode, state);
144 (void)data; (void)keyboard; (void)serial; (void)time;
147static void modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial,
148 uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
149 printf("LOCK_MODIFIERS=%u,%u,%u,%u\n", depressed, latched, locked, group);
150 (void)data; (void)keyboard; (void)serial;
153static const struct wl_keyboard_listener keyboard_listener = {
154 .keymap = keymap, .enter = enter, .leave = leave, .key = key, .modifiers = modifiers,
155};
157static void capabilities(void *data, struct wl_seat *seat, uint32_t caps) {
158 struct client *client = data;
159 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !client->keyboard) {
160 client->keyboard = wl_seat_get_keyboard(seat);
161 wl_keyboard_add_listener(client->keyboard, &keyboard_listener, client);
162 }
165static const struct wl_seat_listener seat_listener = {.capabilities = capabilities};
167static void geometry(void *data, struct wl_output *output, int32_t x, int32_t y,
168 int32_t physical_width, int32_t physical_height, int32_t subpixel,
169 const char *make, const char *model, int32_t transform) {
170 (void)data; (void)output; (void)x; (void)y; (void)physical_width;
171 (void)physical_height; (void)subpixel; (void)make; (void)model; (void)transform;
174static void mode(void *data, struct wl_output *output, uint32_t flags,
175 int32_t width, int32_t height, int32_t refresh) {
176 (void)data; (void)output; (void)flags; (void)width; (void)height; (void)refresh;
179static const struct wl_output_listener output_listener = {.geometry = geometry, .mode = mode};
181static void global(void *data, struct wl_registry *registry, uint32_t name,
182 const char *interface, uint32_t version) {
183 struct client *client = data;
184 if (!strcmp(interface, "zwp_virtual_keyboard_manager_v1")) client->virtual_keyboard = true;
185 if (!strcmp(interface, "zwlr_virtual_pointer_manager_v1")) client->virtual_pointer = true;
186 if (!strcmp(interface, "wl_compositor")) {
187 client->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
188 } else if (!strcmp(interface, "wl_shm")) {
189 client->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
190 } else if (!strcmp(interface, "wl_seat")) {
191 client->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
192 wl_seat_add_listener(client->seat, &seat_listener, client);
193 } else if (!strcmp(interface, "ext_session_lock_manager_v1")) {
194 client->manager = wl_registry_bind(registry, name, &ext_session_lock_manager_v1_interface, 1);
195 } else if (!strcmp(interface, "wl_output")) {
196 for (size_t i = 0; i < 16; i++) {
197 struct output *output = &client->outputs[i];
198 if (output->output) continue;
199 output->client = client;
200 output->name = name;
201 output->output = wl_registry_bind(registry, name, &wl_output_interface, 1);
202 wl_output_add_listener(output->output, &output_listener, output);
203 add_surface(output);
204 printf("LOCK_OUTPUT=%u\n", name);
205 break;
206 }
207 }
208 (void)version;
211static void global_remove(void *data, struct wl_registry *registry, uint32_t name) {
212 struct client *client = data;
213 for (size_t i = 0; i < 16; i++) {
214 struct output *output = &client->outputs[i];
215 if (!output->output || output->name != name) continue;
216 if (output->lock_surface) ext_session_lock_surface_v1_destroy(output->lock_surface);
217 if (output->surface) wl_surface_destroy(output->surface);
218 if (output->buffer) wl_buffer_destroy(output->buffer);
219 wl_output_destroy(output->output);
220 memset(output, 0, sizeof(*output));
221 printf("LOCK_OUTPUT_REMOVED=%u\n", name);
222 }
223 (void)registry;
226static const struct wl_registry_listener registry_listener = {
227 .global = global, .global_remove = global_remove,
228};
230int main(int argc, char **argv) {
231 if (argc != 2 || (strcmp(argv[1], "draw") && strcmp(argv[1], "blank") &&
232 strcmp(argv[1], "early-unlock") && strcmp(argv[1], "destroy-locked") &&
233 strcmp(argv[1], "globals"))) return 2;
234 setvbuf(stdout, NULL, _IOLBF, 0);
235 struct client client = {.mode = argv[1], .running = true};
236 client.display = wl_display_connect(NULL);
237 if (!client.display) return 3;
238 struct wl_registry *registry = wl_display_get_registry(client.display);
239 wl_registry_add_listener(registry, &registry_listener, &client);
240 if (wl_display_roundtrip(client.display) < 0 || wl_display_roundtrip(client.display) < 0 ||
241 !client.manager || !client.compositor || !client.shm) return 3;
242 if (!strcmp(client.mode, "globals")) {
243 printf("LOCK_GLOBALS keyboard=%d pointer=%d\n", client.virtual_keyboard, client.virtual_pointer);
244 wl_display_disconnect(client.display);
245 return client.virtual_keyboard || client.virtual_pointer ? 13 : 0;
246 }
247 clock_gettime(CLOCK_MONOTONIC, &client.start);
248 client.lock = ext_session_lock_manager_v1_lock(client.manager);
249 ext_session_lock_v1_add_listener(client.lock, &lock_listener, &client);
250 if (!strcmp(client.mode, "early-unlock")) {
251 invalid_request(client.lock, EXT_SESSION_LOCK_V1_UNLOCK_AND_DESTROY);
252 } else {
253 for (size_t i = 0; i < 16; i++) {
254 if (client.outputs[i].output) add_surface(&client.outputs[i]);
255 }
256 }
257 puts("LOCK_REQUESTED");
258 while (client.running) {
259 if (wl_display_dispatch_pending(client.display) < 0) break;
260 if (!client.running) break;
261 if (wl_display_flush(client.display) < 0 && errno != EAGAIN) break;
262 struct pollfd fds[2] = {
263 {.fd = wl_display_get_fd(client.display), .events = POLLIN},
264 {.fd = STDIN_FILENO, .events = POLLIN},
265 };
266 if (poll(fds, 2, -1) < 0) {
267 if (errno == EINTR) continue;
268 return 10;
269 }
270 if (fds[0].revents && wl_display_dispatch(client.display) < 0) break;
271 if (fds[1].revents & POLLIN) {
272 char commands[32];
273 ssize_t count = read(STDIN_FILENO, commands, sizeof(commands));
274 for (ssize_t i = 0; i < count; i++) {
275 if (commands[i] == 'q') client.running = false;
276 if (commands[i] == 'd') invalid_request(client.lock, EXT_SESSION_LOCK_V1_DESTROY);
277 if (commands[i] == 'u') {
278 ext_session_lock_v1_unlock_and_destroy(client.lock);
279 client.unlocked = true;
280 client.finished = true;
281 client.running = false;
282 }
283 }
284 }
285 }
286 /* A flush followed immediately by disconnect can give the server a
287 * hangup before it dispatches the queued unlock. Wait for a sync reply
288 * after the request so the fixture proves it was processed. */
289 if (client.unlocked && wl_display_get_error(client.display) == 0 &&
290 wl_display_roundtrip(client.display) >= 0) {
291 puts("LOCK_UNLOCK_CONFIRMED");
292 }
293 int error = wl_display_get_error(client.display);
294 if (error) {
295 const struct wl_interface *interface = NULL;
296 uint32_t object = 0;
297 uint32_t code = wl_display_get_protocol_error(client.display, &interface, &object);
298 printf("LOCK_PROTOCOL_ERROR=%s,%u object=%u\n", interface ? interface->name : "io", code, object);
299 } else if (client.unlocked) {
300 puts("LOCK_UNLOCK_SENT");
301 }
302 wl_display_disconnect(client.display);
303 return error ? 12 : 0;