AtlatestRepositorysigil-wm
1
/* SPDX-License-Identifier: BSD-3-Clause */2
#define _POSIX_C_SOURCE 200809L3
#include <errno.h>4
#include <poll.h>5
#include <stdbool.h>6
#include <stdint.h>7
#include <stdio.h>8
#include <stdlib.h>9
#include <string.h>10
#include <sys/mman.h>11
#include <time.h>12
#include <unistd.h>13
#include <wayland-client.h>14
#include "xdg-shell-client-protocol.h"16
static struct wl_display *display;17
static struct wl_compositor *compositor;18
static struct wl_shm *shm;19
static struct xdg_wm_base *shell;20
static struct wl_surface *surface;21
static int width = 150, height = 150;22
static unsigned commits, callbacks;23
static bool configured, failed;24
static double milliseconds(void) {25
struct timespec now;26
clock_gettime(CLOCK_MONOTONIC, &now);27
return now.tv_sec * 1000.0 + now.tv_nsec / 1e6;28
}29
static void require(bool ok, const char *message) {30
if (ok) return;31
fprintf(stderr, "damage client: %s\n", message);32
exit(2);33
}34
static void done(void *data, struct wl_callback *callback, uint32_t time) {35
(void)time;36
unsigned sequence = (uintptr_t)data;37
wl_callback_destroy(callback);38
callbacks++;39
if (sequence != callbacks) failed = true;40
printf("DAMAGE_FRAME_DONE=%u\n", sequence);41
fflush(stdout);42
}43
static const struct wl_callback_listener frame_listener = {.done = done};44
static void draw(bool initial) {45
char path[] = "/tmp/sigil-damage-client.XXXXXX";46
int fd = mkstemp(path);47
unlink(path);48
require(fd >= 0, "shm file");49
size_t size = (size_t)width * height * 4;50
require(ftruncate(fd, size) == 0, "shm size");51
uint32_t *pixels = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);52
require(pixels != MAP_FAILED, "shm map");53
uint32_t colour = commits % 2 ? 0xffeeeeee : 0xff222222;54
for (int y = 0; y < height; y++)55
for (int x = 0; x < width; x++)56
pixels[y * width + x] = x >= 20 && x < 28 && y >= 20 && y < 36 ? colour : 0xff222222;57
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);58
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, width * 4, WL_SHM_FORMAT_XRGB8888);59
wl_shm_pool_destroy(pool);60
munmap(pixels, size); close(fd);61
if (!initial && commits == 3) {62
const char *barrier = getenv("SIGIL_WM_FRAME_FAILURE");63
if (barrier) { FILE *file = fopen(barrier, "w"); require(file != NULL, "failure barrier"); fclose(file); }64
}65
wl_surface_attach(surface, buffer, 0, 0);66
if (initial) wl_surface_damage_buffer(surface, 0, 0, width, height);67
else wl_surface_damage_buffer(surface, 20, 20, 8, 16);68
commits++;69
wl_callback_add_listener(wl_surface_frame(surface), &frame_listener, (void *)(uintptr_t)commits);70
wl_surface_commit(surface);71
wl_buffer_destroy(buffer);72
printf("DAMAGE_COMMIT=%u colour=%06x damage=%s\n", commits, colour & 0xffffff, initial ? "whole" : "20,20,8,16");73
fflush(stdout);74
}75
static void configure(void *data, struct xdg_surface *xdg, uint32_t serial) {76
(void)data;77
xdg_surface_ack_configure(xdg, serial);78
if (!configured) { configured = true; draw(true); }79
else wl_surface_commit(surface);80
}81
static const struct xdg_surface_listener surface_listener = {.configure = configure};82
static void top_configure(void *data, struct xdg_toplevel *top, int32_t w, int32_t h, struct wl_array *states) {83
(void)data; (void)top; (void)states;84
if (w) width = w;85
if (h) height = h;86
}87
static void close_top(void *data, struct xdg_toplevel *top) { (void)data; (void)top; failed = true; }88
static const struct xdg_toplevel_listener top_listener = {.configure = top_configure, .close = close_top};89
static void ping(void *data, struct xdg_wm_base *base, uint32_t serial) { (void)data; xdg_wm_base_pong(base, serial); }90
static const struct xdg_wm_base_listener shell_listener = {.ping = ping};91
static void global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {92
(void)data; (void)version;93
if (!strcmp(interface, "wl_compositor")) compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);94
else if (!strcmp(interface, "wl_shm")) shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);95
else if (!strcmp(interface, "xdg_wm_base")) {96
shell = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);97
xdg_wm_base_add_listener(shell, &shell_listener, NULL);98
}99
}100
static void removed(void *data, struct wl_registry *registry, uint32_t name) { (void)data; (void)registry; (void)name; }101
static const struct wl_registry_listener registry_listener = {.global = global, .global_remove = removed};102
int main(void) {103
setvbuf(stdout, NULL, _IOLBF, 0);104
display = wl_display_connect(NULL);105
require(display != NULL, "connect");106
struct wl_registry *registry = wl_display_get_registry(display);107
wl_registry_add_listener(registry, ®istry_listener, NULL);108
require(wl_display_roundtrip(display) >= 0 && compositor && shm && shell, "registry");109
surface = wl_compositor_create_surface(compositor);110
struct xdg_surface *xdg = xdg_wm_base_get_xdg_surface(shell, surface);111
xdg_surface_add_listener(xdg, &surface_listener, NULL);112
struct xdg_toplevel *top = xdg_surface_get_toplevel(xdg);113
xdg_toplevel_add_listener(top, &top_listener, NULL);114
wl_surface_commit(surface);115
double end = milliseconds() + 5000, next = milliseconds() + 200;116
while (!failed && milliseconds() < end && callbacks < 12) {117
require(wl_display_dispatch_pending(display) >= 0, "dispatch pending");118
require(wl_display_flush(display) >= 0 || errno == EAGAIN, "flush");119
struct pollfd fd = {.fd = wl_display_get_fd(display), .events = POLLIN};120
int result = poll(&fd, 1, 10);121
require(result >= 0 || errno == EINTR, "poll");122
if (fd.revents & POLLIN) require(wl_display_dispatch(display) >= 0, "dispatch");123
if (configured && commits < 12 && milliseconds() >= next) {124
if (callbacks != commits) { fprintf(stderr, "DAMAGE_MISSING_CALLBACK commit=%u callbacks=%u\n", commits, callbacks); failed = true; }125
else draw(false);126
next = milliseconds() + 120;127
}128
}129
bool passed = !failed && commits == 12 && callbacks == commits;130
xdg_toplevel_destroy(top); xdg_surface_destroy(xdg); wl_surface_destroy(surface);131
wl_display_flush(display); wl_display_disconnect(display);132
printf("DAMAGE_CALLBACKS=%s commits=%u callbacks=%u\n", passed ? "PASS" : "FAIL", commits, callbacks);133
return passed ? 0 : 1;134
}