linux - teclas - proyectar una pantalla y trabajar en otra
Captura de imágenes de pantalla/monitor, envío de entrada de teclado en Linux (3)
Necesito procesar las imágenes enviadas a la pantalla de video de mi laptop, y debo enviar la entrada del teclado a mi sistema Linux, usando un C ++ o un programa shell.
Mi objetivo es procesar imágenes que son parte de un juego de FPS, y luego tomar medidas dentro de ese juego (de ahí la entrada del teclado) en base a estas imágenes. En lugar de tratar de comprender (si es que es posible) cómo interactuar con el juego X o Y, usando alguna API, pensé que esta era la forma más rápida de interactuar con cualquier juego, secuestrando la entrada y salida de Linux de alguna manera.
¿Hay alguna manera de hacer esto sin ningún kernel, o hacking del controlador del dispositivo? Utilicé recordmydesktop para grabar mi escritorio como un video antes, supongo que podría hackear su código y tratar de aplicar ingeniería inversa a algo de eso. ¿Alguna otra idea? Estoy en Ubuntu 11.
Finalmente tengo una solución. Creo que UrT carga OpenGL por sí solo, por lo que cosas como wallhacks, etc. no son posibles. Entonces, la mejor opción restante es tomar capturas de pantalla de X. Esto funcionó bastante rápido, incluso desde un lenguaje de scripting como Python. El siguiente código toma capturas de pantalla sucesivas y las muestra como una animación a través de OpenCV. Por supuesto, debe iniciar UrT en modo minimizado. El resto de los detalles están en mi proyecto .
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)
Además, para enviar claves al juego, el método anterior no funcionó, tuve que usar xdotool para enviar la clave de acceso directo a UrT,
xdotool search --name ioUrbanTerror windowactivate keydown W
Gracias a la respuesta de @ awoodland, busqué recursos relacionados y encontré esto
http://bzr.sesse.net/glcapture/glcapture.c
Compilo este código como
gcc -shared -fPIC -o glcapture.so glcapture.c -ldl
y cargarlo para el juego FPS Urban Terror en un script como
LD_PRELOAD=`pwd`/glcapture.so [DIR]/UrbanTerror/ioUrbanTerror.i386
En el momento en que se ejecuta este script, el juego se carga. Por alguna razón, los gráficos del juego no se muestran, pero puedo mejorarlo más tarde. Cuando salgo del juego, veo los mensajes gettimeofday impresos en la consola, lo que me dice que el gancho funcionó. Proporcionaré más detalles a medida que sigo trabajando con este código.
Para enviar pulsaciones de teclas, seguí el enlace
http://bharathisubramanian.wordpress.com/2010/03/14/x11-fake-key-event-generation-using-xtest-ext/
Después de que instalé el paquete necesario en Ubuntu con
sudo apt-get install libxtst-dev
Luego fakeKey.c compiló y trabajó sin problemas.
Nota: Comencé un proyecto en Github para esto , cualquiera puede bifurcar, ayudar, etc.
No necesita hacer nada tan bajo como el núcleo o los controladores del dispositivo para hacer esto.
Puede usar la extensión XTest X11 para falsificar eventos de entrada programáticamente, por ejemplo (a partir de esta publicación , hay otro ejemplo para el teclado ).
#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;
}
Para capturar las imágenes, la forma más sencilla es usar la interposición de funciones (mediante LD_PRELOAD
) para "interceptar" llamadas a glXSwapBuffers
, que se glXSwapBuffers
que se glXSwapBuffers
cada cuadro. Desde allí, puede copiar el contenido del framebuffer, usando glReadPixels
y hacer con él lo que quiera.
Por ejemplo, esquema no probado para interceptar marcos 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);
}
Luego desea compilar esto como un objeto compartido y LD_PRELOAD
ese objeto compartido antes de ejecutar cualquier juego que esté viendo.
Si resulta ser una aplicación SDL, puede interceptar las llamadas a SDL_Flip
o a SDL_UpdateRect
según corresponda.