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