Skip to content

Instantly share code, notes, and snippets.

@mrbid
Last active August 19, 2022 13:09
Show Gist options
  • Save mrbid/deacf1f19ec521de68d1c017977a2653 to your computer and use it in GitHub Desktop.
Save mrbid/deacf1f19ec521de68d1c017977a2653 to your computer and use it in GitHub Desktop.
Generate screenbuffers to /dev/shm on demand.
/*
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