WaitFor.c revision 706f2543
1/***********************************************************
2
3Copyright 1987, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25
26Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
27
28                        All Rights Reserved
29
30Permission to use, copy, modify, and distribute this software and its
31documentation for any purpose and without fee is hereby granted,
32provided that the above copyright notice appear in all copies and that
33both that copyright notice and this permission notice appear in
34supporting documentation, and that the name of Digital not be
35used in advertising or publicity pertaining to distribution of the
36software without specific, written prior permission.
37
38DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44SOFTWARE.
45
46******************************************************************/
47
48
49/*****************************************************************
50 * OS Dependent input routines:
51 *
52 *  WaitForSomething
53 *  TimerForce, TimerSet, TimerCheck, TimerFree
54 *
55 *****************************************************************/
56
57#include <X11/Xpoll.h>
58
59#ifdef HAVE_DIX_CONFIG_H
60#include <dix-config.h>
61#endif
62
63#ifdef WIN32
64#include <X11/Xwinsock.h>
65#endif
66#include <X11/Xos.h>			/* for strings, fcntl, time */
67#include <errno.h>
68#include <stdio.h>
69#include <X11/X.h>
70#include "misc.h"
71
72#include "osdep.h"
73#include "dixstruct.h"
74#include "opaque.h"
75#ifdef DPMSExtension
76#include "dpmsproc.h"
77#endif
78
79#ifdef WIN32
80/* Error codes from windows sockets differ from fileio error codes  */
81#undef EINTR
82#define EINTR WSAEINTR
83#undef EINVAL
84#define EINVAL WSAEINVAL
85#undef EBADF
86#define EBADF WSAENOTSOCK
87/* Windows select does not set errno. Use GetErrno as wrapper for
88   WSAGetLastError */
89#define GetErrno WSAGetLastError
90#else
91/* This is just a fallback to errno to hide the differences between unix and
92   Windows in the code */
93#define GetErrno() errno
94#endif
95
96/* like ffs, but uses fd_mask instead of int as argument, so it works
97   when fd_mask is longer than an int, such as common 64-bit platforms */
98/* modifications by raphael */
99int
100mffs(fd_mask mask)
101{
102    int i;
103
104    if (!mask) return 0;
105    i = 1;
106    while (!(mask & 1))
107    {
108	i++;
109	mask >>= 1;
110    }
111    return i;
112}
113
114#ifdef DPMSExtension
115#include <X11/extensions/dpmsconst.h>
116#endif
117
118struct _OsTimerRec {
119    OsTimerPtr		next;
120    CARD32		expires;
121    CARD32              delta;
122    OsTimerCallback	callback;
123    pointer		arg;
124};
125
126static void DoTimer(OsTimerPtr timer, CARD32 now, OsTimerPtr *prev);
127static void CheckAllTimers(void);
128static OsTimerPtr timers = NULL;
129
130/*****************
131 * WaitForSomething:
132 *     Make the server suspend until there is
133 *	1. data from clients or
134 *	2. input events available or
135 *	3. ddx notices something of interest (graphics
136 *	   queue ready, etc.) or
137 *	4. clients that have buffered replies/events are ready
138 *
139 *     If the time between INPUT events is
140 *     greater than ScreenSaverTime, the display is turned off (or
141 *     saved, depending on the hardware).  So, WaitForSomething()
142 *     has to handle this also (that's why the select() has a timeout.
143 *     For more info on ClientsWithInput, see ReadRequestFromClient().
144 *     pClientsReady is an array to store ready client->index values into.
145 *****************/
146
147int
148WaitForSomething(int *pClientsReady)
149{
150    int i;
151    struct timeval waittime, *wt;
152    INT32 timeout = 0;
153    fd_set clientsReadable;
154    fd_set clientsWritable;
155    int curclient;
156    int selecterr;
157    static int nready;
158    fd_set devicesReadable;
159    CARD32 now = 0;
160    Bool    someReady = FALSE;
161
162    FD_ZERO(&clientsReadable);
163
164    if (nready)
165        SmartScheduleStopTimer();
166    nready = 0;
167
168    /* We need a while loop here to handle
169       crashed connections and the screen saver timeout */
170    while (1)
171    {
172	/* deal with any blocked jobs */
173	if (workQueue)
174	    ProcessWorkQueue();
175	if (XFD_ANYSET (&ClientsWithInput))
176	{
177	    if (!SmartScheduleDisable)
178	    {
179		someReady = TRUE;
180		waittime.tv_sec = 0;
181		waittime.tv_usec = 0;
182		wt = &waittime;
183	    }
184	    else
185	    {
186		XFD_COPYSET (&ClientsWithInput, &clientsReadable);
187		break;
188	    }
189	}
190	if (someReady)
191	{
192	    XFD_COPYSET(&AllSockets, &LastSelectMask);
193	    XFD_UNSET(&LastSelectMask, &ClientsWithInput);
194	}
195	else
196	{
197        wt = NULL;
198	if (timers)
199        {
200            now = GetTimeInMillis();
201	    timeout = timers->expires - now;
202            if (timeout > 0 && timeout > timers->delta + 250) {
203                /* time has rewound.  reset the timers. */
204                CheckAllTimers();
205            }
206
207	    if (timers) {
208		timeout = timers->expires - now;
209		if (timeout < 0)
210		    timeout = 0;
211		waittime.tv_sec = timeout / MILLI_PER_SECOND;
212		waittime.tv_usec = (timeout % MILLI_PER_SECOND) *
213				   (1000000 / MILLI_PER_SECOND);
214		wt = &waittime;
215	    }
216	}
217	XFD_COPYSET(&AllSockets, &LastSelectMask);
218	}
219
220	BlockHandler((pointer)&wt, (pointer)&LastSelectMask);
221	if (NewOutputPending)
222	    FlushAllOutput();
223	/* keep this check close to select() call to minimize race */
224	if (dispatchException)
225	    i = -1;
226	else if (AnyClientsWriteBlocked)
227	{
228	    XFD_COPYSET(&ClientsWriteBlocked, &clientsWritable);
229	    i = Select (MaxClients, &LastSelectMask, &clientsWritable, NULL, wt);
230	}
231	else
232	{
233	    i = Select (MaxClients, &LastSelectMask, NULL, NULL, wt);
234	}
235	selecterr = GetErrno();
236	WakeupHandler(i, (pointer)&LastSelectMask);
237	if (i <= 0) /* An error or timeout occurred */
238	{
239	    if (dispatchException)
240		return 0;
241	    if (i < 0)
242	    {
243		if (selecterr == EBADF)    /* Some client disconnected */
244		{
245		    CheckConnections ();
246		    if (! XFD_ANYSET (&AllClients))
247			return 0;
248		}
249		else if (selecterr == EINVAL)
250		{
251		    FatalError("WaitForSomething(): select: %s\n",
252			strerror(selecterr));
253            }
254		else if (selecterr != EINTR && selecterr != EAGAIN)
255		{
256		    ErrorF("WaitForSomething(): select: %s\n",
257			strerror(selecterr));
258		}
259	    }
260	    else if (someReady)
261	    {
262		/*
263		 * If no-one else is home, bail quickly
264		 */
265		XFD_COPYSET(&ClientsWithInput, &LastSelectMask);
266		XFD_COPYSET(&ClientsWithInput, &clientsReadable);
267		break;
268	    }
269	    if (*checkForInput[0] != *checkForInput[1])
270		return 0;
271
272	    if (timers)
273	    {
274                int expired = 0;
275		now = GetTimeInMillis();
276		if ((int) (timers->expires - now) <= 0)
277		    expired = 1;
278
279		while (timers && (int) (timers->expires - now) <= 0)
280		    DoTimer(timers, now, &timers);
281
282                if (expired)
283                    return 0;
284	    }
285	}
286	else
287	{
288	    fd_set tmp_set;
289
290	    if (*checkForInput[0] == *checkForInput[1]) {
291	        if (timers)
292	        {
293                    int expired = 0;
294		    now = GetTimeInMillis();
295		    if ((int) (timers->expires - now) <= 0)
296		        expired = 1;
297
298		    while (timers && (int) (timers->expires - now) <= 0)
299		        DoTimer(timers, now, &timers);
300
301                    if (expired)
302                        return 0;
303	        }
304	    }
305	    if (someReady)
306		XFD_ORSET(&LastSelectMask, &ClientsWithInput, &LastSelectMask);
307	    if (AnyClientsWriteBlocked && XFD_ANYSET (&clientsWritable))
308	    {
309		NewOutputPending = TRUE;
310		XFD_ORSET(&OutputPending, &clientsWritable, &OutputPending);
311		XFD_UNSET(&ClientsWriteBlocked, &clientsWritable);
312		if (! XFD_ANYSET(&ClientsWriteBlocked))
313		    AnyClientsWriteBlocked = FALSE;
314	    }
315
316	    XFD_ANDSET(&devicesReadable, &LastSelectMask, &EnabledDevices);
317	    XFD_ANDSET(&clientsReadable, &LastSelectMask, &AllClients);
318	    XFD_ANDSET(&tmp_set, &LastSelectMask, &WellKnownConnections);
319	    if (XFD_ANYSET(&tmp_set))
320		QueueWorkProc(EstablishNewConnections, NULL,
321			      (pointer)&LastSelectMask);
322
323	    if (XFD_ANYSET (&devicesReadable) || XFD_ANYSET (&clientsReadable))
324		break;
325	    /* check here for DDXes that queue events during Block/Wakeup */
326	    if (*checkForInput[0] != *checkForInput[1])
327		return 0;
328	}
329    }
330
331    nready = 0;
332    if (XFD_ANYSET (&clientsReadable))
333    {
334#ifndef WIN32
335	for (i=0; i<howmany(XFD_SETSIZE, NFDBITS); i++)
336	{
337	    int highest_priority = 0;
338
339	    while (clientsReadable.fds_bits[i])
340	    {
341	        int client_priority, client_index;
342
343		curclient = mffs (clientsReadable.fds_bits[i]) - 1;
344		client_index = /* raphael: modified */
345			ConnectionTranslation[curclient + (i * (sizeof(fd_mask) * 8))];
346#else
347	int highest_priority = 0;
348	fd_set savedClientsReadable;
349	XFD_COPYSET(&clientsReadable, &savedClientsReadable);
350	for (i = 0; i < XFD_SETCOUNT(&savedClientsReadable); i++)
351	{
352	    int client_priority, client_index;
353
354	    curclient = XFD_FD(&savedClientsReadable, i);
355	    client_index = GetConnectionTranslation(curclient);
356#endif
357		/*  We implement "strict" priorities.
358		 *  Only the highest priority client is returned to
359		 *  dix.  If multiple clients at the same priority are
360		 *  ready, they are all returned.  This means that an
361		 *  aggressive client could take over the server.
362		 *  This was not considered a big problem because
363		 *  aggressive clients can hose the server in so many
364		 *  other ways :)
365		 */
366		client_priority = clients[client_index]->priority;
367		if (nready == 0 || client_priority > highest_priority)
368		{
369		    /*  Either we found the first client, or we found
370		     *  a client whose priority is greater than all others
371		     *  that have been found so far.  Either way, we want
372		     *  to initialize the list of clients to contain just
373		     *  this client.
374		     */
375		    pClientsReady[0] = client_index;
376		    highest_priority = client_priority;
377		    nready = 1;
378		}
379		/*  the following if makes sure that multiple same-priority
380		 *  clients get batched together
381		 */
382		else if (client_priority == highest_priority)
383		{
384		    pClientsReady[nready++] = client_index;
385		}
386#ifndef WIN32
387		clientsReadable.fds_bits[i] &= ~(((fd_mask)1L) << curclient);
388	    }
389#else
390	    FD_CLR(curclient, &clientsReadable);
391#endif
392	}
393    }
394
395    if (nready)
396        SmartScheduleStartTimer();
397
398    return nready;
399}
400
401/* If time has rewound, re-run every affected timer.
402 * Timers might drop out of the list, so we have to restart every time. */
403static void
404CheckAllTimers(void)
405{
406    OsTimerPtr timer;
407    CARD32 now;
408
409start:
410    now = GetTimeInMillis();
411
412    for (timer = timers; timer; timer = timer->next) {
413        if (timer->expires - now > timer->delta + 250) {
414            TimerForce(timer);
415            goto start;
416        }
417    }
418}
419
420static void
421DoTimer(OsTimerPtr timer, CARD32 now, OsTimerPtr *prev)
422{
423    CARD32 newTime;
424
425    *prev = timer->next;
426    timer->next = NULL;
427    newTime = (*timer->callback)(timer, now, timer->arg);
428    if (newTime)
429	TimerSet(timer, 0, newTime, timer->callback, timer->arg);
430}
431
432OsTimerPtr
433TimerSet(OsTimerPtr timer, int flags, CARD32 millis,
434    OsTimerCallback func, pointer arg)
435{
436    register OsTimerPtr *prev;
437    CARD32 now = GetTimeInMillis();
438
439    if (!timer)
440    {
441	timer = malloc(sizeof(struct _OsTimerRec));
442	if (!timer)
443	    return NULL;
444    }
445    else
446    {
447	for (prev = &timers; *prev; prev = &(*prev)->next)
448	{
449	    if (*prev == timer)
450	    {
451		*prev = timer->next;
452		if (flags & TimerForceOld)
453		    (void)(*timer->callback)(timer, now, timer->arg);
454		break;
455	    }
456	}
457    }
458    if (!millis)
459	return timer;
460    if (flags & TimerAbsolute) {
461        timer->delta = millis - now;
462    }
463    else {
464        timer->delta = millis;
465	millis += now;
466    }
467    timer->expires = millis;
468    timer->callback = func;
469    timer->arg = arg;
470    if ((int) (millis - now) <= 0)
471    {
472	timer->next = NULL;
473	millis = (*timer->callback)(timer, now, timer->arg);
474	if (!millis)
475	    return timer;
476    }
477    for (prev = &timers;
478	 *prev && (int) ((*prev)->expires - millis) <= 0;
479	 prev = &(*prev)->next)
480        ;
481    timer->next = *prev;
482    *prev = timer;
483    return timer;
484}
485
486Bool
487TimerForce(OsTimerPtr timer)
488{
489    OsTimerPtr *prev;
490
491    for (prev = &timers; *prev; prev = &(*prev)->next)
492    {
493	if (*prev == timer)
494	{
495	    DoTimer(timer, GetTimeInMillis(), prev);
496	    return TRUE;
497	}
498    }
499    return FALSE;
500}
501
502
503void
504TimerCancel(OsTimerPtr timer)
505{
506    OsTimerPtr *prev;
507
508    if (!timer)
509	return;
510    for (prev = &timers; *prev; prev = &(*prev)->next)
511    {
512	if (*prev == timer)
513	{
514	    *prev = timer->next;
515	    break;
516	}
517    }
518}
519
520void
521TimerFree(OsTimerPtr timer)
522{
523    if (!timer)
524	return;
525    TimerCancel(timer);
526    free(timer);
527}
528
529void
530TimerCheck(void)
531{
532    CARD32 now = GetTimeInMillis();
533
534    while (timers && (int) (timers->expires - now) <= 0)
535	DoTimer(timers, now, &timers);
536}
537
538void
539TimerInit(void)
540{
541    OsTimerPtr timer;
542
543    while ((timer = timers))
544    {
545	timers = timer->next;
546	free(timer);
547    }
548}
549
550#ifdef DPMSExtension
551
552#define DPMS_CHECK_MODE(mode,time)\
553    if (time > 0 && DPMSPowerLevel < mode && timeout >= time)\
554	DPMSSet(serverClient, mode);
555
556#define DPMS_CHECK_TIMEOUT(time)\
557    if (time > 0 && (time - timeout) > 0)\
558	return time - timeout;
559
560static CARD32
561NextDPMSTimeout(INT32 timeout)
562{
563    /*
564     * Return the amount of time remaining until we should set
565     * the next power level. Fallthroughs are intentional.
566     */
567    switch (DPMSPowerLevel)
568    {
569	case DPMSModeOn:
570	    DPMS_CHECK_TIMEOUT(DPMSStandbyTime)
571
572	case DPMSModeStandby:
573	    DPMS_CHECK_TIMEOUT(DPMSSuspendTime)
574
575	case DPMSModeSuspend:
576	    DPMS_CHECK_TIMEOUT(DPMSOffTime)
577
578	default: /* DPMSModeOff */
579	    return 0;
580    }
581}
582#endif /* DPMSExtension */
583
584static CARD32
585ScreenSaverTimeoutExpire(OsTimerPtr timer,CARD32 now,pointer arg)
586{
587    INT32 timeout      = now - lastDeviceEventTime.milliseconds;
588    CARD32 nextTimeout = 0;
589
590#ifdef DPMSExtension
591    /*
592     * Check each mode lowest to highest, since a lower mode can
593     * have the same timeout as a higher one.
594     */
595    if (DPMSEnabled)
596    {
597	DPMS_CHECK_MODE(DPMSModeOff,     DPMSOffTime)
598	DPMS_CHECK_MODE(DPMSModeSuspend, DPMSSuspendTime)
599	DPMS_CHECK_MODE(DPMSModeStandby, DPMSStandbyTime)
600
601	nextTimeout = NextDPMSTimeout(timeout);
602    }
603
604    /*
605     * Only do the screensaver checks if we're not in a DPMS
606     * power saving mode
607     */
608    if (DPMSPowerLevel != DPMSModeOn)
609	return nextTimeout;
610#endif /* DPMSExtension */
611
612    if (!ScreenSaverTime)
613	return nextTimeout;
614
615    if (timeout < ScreenSaverTime)
616    {
617	return nextTimeout > 0 ?
618		min(ScreenSaverTime - timeout, nextTimeout) :
619		ScreenSaverTime - timeout;
620    }
621
622    ResetOsBuffers(); /* not ideal, but better than nothing */
623    dixSaveScreens(serverClient, SCREEN_SAVER_ON, ScreenSaverActive);
624
625    if (ScreenSaverInterval > 0)
626    {
627	nextTimeout = nextTimeout > 0 ?
628		min(ScreenSaverInterval, nextTimeout) :
629		ScreenSaverInterval;
630    }
631
632    return nextTimeout;
633}
634
635static OsTimerPtr ScreenSaverTimer = NULL;
636
637void
638FreeScreenSaverTimer(void)
639{
640    if (ScreenSaverTimer) {
641	TimerFree(ScreenSaverTimer);
642	ScreenSaverTimer = NULL;
643    }
644}
645
646void
647SetScreenSaverTimer(void)
648{
649    CARD32 timeout = 0;
650
651#ifdef DPMSExtension
652    if (DPMSEnabled)
653    {
654	/*
655	 * A higher DPMS level has a timeout that's either less
656	 * than or equal to that of a lower DPMS level.
657	 */
658	if (DPMSStandbyTime > 0)
659	    timeout = DPMSStandbyTime;
660
661	else if (DPMSSuspendTime > 0)
662	    timeout = DPMSSuspendTime;
663
664	else if (DPMSOffTime > 0)
665	    timeout = DPMSOffTime;
666    }
667#endif
668
669    if (ScreenSaverTime > 0)
670    {
671	timeout = timeout > 0 ?
672		min(ScreenSaverTime, timeout) :
673		ScreenSaverTime;
674    }
675
676#ifdef SCREENSAVER
677    if (timeout && !screenSaverSuspended) {
678#else
679    if (timeout) {
680#endif
681	ScreenSaverTimer = TimerSet(ScreenSaverTimer, 0, timeout,
682	                            ScreenSaverTimeoutExpire, NULL);
683    }
684    else if (ScreenSaverTimer) {
685	FreeScreenSaverTimer();
686    }
687}
688
689