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