Search code examples
cx11epollunix-socket

Poll X events via file descriptor or socket


Since X is a server, I there a way to listen for X events via a socket instead of using XNextEvent?

I have a program that monitors system events via epoll with epoll_wait called in a loop running in the main thread. Since I haven't been able to figure out how to listen for X events via a file descriptor, I had to resort to running a dedicated loop for XNextEvent in a separate thread and putting mutexes around calls to the functions that are called when events occur.

Here's the function I wrote for polling the X events, if anyone is curious. I'm only listening for events related to keyboard layout switching, at the moment.

void* xevent_loop(void*) {
  int xkbEventType;
  XkbQueryExtension(dpy, 0, &xkbEventType, 0, 0, 0);
  XkbSelectEventDetails(dpy,
    XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask,
    XkbGroupStateMask);
  XSync(dpy, False);

  for (XEvent e;;) {
    XNextEvent(dpy, &e);
    if (e.type == xkbEventType) {
      XkbEvent* xkbe = (XkbEvent*) &e;
      if (xkbe->any.xkb_type == XkbStateNotify) {
        pthread_mutex_lock(&setroot_mutex);
        fmt_kbd_layout(xkbe->state.group);
        setroot();
        pthread_mutex_unlock(&setroot_mutex);
      }
    }
  }
  return NULL;
}

Solution

  • You can get the connection's file descriptor by using the macro ConnectionNumber(dpy) on the display. See man ConnectionNumber:

    The ConnectionNumber macro returns a connection number for the specified display.

    This can be used in select() and poll() to check for an event being ready. The definition is in /usr/include/X11/Xlib.h.