AtlatestRepositorysigil-wm

sigil-wm / tree / testdamage-client.c

1/* SPDX-License-Identifier: BSD-3-Clause */
2#define _POSIX_C_SOURCE 200809L
3#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"
16static struct wl_display *display;
17static struct wl_compositor *compositor;
18static struct wl_shm *shm;
19static struct xdg_wm_base *shell;
20static struct wl_surface *surface;
21static int width = 150, height = 150;
22static unsigned commits, callbacks;
23static bool configured, failed;
24static double milliseconds(void) {
25 struct timespec now;
26 clock_gettime(CLOCK_MONOTONIC, &now);
27 return now.tv_sec * 1000.0 + now.tv_nsec / 1e6;
29static void require(bool ok, const char *message) {
30 if (ok) return;
31 fprintf(stderr, "damage client: %s\n", message);
32 exit(2);
34static 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);
43static const struct wl_callback_listener frame_listener = {.done = done};
44static 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);
75static 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);
81static const struct xdg_surface_listener surface_listener = {.configure = configure};
82static 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;
87static void close_top(void *data, struct xdg_toplevel *top) { (void)data; (void)top; failed = true; }
88static const struct xdg_toplevel_listener top_listener = {.configure = top_configure, .close = close_top};
89static void ping(void *data, struct xdg_wm_base *base, uint32_t serial) { (void)data; xdg_wm_base_pong(base, serial); }
90static const struct xdg_wm_base_listener shell_listener = {.ping = ping};
91static 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 }
100static void removed(void *data, struct wl_registry *registry, uint32_t name) { (void)data; (void)registry; (void)name; }
101static const struct wl_registry_listener registry_listener = {.global = global, .global_remove = removed};
102int 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, &registry_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;