Skip to content

Instantly share code, notes, and snippets.

@vadz
Last active September 6, 2024 16:13
Show Gist options
  • Save vadz/2a5735efb0156c89e9ad43448f8ac2ce to your computer and use it in GitHub Desktop.
Save vadz/2a5735efb0156c89e9ad43448f8ac2ce to your computer and use it in GitHub Desktop.
Simple shared library to debug XCB events received by the application
// Compile with
//
// $ cc -Wall -shared -fPIC -o libxcb_debug.so xcb_debug.c `pkg-config --cflags --libs xcb`
//
// Use with
//
// $ LD_PRELOAD=libxcb_debug.so any_program_using_xcb
#define _GNU_SOURCE
#include <dlfcn.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <xcb/xcb.h>
__attribute__((constructor))
void init(void)
{
fprintf(stderr, "libxcb_debug.so loaded into %d\n", getpid());
}
__attribute__((constructor))
void fini(void)
{
fprintf(stderr, "\nlibxcb_debug.so unloaded from %d\n", getpid());
}
static const char *event_names[] = {
NULL,
NULL,
"KEY_PRESS",
"KEY_RELEASE",
"BUTTON_PRESS",
"BUTTON_RELEASE",
"MOTION_NOTIFY",
"ENTER_NOTIFY",
"LEAVE_NOTIFY",
"FOCUS_IN",
"FOCUS_OUT",
"KEYMAP_NOTIFY",
"EXPOSE",
"GRAPHICS_EXPOSURE",
"NO_EXPOSURE",
"VISIBILITY_NOTIFY",
"CREATE_NOTIFY",
"DESTROY_NOTIFY",
"UNMAP_NOTIFY",
"MAP_NOTIFY",
"MAP_REQUEST",
"REPARENT_NOTIFY",
"CONFIGURE_NOTIFY",
"CONFIGURE_REQUEST",
"GRAVITY_NOTIFY",
"RESIZE_REQUEST",
"CIRCULATE_NOTIFY",
"CIRCULATE_REQUEST",
"PROPERTY_NOTIFY",
"SELECTION_CLEAR",
"SELECTION_REQUEST",
"SELECTION_NOTIFY",
"COLORMAP_NOTIFY",
"CLIENT_MESSAGE",
"MAPPING_NOTIFY",
"GE_GENERIC",
};
static xcb_generic_event_t *debug_event(void *ptr)
{
xcb_generic_event_t *e = (xcb_generic_event_t *)ptr;
if (!e)
return NULL;
static int counter = 0;
char buf[32];
static char last_buf[32];
uint8_t response_type = e->response_type & ~0x80;
switch (response_type) {
case XCB_KEY_PRESS:
case XCB_KEY_RELEASE: {
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e;
snprintf(buf, sizeof(buf), "KEY_%s: %x",
response_type == XCB_KEY_PRESS ? "PRESS" : "RELEASE",
ev->detail
);
break;
}
case XCB_BUTTON_PRESS:
case XCB_BUTTON_RELEASE: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)e;
snprintf(buf, sizeof(buf), "BUTTON_%s: %x",
response_type == XCB_BUTTON_PRESS ? "PRESS" : "RELEASE",
ev->detail
);
break;
}
default: {
const char *p = NULL;
if (response_type < sizeof(event_names) / sizeof(event_names[0]))
p = event_names[response_type];
if (p)
strncpy(buf, p, sizeof(buf));
else
snprintf(buf, sizeof(buf), "unknown (%u)", response_type);
break;
}
}
if (strcmp(buf, last_buf) != 0) {
fprintf(stderr,
"\n[%06u] %s%s",
counter,
buf,
e->response_type & 0x80 ? " (sent)" : ""
);
strncpy(last_buf, buf, sizeof(last_buf));
} else {
fprintf(stderr, ".");
}
counter++;
return e;
}
xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c)
{
typedef xcb_generic_event_t *(*xcb_wait_for_event_t)(xcb_connection_t *);
static xcb_wait_for_event_t orig_xcb_wait_for_event = NULL;
if (!orig_xcb_wait_for_event) {
orig_xcb_wait_for_event = (xcb_wait_for_event_t)dlsym(RTLD_NEXT, "xcb_wait_for_event");
if (!orig_xcb_wait_for_event) {
fprintf(stderr, "Failed to get xcb_wait_for_event(): %s\n", dlerror());
exit(127);
}
}
return debug_event(orig_xcb_wait_for_event(c));
}
xcb_generic_event_t *xcb_poll_for_event(xcb_connection_t *c)
{
typedef xcb_generic_event_t *(*xcb_poll_for_event_t)(xcb_connection_t *);
static xcb_poll_for_event_t orig_xcb_poll_for_event = NULL;
if (!orig_xcb_poll_for_event) {
orig_xcb_poll_for_event = (xcb_poll_for_event_t)dlsym(RTLD_NEXT, "xcb_poll_for_event");
if (!orig_xcb_poll_for_event) {
fprintf(stderr, "Failed to get xcb_poll_for_event(): %s\n", dlerror());
exit(127);
}
}
return debug_event(orig_xcb_poll_for_event(c));
}
xcb_generic_event_t *xcb_poll_for_queued_event(xcb_connection_t *c)
{
typedef xcb_generic_event_t *(*xcb_poll_for_queued_event_t)(xcb_connection_t *);
static xcb_poll_for_queued_event_t orig_xcb_poll_for_queued_event = NULL;
if (!orig_xcb_poll_for_queued_event) {
orig_xcb_poll_for_queued_event = (xcb_poll_for_queued_event_t)dlsym(RTLD_NEXT, "xcb_poll_for_queued_event");
if (!orig_xcb_poll_for_queued_event) {
fprintf(stderr, "Failed to get xcb_poll_for_queued_event(): %s\n", dlerror());
exit(127);
}
}
return debug_event(orig_xcb_poll_for_queued_event(c));
}
void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e)
{
typedef xcb_generic_event_t *(*xcb_wait_for_reply_t)(xcb_connection_t *, unsigned int, xcb_generic_error_t **);
static xcb_wait_for_reply_t orig_xcb_wait_for_reply = NULL;
if (!orig_xcb_wait_for_reply) {
orig_xcb_wait_for_reply = (xcb_wait_for_reply_t)dlsym(RTLD_NEXT, "xcb_wait_for_reply");
if (!orig_xcb_wait_for_reply) {
fprintf(stderr, "Failed to get xcb_wait_for_reply(): %s\n", dlerror());
exit(127);
}
}
return debug_event(orig_xcb_wait_for_reply(c, request, e));
}
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error)
{
typedef int (*xcb_poll_for_reply_t)(xcb_connection_t *, unsigned int, void **, xcb_generic_error_t **);
static xcb_poll_for_reply_t orig_xcb_poll_for_reply = NULL;
if (!orig_xcb_poll_for_reply) {
orig_xcb_poll_for_reply = (xcb_poll_for_reply_t)dlsym(RTLD_NEXT, "xcb_poll_for_reply");
if (!orig_xcb_poll_for_reply) {
fprintf(stderr, "Failed to get xcb_poll_for_reply(): %s\n", dlerror());
exit(127);
}
}
int rc = orig_xcb_poll_for_reply(c, request, reply, error);
debug_event(*reply);
return rc;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment