【问题标题】:Capturing Window Events in Linux/Ubuntu (Screen-Capture + Mouse/Keyboard-Events)在 Linux/Ubuntu 中捕获窗口事件(屏幕捕获 + 鼠标/键盘事件)
【发布时间】:2015-05-07 19:32:55
【问题描述】:

我想为 Linux 开发一个需要通过标准 IO(鼠标 + 键盘)与其他程序交互的 C++ 应用程序。它应该能够将鼠标/键盘输入发送到窗口并捕获该特定窗口的“屏幕截图”。

(1) 现在我做了一些研究,我知道 Linux 使用“X Window”系统。是否建议从这一层开始编程或我应该使用一些更高级别的框架(例如 wxWidgets)?

(2) 是否可以将输入事件发送到/捕获窗口的屏幕,即使它没有聚焦?

(3) 我不是要你提供代码,但我很想通过一些关于该主题的文献来工作。遗憾的是,我找不到关于这个主题的任何好的资源。

如果有人能帮我解决这个问题,那就太好了!

提前谢谢你!!

[ 注意:这个程序应该作为后端运行,然后从 java 应用程序执行命令。计划是分别为 Windows/Linux 实现这个后端 C++ 应用程序]

【问题讨论】:

    标签: c++ linux keyboard mouse capture


    【解决方案1】:

    解决方案 1 我可以推荐一段用于发送键盘事件的代码,我自己用它来模拟按键。它基于 XSendEvent。

    #include <X11/Xlib.h>
    #include <X11/keysym.h>
    
    /** Modifier states */
    #define MOD_ALT 0x8
    #define MOD_CONTROL 0x4
    #define MOD_CONTROL_ALT 0xc
    #define MOD_SHIFT_CONTROL 0x5
    #define MOD_SHIFT_ALT 0x9
    
    XKeyEvent createKeyEvent(Display *display, Window &win,
                               Window &winRoot, bool press,
                               int keycode, int modifiers)
    {
       XKeyEvent event;
    
       event.display     = display;
       event.window      = win;
       event.root        = winRoot;
       event.subwindow   = None;
       event.time        = CurrentTime;
       event.x           = 1;
       event.y           = 1;
       event.x_root      = 1;
       event.y_root      = 1;
       event.same_screen = True;
       event.keycode     = XKeysymToKeycode(display, keycode);
       event.state       = modifiers;
    
       if(press)
          event.type = KeyPress;
       else
          event.type = KeyRelease;
    
       return event;
    }
    
    
    void pressKey(Display* display, Window &win_focus, Window &win_root,
                  KeySym key, int modifiers)
    {
        // Send a fake key press event to the window.
        XKeyEvent event = createKeyEvent(display, win_focus, win_root, true, key, modifiers);
        XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
    
        // Send a fake key release event to the window.
        event = createKeyEvent(display, win_focus, win_root, false, key, modifiers);
        XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
    }
    

    然后,您可以使用它来发送按键事件(在本例中为 Alt+W),如下所示:

    // Obtain the X11 display.
    Display *display = XOpenDisplay(0);
    if (display == NULL)
    {
      printf("Null display!\n");
      return 1;
    }
    
    // Get the root window for the current display
    Window win_root = XDefaultRootWindow(display);
    
    // Find the window which has the current keyboard focus
    Window win_focus;
    int    revert;
    XGetInputFocus(display, &win_focus, &revert);
    
    
    pressKey(display, win_focus, win_root, XK_w, MOD_ALT);
    
    if (display)
      XCloseDisplay(display);
    

    解决方案 2 使用 XTest 库可以实现类似的行为。它虽然在“更高级别”上运行,例如窗口管理器也会接收这些按键。 XTest 的 pressKey 函数看起来像这样(不是很干净的代码):

    void pressKey(Display* display, KeySym modifier1, KeySym modifier2, KeySym key)
    {
        // Release every other modifier
        KeyCode keycodec = XKeysymToKeycode(display, XK_Control_L);
        KeyCode keycodea = XKeysymToKeycode(display, XK_Alt_L);
        KeyCode keycodes = XKeysymToKeycode(display, XK_Shift_L);
        XTestFakeKeyEvent(display, keycodec, False, 0); // key release event
        XTestFakeKeyEvent(display, keycodea, False, 0); // key release event
        XTestFakeKeyEvent(display, keycodes, False, 0); // key release event
        XFlush(display);
    
        // Press the actual keys
        KeyCode keycode1 = XKeysymToKeycode(display, modifier1);
        KeyCode keycode2 = XKeysymToKeycode(display, modifier2);
        KeyCode keycode3 = XKeysymToKeycode(display, key);
        if (keycode1)
            XTestFakeKeyEvent(display, keycode1, True, 0); // key press event
        if (keycode2)
            XTestFakeKeyEvent(display, keycode2, True, 0); // key press event
        if (keycode3)
        {
            XTestFakeKeyEvent(display, keycode3, True, 0); // key press event
            XTestFakeKeyEvent(display, keycode3, False, 0); // key release event
        }
        if (keycode2)
            XTestFakeKeyEvent(display, keycode2, False, 0); // key release event
        if (keycode1)
            XTestFakeKeyEvent(display, keycode1, False, 0); // key release event
        XFlush(display);
    }
    

    解决方案 3 另一种解决方案是使用 xdotool 等工具编写您的出路脚本。基本上,您所描述的大部分内容都可以通过 bash 脚本来实现。此外,xdotool source code 是有关如何实现您想要的一切的重要信息来源。

    【讨论】:

    • 嗨,Andrzej,感谢您快速而简洁的回答!所以你建议我应该使用 XWindow。然后我有一些更详细的问题。 (1) 如何获得没有键盘焦点的特定窗口的“句柄”? (2)如果我能做与那个窗口相关的所有事情(例如鼠标坐标不是相对于屏幕,而是相对于窗口),那将是最好的。你知道这是否可能吗? (3) XWindow 是否支持截取窗口的截图 这对我来说是必须的。你有什么建议吗? (如果你知道一个好的来源,我很乐意看看!)
    • 嗯,首先我假设您希望这是一个如您所说的 C++ 应用程序。在这种情况下,x libs 确实是要走的路。有 XSendEvent 方法,还有 XTest 方法,这对您来说可能更简单。不过,只需通过脚本编写,也可以使用外部工具实现很多目标。我也会用那个解决方案扩展我的答案。
    • @Marvin,如果该解决方案对您有所帮助,您能接受答案吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-17
    • 1970-01-01
    • 1970-01-01
    • 2014-04-28
    • 2015-06-13
    相关资源
    最近更新 更多