LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   XNextEvent select (https://www.linuxquestions.org/questions/programming-9/xnextevent-select-409355/)

AngryLlama 01-29-2006 01:21 PM

XNextEvent select [solved]
 
Hello, I have a program that uses XNextEvent in my main loop. When an event occurs, I handle it then the program blocks on XNextEvent.

The problem: I want XNextEvent to block, but I want XNextEvent to timeout and unblock after a certain amount of time (say, 0.1sec). I've been trying to figure out how I could accomplish this with the XNextEvent and XPeekEvent family of functions. I'm handling the problem now by creating two threads. One loops on XNextEvent. The other loops on select. I would really like to remove any threads I don't need.

This problem has been solved. Read this post of this thread.

jlliagre 01-30-2006 08:14 AM

use XPending.

AngryLlama 01-30-2006 03:46 PM

Thanks, but I really don't see how I could use XPending to fix this situation. I'm assuming you mean I can check XPending inside of my timer thread. That is, something like:
Code:

while(1) {
  nanosleep(&ts,NULL);
  MyTimerHandler();
  while( XPending(dpy) ) {
    XNextEvent(m_dpy, &xev);
    MyEventHandler(&xev);
  }
}

I suppose this will work, however, my input will only be accepted as fast as my timer's granularity. That is, if I have a 1sec timer, then all my messages will be dispatched in swarms every 1 second. Besides, I don't see how that code differs from the following code (which has the same problem):

Code:

while(1) {
  nanosleep(&ts,NULL);
  MyTimerHandler();
  while( XCheckWindowEvent(dpy, win, ExposureMask|KeyMask..., &xev) ) {
    MyEventHandler(&xev);
  }
}

I would use threads, except X11 behaves horribly (I even tried putting mutexes liberally at possible critical points). My threaded version looked something like this:

Code:

void* XInputThread(void*) {
  XEvent &xev;
  MyEvent &ev;

  while( someCondition ) {
    XNextEvent(dpy, &xev);
    pthread_mutex_lock(&queueMutex);
    MyEventFromXEvent(&ev, &xev)
    queue.push_back(ev);
    pthread_mutex_unlock(&queueMutex);
    sem_post(&queueSem);
  }
}

void TimerSigHandler(int signum) {
  MyEvent &ev;

  pthread_mutex_lock(&queueMutex);
  MyEventFromTime(&ev, timeval);
  queue.push_back(ev);
  pthread_mutex_unlock(&queueMutex); 
  sem_post(&queueSem);
}

int main() {
  ...

  pthread_create( &xinputThread, NULL, XInputThread, NULL );

  memset (&sa, 0, sizeof (sa));
  sa.sa_handler = &TimerSigHandler;
  sigaction (SIGALRM, &sa, NULL);

  timer.it_value.tv_sec = 0;
  timer.it_value.tv_usec = 10000;
  timer.it_interval.tv_sec = 0;
  timer.it_interval.tv_usec = 10000;
  setitimer (ITIMER_REAL, &timer, NULL);

  ...

  while(1) {
    sem_wait(&queueSem);
    pthread_mutex_lock(&queueMutex);
    ev = queue.top();
    queue.pop();
    pthread_mutex_unlock(&queueMutex);

    ProcessTheEvent(&ev);
  }
  ...

}

I tried simplifying these code listings. The third one (the threaded one) causes widget to be half-drawn or corrupt. I'll also get BadIDChoice, X_CreatePixmap, and sync errors from XLib.

The second snippit _works_ but it is sort of hackerish. The threaded model is much cleaner and more responsive, but it just won't work in XLib.

How do people normally solve this problem? Thank you.

jlliagre 01-31-2006 03:00 AM

Quote:

Originally Posted by AngryLlama
Thanks, but I really don't see how I could use XPending to fix this situation. I'm assuming you mean I can check XPending inside of my timer thread. That is, something like:
Code:

while(1) {
  nanosleep(&ts,NULL);
  MyTimerHandler();
  while( XPending(dpy) ) {
    XNextEvent(m_dpy, &xev);
    MyEventHandler(&xev);
  }
}


Correct
Quote:

I suppose this will work, however, my input will only be accepted as fast as my timer's granularity. That is, if I have a 1sec timer, then all my messages will be dispatched in swarms every 1 second. Besides, I don't see how that code differs from the following code (which has the same problem):

Code:

while(1) {
  nanosleep(&ts,NULL);
  MyTimerHandler();
  while( XCheckWindowEvent(dpy, win, ExposureMask|KeyMask..., &xev) ) {
    MyEventHandler(&xev);
  }
}


It mostly doesn't, the former is just simpler, and you didn't mentioned the XCheckWindowEvent call in your initial posting.
Quote:

I would use threads, except X11 behaves horribly (I even tried putting mutexes liberally at possible critical points).
[...]
I tried simplifying these code listings. The third one (the threaded one) causes widget to be half-drawn or corrupt. I'll also get BadIDChoice, X_CreatePixmap, and sync errors from XLib.

The second snippit _works_ but it is sort of hackerish. The threaded model is much cleaner and more responsive, but it just won't work in XLib.

How do people normally solve this problem? Thank you.
X11 is not thread safe, trying to use threads with it is doomed.

You can have multiple threads, but only one of them can use XLib.

Why not lowering your timer granularity to an acceptable value ?

AngryLlama 02-02-2006 04:23 PM

Quote:

Originally Posted by jlliagre
Correct

It mostly doesn't, the former is just simpler, and you didn't mentioned the XCheckWindowEvent call in your initial posting.


X11 is not thread safe, trying to use threads with it is doomed.

You can have multiple threads, but only one of them can use XLib.

Why not lowering your timer granularity to an acceptable value ?

Yeah, I guess that is what I'll have to do. Its just a shame I cannot funnel these two event types into one work queue. Thanks for the help

AngryLlama 06-19-2006 09:38 PM

Sorry to pull this thread from the grave, but I still need a solution. I tried lowering my timer granularity and calling XCheckIfEvent. This method works... but it uses way too much CPU time. I need a way to block on input or a timer. Just like the select() call for the console.

I am trying to accept user input and use a timer to drive some animation. It would be ideal if the time is adjustable.

Thanks

paulsm4 06-19-2006 11:49 PM

Hi -

If many libraries that are higher-level than Xlib support timers. For example, Xt (and consequently any Xt-based GUI, like Motif) has "XtAppAddTimeOut()". Another example is the GTK+ library, which has gtk_timeout_add () (which I use in one of my own animation viewers).

Perhaps you might consider trying a higher-level library, instead of coding directly in Xlib. Or perhaps you can look at one of these implementations and see how they do it (although I doubt you'll find anything terribly useful).

As far as I know, Xlib doesn't provide any way to create "user defined events", so that option's out, too.

Xlib *can*, however, generate an event when you change a property. Perhaps your "timer" thread might be able to call "XChangeProperty()" and generate a usable Xlib event without too much overhead. Just a thought...

Hope that helps (at least a little bit)

Your .. PSM

AngryLlama 06-20-2006 12:32 AM

Quote:

Originally Posted by paulsm4
Hi -

If many libraries that are higher-level than Xlib support timers. For example, Xt (and consequently any Xt-based GUI, like Motif) has "XtAppAddTimeOut()". Another example is the GTK+ library, which has gtk_timeout_add () (which I use in one of my own animation viewers).

Perhaps you might consider trying a higher-level library, instead of coding directly in Xlib. Or perhaps you can look at one of these implementations and see how they do it (although I doubt you'll find anything terribly useful).

As far as I know, Xlib doesn't provide any way to create "user defined events", so that option's out, too.

Xlib *can*, however, generate an event when you change a property. Perhaps your "timer" thread might be able to call "XChangeProperty()" and generate a usable Xlib event without too much overhead. Just a thought...

Hope that helps (at least a little bit)

Your .. PSM

My code is for a custom toolkit that runs under a variety of screens. Xlib is one of the "screens" it supports, but using another widget toolkit is not an option. I will the the ChangeProperty hack and see how well that performs... I'm surprised there is not a better way to do this. It seems like a pretty common thing.

AngryLlama 09-12-2006 05:40 PM

Quote:

Originally Posted by AngryLlama
My code is for a custom toolkit that runs under a variety of screens. Xlib is one of the "screens" it supports, but using another widget toolkit is not an option. I will the the ChangeProperty hack and see how well that performs... I'm surprised there is not a better way to do this. It seems like a pretty common thing.

Using ChangeProperty has even worse results then constantly running a finegrained timer. There has to be some way to do this. How do X apps (and toolkits) allow for animation? I mean, right now I have a caret that blinks while I am able to type. This problem has set my development back for over a month and I still don't have a solution.

AngryLlama 09-20-2006 08:33 PM

I finally came up with a solution to the problem. The answer is simple but not obvious. I never realized that you could obtain a file descriptor to an X11 display. This example I wrote does this and shows how one can make a loop that accepts X Events while allowing a timeout. Creating an interval timer from my example is just as simple.

Code:

#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
        if (select(x11_fd+1, &in_fds, 0, 0, &tv))
            printf("Event Received!\n");
        else
            // Handle timer here
            printf("Timer Fired!\n");

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


AngryLlama 09-20-2006 10:49 PM

And here is the interval timer that will still accept X input.

Code:

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

#ifndef timeradd
# define timeradd(a, b, result)                          \
  do {                                        \
    (result)->tv_sec = (a)->tv_sec + (b)->tv_sec;                \
    (result)->tv_usec = (a)->tv_usec + (b)->tv_usec;                  \
    if ((result)->tv_usec >= 1000000)                        \
      {                                      \
    ++(result)->tv_sec;                          \
    (result)->tv_usec -= 1000000;                        \
      }                                      \
  } while (0)
#endif
#ifndef timersub
 # define timersub(a, b, result)                          \
  do {                                        \
    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                \
    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                  \
    if ((result)->tv_usec < 0) {                          \
      --(result)->tv_sec;                            \
      (result)->tv_usec += 1000000;                      \
    }                                        \
  } while (0)
#endif


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

int main() {
    struct timeval tv;
    struct timeval stv;
    struct timeval tv_period;

    XEvent ev;

    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 connection (or something like that)
    x11_fd = ConnectionNumber(dis);

    // Set Timer Period (this is how you configure the frequency):
    tv_period.tv_sec = 0;
    tv_period.tv_usec = 500000; // 500000us = 500ms = 0.5s


    tv.tv_sec = tv_period.tv_sec;      // Set tv=1 sec so select() will timeout.
    tv.tv_usec = tv_period.tv_usec;
    gettimeofday(&stv, 0);              // Get the time of day and
    timeradd(&stv, &tv_period, &stv);  // Trust my math for now.. :)

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


        // Wait for X Event or a Timer
        if (select(x11_fd+1, &in_fds, 0, 0, &tv)) {
            printf("Event Received!\n");

            gettimeofday(&tv, 0);
            timersub(&stv, &tv, &tv);  // set tv = remaining time.
        }
        else {
            printf("Timer Fired!\n");
            // Initialize timer variables again.
            tv.tv_sec = tv_period.tv_sec;      // Set tv=1 sec so select() will timeout.
            tv.tv_usec = tv_period.tv_usec;
            gettimeofday(&stv, 0);
            timeradd(&stv, &tv_period, &stv);  // Trust my math for now.. :)
        }

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


jlliagre 09-21-2006 12:04 AM

Thanks for the feedback.
I was wondering what the ConnectionNumber call could be used for, now I have a good answer.
By the way, your code won't be portable on OpenVMS.

AngryLlama 09-21-2006 12:45 AM

Quote:

Originally Posted by jlliagre
Thanks for the feedback.
I was wondering what the ConnectionNumber call could be used for, now I have a good answer.
By the way, your code won't be portable on OpenVMS.

Your right about the OpenVMS thing. Doesn't it have some special function for Multiplexed input though? I forget what it is called.. any clues?

EDIT: The function is XMultiplexInput(). I suppose I could do some #ifdefs and make it so the code will run on both. However, if I was using OpenVMS then I wouldn't of had this problem in the first place. :) And for sake of simplicity..


All times are GMT -5. The time now is 03:31 PM.