我需要处理发送到笔记本电脑视频显示器的图像,我需要使用C ++或shell程序将键盘输入发送到我的Linux系统。
我的目标是处理属于FPS游戏的图像,然后根据这些图像在游戏中进行操作(因此键盘输入)。我没有尝试理解(如果它甚至可能)如何使用某些API与游戏X或Y进行交互,我认为这是与任何游戏进行交互的最快捷方式,劫持Linux输入和输出。
有没有任何方法可以在没有任何内核或设备驱动程序黑客的情况下执行此操作?之前我使用recordmydesktop将我的桌面记录为视频,我想我可以破解其代码并尝试从中进行逆向工程。还有其他想法吗?我在Ubuntu 11上。
答案 0 :(得分:5)
您无需像内核或设备驱动程序那样执行任何操作。
您可以使用XTest X11 extension以编程方式伪造输入事件,例如(来自this posting,有another example for keyboard)。
#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;
}
要捕获图像,最简单的方法是使用function interposition(通过LD_PRELOAD
)来“截取”对glXSwapBuffers
的调用,每次绘制帧时都会调用这些调用。从那里,您可以使用glReadPixels
复制帧缓冲的内容,并根据需要进行操作。
E.g。用于拦截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
的调用。
答案 1 :(得分:1)
感谢@ 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编译和工作没有问题。
注意:我started a project on Github for this,欢迎任何人分叉,帮助等。
答案 2 :(得分:0)
我终于找到了解决方案。我相信UrT自己加载OpenGL,因此诸如wallhacks之类的东西是不可能的。然后剩下的最佳选择是拍摄X截图。即使从像Python这样的脚本语言,这也非常快。以下代码采用连续的屏幕截图,并通过OpenCV将其显示为动画。您需要以最小化模式启动UrT。 The rest of the details are in my project
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