From d4a90b286c6b3a4790b729328cf471b425b67b93 Mon Sep 17 00:00:00 2001 From: Toni Uhlig Date: Wed, 14 Jun 2017 14:55:43 +0200 Subject: x11 screen diff --- Makefile | 9 ++- README.md | 4 ++ xdiff.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 xdiff.c diff --git a/Makefile b/Makefile index a41a618..d60ceb7 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ ifneq ($(strip $(MAKE_NCURSES)),) TARGETS += gol endif ifneq ($(strip $(MAKE_X11)),) -TARGETS += xidle +TARGETS += xidle xdiff endif @@ -49,6 +49,13 @@ xidle: xidle.o @echo 'Finished building target: $@' @echo ' ' +xdiff: xdiff.o + @echo 'Building target: $@' + @echo 'Invoking: GCC C Linker' + $(CC) $(LDFLAGS) -o "$@" "$<" -lX11 -lXext -lXss + @echo 'Finished building target: $@' + @echo ' ' + strip: strip -s $(TARGETS) diff --git a/README.md b/README.md index 7a630fa..953f403 100644 --- a/README.md +++ b/README.md @@ -25,5 +25,9 @@ xidle.c ======== Report XServer idle time (no cursor movement).
+xdiff.c +======== +Report X11 root window pixel diff (without cursor).
+
Thats all folks.
diff --git a/xdiff.c b/xdiff.c new file mode 100644 index 0000000..dc6d6e7 --- /dev/null +++ b/xdiff.c @@ -0,0 +1,187 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + + +static uint64_t calcChkSum(XImage* ximg, int width, int height) +{ + assert(ximg); + + uint64_t chksum = 0; + unsigned long red_mask = ximg->red_mask; + unsigned long green_mask = ximg->green_mask; + unsigned long blue_mask = ximg->blue_mask; + + for (int x = 0; x < width; ++x) { + for (int y = 0; y < height; ++y) { + unsigned long pixel = XGetPixel(ximg, x, y); + + unsigned int blue = pixel & blue_mask; /* 0x0000FF */ + unsigned int green = pixel & green_mask; /* 0x00FF00 */ + unsigned int red = pixel & red_mask; /* 0xFF0000 */ + unsigned int rgb_chk = (red | green | blue); + + chksum = (chksum << 24) | ( (chksum >> 40) ^ (rgb_chk & 0xFFFFFF) ); + } + } + return chksum; +} + +static ssize_t calcImgDiff(XImage* xnew, XImage* xold, int width, int height, unsigned int maxdiff) +{ + assert(xnew || xold); + + unsigned int diff = 0; + for (int x = 0; x < width; ++x) { + for (int y = 0; y < height; ++y) { + unsigned long newpixel = XGetPixel(xnew, x, y); + unsigned long oldpixel = XGetPixel(xold, x, y); + if (newpixel != oldpixel) + diff++; + if (diff >= maxdiff) + return diff; + } + } + + return diff; +} + +static int xinit(char* disp_name, Display** disp_ptr, Window* root_ptr, int* width_ptr, int* height_ptr) +{ + assert(disp_ptr || root_ptr || width_ptr || height_ptr); + + *disp_ptr = XOpenDisplay(disp_name); + if (! *disp_ptr) + return 1; + *root_ptr = DefaultRootWindow(*disp_ptr); + XWindowAttributes attr; + if (XGetWindowAttributes(*disp_ptr, *root_ptr, &attr) == 0) + return 1; + *width_ptr = attr.width; + *height_ptr = attr.height; + return 0; +} + +static int genChkSum = 0; +static long long maxIter = -1; +static useconds_t sleep_interval = 1 * 1000 * 1000; +static unsigned int maxdiff = 1000; +static unsigned int bounds[4]; + +static void parseArgs(int argc, char** argv) +{ + int opt; + + while ((opt = getopt(argc, argv, "cd:m:i:x:y:w:h:")) != -1) { + switch (opt) { + case 'c': + genChkSum = 1; + break; + case 'd': + maxdiff = (unsigned int)atoi(optarg); + break; + case 'm': + maxIter = atoll(optarg); + break; + case 'i': + sleep_interval = atoll(optarg) * 1000; + break; + case 'x': + bounds[0] = (unsigned int)atoi(optarg); + break; + case 'y': + bounds[1] = (unsigned int)atoi(optarg); + break; + case 'w': + bounds[2] = (unsigned int)atoi(optarg); + break; + case 'h': + bounds[3] = (unsigned int)atoi(optarg); + break; + default: + fprintf(stderr, "usage: %s [-c] [-d maxDiff] [-m maxIter] [-i sleepTime in ms] [-x n] [-y n] [-w n] [-h n]\n", argv[0]); + exit(1); + } + } +} + +static int doLoopOnce(Display* disp, Window* root, XImage** old) +{ + assert(disp || root || old); + + XImage* image = XGetImage(disp, *root, bounds[0], bounds[1], bounds[2], bounds[3], AllPlanes, ZPixmap); + if (!image) + return 1; + + if (genChkSum != 0) { + printf("ChkSum: %#" PRIx64 "\n", calcChkSum(image, bounds[2], bounds[3])); + } + + printf("ImgDif: %llu\n", (long long unsigned int)calcImgDiff(image, *old, bounds[2], bounds[3], maxdiff)); + + XDestroyImage(*old); + *old = image; + usleep(sleep_interval); + return 0; +} + +static void checkBound(unsigned int value, unsigned int max, const char* name) +{ + if (value > max) { + printf("Bound check failed for %s: %u > %u\n", name, value, max); + abort(); + } +} + +int main(int argc, char** argv) +{ + Display* disp; + Window root; + int width, height; + + if (xinit(NULL, &disp, &root, &width, &height)) { + fprintf(stderr, "%s: xinit failed\n", argv[0]); + abort(); + } + printf("X Resolution: %ux%u\n", width, height); + + bounds[0] = 0; + bounds[1] = 0; + bounds[2] = width; + bounds[3] = height; + parseArgs(argc, argv); + checkBound(bounds[0], width-1, "-x"); + checkBound(bounds[1], height-1, "-y"); + checkBound(bounds[2], width-bounds[0], "-w"); + checkBound(bounds[3], height-bounds[1], "-h"); + printf("X Image Bounds (X,Y,W,H): %u, %u, %u, %u\n", bounds[0], bounds[1], bounds[2], bounds[3]); + + XImage* old = XGetImage(disp, root, bounds[0], bounds[1], bounds[2], bounds[3], AllPlanes, ZPixmap); + if (!old) { + fprintf(stderr, "%s: XGetImage failed\n", argv[0]); + abort(); + } + + if (maxIter < 0) { + while (1) { + if (doLoopOnce(disp, &root, &old) != 0) + break; + } + } else { + for (long long i = 0; i < maxIter; ++i) { + if (doLoopOnce(disp, &root, &old) != 0) + break; + } + } + + return 0; +} -- cgit v1.2.3