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