'Non blocking event handling in X11 without swallowing event

Sorry for the poor title, it wasn't easy to describe it better. I am learning C and as a toy project I am building a simple status bar for my window manager. Basically it just outputs text to the root X11 window name, which gets picked up by the wm (DWM).

Now I wanted to add simple click detected, so that I can do something when a specific area is clicked. The polling of the mouse happens in a while loop; and the update of the status bar also lives in the while loop, but with a different (less frequent) interval. Everything works, except that no other X11 applications are receiving the mouse clicks I listen to in my while loop.

I used XNextEvent first but found out that was blocking the while loop if the mouse was idle. Then I learned about XCheckWindowEvent which returns a boolean if the event I want to poll matches, then I can handle it. But the loop will not pause when the mouse is idle (which is most of the time).

However since doing so, whenever I move the mouse, I cannot click on any other window anymore. The cursor moves just fine, but clicks do not seem to work.

Am I making any sense?

This is the listing of my program (slightly simplified as for the status setting part, since that probably will distract).

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <wchar.h>
#include <unistd.h>
#include <fcntl.h>
#include <X11/Xlib.h>

#define EVTMASK (ButtonPressMask | PointerMotionMask)

void set_status(Display *display, Window window, char *str);
void process_mouse(Display *display, Window window, XEvent xevent);

char *key_name[] = {
    "first",
    "second (or middle)",
    "third"
};

int 
main(void) 
{
  const int MSIZE = 1024;
  Display *display;
  XEvent xevent;
  Window window;
  char *status;
  char *bg_color    = "#000000";
  char *clr_yellow  = "#ecbe7b";
  time_t previousTime = time(NULL);
  time_t interval_status = 1;
  time_t currentTime;

  if (!(display = XOpenDisplay(NULL))) {
    fprintf(stderr, "Cannot open display.\n");
    return EXIT_FAILURE;
  }

  // Setup X11 for mouse grabbing
  window = DefaultRootWindow(display);
  XAllowEvents(display, AsyncBoth, CurrentTime);

  XGrabPointer(display, 
      window,
      1, 
      PointerMotionMask | ButtonPressMask | ButtonReleaseMask , 
      GrabModeAsync,
      GrabModeAsync, 
      None,
      None,
      CurrentTime);

  status = (char*) malloc(sizeof(char)*MSIZE);
  if(!status)
    return EXIT_FAILURE;

  while(1)
  { 
    process_mouse(display, window, xevent);
    if((time(&currentTime) - previousTime) >= interval_status)
    {
      int ret = snprintf(status, MSIZE, "^b%s^^c%s^%s Status me!", bg_color, clr_yellow);
      set_status(display, window, status);
      previousTime += interval_status;
    }
  }

  return 0;
}

void 
set_status(Display *display, Window window, char *str) 
{
  XStoreName(display, window, str);
  XSync(display, False);
}

void
process_mouse(Display *display, Window window, XEvent xevent)
{
  if(XCheckWindowEvent(display, window, EVTMASK, &xevent)) {
    switch (xevent.type) {
      case MotionNotify:
        printf("Mouse move      : [%d, %d]\n", xevent.xmotion.x_root, xevent.xmotion.y_root);
        break;
      case ButtonPress:
        printf("Button pressed  : %s\n", key_name[xevent.xbutton.button - 1]);
        break;
      case ButtonRelease:
        printf("Button released : %s\n", key_name[xevent.xbutton.button - 1]);
        break;
    }
    XPutBackEvent(display, &xevent);
  }
}

Open question: should I go back to the NextEvent method and listen to the mouse on a different thread?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source