Skip to content

Instantly share code, notes, and snippets.

@richard-to
Created April 7, 2014 10:30
Show Gist options
  • Save richard-to/10017943 to your computer and use it in GitHub Desktop.
Save richard-to/10017943 to your computer and use it in GitHub Desktop.
X11 Example code for grabbing screenshots of window and sending key events
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
using namespace cv;
using namespace std;
#define ACTIVE_WINDOWS "_NET_CLIENT_LIST"
#define WINDOW_TITLE "Wolf3d Screenshot"
#define WINDOW_DOXBOX "DOSBox"
// Find the DOSBox Window so we can do cool stuff with it!
bool findDOSBoxWindow(Display *display, Window &window)
{
bool found = false;
Window rootWindow = RootWindow(display, DefaultScreen(display));
Atom atom = XInternAtom(display, ACTIVE_WINDOWS, true);
Atom actualType;
int format;
unsigned long numItems;
unsigned long bytesAfter;
unsigned char *data = '\0';
Window *list;
char *windowName;
int status = XGetWindowProperty(display, rootWindow, atom, 0L, (~0L), false,
AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, &data);
list = (Window *)data;
if (status >= Success && numItems) {
for (int i = 0; i < numItems; ++i) {
status = XFetchName(display, list[i], &windowName);
if (status >= Success) {
string windowNameStr(windowName);
if (windowNameStr.find(WINDOW_DOXBOX) == 0) {
window = list[i];
found = true;
break;
}
}
}
}
XFree(windowName);
XFree(data);
return found;
}
int main(int argc, char *argv[])
{
Display *display = XOpenDisplay(NULL);
Window rootWindow = RootWindow(display, DefaultScreen(display));
Window DOSBoxWindow;
XWindowAttributes DOSBoxWindowAttributes;
if (findDOSBoxWindow(display, DOSBoxWindow) == false) {
printf("Error: Cannot find DOSBox window. Exiting program.");
return 0;
}
XGetWindowAttributes(display, DOSBoxWindow, &DOSBoxWindowAttributes);
int width = DOSBoxWindowAttributes.width;
int height = DOSBoxWindowAttributes.height;
namedWindow(WINDOW_TITLE, WINDOW_AUTOSIZE);
Mat frame = Mat::zeros(height, width, CV_8UC3);
Vec3b frameRGB;
XColor colors;
XImage *image;
unsigned long red_mask;
unsigned long green_mask;
unsigned long blue_mask;
while (true) {
image = XGetImage(
display, DOSBoxWindow, 0, 0, width, height, AllPlanes, ZPixmap);
red_mask = image->red_mask;
green_mask = image->green_mask;
blue_mask = image->blue_mask;
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
colors.pixel = XGetPixel(image, j, i);
// TODO(richard-to): Figure out why red and blue are swapped
frameRGB = frame.at<Vec3b>(i, j);
frameRGB.val[0] = colors.pixel & blue_mask;
frameRGB.val[1] = (colors.pixel & green_mask) >> 8;
frameRGB.val[2] = (colors.pixel & red_mask) >> 16;
frame.at<Vec3b>(i, j) = frameRGB;
}
}
XFree(image);
imshow(WINDOW_TITLE, frame);
if (waitKey(10) >= 0) {
break;
}
}
}
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
using namespace cv;
using namespace std;
#define ACTIVE_WINDOWS "_NET_CLIENT_LIST"
#define WINDOW_TITLE "Wolf3d Screenshot"
#define WINDOW_DOXBOX "DOSBox"
// Find the DOSBox Window so we can do cool stuff with it!
bool findDOSBoxWindow(Display *display, Window &window)
{
bool found = false;
Window rootWindow = RootWindow(display, DefaultScreen(display));
Atom atom = XInternAtom(display, ACTIVE_WINDOWS, true);
Atom actualType;
int format;
unsigned long numItems;
unsigned long bytesAfter;
unsigned char *data = '\0';
Window *list;
char *windowName;
int status = XGetWindowProperty(display, rootWindow, atom, 0L, (~0L), false,
AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, &data);
list = (Window *)data;
if (status >= Success && numItems) {
for (int i = 0; i < numItems; ++i) {
status = XFetchName(display, list[i], &windowName);
if (status >= Success) {
string windowNameStr(windowName);
if (windowNameStr.find(WINDOW_DOXBOX) == 0) {
window = list[i];
found = true;
break;
}
}
}
}
XFree(windowName);
XFree(data);
return found;
}
void millisleep(int ms)
{
usleep(ms * 1000);
}
int main(int argc, char *argv[])
{
Display *display = XOpenDisplay(NULL);
Window rootWindow = RootWindow(display, DefaultScreen(display));
Window DOSBoxWindow;
XWindowAttributes DOSBoxWindowAttributes;
if (findDOSBoxWindow(display, DOSBoxWindow) == false) {
printf("Error: Cannot find DOSBox window. Exiting program.");
return 0;
}
XKeyEvent event;
event.type = KeyPress;
event.display = display;
event.send_event = False;
event.window = DOSBoxWindow;
event.root = rootWindow;
event.time = CurrentTime;
event.same_screen = True;
event.keycode = XKeysymToKeycode(display, XK_Up);
XSendEvent(display, DOSBoxWindow, True, KeyPressMask, (XEvent *)&event);
XFlush(display);
millisleep(100);
event.type = KeyRelease;
XSendEvent(display, DOSBoxWindow, True, KeyReleaseMask, (XEvent *)&event);
XFlush(display);
}
@letyrodridc
Copy link

Hi Richard, amazing work! Thanks so much for sharing. Notice that "// TODO(richard-to): Figure out why red and blue are swapped" I think the reason is that OpenCV works in BGR not RGB. Something like that, the same happens when reading with OpenCV and using MatPlotLib for displaying. Best.

@mister-good-deal
Copy link

mister-good-deal commented Feb 21, 2023

For way better performances use the following:

XImage* image = XGetImage(display, DOSBoxWindow, 0, 0, width, height, AllPlanes, ZPixmap);

cv::Mat frame(height, width, CV_8UC4, image->data);

cv::cvtColor(frame, frame, cv::COLOR_BGRA2BGR);

Instead of

image = XGetImage(
            display, DOSBoxWindow, 0, 0, width, height, AllPlanes, ZPixmap);

        red_mask = image->red_mask;
        green_mask = image->green_mask;
        blue_mask = image->blue_mask;

        for (int i = 0; i < height; ++i) {
            for (int j = 0; j < width; ++j) {
                colors.pixel = XGetPixel(image, j, i);
               
                // TODO(richard-to): Figure out why red and blue are swapped
                frameRGB = frame.at<Vec3b>(i, j);            
                frameRGB.val[0] = colors.pixel & blue_mask;
                frameRGB.val[1] = (colors.pixel & green_mask) >> 8;
                frameRGB.val[2] = (colors.pixel & red_mask) >> 16;       
                frame.at<Vec3b>(i, j) = frameRGB;
            }
        }
    // ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment