NextEvent.c revision 7854a16a
1/***********************************************************
2Copyright (c) 1993, Oracle and/or its affiliates.
3
4Permission is hereby granted, free of charge, to any person obtaining a
5copy of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation
7the rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the
9Software is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice (including the next
12paragraph) shall be included in all copies or substantial portions of the
13Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21DEALINGS IN THE SOFTWARE.
22
23Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
24
25                        All Rights Reserved
26
27Permission to use, copy, modify, and distribute this software and its
28documentation for any purpose and without fee is hereby granted,
29provided that the above copyright notice appear in all copies and that
30both that copyright notice and this permission notice appear in
31supporting documentation, and that the name of Digital not be
32used in advertising or publicity pertaining to distribution of the
33software without specific, written prior permission.
34
35DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
41SOFTWARE.
42
43******************************************************************/
44
45/*
46
47Copyright 1987, 1988, 1994, 1998, 2001  The Open Group
48
49Permission to use, copy, modify, distribute, and sell this software and its
50documentation for any purpose is hereby granted without fee, provided that
51the above copyright notice appear in all copies and that both that
52copyright notice and this permission notice appear in supporting
53documentation.
54
55The above copyright notice and this permission notice shall be included in
56all copies or substantial portions of the Software.
57
58THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
61OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
62AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
63CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64
65Except as contained in this notice, the name of The Open Group shall not be
66used in advertising or otherwise to promote the sale, use or other dealings
67in this Software without prior written authorization from The Open Group.
68
69*/
70
71#ifdef HAVE_CONFIG_H
72#include <config.h>
73#endif
74#include "IntrinsicI.h"
75#include <stdio.h>
76#include <errno.h>
77
78#ifdef _WIN32
79typedef long suseconds_t;
80#endif
81
82static TimerEventRec *freeTimerRecs;
83static WorkProcRec *freeWorkRecs;
84static SignalEventRec *freeSignalRecs;
85
86/* Some systems running NTP daemons are known to return strange usec
87 * values from gettimeofday.
88 */
89
90#ifndef NEEDS_NTPD_FIXUP
91#ifdef sun
92#define NEEDS_NTPD_FIXUP 1
93#else
94#define NEEDS_NTPD_FIXUP 0
95#endif
96#endif
97
98#if NEEDS_NTPD_FIXUP
99#define FIXUP_TIMEVAL(t) { \
100        while ((t).tv_usec >= 1000000) { \
101            (t).tv_usec -= 1000000; \
102            (t).tv_sec++; \
103        } \
104        while ((t).tv_usec < 0) { \
105            if ((t).tv_sec > 0) { \
106                (t).tv_usec += 1000000; \
107                (t).tv_sec--; \
108            } else { \
109                (t).tv_usec = 0; \
110                break; \
111            } \
112        }}
113#else
114#define FIXUP_TIMEVAL(t)
115#endif                          /*NEEDS_NTPD_FIXUP */
116
117/*
118 * Private routines
119 */
120#define ADD_TIME(dest, src1, src2) { \
121        if(((dest).tv_usec = (src1).tv_usec + (src2).tv_usec) >= 1000000) {\
122              (dest).tv_usec -= 1000000;\
123              (dest).tv_sec = (src1).tv_sec + (src2).tv_sec + 1 ; \
124        } else { (dest).tv_sec = (src1).tv_sec + (src2).tv_sec ; \
125           if(((dest).tv_sec >= 1) && (((dest).tv_usec <0))) { \
126            (dest).tv_sec --;(dest).tv_usec += 1000000; } } }
127
128#define TIMEDELTA(dest, src1, src2) { \
129        if(((dest).tv_usec = (src1).tv_usec - (src2).tv_usec) < 0) {\
130              (dest).tv_usec += 1000000;\
131              (dest).tv_sec = (src1).tv_sec - (src2).tv_sec - 1;\
132        } else  (dest).tv_sec = (src1).tv_sec - (src2).tv_sec;  }
133
134#define IS_AFTER(t1, t2) (((t2).tv_sec > (t1).tv_sec) \
135        || (((t2).tv_sec == (t1).tv_sec)&& ((t2).tv_usec > (t1).tv_usec)))
136
137#define IS_AT_OR_AFTER(t1, t2) (((t2).tv_sec > (t1).tv_sec) \
138        || (((t2).tv_sec == (t1).tv_sec)&& ((t2).tv_usec >= (t1).tv_usec)))
139
140#ifdef USE_POLL
141#ifndef XT_DEFAULT_FDLIST_SIZE
142#define XT_DEFAULT_FDLIST_SIZE 32
143#endif
144#endif
145
146static void
147AdjustHowLong(unsigned long *howlong, const struct timeval *start_time)
148{
149    struct timeval new_time, time_spent, lstart_time;
150
151    lstart_time = *start_time;
152    X_GETTIMEOFDAY(&new_time);
153    FIXUP_TIMEVAL(new_time);
154    TIMEDELTA(time_spent, new_time, lstart_time);
155    if (*howlong <=
156        (unsigned long) (time_spent.tv_sec * 1000 + time_spent.tv_usec / 1000))
157        *howlong = (unsigned long) 0;   /* Timed out */
158    else
159        *howlong =
160            (*howlong -
161             (unsigned long) (time_spent.tv_sec * 1000 +
162                              time_spent.tv_usec / 1000));
163}
164
165typedef struct {
166    struct timeval cur_time;
167    struct timeval start_time;
168    struct timeval wait_time;
169    struct timeval new_time;
170    struct timeval time_spent;
171    struct timeval max_wait_time;
172#ifdef USE_POLL
173    int poll_wait;
174#else
175    struct timeval *wait_time_ptr;
176#endif
177} wait_times_t, *wait_times_ptr_t;
178
179static struct timeval zero_time = { 0, 0 };
180
181#ifdef USE_POLL
182#define X_BLOCK -1
183#define X_DONT_BLOCK 0
184#else
185static fd_set zero_fd;
186#endif
187
188static void
189InitTimes(Boolean block,
190          unsigned long *howlong,
191          wait_times_ptr_t wt)
192{
193    if (block) {
194        X_GETTIMEOFDAY(&wt->cur_time);
195        FIXUP_TIMEVAL(wt->cur_time);
196        wt->start_time = wt->cur_time;
197        if (howlong == NULL) {  /* special case for ever */
198#ifdef USE_POLL
199            wt->poll_wait = X_BLOCK;
200#else
201            wt->wait_time_ptr = NULL;
202#endif
203        }
204        else {                  /* block until at most */
205            wt->max_wait_time.tv_sec = (time_t) (*howlong / 1000);
206            wt->max_wait_time.tv_usec =
207                (suseconds_t) ((*howlong % 1000) * 1000);
208#ifdef USE_POLL
209            wt->poll_wait = (int) *howlong;
210#else
211            wt->wait_time_ptr = &wt->max_wait_time;
212#endif
213        }
214    }
215    else {                      /* don't block */
216        wt->max_wait_time = zero_time;
217#ifdef USE_POLL
218        wt->poll_wait = X_DONT_BLOCK;
219#else
220        wt->wait_time_ptr = &wt->max_wait_time;
221#endif
222    }
223}
224
225typedef struct {
226#ifdef USE_POLL
227    struct pollfd *fdlist;
228    struct pollfd *stack;
229    int fdlistlen, num_dpys;
230#else
231    fd_set rmask, wmask, emask;
232    int nfds;
233#endif
234} wait_fds_t, *wait_fds_ptr_t;
235
236static void
237InitFds(XtAppContext app,
238        Boolean ignoreEvents,
239        Boolean ignoreInputs,
240        wait_fds_ptr_t wf)
241{
242    int ii;
243
244    app->rebuild_fdlist = FALSE;
245#ifdef USE_POLL
246#ifndef POLLRDNORM
247#define POLLRDNORM 0
248#endif
249
250#ifndef POLLRDBAND
251#define POLLRDBAND 0
252#endif
253
254#ifndef POLLWRNORM
255#define POLLWRNORM 0
256#endif
257
258#ifndef POLLWRBAND
259#define POLLWRBAND 0
260#endif
261
262#define XPOLL_READ (POLLIN|POLLRDNORM|POLLPRI|POLLRDBAND)
263#define XPOLL_WRITE (POLLOUT|POLLWRNORM|POLLWRBAND)
264#define XPOLL_EXCEPT 0
265
266    if (!ignoreEvents)
267        wf->fdlistlen = wf->num_dpys = app->count;
268    else
269        wf->fdlistlen = wf->num_dpys = 0;
270
271    if (!ignoreInputs && app->input_list != NULL) {
272        for (ii = 0; ii < (int) app->input_max; ii++)
273            if (app->input_list[ii] != NULL)
274                wf->fdlistlen++;
275    }
276
277    if (!wf->fdlist || wf->fdlist == wf->stack) {
278        wf->fdlist = (struct pollfd *)
279            XtStackAlloc(sizeof(struct pollfd) * (size_t) wf->fdlistlen,
280                         wf->stack);
281    }
282    else {
283        wf->fdlist = XtReallocArray(wf->fdlist, (Cardinal) wf->fdlistlen,
284                                    (Cardinal) sizeof(struct pollfd));
285    }
286
287    if (wf->fdlistlen) {
288        struct pollfd *fdlp = wf->fdlist;
289        InputEvent *iep;
290
291        if (!ignoreEvents)
292            for (ii = 0; ii < wf->num_dpys; ii++, fdlp++) {
293                fdlp->fd = ConnectionNumber(app->list[ii]);
294                fdlp->events = POLLIN;
295            }
296        if (!ignoreInputs && app->input_list != NULL)
297            for (ii = 0; ii < app->input_max; ii++)
298                if (app->input_list[ii] != NULL) {
299                    iep = app->input_list[ii];
300                    fdlp->fd = ii;
301                    fdlp->events = 0;
302                    for (; iep; iep = iep->ie_next) {
303                        if (iep->ie_condition & XtInputReadMask)
304                            fdlp->events |= XPOLL_READ;
305                        if (iep->ie_condition & XtInputWriteMask)
306                            fdlp->events |= XPOLL_WRITE;
307                        if (iep->ie_condition & XtInputExceptMask)
308                            fdlp->events |= XPOLL_EXCEPT;
309                    }
310                    fdlp++;
311                }
312    }
313#else
314    wf->nfds = app->fds.nfds;
315    if (!ignoreInputs) {
316        wf->rmask = app->fds.rmask;
317        wf->wmask = app->fds.wmask;
318        wf->emask = app->fds.emask;
319    }
320    else
321        wf->rmask = wf->wmask = wf->emask = zero_fd;
322
323    if (!ignoreEvents)
324        for (ii = 0; ii < app->count; ii++) {
325            FD_SET(ConnectionNumber(app->list[ii]), &wf->rmask);
326        }
327#endif
328}
329
330static void
331AdjustTimes(XtAppContext app,
332            Boolean block,
333            const unsigned long *howlong,
334            Boolean ignoreTimers,
335            wait_times_ptr_t wt)
336{
337    if (app->timerQueue != NULL && !ignoreTimers && block) {
338#ifdef USE_POLL
339        if (IS_AFTER(wt->cur_time, app->timerQueue->te_timer_value)) {
340            TIMEDELTA(wt->wait_time, app->timerQueue->te_timer_value,
341                      wt->cur_time);
342            if (howlong == NULL || IS_AFTER(wt->wait_time, wt->max_wait_time))
343                wt->poll_wait =
344                    (int) (wt->wait_time.tv_sec * 1000 +
345                           wt->wait_time.tv_usec / 1000);
346            else
347                wt->poll_wait =
348                    (int) (wt->max_wait_time.tv_sec * 1000 +
349                           wt->max_wait_time.tv_usec / 1000);
350        }
351        else
352            wt->poll_wait = X_DONT_BLOCK;
353#else
354        if (IS_AFTER(wt->cur_time, app->timerQueue->te_timer_value)) {
355            TIMEDELTA(wt->wait_time, app->timerQueue->te_timer_value,
356                      wt->cur_time);
357            if (howlong == NULL || IS_AFTER(wt->wait_time, wt->max_wait_time))
358                wt->wait_time_ptr = &wt->wait_time;
359            else
360                wt->wait_time_ptr = &wt->max_wait_time;
361        }
362        else
363            wt->wait_time_ptr = &zero_time;
364#endif
365    }
366}
367
368static int
369IoWait(wait_times_ptr_t wt, wait_fds_ptr_t wf)
370{
371#ifdef USE_POLL
372    return poll(wf->fdlist, (nfds_t) wf->fdlistlen, wt->poll_wait);
373#else
374    return Select (wf->nfds, &wf->rmask, &wf->wmask, &wf->emask,
375                   wt->wait_time_ptr);
376#endif
377}
378
379static void
380FindInputs(XtAppContext app,
381           wait_fds_ptr_t wf,
382           int nfds _X_UNUSED,
383           Boolean ignoreEvents,
384           Boolean ignoreInputs,
385           int *dpy_no,
386           int *found_input)
387{
388    InputEvent *ep;
389    int ii;
390
391#ifdef USE_POLL                 /* { check ready file descriptors block */
392    struct pollfd *fdlp;
393
394    *dpy_no = -1;
395    *found_input = False;
396
397    if (!ignoreEvents) {
398        fdlp = wf->fdlist;
399        for (ii = 0; ii < wf->num_dpys; ii++, fdlp++) {
400            if (*dpy_no == -1 && fdlp->revents & (POLLIN | POLLHUP | POLLERR) &&
401#ifdef XTHREADS
402                !(fdlp->revents & POLLNVAL) &&
403#endif
404                XEventsQueued(app->list[ii], QueuedAfterReading)) {
405                *dpy_no = ii;
406                break;
407            }
408        }
409    }
410
411    if (!ignoreInputs) {
412        fdlp = &wf->fdlist[wf->num_dpys];
413        for (ii = wf->num_dpys; ii < wf->fdlistlen; ii++, fdlp++) {
414            XtInputMask condition = 0;
415
416            if (fdlp->revents) {
417                if (fdlp->revents & (XPOLL_READ | POLLHUP | POLLERR)
418#ifdef XTHREADS
419                    && !(fdlp->revents & POLLNVAL)
420#endif
421                    )
422                    condition = XtInputReadMask;
423                if (fdlp->revents & XPOLL_WRITE)
424                    condition |= XtInputWriteMask;
425                if (fdlp->revents & XPOLL_EXCEPT)
426                    condition |= XtInputExceptMask;
427            }
428            if (condition) {
429                *found_input = True;
430                for (ep = app->input_list[fdlp->fd]; ep; ep = ep->ie_next)
431                    if (condition & ep->ie_condition) {
432                        InputEvent *oq;
433
434                        /* make sure this input isn't already marked outstanding */
435                        for (oq = app->outstandingQueue; oq; oq = oq->ie_oq)
436                            if (oq == ep)
437                                break;
438                        if (!oq) {
439                            ep->ie_oq = app->outstandingQueue;
440                            app->outstandingQueue = ep;
441                        }
442                    }
443            }
444        }
445    }
446#else                           /* }{ */
447#ifdef XTHREADS
448    fd_set rmask;
449#endif
450    int dd;
451
452    *dpy_no = -1;
453    *found_input = False;
454
455#ifdef XTHREADS
456    rmask = app->fds.rmask;
457    for (dd = app->count; dd-- > 0;)
458        FD_SET(ConnectionNumber(app->list[dd]), &rmask);
459#endif
460
461    for (ii = 0; ii < wf->nfds && nfds > 0; ii++) {
462        XtInputMask condition = 0;
463
464        if (FD_ISSET(ii, &wf->rmask)
465#ifdef XTHREADS
466            && FD_ISSET(ii, &rmask)
467#endif
468            ) {
469            nfds--;
470            if (!ignoreEvents) {
471                for (dd = 0; dd < app->count; dd++) {
472                    if (ii == ConnectionNumber(app->list[dd])) {
473                        if (*dpy_no == -1) {
474                            if (XEventsQueued
475                                (app->list[dd], QueuedAfterReading))
476                                *dpy_no = dd;
477                            /*
478                             * An error event could have arrived
479                             * without any real events, or events
480                             * could have been swallowed by Xlib,
481                             * or the connection may be broken.
482                             * We can't tell the difference, so
483                             * assume Xlib will eventually discover
484                             * a broken connection.
485                             */
486                        }
487                        goto ENDILOOP;
488                    }
489                }
490            }
491            condition = XtInputReadMask;
492        }
493        if (FD_ISSET(ii, &wf->wmask)
494#ifdef XTHREADS
495            && FD_ISSET(ii, &app->fds.wmask)
496#endif
497            ) {
498            condition |= XtInputWriteMask;
499            nfds--;
500        }
501        if (FD_ISSET(ii, &wf->emask)
502#ifdef XTHREADS
503            && FD_ISSET(ii, &app->fds.emask)
504#endif
505            ) {
506            condition |= XtInputExceptMask;
507            nfds--;
508        }
509        if (condition) {
510            for (ep = app->input_list[ii]; ep; ep = ep->ie_next)
511                if (condition & ep->ie_condition) {
512                    /* make sure this input isn't already marked outstanding */
513                    InputEvent *oq;
514
515                    for (oq = app->outstandingQueue; oq; oq = oq->ie_oq)
516                        if (oq == ep)
517                            break;
518                    if (!oq) {
519                        ep->ie_oq = app->outstandingQueue;
520                        app->outstandingQueue = ep;
521                    }
522                }
523            *found_input = True;
524        }
525 ENDILOOP:;
526    }                           /* endfor */
527#endif                          /* } */
528}
529
530/*
531 * Routine to block in the toolkit.  This should be the only call to select.
532 *
533 * This routine returns when there is something to be done.
534 *
535 * Before calling this with ignoreInputs==False, app->outstandingQueue should
536 * be checked; this routine will not verify that an alternate input source
537 * has not already been enqueued.
538 *
539 *
540 * _XtWaitForSomething( appContext,
541 *                      ignoreEvent, ignoreTimers, ignoreInputs, ignoreSignals,
542 *                      block, drop_lock, howlong)
543 * XtAppContext app;         (Displays to check wait on)
544 *
545 * Boolean ignoreEvents;     (Don't return if XEvents are available
546 *                              Also implies forget XEvents exist)
547 *
548 * Boolean ignoreTimers;     (Ditto for timers)
549 *
550 * Boolean ignoreInputs;     (Ditto for input callbacks )
551 *
552 * Boolean ignoreSignals;    (Ditto for signals)
553 *
554 * Boolean block;            (Okay to block)
555 *
556 * Boolean drop_lock         (drop lock before going into select/poll)
557 *
558 * TimeVal howlong;          (howlong to wait for if blocking and not
559 *                              doing Timers... Null means forever.
560 *                              Maybe should mean shortest of both)
561 * Returns display for which input is available, if any
562 * and if ignoreEvents==False, else returns -1
563 *
564 * if ignoring everything && block=True && howlong=NULL, you'll have
565 * lots of time for coffee; better not try it!  In fact, it probably
566 * makes little sense to do this regardless of the value of howlong
567 * (bottom line is, we don't bother checking here).
568 *
569 * If drop_lock is FALSE, the app->lock->mutex is not unlocked before
570 * entering select/poll. It is illegal for drop_lock to be FALSE if
571 * ignoreTimers, ignoreInputs, or ignoreSignals is FALSE.
572 */
573int
574_XtWaitForSomething(XtAppContext app,
575                    _XtBoolean ignoreEvents,
576                    _XtBoolean ignoreTimers,
577                    _XtBoolean ignoreInputs,
578                    _XtBoolean ignoreSignals,
579                    _XtBoolean block,
580                    _XtBoolean drop_lock, /* only needed with XTHREADS */
581                    unsigned long *howlong)
582{
583    wait_times_t wt;
584    wait_fds_t wf;
585    int nfds, dpy_no, found_input, dd;
586
587#ifdef XTHREADS
588    Boolean push_thread = TRUE;
589    Boolean pushed_thread = FALSE;
590    int level = 0;
591#endif
592#ifdef USE_POLL
593    struct pollfd fdlist[XT_DEFAULT_FDLIST_SIZE];
594#endif
595
596#ifdef XTHREADS
597    /* assert ((ignoreTimers && ignoreInputs && ignoreSignals) || drop_lock); */
598    /* If not multi-threaded, never drop lock */
599    if (app->lock == (ThreadAppProc) NULL)
600        drop_lock = FALSE;
601#else
602    (void) drop_lock;           /* avoid unused warning */
603#endif
604
605    InitTimes((Boolean) block, howlong, &wt);
606
607#ifdef USE_POLL
608    wf.fdlist = NULL;
609    wf.stack = fdlist;
610    wf.fdlistlen = wf.num_dpys = 0;
611#endif
612
613 WaitLoop:
614    app->rebuild_fdlist = TRUE;
615
616    while (1) {
617        AdjustTimes(app, (Boolean) block, howlong, (Boolean) ignoreTimers, &wt);
618
619        if (block && app->block_hook_list) {
620            BlockHook hook;
621
622            for (hook = app->block_hook_list; hook != NULL; hook = hook->next)
623                (*hook->proc) (hook->closure);
624
625            if (!ignoreEvents)
626                /* see if the hook(s) generated any protocol */
627                for (dd = 0; dd < app->count; dd++)
628                    if (XEventsQueued(app->list[dd], QueuedAlready)) {
629#ifdef USE_POLL
630                        XtStackFree((XtPointer) wf.fdlist, fdlist);
631#endif
632                        return dd;
633                    }
634        }
635
636        if (app->rebuild_fdlist)
637            InitFds(app, (Boolean) ignoreEvents, (Boolean) ignoreInputs, &wf);
638
639#ifdef XTHREADS                 /* { */
640        if (drop_lock) {
641            YIELD_APP_LOCK(app, &push_thread, &pushed_thread, &level);
642            nfds = IoWait(&wt, &wf);
643            RESTORE_APP_LOCK(app, level, &pushed_thread);
644        }
645        else
646#endif                          /* } */
647            nfds = IoWait(&wt, &wf);
648        if (nfds == -1) {
649            /*
650             *  interrupt occurred recalculate time value and wait again.
651             */
652            if (errno == EINTR || errno == EAGAIN) {
653                if (errno == EAGAIN) {
654                    errno = 0;  /* errno is not self resetting */
655                    continue;
656                }
657                errno = 0;      /* errno is not self resetting */
658
659                /* was it interrupted by a signal that we care about? */
660                if (!ignoreSignals && app->signalQueue != NULL) {
661                    SignalEventRec *se_ptr = app->signalQueue;
662
663                    while (se_ptr != NULL) {
664                        if (se_ptr->se_notice) {
665                            if (block && howlong != NULL)
666                                AdjustHowLong(howlong, &wt.start_time);
667#ifdef USE_POLL
668                            XtStackFree((XtPointer) wf.fdlist, fdlist);
669#endif
670                            return -1;
671                        }
672                        se_ptr = se_ptr->se_next;
673                    }
674                }
675
676                if (!ignoreEvents)
677                    /* get Xlib to detect a bad connection */
678                    for (dd = 0; dd < app->count; dd++)
679                        if (XEventsQueued(app->list[dd], QueuedAfterReading)) {
680#ifdef USE_POLL
681                            XtStackFree((XtPointer) wf.fdlist, fdlist);
682#endif
683                            return dd;
684                        }
685
686                if (block) {
687#ifdef USE_POLL
688                    if (wt.poll_wait == X_BLOCK)
689#else
690                    if (wt.wait_time_ptr == NULL)
691#endif
692                        continue;
693                    X_GETTIMEOFDAY(&wt.new_time);
694                    FIXUP_TIMEVAL(wt.new_time);
695                    TIMEDELTA(wt.time_spent, wt.new_time, wt.cur_time);
696                    wt.cur_time = wt.new_time;
697#ifdef USE_POLL
698                    if ((wt.time_spent.tv_sec * 1000 +
699                         wt.time_spent.tv_usec / 1000) < wt.poll_wait) {
700                        wt.poll_wait -=
701                            (int) (wt.time_spent.tv_sec * 1000 +
702                                   wt.time_spent.tv_usec / 1000);
703                        continue;
704                    }
705                    else
706#else
707                    if (IS_AFTER(wt.time_spent, *wt.wait_time_ptr)) {
708                        TIMEDELTA(wt.wait_time, *wt.wait_time_ptr,
709                                  wt.time_spent);
710                        wt.wait_time_ptr = &wt.wait_time;
711                        continue;
712                    }
713                    else
714#endif
715                        nfds = 0;
716                }
717            }
718            else {
719                char Errno[12];
720                String param = Errno;
721                Cardinal param_count = 1;
722
723                sprintf(Errno, "%d", errno);
724                XtAppWarningMsg(app, "communicationError", "select",
725                                XtCXtToolkitError,
726                                "Select failed; error code %s", &param,
727                                &param_count);
728                continue;
729            }
730        }                       /* timed out or input available */
731        break;
732    }
733
734    if (nfds == 0) {
735        /* Timed out */
736        if (howlong)
737            *howlong = (unsigned long) 0;
738#ifdef USE_POLL
739        XtStackFree((XtPointer) wf.fdlist, fdlist);
740#endif
741        return -1;
742    }
743
744    if (block && howlong != NULL)
745        AdjustHowLong(howlong, &wt.start_time);
746
747    if (ignoreInputs && ignoreEvents) {
748#ifdef USE_POLL
749        XtStackFree((XtPointer) wf.fdlist, fdlist);
750#endif
751        return -1;
752    }
753    else
754        FindInputs(app, &wf, nfds,
755                   (Boolean) ignoreEvents, (Boolean) ignoreInputs,
756                   &dpy_no, &found_input);
757
758    if (dpy_no >= 0 || found_input) {
759#ifdef USE_POLL
760        XtStackFree((XtPointer) wf.fdlist, fdlist);
761#endif
762        return dpy_no;
763    }
764    if (block)
765        goto WaitLoop;
766    else {
767#ifdef USE_POLL
768        XtStackFree((XtPointer) wf.fdlist, fdlist);
769#endif
770        return -1;
771    }
772}
773
774#define IeCallProc(ptr) \
775    (*ptr->ie_proc) (ptr->ie_closure, &ptr->ie_source, (XtInputId*)&ptr);
776
777#define TeCallProc(ptr) \
778    (*ptr->te_proc) (ptr->te_closure, (XtIntervalId*)&ptr);
779
780#define SeCallProc(ptr) \
781    (*ptr->se_proc) (ptr->se_closure, (XtSignalId*)&ptr);
782
783/*
784 * Public Routines
785 */
786
787XtIntervalId
788XtAddTimeOut(unsigned long interval,
789             XtTimerCallbackProc proc,
790             XtPointer closure)
791{
792    return XtAppAddTimeOut(_XtDefaultAppContext(), interval, proc, closure);
793}
794
795static void
796QueueTimerEvent(XtAppContext app, TimerEventRec *ptr)
797{
798    TimerEventRec *t, **tt;
799
800    tt = &app->timerQueue;
801    t = *tt;
802    while (t != NULL && IS_AFTER(t->te_timer_value, ptr->te_timer_value)) {
803        tt = &t->te_next;
804        t = *tt;
805    }
806    ptr->te_next = t;
807    *tt = ptr;
808}
809
810XtIntervalId
811XtAppAddTimeOut(XtAppContext app,
812                unsigned long interval,
813                XtTimerCallbackProc proc,
814                XtPointer closure)
815{
816    TimerEventRec *tptr;
817    struct timeval current_time;
818
819    LOCK_APP(app);
820    LOCK_PROCESS;
821    if (freeTimerRecs) {
822        tptr = freeTimerRecs;
823        freeTimerRecs = tptr->te_next;
824    }
825    else
826        tptr = XtNew(TimerEventRec);
827
828    UNLOCK_PROCESS;
829    tptr->te_next = NULL;
830    tptr->te_closure = closure;
831    tptr->te_proc = proc;
832    tptr->app = app;
833    tptr->te_timer_value.tv_sec = (time_t) (interval / 1000);
834    tptr->te_timer_value.tv_usec = (suseconds_t) ((interval % 1000) * 1000);
835    X_GETTIMEOFDAY(&current_time);
836    FIXUP_TIMEVAL(current_time);
837    ADD_TIME(tptr->te_timer_value, tptr->te_timer_value, current_time);
838    QueueTimerEvent(app, tptr);
839    UNLOCK_APP(app);
840
841    return ((XtIntervalId) tptr);
842}
843
844void
845XtRemoveTimeOut(XtIntervalId id)
846{
847    TimerEventRec *t, *last, *tid = (TimerEventRec *) id;
848    XtAppContext app = tid->app;
849
850    /* find it */
851    LOCK_APP(app);
852    for (t = app->timerQueue, last = NULL;
853         t != NULL && t != tid; t = t->te_next)
854        last = t;
855
856    if (t == NULL) {
857        UNLOCK_APP(app);
858        return;                 /* couldn't find it */
859    }
860    if (last == NULL) {         /* first one on the list */
861        app->timerQueue = t->te_next;
862    }
863    else
864        last->te_next = t->te_next;
865
866    LOCK_PROCESS;
867    t->te_next = freeTimerRecs;
868    freeTimerRecs = t;
869    UNLOCK_PROCESS;
870    UNLOCK_APP(app);
871}
872
873XtWorkProcId
874XtAddWorkProc(XtWorkProc proc, XtPointer closure)
875{
876    return XtAppAddWorkProc(_XtDefaultAppContext(), proc, closure);
877}
878
879XtWorkProcId
880XtAppAddWorkProc(XtAppContext app, XtWorkProc proc, XtPointer closure)
881{
882    WorkProcRec *wptr;
883
884    LOCK_APP(app);
885    LOCK_PROCESS;
886    if (freeWorkRecs) {
887        wptr = freeWorkRecs;
888        freeWorkRecs = wptr->next;
889    }
890    else
891        wptr = XtNew(WorkProcRec);
892
893    UNLOCK_PROCESS;
894    wptr->next = app->workQueue;
895    wptr->closure = closure;
896    wptr->proc = proc;
897    wptr->app = app;
898    app->workQueue = wptr;
899    UNLOCK_APP(app);
900
901    return (XtWorkProcId) wptr;
902}
903
904void
905XtRemoveWorkProc(XtWorkProcId id)
906{
907    WorkProcRec *wid = (WorkProcRec *) id, *w, *last;
908    XtAppContext app = wid->app;
909
910    LOCK_APP(app);
911    /* find it */
912    for (w = app->workQueue, last = NULL; w != NULL && w != wid; w = w->next)
913        last = w;
914
915    if (w == NULL) {
916        UNLOCK_APP(app);
917        return;                 /* couldn't find it */
918    }
919
920    if (last == NULL)
921        app->workQueue = w->next;
922    else
923        last->next = w->next;
924    LOCK_PROCESS;
925    w->next = freeWorkRecs;
926    freeWorkRecs = w;
927    UNLOCK_PROCESS;
928    UNLOCK_APP(app);
929}
930
931XtSignalId
932XtAddSignal(XtSignalCallbackProc proc, XtPointer closure)
933{
934    return XtAppAddSignal(_XtDefaultAppContext(), proc, closure);
935}
936
937XtSignalId
938XtAppAddSignal(XtAppContext app, XtSignalCallbackProc proc, XtPointer closure)
939{
940    SignalEventRec *sptr;
941
942    LOCK_APP(app);
943    LOCK_PROCESS;
944    if (freeSignalRecs) {
945        sptr = freeSignalRecs;
946        freeSignalRecs = sptr->se_next;
947    }
948    else
949        sptr = XtNew(SignalEventRec);
950
951    UNLOCK_PROCESS;
952    sptr->se_next = app->signalQueue;
953    sptr->se_closure = closure;
954    sptr->se_proc = proc;
955    sptr->app = app;
956    sptr->se_notice = FALSE;
957    app->signalQueue = sptr;
958    UNLOCK_APP(app);
959    return (XtSignalId) sptr;
960}
961
962void
963XtRemoveSignal(XtSignalId id)
964{
965    SignalEventRec *sid = (SignalEventRec *) id, *s, *last = NULL;
966    XtAppContext app = sid->app;
967
968    LOCK_APP(app);
969    for (s = app->signalQueue; s != NULL && s != sid; s = s->se_next)
970        last = s;
971    if (s == NULL) {
972        UNLOCK_APP(app);
973        return;
974    }
975    if (last == NULL)
976        app->signalQueue = s->se_next;
977    else
978        last->se_next = s->se_next;
979    LOCK_PROCESS;
980    s->se_next = freeSignalRecs;
981    freeSignalRecs = s;
982    UNLOCK_PROCESS;
983    UNLOCK_APP(app);
984}
985
986void
987XtNoticeSignal(XtSignalId id)
988{
989    /*
990     * It would be overkill to lock the app to set this flag.
991     * In the worst case, 2..n threads would be modifying this
992     * flag. The last one wins. Since signals occur asynchronously
993     * anyway, this can occur with or without threads.
994     *
995     * The other issue is that thread t1 sets the flag in a
996     * signalrec that has been deleted in thread t2. We rely
997     * on a detail of the implementation, i.e. free'd signalrecs
998     * aren't really free'd, they're just moved to a list of
999     * free recs, so deref'ing one won't hurt anything.
1000     *
1001     * Lastly, and perhaps most importantly, since POSIX threads
1002     * says that the handling of asynchronous signals in a synchronous
1003     * threads environment is undefined. Therefore it would be an
1004     * error for both signals and threads to be in use in the same
1005     * program.
1006     */
1007    SignalEventRec *sid = (SignalEventRec *) id;
1008
1009    sid->se_notice = TRUE;
1010}
1011
1012XtInputId
1013XtAddInput(int source,
1014           XtPointer Condition,
1015           XtInputCallbackProc proc,
1016           XtPointer closure)
1017{
1018    return XtAppAddInput(_XtDefaultAppContext(),
1019                         source, Condition, proc, closure);
1020}
1021
1022XtInputId
1023XtAppAddInput(XtAppContext app,
1024              int source,
1025              XtPointer Condition,
1026              XtInputCallbackProc proc,
1027              XtPointer closure)
1028{
1029    InputEvent *sptr;
1030    XtInputMask condition = (XtInputMask) Condition;
1031
1032    LOCK_APP(app);
1033    if (!condition ||
1034        condition & (unsigned
1035                     long) (~(XtInputReadMask | XtInputWriteMask |
1036                              XtInputExceptMask)))
1037        XtAppErrorMsg(app, "invalidParameter", "xtAddInput", XtCXtToolkitError,
1038                      "invalid condition passed to XtAppAddInput", NULL, NULL);
1039
1040    if (app->input_max <= source) {
1041        Cardinal n = (Cardinal) (source + 1);
1042        int ii;
1043
1044        app->input_list = XtReallocArray(app->input_list, n,
1045                                         (Cardinal) sizeof(InputEvent *));
1046        for (ii = app->input_max; ii < (int) n; ii++)
1047            app->input_list[ii] = (InputEvent *) NULL;
1048        app->input_max = (short) n;
1049    }
1050    sptr = XtNew(InputEvent);
1051
1052    sptr->ie_proc = proc;
1053    sptr->ie_closure = closure;
1054    sptr->app = app;
1055    sptr->ie_oq = NULL;
1056    sptr->ie_source = source;
1057    sptr->ie_condition = condition;
1058    sptr->ie_next = app->input_list[source];
1059    app->input_list[source] = sptr;
1060
1061#ifdef USE_POLL
1062    if (sptr->ie_next == NULL)
1063        app->fds.nfds++;
1064#else
1065    if (condition & XtInputReadMask)
1066        FD_SET(source, &app->fds.rmask);
1067    if (condition & XtInputWriteMask)
1068        FD_SET(source, &app->fds.wmask);
1069    if (condition & XtInputExceptMask)
1070        FD_SET(source, &app->fds.emask);
1071
1072    if (app->fds.nfds < (source + 1))
1073        app->fds.nfds = source + 1;
1074#endif
1075    app->input_count++;
1076    app->rebuild_fdlist = TRUE;
1077    UNLOCK_APP(app);
1078    return ((XtInputId) sptr);
1079}
1080
1081void
1082XtRemoveInput(register XtInputId id)
1083{
1084    register InputEvent *sptr, *lptr;
1085    XtAppContext app = ((InputEvent *) id)->app;
1086    register int source = ((InputEvent *) id)->ie_source;
1087    Boolean found = False;
1088
1089    LOCK_APP(app);
1090    sptr = app->outstandingQueue;
1091    lptr = NULL;
1092    for (; sptr != NULL; sptr = sptr->ie_oq) {
1093        if (sptr == (InputEvent *) id) {
1094            if (lptr == NULL)
1095                app->outstandingQueue = sptr->ie_oq;
1096            else
1097                lptr->ie_oq = sptr->ie_oq;
1098        }
1099        lptr = sptr;
1100    }
1101
1102    if (app->input_list && (sptr = app->input_list[source]) != NULL) {
1103        for (lptr = NULL; sptr; sptr = sptr->ie_next) {
1104            if (sptr == (InputEvent *) id) {
1105#ifndef USE_POLL
1106                XtInputMask condition = 0;
1107#endif
1108                if (lptr == NULL) {
1109                    app->input_list[source] = sptr->ie_next;
1110                }
1111                else {
1112                    lptr->ie_next = sptr->ie_next;
1113                }
1114#ifndef USE_POLL
1115                for (lptr = app->input_list[source]; lptr; lptr = lptr->ie_next)
1116                    condition |= lptr->ie_condition;
1117                if ((sptr->ie_condition & XtInputReadMask) &&
1118                    !(condition & XtInputReadMask))
1119                    FD_CLR(source, &app->fds.rmask);
1120                if ((sptr->ie_condition & XtInputWriteMask) &&
1121                    !(condition & XtInputWriteMask))
1122                    FD_CLR(source, &app->fds.wmask);
1123                if ((sptr->ie_condition & XtInputExceptMask) &&
1124                    !(condition & XtInputExceptMask))
1125                    FD_CLR(source, &app->fds.emask);
1126#endif
1127                XtFree((char *) sptr);
1128                found = True;
1129                break;
1130            }
1131            lptr = sptr;
1132        }
1133    }
1134
1135    if (found) {
1136        app->input_count--;
1137#ifdef USE_POLL
1138        if (app->input_list[source] == NULL)
1139            app->fds.nfds--;
1140#endif
1141        app->rebuild_fdlist = TRUE;
1142    }
1143    else
1144        XtAppWarningMsg(app, "invalidProcedure", "inputHandler",
1145                        XtCXtToolkitError,
1146                        "XtRemoveInput: Input handler not found", NULL, NULL);
1147    UNLOCK_APP(app);
1148}
1149
1150void
1151_XtRemoveAllInputs(XtAppContext app)
1152{
1153    int i;
1154
1155    for (i = 0; i < app->input_max; i++) {
1156        InputEvent *ep = app->input_list[i];
1157
1158        while (ep) {
1159            InputEvent *next = ep->ie_next;
1160
1161            XtFree((char *) ep);
1162            ep = next;
1163        }
1164    }
1165    XtFree((char *) app->input_list);
1166}
1167
1168/* Do alternate input and timer callbacks if there are any */
1169
1170static void
1171DoOtherSources(XtAppContext app)
1172{
1173    TimerEventRec *te_ptr;
1174    InputEvent *ie_ptr;
1175    struct timeval cur_time;
1176
1177#define DrainQueue() \
1178        for (ie_ptr = app->outstandingQueue; ie_ptr != NULL;) { \
1179            app->outstandingQueue = ie_ptr->ie_oq;              \
1180            ie_ptr ->ie_oq = NULL;                              \
1181            IeCallProc(ie_ptr);                                 \
1182            ie_ptr = app->outstandingQueue;                     \
1183        }
1184/*enddef*/
1185    DrainQueue();
1186    if (app->input_count > 0) {
1187        /* Call _XtWaitForSomething to get input queued up */
1188        (void) _XtWaitForSomething(app,
1189                                   TRUE, TRUE, FALSE, TRUE,
1190                                   FALSE, TRUE, (unsigned long *) NULL);
1191        DrainQueue();
1192    }
1193    if (app->timerQueue != NULL) {      /* check timeout queue */
1194        X_GETTIMEOFDAY(&cur_time);
1195        FIXUP_TIMEVAL(cur_time);
1196        while (IS_AT_OR_AFTER(app->timerQueue->te_timer_value, cur_time)) {
1197            te_ptr = app->timerQueue;
1198            app->timerQueue = te_ptr->te_next;
1199            te_ptr->te_next = NULL;
1200            if (te_ptr->te_proc != NULL)
1201                TeCallProc(te_ptr);
1202            LOCK_PROCESS;
1203            te_ptr->te_next = freeTimerRecs;
1204            freeTimerRecs = te_ptr;
1205            UNLOCK_PROCESS;
1206            if (app->timerQueue == NULL)
1207                break;
1208        }
1209    }
1210    if (app->signalQueue != NULL) {
1211        SignalEventRec *se_ptr = app->signalQueue;
1212
1213        while (se_ptr != NULL) {
1214            if (se_ptr->se_notice) {
1215                se_ptr->se_notice = FALSE;
1216                if (se_ptr->se_proc != NULL)
1217                    SeCallProc(se_ptr);
1218            }
1219            se_ptr = se_ptr->se_next;
1220        }
1221    }
1222#undef DrainQueue
1223}
1224
1225/* If there are any work procs, call them.  Return whether we did so */
1226
1227static Boolean
1228CallWorkProc(XtAppContext app)
1229{
1230    register WorkProcRec *w = app->workQueue;
1231    Boolean delete;
1232
1233    if (w == NULL)
1234        return FALSE;
1235
1236    app->workQueue = w->next;
1237
1238    delete = (*(w->proc)) (w->closure);
1239
1240    if (delete) {
1241        LOCK_PROCESS;
1242        w->next = freeWorkRecs;
1243        freeWorkRecs = w;
1244        UNLOCK_PROCESS;
1245    }
1246    else {
1247        w->next = app->workQueue;
1248        app->workQueue = w;
1249    }
1250    return TRUE;
1251}
1252
1253/*
1254 * XtNextEvent()
1255 * return next event;
1256 */
1257
1258void
1259XtNextEvent(XEvent *event)
1260{
1261    XtAppNextEvent(_XtDefaultAppContext(), event);
1262}
1263
1264void
1265_XtRefreshMapping(XEvent *event, _XtBoolean dispatch)
1266{
1267    XtPerDisplay pd;
1268
1269    LOCK_PROCESS;
1270    pd = _XtGetPerDisplay(event->xmapping.display);
1271
1272    if (event->xmapping.request != MappingPointer &&
1273        pd && pd->keysyms && (event->xmapping.serial >= pd->keysyms_serial))
1274        _XtBuildKeysymTables(event->xmapping.display, pd);
1275
1276    XRefreshKeyboardMapping(&event->xmapping);
1277    if (dispatch && pd && pd->mapping_callbacks)
1278        XtCallCallbackList((Widget) NULL,
1279                           (XtCallbackList) pd->mapping_callbacks,
1280                           (XtPointer) event);
1281    UNLOCK_PROCESS;
1282}
1283
1284void
1285XtAppNextEvent(XtAppContext app, XEvent *event)
1286{
1287    int i, d;
1288
1289    LOCK_APP(app);
1290    for (;;) {
1291        if (app->count == 0)
1292            DoOtherSources(app);
1293        else {
1294            for (i = 1; i <= app->count; i++) {
1295                d = (i + app->last) % app->count;
1296                if (d == 0)
1297                    DoOtherSources(app);
1298                if (XEventsQueued(app->list[d], QueuedAfterReading))
1299                    goto GotEvent;
1300            }
1301            for (i = 1; i <= app->count; i++) {
1302                d = (i + app->last) % app->count;
1303                if (XEventsQueued(app->list[d], QueuedAfterFlush))
1304                    goto GotEvent;
1305            }
1306        }
1307
1308        /* We're ready to wait...if there is a work proc, call it */
1309        if (CallWorkProc(app))
1310            continue;
1311
1312        d = _XtWaitForSomething(app,
1313                                FALSE, FALSE, FALSE, FALSE,
1314                                TRUE, TRUE, (unsigned long *) NULL);
1315
1316        if (d != -1) {
1317 GotEvent:
1318            XNextEvent(app->list[d], event);
1319            app->last = (short) d;
1320            if (event->xany.type == MappingNotify)
1321                _XtRefreshMapping(event, False);
1322            UNLOCK_APP(app);
1323            return;
1324        }
1325
1326    }                           /* for */
1327}
1328
1329void
1330XtProcessEvent(XtInputMask mask)
1331{
1332    XtAppProcessEvent(_XtDefaultAppContext(), mask);
1333}
1334
1335void
1336XtAppProcessEvent(XtAppContext app, XtInputMask mask)
1337{
1338    int i, d;
1339    XEvent event;
1340    struct timeval cur_time;
1341
1342    LOCK_APP(app);
1343    if (mask == 0) {
1344        UNLOCK_APP(app);
1345        return;
1346    }
1347
1348    for (;;) {
1349
1350        if (mask & XtIMSignal && app->signalQueue != NULL) {
1351            SignalEventRec *se_ptr = app->signalQueue;
1352
1353            while (se_ptr != NULL) {
1354                if (se_ptr->se_notice) {
1355                    se_ptr->se_notice = FALSE;
1356                    SeCallProc(se_ptr);
1357                    UNLOCK_APP(app);
1358                    return;
1359                }
1360                se_ptr = se_ptr->se_next;
1361            }
1362        }
1363
1364        if (mask & XtIMTimer && app->timerQueue != NULL) {
1365            X_GETTIMEOFDAY(&cur_time);
1366            FIXUP_TIMEVAL(cur_time);
1367            if (IS_AT_OR_AFTER(app->timerQueue->te_timer_value, cur_time)) {
1368                TimerEventRec *te_ptr = app->timerQueue;
1369
1370                app->timerQueue = app->timerQueue->te_next;
1371                te_ptr->te_next = NULL;
1372                if (te_ptr->te_proc != NULL)
1373                    TeCallProc(te_ptr);
1374                LOCK_PROCESS;
1375                te_ptr->te_next = freeTimerRecs;
1376                freeTimerRecs = te_ptr;
1377                UNLOCK_PROCESS;
1378                UNLOCK_APP(app);
1379                return;
1380            }
1381        }
1382
1383        if (mask & XtIMAlternateInput) {
1384            if (app->input_count > 0 && app->outstandingQueue == NULL) {
1385                /* Call _XtWaitForSomething to get input queued up */
1386                (void) _XtWaitForSomething(app,
1387                                           TRUE, TRUE, FALSE, TRUE,
1388                                           FALSE, TRUE, (unsigned long *) NULL);
1389            }
1390            if (app->outstandingQueue != NULL) {
1391                InputEvent *ie_ptr = app->outstandingQueue;
1392
1393                app->outstandingQueue = ie_ptr->ie_oq;
1394                ie_ptr->ie_oq = NULL;
1395                IeCallProc(ie_ptr);
1396                UNLOCK_APP(app);
1397                return;
1398            }
1399        }
1400
1401        if (mask & XtIMXEvent) {
1402            for (i = 1; i <= app->count; i++) {
1403                d = (i + app->last) % app->count;
1404                if (XEventsQueued(app->list[d], QueuedAfterReading))
1405                    goto GotEvent;
1406            }
1407            for (i = 1; i <= app->count; i++) {
1408                d = (i + app->last) % app->count;
1409                if (XEventsQueued(app->list[d], QueuedAfterFlush))
1410                    goto GotEvent;
1411            }
1412        }
1413
1414        /* Nothing to do...wait for something */
1415
1416        if (CallWorkProc(app))
1417            continue;
1418
1419        d = _XtWaitForSomething(app,
1420                                ((mask & XtIMXEvent) ? FALSE : TRUE),
1421                                ((mask & XtIMTimer) ? FALSE : TRUE),
1422                                ((mask & XtIMAlternateInput) ? FALSE : TRUE),
1423                                ((mask & XtIMSignal) ? FALSE : TRUE),
1424                                TRUE, TRUE, (unsigned long *) NULL);
1425
1426        if (mask & XtIMXEvent && d != -1) {
1427 GotEvent:
1428            XNextEvent(app->list[d], &event);
1429            app->last = (short) d;
1430            if (event.xany.type == MappingNotify) {
1431                _XtRefreshMapping(&event, False);
1432            }
1433            XtDispatchEvent(&event);
1434            UNLOCK_APP(app);
1435            return;
1436        }
1437
1438    }
1439}
1440
1441Boolean
1442XtPending(void)
1443{
1444    return (XtAppPending(_XtDefaultAppContext()) != 0);
1445}
1446
1447XtInputMask
1448XtAppPending(XtAppContext app)
1449{
1450    struct timeval cur_time;
1451    int d;
1452    XtInputMask ret = 0;
1453
1454/*
1455 * Check for pending X events
1456 */
1457    LOCK_APP(app);
1458    for (d = 0; d < app->count; d++) {
1459        if (XEventsQueued(app->list[d], QueuedAfterReading)) {
1460            ret = XtIMXEvent;
1461            break;
1462        }
1463    }
1464    if (ret == 0) {
1465        for (d = 0; d < app->count; d++) {
1466            if (XEventsQueued(app->list[d], QueuedAfterFlush)) {
1467                ret = XtIMXEvent;
1468                break;
1469            }
1470        }
1471    }
1472
1473    if (app->signalQueue != NULL) {
1474        SignalEventRec *se_ptr = app->signalQueue;
1475
1476        while (se_ptr != NULL) {
1477            if (se_ptr->se_notice) {
1478                ret |= XtIMSignal;
1479                break;
1480            }
1481            se_ptr = se_ptr->se_next;
1482        }
1483    }
1484
1485/*
1486 * Check for pending alternate input
1487 */
1488    if (app->timerQueue != NULL) {      /* check timeout queue */
1489        X_GETTIMEOFDAY(&cur_time);
1490        FIXUP_TIMEVAL(cur_time);
1491        if ((IS_AT_OR_AFTER(app->timerQueue->te_timer_value, cur_time)) &&
1492            (app->timerQueue->te_proc != NULL)) {
1493            ret |= XtIMTimer;
1494        }
1495    }
1496
1497    if (app->outstandingQueue != NULL)
1498        ret |= XtIMAlternateInput;
1499    else {
1500        /* This won't cause a wait, but will enqueue any input */
1501
1502        if (_XtWaitForSomething(app,
1503                                FALSE, TRUE, FALSE, TRUE,
1504                                FALSE, TRUE, (unsigned long *) NULL) != -1)
1505            ret |= XtIMXEvent;
1506        if (app->outstandingQueue != NULL)
1507            ret |= XtIMAlternateInput;
1508    }
1509    UNLOCK_APP(app);
1510    return ret;
1511}
1512
1513/* Peek at alternate input and timer callbacks if there are any */
1514
1515static Boolean
1516PeekOtherSources(XtAppContext app)
1517{
1518    struct timeval cur_time;
1519
1520    if (app->outstandingQueue != NULL)
1521        return TRUE;
1522
1523    if (app->signalQueue != NULL) {
1524        SignalEventRec *se_ptr = app->signalQueue;
1525
1526        while (se_ptr != NULL) {
1527            if (se_ptr->se_notice)
1528                return TRUE;
1529            se_ptr = se_ptr->se_next;
1530        }
1531    }
1532
1533    if (app->input_count > 0) {
1534        /* Call _XtWaitForSomething to get input queued up */
1535        (void) _XtWaitForSomething(app,
1536                                   TRUE, TRUE, FALSE, TRUE,
1537                                   FALSE, TRUE, (unsigned long *) NULL);
1538        if (app->outstandingQueue != NULL)
1539            return TRUE;
1540    }
1541
1542    if (app->timerQueue != NULL) {      /* check timeout queue */
1543        X_GETTIMEOFDAY(&cur_time);
1544        FIXUP_TIMEVAL(cur_time);
1545        if (IS_AT_OR_AFTER(app->timerQueue->te_timer_value, cur_time))
1546            return TRUE;
1547    }
1548
1549    return FALSE;
1550}
1551
1552Boolean
1553XtPeekEvent(XEvent *event)
1554{
1555    return XtAppPeekEvent(_XtDefaultAppContext(), event);
1556}
1557
1558Boolean XtAppPeekEvent_SkipTimer;
1559
1560Boolean
1561XtAppPeekEvent(XtAppContext app, XEvent *event)
1562{
1563    int i, d;
1564    Boolean foundCall = FALSE;
1565
1566    LOCK_APP(app);
1567    for (i = 1; i <= app->count; i++) {
1568        d = (i + app->last) % app->count;
1569        if (d == 0)
1570            foundCall = PeekOtherSources(app);
1571        if (XEventsQueued(app->list[d], QueuedAfterReading))
1572            goto GotEvent;
1573    }
1574    for (i = 1; i <= app->count; i++) {
1575        d = (i + app->last) % app->count;
1576        if (XEventsQueued(app->list[d], QueuedAfterFlush))
1577            goto GotEvent;
1578    }
1579
1580    if (foundCall) {
1581        event->xany.type = 0;
1582        event->xany.display = NULL;
1583
1584        event->xany.window = 0;
1585        UNLOCK_APP(app);
1586        return FALSE;
1587    }
1588
1589    while (1) {
1590        d = _XtWaitForSomething(app,
1591                                FALSE, FALSE, FALSE, FALSE,
1592                                TRUE, TRUE, (unsigned long *) NULL);
1593
1594        if (d != -1) {          /* event */
1595 GotEvent:
1596            XPeekEvent(app->list[d], event);
1597            app->last = (short) ((d == 0 ? app->count : d) - 1);
1598            UNLOCK_APP(app);
1599            return TRUE;
1600        }
1601        else {                  /* input or timer or signal */
1602            /*
1603             * Check to see why a -1 was returned, if a timer expired,
1604             * call it and block some more
1605             */
1606            if ((app->timerQueue != NULL) && !XtAppPeekEvent_SkipTimer) {       /* timer */
1607                struct timeval cur_time;
1608                Bool did_timer = False;
1609
1610                X_GETTIMEOFDAY(&cur_time);
1611                FIXUP_TIMEVAL(cur_time);
1612                while (IS_AT_OR_AFTER
1613                       (app->timerQueue->te_timer_value, cur_time)) {
1614                    TimerEventRec *te_ptr = app->timerQueue;
1615
1616                    app->timerQueue = app->timerQueue->te_next;
1617                    te_ptr->te_next = NULL;
1618                    if (te_ptr->te_proc != NULL) {
1619                        TeCallProc(te_ptr);
1620                        did_timer = True;
1621                    }
1622                    LOCK_PROCESS;
1623                    te_ptr->te_next = freeTimerRecs;
1624                    freeTimerRecs = te_ptr;
1625                    UNLOCK_PROCESS;
1626                    if (app->timerQueue == NULL)
1627                        break;
1628                }
1629                if (did_timer) {
1630                    for (d = 0; d < app->count; d++)
1631                        /* the timer's procedure may have caused an event */
1632                        if (XEventsQueued(app->list[d], QueuedAfterFlush)) {
1633                            goto GotEvent;
1634                        }
1635                    continue;   /* keep blocking */
1636                }
1637            }
1638            /*
1639             * spec is vague here; we'll assume signals also return FALSE,
1640             * of course to determine whether a signal is pending requires
1641             * walking the signalQueue looking for se_notice flags which
1642             * this code doesn't do.
1643             */
1644#if 0
1645            if (app->signalQueue != NULL) {     /* signal */
1646                event->xany.type = 0;
1647                event->xany.display = NULL;
1648
1649                event->xany.window = 0;
1650                UNLOCK_APP(app);
1651                return FALSE;
1652            }
1653            else
1654#endif
1655            {                   /* input */
1656                event->xany.type = 0;
1657                event->xany.display = NULL;
1658
1659                event->xany.window = 0;
1660                UNLOCK_APP(app);
1661                return FALSE;
1662            }
1663        }
1664    }                           /* end while */
1665}
1666