Release 0.12.0

This commit is contained in:
matze19999 committed 2026-10-06 22:01:38 +02:00
1 parent f74a658097
commit 7f6eeab39e
48 files changed
+3617 -1266

No files matched your search

+173 -66
View File
@@ -2,77 +2,184 @@
#define HYPERDRM_DIAGNOSTICS_H
/* An independent nonblocking connection. Tests never send colors or acquire
* capture. Work per timer iteration is bounded to keep output responsive. */
typedef enum {TEST_IDLE,TEST_CONNECT,TEST_SEND,TEST_LOGIN,TEST_LAYOUT,TEST_DONE,TEST_FAILED} TestStage;
typedef enum {
TEST_IDLE,
TEST_CONNECT,
TEST_SEND,
TEST_LOGIN,
TEST_LAYOUT,
TEST_DONE,
TEST_FAILED
} TestStage;
typedef struct {
int fd;TestStage stage,after_send;TargetConfig target;
uint64_t started,deadline,elapsed;unsigned leds;
char request[384],error[128];size_t sent,used;char *response;
LedBlend *mapping;
int fd;
TestStage stage, after_send;
TargetConfig target;
uint64_t started, deadline, elapsed;
unsigned leds;
char request[384], error[128];
size_t sent, used;
char *response;
LedBlend *mapping;
} ReceiverProbe;
static ReceiverProbe connection_test={.fd=-1};
static const char *probe_stage_name(const ReceiverProbe *probe){
static const char *names[]={"Not tested","Connecting","Sending request","Checking authentication","Reading LED layout","Passed","Failed"};
return names[probe->stage];
static ReceiverProbe connection_test = {.fd = -1};
static const char *probe_stage_name(const ReceiverProbe *probe) {
static const char *names[] = {
"Not tested", "Connecting", "Sending request", "Checking authentication",
"Reading LED layout", "Passed", "Failed"};
return names[probe->stage];
}
static bool probe_busy(const ReceiverProbe *probe){return probe->stage>=TEST_CONNECT && probe->stage<=TEST_LAYOUT;}
static void probe_close(ReceiverProbe *probe){if(probe->fd>=0)close(probe->fd);probe->fd=-1;free(probe->mapping);probe->mapping=NULL;free(probe->response);probe->response=NULL;memset(probe->request,0,sizeof(probe->request));memset(probe->target.token,0,sizeof(probe->target.token));}
static void probe_finish(ReceiverProbe *probe,const char *error,uint64_t now){
probe_close(probe);probe->elapsed=now-probe->started;
probe->stage=error?TEST_FAILED:TEST_DONE;
snprintf(probe->error,sizeof(probe->error),"%s",error?error:"");
static bool probe_busy(const ReceiverProbe *probe) {
return probe->stage >= TEST_CONNECT && probe->stage <= TEST_LAYOUT;
}
static void probe_request(ReceiverProbe *probe,bool login,uint64_t now){
if(login)snprintf(probe->request,sizeof(probe->request),"{\"command\":\"authorize\",\"subcommand\":\"login\",\"token\":\"%s\"}\n",probe->target.token);
else snprintf(probe->request,sizeof(probe->request),"{\"command\":\"serverinfo\"}\n");
probe->sent=probe->used=0;probe->after_send=login?TEST_LOGIN:TEST_LAYOUT;
probe->stage=TEST_SEND;probe->deadline=now+3000;
static void probe_close(ReceiverProbe *probe) {
if (probe->fd >= 0)
close(probe->fd);
probe->fd = -1;
free(probe->mapping);
probe->mapping = NULL;
free(probe->response);
probe->response = NULL;
memset(probe->request, 0, sizeof(probe->request));
memset(probe->target.token, 0, sizeof(probe->target.token));
}
static bool probe_begin(ReceiverProbe *probe,const TargetConfig *target,uint64_t now){
if(probe_busy(probe))return false;
probe_close(probe);memset(probe,0,sizeof(*probe));probe->fd=-1;
probe->target=*target;probe->started=now;
probe->response=malloc(2*1024*1024+1);
if(!probe->response){probe_finish(probe,"Cannot allocate connection test buffer",now);return true;}
probe->fd=target_open(target);
if(probe->fd<0){probe_finish(probe,"Cannot open receiver connection",now);return true;}
probe->stage=TEST_CONNECT;probe->deadline=now+3000;return true;
static void probe_finish(ReceiverProbe *probe, const char *error, uint64_t now) {
probe_close(probe);
probe->elapsed = now - probe->started;
probe->stage = error ? TEST_FAILED : TEST_DONE;
snprintf(probe->error, sizeof(probe->error), "%s", error ? error : "");
}
static void probe_tick(ReceiverProbe *probe,PlaybackJson *json,uint64_t now){
if(!probe_busy(probe))return;
if(now>=probe->deadline){probe_finish(probe,"Receiver timed out",now);return;}
int fd=probe->fd;
if(probe->stage==TEST_CONNECT){
struct pollfd p={fd,POLLOUT,0};int error=0;socklen_t size=sizeof(error);
if(poll(&p,1,0)<=0)return;
if(getsockopt(fd,SOL_SOCKET,SO_ERROR,&error,&size)!=0 || error){probe_finish(probe,"Receiver connection refused or unreachable",now);return;}
probe_request(probe,probe->target.token[0]!=0,now);return;
}
if(probe->stage==TEST_SEND){
size_t length=strlen(probe->request);
ssize_t n=send(fd,probe->request+probe->sent,length-probe->sent,MSG_NOSIGNAL);
if(n<0 && (errno==EAGAIN || errno==EWOULDBLOCK || errno==EINTR))return;
if(n<=0){probe_finish(probe,"Receiver closed the test connection",now);return;}
probe->sent+=(size_t)n;
if(probe->sent==length){probe->stage=probe->after_send;probe->deadline=now+3000;memset(probe->request,0,sizeof(probe->request));}
return;
}
size_t available=2*1024*1024-probe->used;if(available>65536)available=65536;
if(!available){probe_finish(probe,"Receiver response exceeds 2 MiB",now);return;}
ssize_t n=recv(fd,probe->response+probe->used,available,0);
if(n<0 && (errno==EAGAIN || errno==EWOULDBLOCK || errno==EINTR))return;
if(n<=0){probe_finish(probe,"Receiver closed the test connection",now);return;}
probe->used+=(size_t)n;probe->response[probe->used]=0;
char *end=strchr(probe->response,'\n');if(!end)return;*end=0;
void *root=json->parse(probe->response);bool success=playback_true(json,root,"success");if(root)json->put(root);
if(!success){probe_finish(probe,probe->stage==TEST_LOGIN?"Receiver authentication failed":"Receiver rejected serverinfo; check authorization",now);return;}
if(probe->stage==TEST_LOGIN){probe_request(probe,false,now);return;}
LedBlend *mapping=NULL;unsigned count=0;
if(!layout_from_json(probe->response,&mapping,&count)){probe_finish(probe,"Receiver returned an invalid or empty LED layout",now);return;}
probe->leds=count;probe_finish(probe,NULL,now);probe->mapping=mapping;
static void probe_request(ReceiverProbe *probe, bool login, uint64_t now) {
if (login)
snprintf(probe->request, sizeof(probe->request),
"{\"command\":\"authorize\",\"subcommand\":\"login\",\"token\":\"%s\"}\n",
probe->target.token);
else
snprintf(probe->request, sizeof(probe->request), "{\"command\":\"serverinfo\"}\n");
probe->sent = probe->used = 0;
probe->after_send = login ? TEST_LOGIN : TEST_LAYOUT;
probe->stage = TEST_SEND;
probe->deadline = now + 3000;
}
static bool probe_begin(ReceiverProbe *probe, const TargetConfig *target, uint64_t now) {
if (probe_busy(probe))
return false;
probe_close(probe);
memset(probe, 0, sizeof(*probe));
probe->fd = -1;
probe->target = *target;
probe->started = now;
probe->response = malloc(2 * 1024 * 1024 + 1);
if (!probe->response) {
probe_finish(probe, "Cannot allocate connection test buffer", now);
return true;
}
probe->fd = target_open(target);
if (probe->fd < 0) {
probe_finish(probe, "Cannot open receiver connection", now);
return true;
}
probe->stage = TEST_CONNECT;
probe->deadline = now + 3000;
return true;
}
static void probe_tick(ReceiverProbe *probe, PlaybackJson *json, uint64_t now) {
if (!probe_busy(probe))
return;
if (now >= probe->deadline) {
probe_finish(probe, "Receiver timed out", now);
return;
}
int fd = probe->fd;
if (probe->stage == TEST_CONNECT) {
struct pollfd p = {fd, POLLOUT, 0};
int error = 0;
socklen_t size = sizeof(error);
if (poll(&p, 1, 0) <= 0)
return;
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &size) != 0 || error) {
probe_finish(probe, "Receiver connection refused or unreachable", now);
return;
}
probe_request(probe, probe->target.token[0] != 0, now);
return;
}
if (probe->stage == TEST_SEND) {
size_t length = strlen(probe->request);
ssize_t n = send(fd, probe->request + probe->sent, length - probe->sent, MSG_NOSIGNAL);
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
return;
if (n <= 0) {
probe_finish(probe, "Receiver closed the test connection", now);
return;
}
probe->sent += (size_t)n;
if (probe->sent == length) {
probe->stage = probe->after_send;
probe->deadline = now + 3000;
memset(probe->request, 0, sizeof(probe->request));
}
return;
}
size_t available = 2 * 1024 * 1024 - probe->used;
if (available > 65536)
available = 65536;
if (!available) {
probe_finish(probe, "Receiver response exceeds 2 MiB", now);
return;
}
ssize_t n = recv(fd, probe->response + probe->used, available, 0);
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
return;
if (n <= 0) {
probe_finish(probe, "Receiver closed the test connection", now);
return;
}
probe->used += (size_t)n;
probe->response[probe->used] = 0;
char *end = strchr(probe->response, '\n');
if (!end)
return;
*end = 0;
void *root = json->parse(probe->response);
bool success = playback_true(json, root, "success");
if (root)
json->put(root);
if (!success) {
probe_finish(probe,
probe->stage == TEST_LOGIN
? "Receiver authentication failed"
: "Receiver rejected serverinfo; check authorization",
now);
return;
}
if (probe->stage == TEST_LOGIN) {
probe_request(probe, false, now);
return;
}
LedBlend *mapping = NULL;
unsigned count = 0;
if (!layout_from_json(probe->response, &mapping, &count)) {
probe_finish(probe, "Receiver returned an invalid or empty LED layout", now);
return;
}
probe->leds = count;
probe_finish(probe, NULL, now);
probe->mapping = mapping;
}
static inline const char *test_stage_name(void) {
return probe_stage_name(&connection_test);
}
static inline bool test_busy(void) {
return probe_busy(&connection_test);
}
static inline void test_close(void) {
probe_close(&connection_test);
}
static inline bool test_begin(const TargetConfig *target, uint64_t now) {
return probe_begin(&connection_test, target, now);
}
static inline void test_tick(PlaybackJson *json, uint64_t now) {
probe_tick(&connection_test, json, now);
}
static inline const char *test_stage_name(void){return probe_stage_name(&connection_test);}
static inline bool test_busy(void){return probe_busy(&connection_test);}
static inline void test_close(void){probe_close(&connection_test);}
static inline bool test_begin(const TargetConfig *target,uint64_t now){return probe_begin(&connection_test,target,now);}
static inline void test_tick(PlaybackJson *json,uint64_t now){probe_tick(&connection_test,json,now);}
#endif