AtlatestRepositorysigil-wm

sigil-wm / tree / testsurface-frames.c

1/* SPDX-License-Identifier: BSD-3-Clause */
2#include <errno.h>
3#include <poll.h>
4#include <signal.h>
5#include <stdbool.h>
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/wait.h>
11#include <time.h>
12#include <unistd.h>
13#include "api.h"
15static pid_t child;
16static void cleanup(void) { if (child > 0) { kill(child, SIGKILL); waitpid(child, NULL, 0); } }
17static void require(bool ok, const char *message) {
18 if (ok) return;
19 fprintf(stderr, "surface frames: %s\n", message);
20 exit(2);
22static void command(int kind, int64_t id, int64_t a, int64_t b) {
23 struct wm_message m = {.word = {kind, id, a, b}};
24 require(wm_send(&m) == 1, "command refused");
26static double seconds(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return now.tv_sec + now.tv_nsec / 1e9; }
27int main(int argc, char **argv) {
28 require(argc == 3, "usage: surface-frames SAMPLE-PATH CLIP");
29 bool foot = getenv("SIGIL_WM_FRAMES_FOOT") != NULL;
30 bool modern = getenv("SIGIL_WM_FOOT_MODERN") != NULL;
31 setvbuf(stdout, NULL, _IOLBF, 0);
32 atexit(cleanup);
33 require(wm_options(foot ? 8 : 0, 2000) == 1 && wm_decoration_default(2) == 1 && wm_start() == 1, "start");
34 bool running = true, injected = false;
35 int result = 2;
36 double deadline = seconds() + 12;
37 while (running && seconds() < deadline) {
38 struct pollfd fd = {.fd = wm_event_fd(), .events = POLLIN};
39 require(poll(&fd, 1, 50) >= 0 || errno == EINTR, "event poll");
40 struct wm_message event;
41 while (wm_next(&event)) {
42 int64_t *w = event.word;
43 switch (w[0]) {
44 case 1: {
45 int64_t keyboard[] = {1, 25, 600, 2, 'u', 's'};
46 require(wm_submit(3, 3, keyboard, 6) == 1, "keyboard configuration");
47 command(14, 3, 0, 0);
48 break;
49 }
50 case 2: {
51 int64_t output[] = {w[1], 1, 0, 0, 1000, 0, -1, 320, 240, w[5] ? 0 : 60000};
52 require(wm_submit(2, 1, output, 10) == 1, "output configuration");
53 break;
54 }
55 case 4: {
56 int64_t output[] = {w[1], 1, 0, 0, 1000, 0, -1, w[2], w[3], 0};
57 require(wm_submit(2, 1, output, 10) == 1, "nested output configuration");
58 break;
59 }
60 case 6:
61 if (!child) {
62 size_t length = strlen(argv[1]);
63 require(length < 1024, "sample path");
64 int64_t samples[1028] = {60, 1, 30, 30};
65 if (foot) { samples[0] = 120; samples[2] = 15; samples[3] = 18; }
66 for (size_t i = 0; i < length; i++) samples[i + 4] = (unsigned char)argv[1][i];
67 require(wm_submit(15, w[1], samples, length + 4) == 1, "sample recording");
68 require(setenv("WAYLAND_DISPLAY", wm_socket(), 1) == 0, "socket name");
69 child = fork();
70 require(child >= 0, "fork");
71 if (!child) {
72 if (foot) execlp("foot", "foot", "--config=/dev/null",
73 "--override=cursor.blink=yes", "--override=cursor.blink-rate=120",
74 "--override=pad=0x0", "--override=font=JetBrains Mono:size=12",
75 modern ? "--override=colors-dark.background=222222" : "--override=colors.background=222222",
76 modern ? "--override=colors-dark.foreground=eeeeee" : "--override=colors.foreground=eeeeee",
77 modern ? "--override=colors-dark.cursor=222222 eeeeee" : "--override=cursor.color=222222 eeeeee",
78 "--", "sh", "-c",
79 "if [ -n \"$SIGIL_WM_FRAMES_TMUX\" ]; then exec tmux -S \"$XDG_RUNTIME_DIR/blink-tmux\" -f /dev/null new-session \"printf '\\033[2J\\033[H\\033[?25h'; sleep 3\"; else printf '\033[2J\033[H\033[?25h'; sleep 3; fi", NULL);
80 else execl("build/damage-client", "damage-client", NULL);
81 _exit(127);
82 }
83 }
84 break;
85 case 14: command(2, w[1], 150, 150); break;
86 case 75: command(18, w[1], 2, 0); break;
87 case 22:
88 if (foot && !injected && getenv("SIGIL_WM_FRAME_FAILURE")) {
89 FILE *barrier = fopen(getenv("SIGIL_WM_FRAME_FAILURE"), "w");
90 require(barrier != NULL, "foot failure barrier");
91 fclose(barrier);
92 injected = true;
93 }
94 break;
95 case 11: {
96 int64_t plan[] = {1, 1, w[1], 200, w[1], 10, 10, 150, 150, 1, 1, 0, 15, atoi(argv[2])};
97 require(wm_submit(1, 2, plan, 14) == 1, "placement");
98 break;
99 }
100 case 13: {
101 int status;
102 require(child > 0 && waitpid(child, &status, 0) == child, "wait client");
103 child = 0;
104 result = WIFEXITED(status) ? WEXITSTATUS(status) : 2;
105 command(1, 0, 0, 0);
106 break;
107 }
108 case 7: require(w[2] == 0, "native failure"); running = false; break;
109 case 23: require(false, "plan rejected"); break;
110 case 24: require(w[3] == 1, "submission rejected"); break;
111 case 98: require(false, "command rejected"); break;
112 }
113 }
114 }
115 require(!running && wm_join() == 1, "timeout or join");
116 FILE *file = fopen(argv[1], "r");
117 require(file != NULL, "missing sample log");
118 char line[256];
119 unsigned phases = 0, previous = 0, colour, sequence, index, width, height;
120 while (fgets(line, sizeof(line), file)) {
121 char status[8];
122 require(sscanf(line, "FRAME %u %u %u %u %x %7s", &index, &sequence, &width, &height, &colour, status) == 6 &&
123 !strcmp(status, "PASS"), "bad sample");
124 if (colour != 0x222222 && colour != 0xeeeeee) continue;
125 if (colour == previous) continue;
126 if (!foot) require(colour == (phases % 2 ? 0xeeeeee : 0x222222), "phase order");
127 previous = colour;
128 phases++;
129 printf("DAMAGE_PRESENT_PHASE=%u output-sequence=%u colour=%06x\n", phases, sequence, colour);
130 }
131 fclose(file);
132 bool passed = !result && (foot ? phases >= 4 : phases == 12);
133 printf("%s=%s phases=%u expected=%s\n", foot ? "FOOT_BLINK" : "DAMAGE_PRESENTATION",
134 passed ? "PASS" : "FAIL", phases, foot ? "at-least-4" : "12");
135 require(wm_stat(1) == 0 && wm_stat(2) == 0 && wm_stat(3) == 0 && wm_stat(7) == 0, "retained resources");
136 return result ? result : passed ? 0 : 1;