获取所有窗口的X.Org XCB事件

7
我目前正在对Linux进行延迟测试。为了最小化副作用,我尝试编写一个直接使用XCB访问X-Server的C程序。 但是由于我没有C语言方面的经验,只有Java和XCB经验,因此遇到了一些困难。
应用程序需要做的就是显示一个白色框架,并且如果在任何时候按下鼠标按钮(窗口外),它应该立即变成黑色。测试应用程序不必美观或安全,只需快速响应。它仅用于此一次测试(请别嫌我的拙劣风格;-))。
另外,鼠标不能在同一个窗口内,因为还有另一个独立应用程序需要处理事件(测量延迟)。
阅读完XCB教程后,我已修改示例代码以打开一个窗口并记录窗口内的鼠标点击事件:
#include <stdio.h>
#include <xcb/xcb.h>

main ()
{
    /* Open the connection to the X server */
    xcb_connection_t *connection = xcb_connect (NULL, NULL);

    /* Get the first screen */
    xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;

    /* Create black (foreground) graphic context */
    xcb_drawable_t  window;
    uint32_t        mask;
    uint32_t        values[2];

    /* Create a window */
    window = xcb_generate_id (connection);

    mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
    values[0] = screen->white_pixel;
    values[1] = XCB_EVENT_MASK_BUTTON_PRESS;

    xcb_create_window (connection, XCB_COPY_FROM_PARENT, window, screen->root, 0, 0, 500, 500, 10, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values );

    /* Map the window on the screen and flush*/
    xcb_map_window (connection, window);
    xcb_flush (connection);

    /* Get XCB_EVENT_MASK_BUTTON_PRESS event */
    xcb_generic_event_t *event;
    while ((event = xcb_wait_for_event (connection))) {
        switch (event ->response_type & ~0x80) {
        case XCB_EVENT_MASK_BUTTON_PRESS:
            printf("Button pressed!\n");
            break;
        default: 
            /* Unknown event type */
            printf("Unknown event!\n");
            break;
        }
        /* free (event); */
    }
    return 0;
}

为了获取所有窗口的事件,我猜我必须将变量window更改为根窗口。但是我尝试的任何事情都会产生分段错误或根本无法工作。也许根窗口的子窗口(我的应用程序)没有足够的权限来获取其父窗口的事件?但是xwininfo -root是如何工作的呢?最好的尝试:
xcb_connection_t *connection = xcb_connect (NULL, NULL);
xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;
xcb_drawable_t window = screen->root; /* !!! */
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
uint32_t values[2];
values[0] = screen->white_pixel;
values[1] = XCB_EVENT_MASK_BUTTON_PRESS;
xcb_change_window_attributes (connection, window, mask, values); /* !!! */
xcb_map_window (connection, window);
xcb_flush (connection);

我应该如何更改上面的代码以响应整个 X.Org-Server 上的所有 BUTTON_PRESS 事件?


我认为如果您拥有root访问权限,可以直接访问/dev/input/*(使用ioctl)以获取每个窗口的信息。我能够打开一个event*文件并读取键盘按键。鼠标可能有用的参考资料 - SO_fix_the_vote_sorting_bug
我认为你必须使用Xinput2扩展。这就是xinput --test-xi2 --root所做的事情,但由于某种原因只能运行一个xinput实例。这个链接有帮助吗? - jarno
1个回答

12

如果您想捕获所有按钮事件,我有一个解决方案,但我不知道这是否适合您的需求。

它就像一个小窗口管理器,以下是4个文件:

  • simple_window_manager.c
  • events.c
  • events.h
  • Makefile

simple_window_manager.c

#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_aux.h>
#include <stdio.h>
#include <stdlib.h>
#include "events.h"

xcb_connection_t * connection;

int main (int argc, char **argv)
{
  xcb_screen_t      *screen;

  /*open connection and check for error*/
  connection = xcb_connect( NULL, NULL);

  printf("launch connection");

    if (xcb_connection_has_error(connection))
  {
    perror("cannot open display\n");
    exit(1);
  }

  /*get first screen*/
  screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;

  /*define the application as window manager*/
    const uint32_t select_input_val[] =
    {
        XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
        | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW
        | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE
        | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
        | XCB_EVENT_MASK_FOCUS_CHANGE
    };
    xcb_change_window_attributes(connection,
                                 screen->root,
                                 XCB_CW_EVENT_MASK, select_input_val);
    /* Need to xcb_flush to validate error handler */
  xcb_aux_sync(connection);

    if (xcb_poll_for_event(connection) != NULL)
    {
        perror("another window manager is already running");
        exit(1);
    };

  /*flush all request*/
  xcb_flush(connection);

  xcb_generic_event_t *the_events;
  int done; 

    /*enter the main loop*/
  done = 0;
  while (!done && (the_events = xcb_wait_for_event(connection)))
  {
    switch(the_events->response_type)
    {
      /*(re)draw the window*/
      case XCB_EXPOSE:
        printf ("EXPOSE\n");
        break;
      /*exit on keypress*/
      case XCB_KEY_PRESS:
        done = 1;
        break;
      default:
        event_management(the_events);
        printf("The events = %s\n",xcb_event_get_label(the_events->response_type));
    }
    free(the_events);
  }
  /*close connection to server*/
  xcb_disconnect(connection);
  return 0;
}

事件处理程序.c

#include <stdio.h>
#include <stdlib.h>
#include <xcb/xcb.h>
#include <xcb/xcb_util.h>
#include "events.h"


static void button_press_management(xcb_button_press_event_t * event)
{
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void button_release_management(xcb_button_release_event_t * event)
{
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void configure_request_management(xcb_configure_request_event_t * event)
{
    uint16_t config_win_mask = 0;
  uint32_t config_win_vals[7];
  unsigned short i = 0;

if(event->value_mask & XCB_CONFIG_WINDOW_X)
    {
        config_win_mask |= XCB_CONFIG_WINDOW_X;
        config_win_vals[i++] = 300;
        printf(" XCB_CONFIG_WINDOW_X\n");
    }
    if(event->value_mask & XCB_CONFIG_WINDOW_Y)
    {
        config_win_mask |= XCB_CONFIG_WINDOW_Y;
        config_win_vals[i++] = 300;
        printf(" XCB_CONFIG_WINDOW_Y\n");
    }
    if(event->value_mask & XCB_CONFIG_WINDOW_WIDTH)
    {
        config_win_mask |= XCB_CONFIG_WINDOW_WIDTH;
        config_win_vals[i++] = event->width;
        printf(" XCB_CONFIG_WINDOW_WIDTH\n");
    }
    if(event->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
    {
        config_win_mask |= XCB_CONFIG_WINDOW_HEIGHT;
        config_win_vals[i++] = event->height;
        printf("XCB_CONFIG_WINDOW_HEIGHT");
    }
    if(event->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
    {
        config_win_mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
        config_win_vals[i++] = event->border_width;
        printf(" XCB_CONFIG_WINDOW_BORDER_WIDTH\n");
    }
    if(event->value_mask & XCB_CONFIG_WINDOW_SIBLING)
    {
        config_win_mask |= XCB_CONFIG_WINDOW_SIBLING;
        config_win_vals[i++] = event->sibling;
        printf(" XCB_CONFIG_WINDOW_SIBLING\n");
    }
    if(event->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
    {
        config_win_mask |= XCB_CONFIG_WINDOW_STACK_MODE;
        config_win_vals[i++] = event->stack_mode;
        printf(" XCB_CONFIG_WINDOW_STACK_MODE\n");
    }

  xcb_configure_window(connection, event->window, config_win_mask, config_win_vals);
  xcb_flush(connection);
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void client_message_management(xcb_client_message_event_t * event)
{
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void expose_management(xcb_expose_event_t *event)
{
        printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void focus_in_management(xcb_focus_in_event_t *event)
{
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void key_press_management(xcb_key_press_event_t *event)
{

    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void key_release_management(xcb_key_release_event_t *event)
{
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void motion_notify_management(xcb_motion_notify_event_t * event)
{
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void map_request_management(xcb_map_request_event_t * event)
{
  xcb_map_window(connection, event->window);
  xcb_flush(connection);
  xcb_grab_button(connection,0, event->window,XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_ANY, XCB_MOD_MASK_ANY);
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void mapping_notify_management(xcb_motion_notify_event_t * event)
{
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void reparent_notify_management(xcb_reparent_notify_event_t * event)
{

    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void unmap_notify_management(xcb_unmap_notify_event_t * event)
{

    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void enter_notify_management(xcb_enter_notify_event_t * event)
{
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}
static void leave_notify_management(xcb_leave_notify_event_t * event)
{
    printf("event = %s\n",xcb_event_get_label(event->response_type));   
}

void event_management(xcb_generic_event_t *event)
{   
    uint8_t response_type = XCB_EVENT_RESPONSE_TYPE(event);

    if(response_type == 0)
        {
      /* This is an error, not a event */
      perror("response_type = 0");
      return;
      }

    switch(response_type)
        {
            case XCB_BUTTON_PRESS:
                button_press_management((void *) event);
                break;
            case XCB_BUTTON_RELEASE:
                button_release_management((void *)event);
                break;
            case XCB_CONFIGURE_REQUEST:
                configure_request_management((void *)event);
                break;
            case XCB_CLIENT_MESSAGE:
                client_message_management((void *)event);
                break;
            case XCB_EXPOSE:
                expose_management((void *)event);
                break;
            case XCB_FOCUS_IN:
                focus_in_management((void *)event);
                break;
            case XCB_KEY_PRESS:
                key_press_management((void *)event);
                break;
            case XCB_KEY_RELEASE:
                key_release_management((void *)event);
                break;
            case XCB_MAP_REQUEST:
                map_request_management((void *)event);
                break;
            case XCB_MAPPING_NOTIFY:
                mapping_notify_management((void *)event);
                break;case XCB_MOTION_NOTIFY:
                motion_notify_management((void *)event);
                break;
            case XCB_REPARENT_NOTIFY:
                reparent_notify_management((void *)event);
                break;
            case XCB_UNMAP_NOTIFY:
                unmap_notify_management((void *)event);
                break;
            case XCB_ENTER_NOTIFY:
                enter_notify_management((void *)event);
                break;
            case XCB_LEAVE_NOTIFY:
                leave_notify_management((void *)event);
                break;
            default:
                printf("event = %s\n",xcb_event_get_label(event->response_type));   
                printf("%d\n",response_type);
                perror("this kind of event is not managed\n");
                break;
        }
}

事件.h

#include <xcb/xcb.h>
extern xcb_connection_t * connection;
void event_management(xcb_generic_event_t *);

Makefile

CC = gcc 
CFLAGS = -Wall
EXEC_NAME = simple_window_manager 
INCLUDES = 
LIBS =-lxcb -lxcb-util
OBJ_FILES = simple_window_manager.o events.o
INSTALL_DIR = ./my_exec/

all : $(EXEC_NAME)
clean :
    rm $(EXEC_NAME) $(OBJ_FILES)

$(EXEC_NAME) : $(OBJ_FILES)
    $(CC) -o $(EXEC_NAME) $(OBJ_FILES) $(LIBS)

%.o: %.c
    $(CC) $(CFLAGS) $(INCLUDES) -o $@ -c $<

install :
    cp $(EXEC_NAME) $(INSTALL_DIR)$(EXEC_NAME)

复制这4个文件,运行

Makefile

那么请在shell会话中尝试以下操作(稍后我将解释原因):

Xephyr -br -noreset -screen "1024x640" :1&
DISPLAY=:1.0 ./simple_window_manager&
DISPLAY=:1.0 gnome-calculator #for example

为了让所有事件传递到根窗口,您需要将根窗口配置成仿佛您的应用程序是一个窗口管理器。但是,只能有一个窗口管理器,这就是simple_window_manager不能在gnome或kde下运行的原因。您需要使用Xephyr进行测试。

这是我所知道的唯一方法来获取所有事件。我不是专家,希望这可以帮到您。


哇,非常深入的回答。我认为这可能是我遇到问题的解决方案。您能否看一下我的主题,看看您在这里发布的解决方案是否适合我?非常感谢,我的问题在这里 - https://dev59.com/BZTfa4cB1Zd3GeqPXeuS - Noitidart
我询问的原因是因为当我尝试运行您的代码时,xcb_poll_for_event 返回一个非空值,告诉我“另一个窗口管理器已经在运行”错误。 - Noitidart
1
我刚刚测试了一下,确保编译和执行都没有问题。你是否按照指示使用 Xephyr 创建了一个新的显示器“:1”,并在其上启动了 simple_window_manager? - cedlemo
非常感谢,我现在意识到这不是我的解决方案,我不需要创建一个新的显示,我只是想获取当前显示器的事件。:( 感谢您在我的其他主题中的回复,我也会在那里回复 :) - Noitidart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接