summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/xgrab.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/guest-diag/xgrab.c')
-rw-r--r--tools/guest-diag/xgrab.c107
1 files changed, 107 insertions, 0 deletions
diff --git a/tools/guest-diag/xgrab.c b/tools/guest-diag/xgrab.c
new file mode 100644
index 0000000..3f4ec76
--- /dev/null
+++ b/tools/guest-diag/xgrab.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: BSD-3-Clause
+// xgrab.c - capture the X screen (root window) into an append-only frame file.
+// Pure client-side XGetImage on a software X server (Xvfb): no GL, no driver,
+// no present-path hooks. Same record format as sdl_cap.c:
+// [0] 8B "GZCAPV01" + zero pad to 4096
+// [4096..] records: 4B "GZF2" | u32 w | u32 h | u32 stride | u32 bpp |
+// u64 ts_ns | u64 seq | stride*h bytes (top-down BGRA)
+// Trailing record seq=0xFFFFFFFFFFFFFFFF on clean stop.
+// Usage: xgrab <display> <out> <maxframes|0> <pace_us|0>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+ const char *dpy = argc > 1 ? argv[1] : ":1";
+ const char *out = argc > 2 ? argv[2] : "/dev/shm/gzframes.bin";
+ long maxf = argc > 3 ? atol(argv[3]) : 0;
+ long pace = argc > 4 ? atol(argv[4]) : 0;
+ Display *d;
+ Window root;
+ XWindowAttributes wa;
+ FILE *f;
+ char hdr[4096];
+ uint64_t seq = 0;
+
+ d = XOpenDisplay(dpy);
+ if (!d) {
+ fprintf(stderr, "xgrab: cannot open display %s\n", dpy);
+ return 1;
+ }
+ root = DefaultRootWindow(d);
+ if (!XGetWindowAttributes(d, root, &wa)) {
+ fprintf(stderr, "xgrab: no root attrs\n");
+ return 1;
+ }
+ fprintf(stderr, "xgrab: root %dx%d depth=%d\n", wa.width, wa.height,
+ wa.depth);
+ f = fopen(out, "wb");
+ if (!f) {
+ perror("xgrab: open out");
+ return 1;
+ }
+ memset(hdr, 0, sizeof(hdr));
+ memcpy(hdr, "GZCAPV01", 8);
+ fwrite(hdr, 1, sizeof(hdr), f);
+ fflush(f);
+
+ while (!maxf || (long)seq < maxf) {
+ XImage *im = XGetImage(d, root, 0, 0, wa.width, wa.height,
+ AllPlanes, ZPixmap);
+ struct timespec ts;
+ uint64_t tns;
+ uint32_t rh[4];
+ char m[4] = { 'G', 'Z', 'F', '2' };
+ int y;
+
+ if (!im) {
+ usleep(10000);
+ continue;
+ }
+ if (im->bits_per_pixel != 32) {
+ fprintf(stderr, "xgrab: unexpected bpp %d\n",
+ im->bits_per_pixel);
+ XDestroyImage(im);
+ break;
+ }
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ tns = (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
+ rh[0] = (uint32_t)wa.width;
+ rh[1] = (uint32_t)wa.height;
+ rh[2] = (uint32_t)wa.width * 4;
+ rh[3] = 32;
+ fwrite(m, 1, 4, f);
+ fwrite(rh, 4, 4, f);
+ fwrite(&tns, 8, 1, f);
+ fwrite(&seq, 8, 1, f);
+ for (y = 0; y < wa.height; y++)
+ fwrite(im->data + (size_t)y * im->bytes_per_line, 1,
+ (size_t)wa.width * 4, f);
+ fflush(f);
+ seq++;
+ XDestroyImage(im);
+ if (pace)
+ usleep(pace);
+ if ((seq & 63) == 0)
+ fprintf(stderr, "xgrab: frames=%llu\n",
+ (unsigned long long)seq);
+ }
+ {
+ char m[4] = { 'G', 'Z', 'F', '2' };
+ uint64_t end = 0xFFFFFFFFFFFFFFFFull;
+
+ fwrite(m, 1, 4, f);
+ fwrite(&end, 8, 1, f);
+ fflush(f);
+ }
+ fclose(f);
+ XCloseDisplay(d);
+ fprintf(stderr, "xgrab: done frames=%llu\n", (unsigned long long)seq);
+ return 0;
+}