Захват изображения дисплея / монитора, отправка ввода с клавиатуры на Linux
Мне нужно обрабатывать изображения, отправленные на видеодисплей моего ноутбука, и мне нужно отправлять ввод с клавиатуры на мою систему Linux, используя C++ или программу оболочки.
Моя цель - обработать изображения, которые являются частью игры FPS, а затем принять меры внутри этой игры (отсюда и ввод с клавиатуры) на основе этих изображений. Вместо того, чтобы пытаться понять (если это вообще возможно), как взаимодействовать с игрой X или Y, используя некоторый API, я решил, что это самый быстрый способ взаимодействия с любой игрой, каким-то образом угоняющий ввод и вывод Linux.
Есть ли способ сделать это без какого-либо ядра или взлома драйвера устройства? Раньше я использовал recordmydesktop для записи своего рабочего стола в виде видео, думаю, я мог бы взломать его код и попытаться что-то из этого перевернуть. Есть другие идеи? Я на Ubuntu 11.
3 ответа
У меня наконец есть решение. Я считаю, что UrT загружает OpenGL самостоятельно, поэтому такие вещи, как взломы и т. Д., Невозможны. Тогда лучший оставшийся вариант - сделать X скриншотов. Это работало довольно быстро, даже с использованием скриптового языка, такого как Python. Следующий код делает последовательные снимки экрана и отображает их в виде анимации через OpenCV. Вы должны запустить UrT в свернутом режиме, конечно. Остальные детали в моем проекте.
import gtk.gdk
import PIL
from opencv.cv import *
from opencv.highgui import *
from opencv.adaptors import PIL2Ipl
w = gtk.gdk.get_default_root_window()
sz = w.get_size()
print "The size of the window is %d x %d" % sz
size_x = 600
size_y = 400
start_x = 0
start_y = 100
end_x = start_x+size_x
end_y = start_y+size_y
box = (start_x, start_y, start_x+size_x, start_y+size_y)
while True:
pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
width,height = pb.get_width(),pb.get_height()
im = PIL.Image.fromstring("RGB",(width,height),pb.get_pixels())
im = im.crop(box)
cv_img = PIL2Ipl(im)
cvNamedWindow("fps")
cvShowImage("fps", cv_img)
cvWaitKey(30)
Кроме того, для отправки ключей в игру, вышеприведенный метод не работал, мне пришлось использовать xdotool для отправки ключа ходьбы вперед в UrT,
xdotool search --name ioUrbanTerror windowactivate keydown W
Вам не нужно делать ничего более низкого уровня, чем ядро или драйверы устройств, чтобы сделать это.
Например, вы можете использовать расширение XTest X11 для программной имитации входных событий (из этой публикации есть еще один пример для клавиатуры).
#include <X11/extensions/XTest.h>
#include <unistd.h>
int main ()
{
Display *dpy = NULL;
XEvent event;
dpy = XOpenDisplay (NULL);
/* Get the current pointer position */
XQueryPointer (dpy, RootWindow (dpy, 0),
&event.xbutton.root, &event.xbutton.window,
&event.xbutton.x_root, &event.xbutton.y_root,
&event.xbutton.x, &event.xbutton.y,
&event.xbutton.state);
/* Fake the pointer movement to new relative position */
XTestFakeMotionEvent (dpy, 0, event.xbutton.x + 100,
event.xbutton.y + 50, CurrentTime);
XSync(dpy, 0);
XCloseDisplay (dpy);
return 0;
}
Для захвата изображений самым простым способом является использование функции вставки(через LD_PRELOAD
) перехватывать звонкиglXSwapBuffers
, который будет вызываться после прорисовки каждого кадра. Оттуда вы можете скопировать содержимое кадрового буфера, используяglReadPixels
и делай с этим что хочешь.
Например, непроверенный контур для перехвата кадров OpenGL:
// Function pointer to the *real* glXSwapBuffers
static void (*glx_fptr)(Display*, GLXDrawable) = NULL;
// Make sure init gets called when the shared object is loaded. GCC specific.
static void init(void) __attribute__((constructor));
static void init(void) {
dlerror();
// find the real glXSwapBuffers
glx_fptr = dlsym(RTLD_NEXT, "glXSwapBuffers");
if (NULL == glx_fptr)
fprintf(stderr, "[glvidcap] %s\n", dlerror());
}
void glXSwapBuffers(Display *dpy, GLXDrawable drawable) {
unsigned int w = 0;
unsigned int h = 0;
static int x,y;
static Window win;
static unsigned int border,depth;
// Find the window size. (You could skip this and make it all static if you
// Trust the window not to change size
XGetGeometry(dpy, drawable, &win, &x, &y, &w, &h, &border, &depth);
// Assuming frame is some memory you want the frame dumped to:
glReadPixels(0,0,w,h,GL_BGR,GL_UNSIGNED_BYTE, frame);
// Call the real function:
assert(glx_fptr);
glx_fptr(dpy, drawable);
}
Затем вы хотите скомпилировать это как общий объект и LD_PRELOAD
этот общий объект перед запуском любой игры, на которую вы смотрите.
Если это приложение SDL, вы можете перехватывать вызовы SDL_Flip
или же SDL_UpdateRect
по мере необходимости.
Благодаря ответу @awoodland, я искал соответствующие ресурсы и нашел это
http://bzr.sesse.net/glcapture/glcapture.c
Я компилирую этот код как
gcc -shared -fPIC -o glcapture.so glcapture.c -ldl
и загрузить его для FPS-игры Urban Terror в виде скрипта как
LD_PRELOAD=`pwd`/glcapture.so [DIR]/UrbanTerror/ioUrbanTerror.i386
В момент запуска этого скрипта игра загружается. По какой-то причине игровая графика не отображается, но я могу это улучшить позже. Когда я выхожу из игры, я вижу сообщения gettimeofday, напечатанные на консоли, которые говорят мне, что ловушка сработала. Я предоставлю больше деталей, поскольку я продолжаю работать с этим кодом.
Для отправки нажатий клавиш я перешел по ссылке
http://bharathisubramanian.wordpress.com/2010/03/14/x11-fake-key-event-generation-using-xtest-ext/
После того, как я установил необходимый пакет на Ubuntu с
sudo apt-get install libxtst-dev
Затем fakeKey.c скомпилирован и работал без проблем.
Примечание: для этого я запустил проект на Github, любой желающий может получить поддержку, помощь и т. Д.