/* SPDX-License-Identifier: Apache-2.0 */ /* * 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 #include #include #include #include #include #include #include #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; }