Last active
August 19, 2022 13:09
-
-
Save mrbid/deacf1f19ec521de68d1c017977a2653 to your computer and use it in GitHub Desktop.
Generate screenbuffers to /dev/shm on demand.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
James William Fletcher (github.com/mrbid) | |
https://gist.github.com/mrbid/deacf1f19ec521de68d1c017977a2653 | |
This code is an adaption of; | |
https://stackoverflow.com/questions/34176795/any-efficient-way-of-converting-ximage-data-to-pixel-map-e-g-array-of-rgb-quad/38298349#38298349 | |
It will loop indefinitely generating a new output to /dev/shm/dbuff.png every time the existing dbuff.png is deleted. | |
It should generate ~60 fps when compiled with GCC, or around 16 fps with clang for some reason. ¯\_(ツ)_/¯ | |
Compile using: | |
gcc screenshot.c -o screenshot -std=gnu99 -Ofast -lX11 -lXext -lpng | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <strings.h> | |
#include <stdbool.h> | |
#include <unistd.h> | |
#include <png.h> | |
#include <sys/shm.h> | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
#include <X11/extensions/XShm.h> | |
#define BENCHMARK_SECONDS 3 | |
#define uint unsigned int | |
struct shmimage | |
{ | |
XShmSegmentInfo shminfo; | |
XImage *ximage; | |
unsigned char *data; // will point to the image's BGRA packed pixels | |
}; | |
void initimage(struct shmimage *image) | |
{ | |
image->ximage = NULL; | |
image->shminfo.shmaddr = (char*)-1; | |
} | |
void destroyimage(Display *dsp, struct shmimage *image) | |
{ | |
if(image->ximage) | |
{ | |
XShmDetach(dsp, &image->shminfo); | |
XDestroyImage(image->ximage); | |
image->ximage = NULL; | |
} | |
if(image->shminfo.shmaddr != (char*)-1) | |
{ | |
shmdt(image->shminfo.shmaddr); | |
image->shminfo.shmaddr = (char*)-1; | |
} | |
} | |
int createimage(Display *dsp, struct shmimage *image, int width, int height) | |
{ | |
// Create a shared memory area | |
image->shminfo.shmid = shmget(IPC_PRIVATE, width * height * 4, IPC_CREAT | 0600); | |
if(image->shminfo.shmid == -1) | |
return false; | |
// Map the shared memory segment into the address space of this process | |
image->shminfo.shmaddr = (char*) shmat(image->shminfo.shmid, 0, 0); | |
if(image->shminfo.shmaddr == (char*) -1) | |
return false; | |
image->data = (unsigned char*)image->shminfo.shmaddr; | |
image->shminfo.readOnly = false; | |
// Mark the shared memory segment for removal | |
// It will be removed even if this program crashes | |
shmctl(image->shminfo.shmid, IPC_RMID, 0); | |
// Allocate the memory needed for the XImage structure | |
image->ximage = XShmCreateImage(dsp, XDefaultVisual(dsp, XDefaultScreen(dsp)), | |
DefaultDepth(dsp, XDefaultScreen(dsp)), ZPixmap, 0, &image->shminfo, 0, 0); | |
if(!image->ximage) | |
{ | |
destroyimage(dsp, image); | |
return false; | |
} | |
image->ximage->data = (char*)image->data; | |
image->ximage->width = width; | |
image->ximage->height = height; | |
// Ask the X server to attach the shared memory segment and sync | |
XShmAttach(dsp, &image->shminfo); | |
XSync(dsp, false); | |
return true; | |
} | |
void getrootwindow(Display *dsp, struct shmimage *image) | |
{ | |
XShmGetImage(dsp, XDefaultRootWindow(dsp), image->ximage, 0, 0, AllPlanes); | |
// This is how you access the image's BGRA packed pixels | |
// Lets set the alpha channel of each pixel to 0xff // slow, no need to do this | |
// unsigned int *p = image->data; | |
// for(int y = 0; y < image->ximage->height; ++y) | |
// for(int x = 0; x < image->ximage->width; ++x) | |
// *p++ |= 0xff000000; | |
} | |
int savepng(struct shmimage *image, char *path) | |
{ | |
FILE *f = fopen(path, "w"); | |
if(!f) | |
return false; | |
png_image pi; | |
pi.version = PNG_IMAGE_VERSION; | |
pi.width = image->ximage->width; | |
pi.height = image->ximage->height; | |
pi.format = PNG_FORMAT_BGRA; | |
const unsigned int scanline = pi.width * 4; | |
if(!png_image_write_to_stdio(&pi, f, 0, image->data, scanline, NULL)) | |
{ | |
fclose(f); | |
return false; | |
} | |
fclose(f); | |
return true; | |
} | |
int main(int argc, char * argv[]) | |
{ | |
Display *dsp = XOpenDisplay(NULL); | |
if(!dsp) | |
{ | |
printf("could not open a connection to the X server\n"); | |
return 1; | |
} | |
if(!XShmQueryExtension(dsp)) | |
{ | |
XCloseDisplay(dsp); | |
printf("the X server does not support the XSHM extension\n"); | |
return 1; | |
} | |
int screen = XDefaultScreen(dsp); | |
struct shmimage image; | |
initimage(&image); | |
if(!createimage(dsp, &image, XDisplayWidth(dsp, screen), XDisplayHeight(dsp, screen))) | |
{ | |
XCloseDisplay(dsp); | |
return 1; | |
} | |
#if BENCHMARK_SECONDS > 0 | |
int c = 0; | |
time_t st = time(0); | |
while(time(0)-st <= BENCHMARK_SECONDS) | |
{ | |
char name[32]; | |
sprintf(name, "/tmp/%i.png", rand()); | |
getrootwindow(dsp, &image); | |
savepng(&image, name); | |
c++; | |
} | |
printf("Screenshots Per Second: %i\n", c/BENCHMARK_SECONDS); | |
#endif | |
printf("Writing to: /dev/shm/dbuff.png\n"); | |
printf("Delete this file to generate a new output.\n"); | |
while(1) | |
{ | |
usleep(1000); | |
if(access("/dev/shm/dbuff.png", F_OK) != 0) | |
{ | |
getrootwindow(dsp, &image); | |
savepng(&image, "/dev/shm/dbuff.png"); | |
} | |
} | |
destroyimage(dsp, &image); | |
XCloseDisplay(dsp); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment