XCB get events of all windows X.Org
Asked Answered
U

1

7

I'm currently working on a latency test for Linux. For minimizing side effects I try to write a C-program which directly accesses the X-Server with XCB. Because not having any experience in C, but only in Java, nor in XCB, I ran into a few difficulties.

Everything the application should do is to show a white frame and if the button of the mouse is pressed at any time (outside the window), it should change to black in an instant. The test-application doesn't have to be beautiful or safe in any way but only react fast. It is just used for this one test (please don't judge my crappy style ;-) ).

The mouse can not be in the same window, because there is another independent application which also needs to process the event (the one measuring the latency).


After reading the XCB tutorial I have modified the sample code to open a window and log the mouse clicks inside the window:

#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;
}

For getting the events of all windows, I guess I have to change the window variable to the root window. But anything I try produces a Segmentation Fault, or simply does not work.

Maybe a child of root (my application) does not have enough rights for getting events of it's parent? But how is xwininfo -root working then? Best attempt:

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

How do I have to change the above code to react to all BUTTON_PRESS events on the whole X.Org-Server?

Unconsidered answered 4/2, 2015 at 13:18 Comment(2)
I think you can access /dev/input/* directly (using ioctl) to get that info for every window if you have root access. I was able to open an evevt* file, and read keyboard presses. Possibly helpful reference for mouseChapter
I think you have to use the Xinput2 extension. That is what xinput --test-xi2 --root does, but for some reason only one instance of xinput may be running. Does this help?Cogwheel
H
12

So if you want to capture all the buttons events, I have just a solution but I don't know if this could fit your needs.

Its like a little window manager here is 4 files:

  • 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;
}

events.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;
        }
}

events.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)

Copy those 4 files, run

Makefile

then try this in a shell session (I will explain why after):

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

In order to get all the events to the root window, you have to configure the root window like if your application is a window manager. But it can be only one window manager that is the reason why simple_window_manager cannot be run under gnome or kde for example. You have to use Xephyr for your test.

That is the only way I know to get all the events. I am not an expert, I hope this can help you.

Haze answered 5/2, 2015 at 18:17 Comment(4)
Wow very deep answer. I think this might be the solution to a problem I'm having. May you please take a look at my topic and see if this solution you posted here fits me? Thank you so much my question is here - #35906743Opaline
The reason I ask is because when I tried your code I keep getting a non-null value for xcb_poll_for_event telling me error "another window manager is already running".Opaline
I have tested it just 2 minutes ago to be sure but no everything went fine for me, the compilation and the execution. Did you follow the instruction,s I meen using Xephyr to create a new display ":1" and launch the simple_window_manager on it ?Haze
Ah thank you very much, I realize now this is not my solution, I'm not needing to createa a new display, I'm just trying to get the events of the current display. :( Thanks for your reply in my other topic Ill reply there as well :)Opaline

© 2022 - 2024 — McMap. All rights reserved.