SchOS/projects/schcom/schcom-demo.c
Nexoniarz 26bca020b6 licence: SchOS is GPL-2.0-or-later, no longer Apache-2.0
Nexoniarz is the sole author and SchOS was never published under
Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text,
every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE,
README.md, docs/licensing.md and the site say so. projects/schfs-linux
follows the rest of the tree.

The GPL gives copyleft: a fork has to ship its source. It also lifts the
old "Linux drivers, never" rule, since GPL-2.0-only code combines with
GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still
has to pick one version, so docs/licensing.md now keeps Apache-2.0 and
GPLv3 code out of the kernel to keep Linux code possible there. BSD
4-clause code goes from "avoid" to "no", because the advertising clause
is GPL-incompatible.

LDLS still ships no .ko. That boundary is now project policy rather than
a legal requirement, and ldls.md, devices.md and licensing.md section 6
say so. refmod declares license=GPL, and ldls-check passes (27 checks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:48:45 +02:00

149 lines
5.1 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* A Schcom client, and the test for the compositor.
*
* Draws a flat rectangle of a known colour at a known position and commits it.
* Both of those are chosen so the result can be checked from outside the
* machine: `make schcom-shot` screendumps the framebuffer and a script on the
* host asserts the pixel at a given coordinate. That is the same oracle
* pattern the SchFS round-trip uses -- something with no connection to the code
* under test has to agree that it worked.
*
* schcom-demo [--colour 0xRRGGBB] [--at X Y] [--size W H] [--wait SECONDS]
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "proto.h"
static int
read_all(int fd, void *buf, size_t n)
{
unsigned char *p = buf;
while (n) {
ssize_t r = read(fd, p, n);
if (r == 0) return -1;
if (r < 0) { if (errno == EINTR) continue; return -1; }
p += r; n -= (size_t)r;
}
return 0;
}
int
main(int argc, char **argv)
{
uint32_t colour = 0x00C04020;
int32_t at_x = 100, at_y = 80;
uint32_t w = 240, h = 160;
int wait_s = 3;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--colour") && i + 1 < argc)
colour = (uint32_t)strtoul(argv[++i], NULL, 0);
else if (!strcmp(argv[i], "--at") && i + 2 < argc) {
at_x = atoi(argv[++i]); at_y = atoi(argv[++i]);
} else if (!strcmp(argv[i], "--size") && i + 2 < argc) {
w = (uint32_t)atoi(argv[++i]); h = (uint32_t)atoi(argv[++i]);
} else if (!strcmp(argv[i], "--wait") && i + 1 < argc)
wait_s = atoi(argv[++i]);
}
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) { perror("socket"); return 1; }
struct sockaddr_un addr;
memset(&addr, 0, sizeof addr);
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SCHCOM_SOCKET_PATH, sizeof addr.sun_path - 1);
if (connect(fd, (struct sockaddr *)&addr, sizeof addr) < 0) {
fprintf(stderr, "schcom-demo: connect %s: %s\n",
SCHCOM_SOCKET_PATH, strerror(errno));
return 1;
}
/* Handshake first: the compositor accepts nothing else before it. */
struct schcom_hello hello;
memset(&hello, 0, sizeof hello);
hello.hdr.op = SCHCOM_OP_HELLO;
hello.hdr.length = sizeof hello;
hello.hdr.serial = 1;
hello.version = SCHCOM_VERSION;
if (write(fd, &hello, sizeof hello) != (ssize_t)sizeof hello) {
perror("write hello"); return 1;
}
struct schcom_hello ack;
if (read_all(fd, &ack, sizeof ack) < 0 || ack.hdr.op != SCHCOM_OP_HELLO_ACK) {
fprintf(stderr, "schcom-demo: no hello ack\n");
return 1;
}
printf("schcom-demo: connected, protocol v%u\n", ack.version);
struct schcom_create_surface req;
memset(&req, 0, sizeof req);
req.hdr.op = SCHCOM_OP_CREATE_SURFACE;
req.hdr.length = sizeof req;
req.hdr.serial = 2;
req.width = w; req.height = h;
req.x = at_x; req.y = at_y;
if (write(fd, &req, sizeof req) != (ssize_t)sizeof req) {
perror("write create"); return 1;
}
struct schcom_surface surf;
if (read_all(fd, &surf, sizeof surf) < 0) {
fprintf(stderr, "schcom-demo: no reply to create\n");
return 1;
}
if (surf.hdr.op != SCHCOM_OP_SURFACE) {
fprintf(stderr, "schcom-demo: create refused (op %u)\n", surf.hdr.op);
return 1;
}
if (surf.handle_kind != SCHCOM_HANDLE_SHMID) {
fprintf(stderr, "schcom-demo: unexpected handle kind %u\n", surf.handle_kind);
return 1;
}
void *pix = shmat((int)surf.handle, NULL, 0);
if (pix == (void *)-1) { perror("shmat"); return 1; }
printf("schcom-demo: surface %u, %ux%u stride %u\n",
surf.hdr.surface, surf.width, surf.height, surf.stride);
/* Draw: a solid block with a one-pixel border, so a test can tell "the
* buffer arrived" from "the buffer arrived at the wrong offset". */
for (uint32_t y = 0; y < surf.height; y++) {
uint32_t *row = (uint32_t *)((unsigned char *)pix + (size_t)y * surf.stride);
for (uint32_t x = 0; x < surf.width; x++) {
int edge = (x == 0 || y == 0 || x == surf.width - 1 || y == surf.height - 1);
row[x] = edge ? 0x00FFFFFF : colour;
}
}
struct schcom_commit commit;
memset(&commit, 0, sizeof commit);
commit.hdr.op = SCHCOM_OP_COMMIT;
commit.hdr.length = sizeof commit;
commit.hdr.serial = 3;
commit.hdr.surface = surf.hdr.surface;
commit.width = surf.width; commit.height = surf.height;
if (write(fd, &commit, sizeof commit) != (ssize_t)sizeof commit) {
perror("write commit"); return 1;
}
printf("schcom-demo: committed 0x%06X at %d,%d\n", colour, at_x, at_y);
fflush(stdout);
/* Stay alive: the surface belongs to this connection, and the compositor
* drops it when the client goes. Exiting immediately would erase the very
* thing the screenshot is meant to catch. */
sleep(wait_s);
return 0;
}