XTestFakeButtonEvent & XSendEvent 的区别

gol*_*tar 4 c++ linux x11 google-chrome

我正在尝试通过 x11 为 ubuntu 编写简单的鼠标点击器。

首先,我编写了点击过程的第一个变体(基于 XSendEvent):

#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

void mouseClick(int button)
{
    Display *display = XOpenDisplay(NULL);

    XEvent event;

    if(display == NULL)
    {
        std::cout << "clicking error 0" << std::endl;
        exit(EXIT_FAILURE);
    }

    memset(&event, 0x00, sizeof(event));

    event.type = ButtonPress;
    event.xbutton.button = button;
    event.xbutton.same_screen = True;

    XQueryPointer(display, RootWindow(display, DefaultScreen(display)), &event.xbutton.root, &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);

    event.xbutton.subwindow = event.xbutton.window;

    while(event.xbutton.subwindow)
    {
        event.xbutton.window = event.xbutton.subwindow;
        XQueryPointer(display, event.xbutton.window, &event.xbutton.root, &event.xbutton.subwindow, &event.xbutton.x_root, &event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
    }

    if(XSendEvent(display, PointerWindow, True, 0xfff, &event) == 0)
        std::cout << "clicking error 1" << std::endl;

    XFlush(display);

    event.type = ButtonRelease;
    event.xbutton.state = 0x100;

    if(XSendEvent(display, PointerWindow, True, 0xfff, &event) == 0)
        std::cout << "clicking error 2" << std::endl;

    XFlush(display);

    XCloseDisplay(display);
}
Run Code Online (Sandbox Code Playgroud)

这段代码在除 chrome 之外的每个应用程序上都可以正常工作(mozilla 也可以正常工作)。

所以我写了第二个变体(基于 XTestFakeButtonEvent):

#include <X11/extensions/XTest.h>

void SendClick(int button, Bool down) 
{
    Display *display = XOpenDisplay(NULL);
    XTestFakeButtonEvent(display, button, down, CurrentTime);
    XFlush(display);
    XCloseDisplay(display);
}
Run Code Online (Sandbox Code Playgroud)

并且此代码在任何地方都可以正常工作,包括 chrome。

调用这些函数非常简单

// XSendEvent variant
mouseClick(1);

// XTestFakeButtonEvent variant
SendClick(1, true);   // press lmb
SendClick(1, false);  // release lmb
Run Code Online (Sandbox Code Playgroud)

1:帮助我了解我在第一个变体中做错了什么(或者 Chrome 可能有什么问题)。

1.1:当我使用 XOpenDisplay(NULL); 打开显示时,我认为我正在尝试为不需要的窗口发送事件。chrome 与 x11 服务器有不同的连接系统吗?

2:在应用程序中使用第二个变体是个好主意吗?它很短,适用于我拥有的每个应用程序)

PS 要编译此代码,您需要添加 -lX11 -lXtst 库

n. *_* m. 8

XSendEvent产生标记为已发送的事件。服务器发送的事件没有标记。

   typedef struct {
           int type;
           unsigned long serial; 
           Bool send_event;        // <----- here
           Display *display;
           Window window;
   } XAnyEvent;
Run Code Online (Sandbox Code Playgroud)

出于安全原因,某些应用程序会忽略设置了此标志的事件。想想以某种方式访问​​您的 X11 服务器的恶意软件——它可以通过发送这些事件来诱使任何应用程序为所欲为。

在您自己的机器上使用第二个变体是完全可以的,但它依赖于可以禁用的扩展(再次,出于安全原因),因此不一定适用于其他人的 X11 服务器。

  • 谢谢男人。据我所知,chrome 具有反点击保护,您不能使用 XSendEvent,因为 XSendEvent 不仅允许为活动窗口发送点击,而且允许为任何窗口发送点击,因此您可以在非活动窗口中点击,而用户看不到这个)我也我看了xdotool的源代码。_xdo_mousebutton 使用 XTestFakeButtonEvent 如果您在其他情况下将点击发送到 CURRENTWINDOW 和 XSendEvent。 (3认同)