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