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