summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/xgrab.c
blob: eb605d65cd26d1546db63930710e5d6b92df9950 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// 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;
    int r_x = argc > 5 ? atoi(argv[5]) : -1;
    int r_y = argc > 6 ? atoi(argv[6]) : 0;
    int r_w = argc > 7 ? atoi(argv[7]) : 0;
    int r_h = argc > 8 ? atoi(argv[8]) : 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;
    }
    if (r_x < 0 || r_w <= 0 || r_h <= 0 || r_x + r_w > wa.width ||
        r_y + r_h > wa.height) {
        r_x = 0; r_y = 0; r_w = wa.width; r_h = wa.height;
    }
    fprintf(stderr, "xgrab: root %dx%d depth=%d region %d,%d %dx%d\n",
            wa.width, wa.height, wa.depth, r_x, r_y, r_w, r_h);
    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, r_x, r_y, r_w, r_h, 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)r_w;
        rh[1] = (uint32_t)r_h;
        rh[2] = (uint32_t)r_w * 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 < r_h; y++)
            fwrite(im->data + (size_t)y * im->bytes_per_line, 1,
                   (size_t)r_w * 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;
}