Created
April 7, 2014 10:30
-
-
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
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
#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; | |
} | |
} | |
} |
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
#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); | |
} |
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
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.