how to quit the blocking of xlib's XNextEvent

18,753

Solution 1

No. This is why most UI frameworks (Gtk, KDE, etc) use custom main loops to be able to listen for more event sources.

Internally, XNextEvent uses a socket, so it calls select() to know when input is available. Call ConnectionNumber(display) to get the file descriptor that you need to pass select()

That allows you to listen for several file descriptors.

Sample code from http://www.linuxquestions.org/questions/showthread.php?p=2431345#post2431345

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

Display *dis;
Window win;
int x11_fd;
fd_set in_fds;

struct timeval tv;
XEvent ev;

int main() {
    dis = XOpenDisplay(NULL);
    win = XCreateSimpleWindow(dis, RootWindow(dis, 0), 1, 1, 256, 256, \
        0, BlackPixel (dis, 0), BlackPixel(dis, 0));

    // You don't need all of these. Make the mask as you normally would.
    XSelectInput(dis, win, 
        ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
        ButtonPressMask | ButtonReleaseMask  | StructureNotifyMask 
        );

    XMapWindow(dis, win);
    XFlush(dis);

    // This returns the FD of the X11 display (or something like that)
    x11_fd = ConnectionNumber(dis);

    // Main loop
    while(1) {
        // Create a File Description Set containing x11_fd
        FD_ZERO(&in_fds);
        FD_SET(x11_fd, &in_fds);

        // Set our timer.  One second sounds good.
        tv.tv_usec = 0;
        tv.tv_sec = 1;

        // Wait for X Event or a Timer
        int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
        if (num_ready_fds > 0)
            printf("Event Received!\n");
        else if (num_ready_fds == 0)
            // Handle timer here
            printf("Timer Fired!\n");
        else
            printf("An error occured!\n");

        // Handle XEvents and flush the input 
        while(XPending(dis))
            XNextEvent(dis, &ev);
    }
    return(0);
}

Solution 2

You can quit the blocking XNextEvent, by sending yourself a dummy event.

Window interClientCommunicationWindow;
Bool x11EventLoopActive = True;

// create a dummy window, that we can use to end the blocking XNextEvent call
interClientCommunicationWindow = XCreateSimpleWindow(dpy, root, 10, 10, 10, 10, 0, 0, 0);
XSelectInput(dpy, interClientCommunicationWindow, StructureNotifyMask);

XEvent event;
while(x11EventLoopActive) {
  XNextEvent(dpy, &event);
  ...
}

In another thread you can do this to end the loop:

x11EventLoopActive = False;
// push a dummy event into the queue so that the event loop has a chance to stop
XClientMessageEvent dummyEvent;
memset(&dummyEvent, 0, sizeof(XClientMessageEvent));
dummyEvent.type = ClientMessage;
dummyEvent.window = interClientCommunicationWindow;
dummyEvent.format = 32;
XSendEvent(dpy, interClientCommunicationWindow, 0, 0, (XEvent*)&dummyEvent);
XFlush(dpy);

Solution 3

You should use : Bool XCheckMaskEvent(Display*, long, XEvent)

The XCheckMaskEvent function first searches the event queue, and then any events available on the server connection for the first event that matches the specified mask.

If it finds a match, XCheckMaskEvent removes that event, copies it into the specified XEvent structure, and returns True. The other events stored in the queue are not discarded.

If the event you requested is not available, XCheckMaskEvent returns False, and the output buffer will have been flushed.

Share:
18,753
Admin
Author by

Admin

Updated on June 03, 2022

Comments

  • Admin
    Admin almost 2 years

    Under windows, the GUI thread usually call GetMessage to waiting for message, when another thread use PoseMessage put a message into the queue, then the GUI thread will return GetMessage (quit blocking).

    Does anyone can tell me, when I use XNextEvent under XWindows to waiting for event, how can I "wakeup" the GUI thread in another thread. Is there some API like PoseMessage I can use ?.

  • David Given
    David Given about 9 years
    Danger, Will Robinson! Xlib is not thread safe! The above code will look like it works, but will in fact crash randomly and infrequently in a very hard-to-debug manner. Don't do it!
  • Noitidart
    Noitidart over 8 years
    Hi Aaron this is an awesome solution. This is only catching events in that window, is it possible to listen to mouse events globally? And possible to block the mouse events with this method?
  • Aaron Digulla
    Aaron Digulla over 8 years
    @Noitidart: Maybe but I can't answer that in a comment. Ask a new question, please and don't forget to give more details what exactly you need to achieve.
  • Noitidart
    Noitidart over 8 years
    Ah thanks so much I posted question here please: stackoverflow.com/questions/32262767/mouse-events-callback
  • Noitidart
    Noitidart over 8 years
    Hi Aaron I've been doing some research and am trying to use XMaskWindow, are you familiar with that? But it's never giving me anything I posted about it here: stackoverflow.com/questions/33717635/…
  • Kevin
    Kevin almost 8 years
    I couldn't get that to work. I was waiting for PointerBarrier events, and unsure of what mask would be appropriate? What did work, was using if (XPending(_display) > 0); then XNextEvent; else continue;
  • Daniel Janus
    Daniel Janus almost 8 years
    The first argument to select() should be x11_fd + 1, not 1. From man 2 select: > nfds is the highest-numbered file descriptor in any of the three sets, plus 1.
  • Beeeaaar
    Beeeaaar over 5 years
    Thanks for this answer. @kevinf This worked for me: while(XCheckMaskEvent(display -1, &event)) { /*do things*/ } Some implementations mask is unsigned, so you may have to cast. On Ubuntu 18 mask is 32 bit signed, so just passing -1 covers any flag.
  • elmindreda
    elmindreda about 5 years
    That will make it thread safe but it will still have a race condition.
  • anil valmiki
    anil valmiki over 4 years
    when I am using above code it waits at select in first loop and not hitting XPending() line, I exit from program when timer fired
  • Aaron Digulla
    Aaron Digulla over 4 years
    @anilvalmiki A timer will cause an interrupt. Interrupts can't be handled with select(). You can't call X11 code while inside an interrupt handler. Ask a new question if you need help to make it work.
  • 12431234123412341234123
    12431234123412341234123 over 3 years
    Prefer pselect() or poll() over select(), since pselect() has better signal handling and can avoid race conditions with signals that can happen with select().
  • Scott Franco
    Scott Franco about 3 years
    Yes, I tried stuffing the queue on an interrupt (signal handler). I can vouch for the fact that it has intermittent failures.