AtlatestRepositorysigil-wm
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"19
struct client;20
struct 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
};29
struct 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
};44
static 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;49
}51
static 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);80
}82
static const struct ext_session_lock_surface_v1_listener surface_listener = {83
.configure = configure,84
};86
static 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);94
}96
/* Deliberately invalid protocol requests must keep their local proxy until97
* the error arrives, so libwayland can attribute it to this interface/id.98
* The generated destructor would discard that metadata before dispatch. */99
static 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);103
}105
static 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);109
}111
static 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);117
}119
static const struct ext_session_lock_v1_listener lock_listener = {120
.locked = locked, .finished = finished,121
};123
static 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;127
}129
static 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;133
}135
static 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;139
}141
static 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;145
}147
static 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;151
}153
static const struct wl_keyboard_listener keyboard_listener = {154
.keymap = keymap, .enter = enter, .leave = leave, .key = key, .modifiers = modifiers,155
};157
static 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
}163
}165
static const struct wl_seat_listener seat_listener = {.capabilities = capabilities};167
static 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;172
}174
static 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;177
}179
static const struct wl_output_listener output_listener = {.geometry = geometry, .mode = mode};181
static 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;209
}211
static 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;224
}226
static const struct wl_registry_listener registry_listener = {227
.global = global, .global_remove = global_remove,228
};230
int 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, ®istry_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 a287
* hangup before it dispatches the queued unlock. Wait for a sync reply288
* 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;304
}