Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XCB get events of all windows X.Org

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?

like image 260
Beneter Avatar asked Feb 04 '15 13:02

Beneter


1 Answers

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.

like image 82
cedlemo Avatar answered Nov 13 '22 15:11

cedlemo