Захват изображения дисплея / монитора, отправка ввода с клавиатуры на 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, любой желающий может получить поддержку, помощь и т. Д.

Другие вопросы по тегам