XCB получить события всех окон X.Org
В настоящее время я работаю над тестом латентности для Linux. Для минимизации побочных эффектов я пытаюсь написать C-программу, которая напрямую обращается к X-серверу с помощью XCB. Поскольку не имея никакого опыта в 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
Переменная в корневое окно. Но все, что я пробую, приводит к ошибке сегментации или просто не работает.
Может быть, у ребенка root (моего приложения) недостаточно прав для получения событий своего родителя? Но как это 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);
Как мне изменить вышеуказанный код, чтобы он реагировал на все события BUTTON_PRESS на всем X.Org-сервере?
1 ответ
Поэтому, если вы хотите захватить все события кнопок, у меня есть только решение, но я не знаю, может ли это соответствовать вашим потребностям.
Это как маленький оконный менеджер, здесь 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;
}
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)
Скопируйте эти 4 файла, запустите
Makefile
затем попробуйте это в сеансе оболочки (я объясню почему после):
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 для вашего теста.
Это единственный способ, которым я знаю, чтобы получить все события. Я не эксперт, надеюсь, это поможет вам.