在 Linux 上捕获显示/监视器图像、发送键盘输入

发布于 2024-12-01 22:46:55 字数 476 浏览 4 评论 0原文

我需要处理发送到笔记本电脑视频显示器的图像,并且需要使用 C++ 或 shell 程序将键盘输入发送到我的 Linux 系统。

我的目标是处理 FPS 游戏中的图像,然后根据这些图像在游戏中采取行动(因此是键盘输入)。我没有试图理解(如果可能的话)如何使用一些 API 与游戏 X 或 Y 交互,而是认为这是与任何游戏交互的最快方式,以某种方式劫持 Linux 输入和输出。

有没有办法在没有任何内核或设备驱动程序黑客攻击的情况下做到这一点?我之前使用 recordmydesktop 将我的桌面录制为视频,我想我可以破解它的代码并尝试从中进行逆向工程。还有其他想法吗?我使用的是 Ubuntu 11。

相关问题

I need to process images sent to my laptop's video display, and I need to send keyboard input to my Linux system, using a C++ or shell program.

My goal is to process images that are part of an FPS game, then taking action inside that game (hence the keyboard input) based on these images. Instead of trying to understand (if it's even possible) how to interface to game X or Y, using some API, I figured this is the quickest way to interface to any game, hijacking Linux input and output somehow.

Is there any way to do this without any kernel, or device driver hacking? I used recordmydesktop to record my desktop as a video before, I guess I could hack its code and try to reverse-engineer something from that. Any other ideas? I am on Ubuntu 11.

Related Question

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

墨洒年华 2024-12-08 22:46:56

您不需要执行任何与内核或设备驱动程序一样低级别的操作来执行此操作。

例如,您可以使用 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< 复制帧缓冲区的内容/code>并用它做你想做的事。

例如,用于拦截 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_FlipSDL_UpdateRect 的调用。

You don't need to do anything as low level as the kernel or device drivers to do this.

You can use the XTest X11 extension to fake input events programatically, for example (from this posting, there's 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;  
}   

To capture the images the simplest way is to use function interposition (via LD_PRELOAD) to "intercept" calls to glXSwapBuffers, which will be called once each frame is drawn. From there you can copy the contents of the framebuffer, using glReadPixels and do with it what you want.

E.g. untested outline for intercepting OpenGL frames:

// 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);
}

Then you want to compile this as a shared object and LD_PRELOAD that shared object before running whatever game you're looking at.

If it happens to be an SDL application instead you can intercept the calls to SDL_Flip or SDL_UpdateRect as appropriate.

少女的英雄梦 2024-12-08 22:46:56

感谢@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上启动了一个项目,欢迎任何人分叉、提供帮助等。

Thanks to @awoodland's answer, I looked for related resources, and found this

http://bzr.sesse.net/glcapture/glcapture.c

I compile this code as

gcc -shared -fPIC -o glcapture.so glcapture.c -ldl

and load it for FPS game Urban Terror in a script as

LD_PRELOAD=`pwd`/glcapture.so [DIR]/UrbanTerror/ioUrbanTerror.i386

The moment this script is ran, the game is loaded. For some reason, the game graphics do not show, but I can improve that later. When I exit the game, I see gettimeofday messages printed on the console, which tells me the hook worked. I'll provide more details as I continue to work this code.

For sending key presses, I followed the link

http://bharathisubramanian.wordpress.com/2010/03/14/x11-fake-key-event-generation-using-xtest-ext/

After I installed the necessary package on Ubuntu with

sudo apt-get install libxtst-dev

Then fakeKey.c compiled and worked without problems.

Note: I started a project on Github for this, anyone is welcome to fork, help, etc.

泪是无色的血 2024-12-08 22:46:56

我终于有解决办法了。我相信 UrT 会自行加载 OpenGL,因此诸如 wallhacks 之类的事情是不可能的。那么剩下的最佳选择就是进行 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

I finally have a solution. I believe UrT loads OpenGL on its own so that things such as wallhacks, etc are not possible. Then the best remaining option is taking X screenshots. This worked quite fast, even from a scripting language like Python. The following code takes successive screenshots and displays them as an animation through OpenCV. You need to start UrT in minimized mode of course. 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) 

Also, for sending keys to the game, the method above did not work, I had to use xdotool in order to send forward walk key to UrT,

xdotool search --name ioUrbanTerror  windowactivate keydown W
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文