AtlatestRepositorysigil-wm
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"15
static pid_t child;16
static void cleanup(void) { if (child > 0) { kill(child, SIGKILL); waitpid(child, NULL, 0); } }17
static void require(bool ok, const char *message) {18
if (ok) return;19
fprintf(stderr, "surface frames: %s\n", message);20
exit(2);21
}22
static 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");25
}26
static double seconds(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return now.tv_sec + now.tv_nsec / 1e9; }27
int 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;137
}