WaitFor.c revision 05b261ec
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#ifdef SMART_SCHEDULE
159    Bool    someReady = FALSE;
160#endif
161
162    FD_ZERO(&clientsReadable);
163
164    /* We need a while loop here to handle
165       crashed connections and the screen saver timeout */
166    while (1)
167    {
168	/* deal with any blocked jobs */
169	if (workQueue)
170	    ProcessWorkQueue();
171	if (XFD_ANYSET (&ClientsWithInput))
172	{
173#ifdef SMART_SCHEDULE
174	    if (!SmartScheduleDisable)
175	    {
176		someReady = TRUE;
177		waittime.tv_sec = 0;
178		waittime.tv_usec = 0;
179		wt = &waittime;
180	    }
181	    else
182#endif
183	    {
184		XFD_COPYSET (&ClientsWithInput, &clientsReadable);
185		break;
186	    }
187	}
188#ifdef SMART_SCHEDULE
189	if (someReady)
190	{
191	    XFD_COPYSET(&AllSockets, &LastSelectMask);
192	    XFD_UNSET(&LastSelectMask, &ClientsWithInput);
193	}
194	else
195	{
196#endif
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#ifdef SMART_SCHEDULE
219	}
220	SmartScheduleIdle = TRUE;
221#endif
222	BlockHandler((pointer)&wt, (pointer)&LastSelectMask);
223	if (NewOutputPending)
224	    FlushAllOutput();
225	/* keep this check close to select() call to minimize race */
226	if (dispatchException)
227	    i = -1;
228	else if (AnyClientsWriteBlocked)
229	{
230	    XFD_COPYSET(&ClientsWriteBlocked, &clientsWritable);
231	    i = Select (MaxClients, &LastSelectMask, &clientsWritable, NULL, wt);
232	}
233	else
234	{
235	    i = Select (MaxClients, &LastSelectMask, NULL, NULL, wt);
236	}
237	selecterr = GetErrno();
238	WakeupHandler(i, (pointer)&LastSelectMask);
239#ifdef SMART_SCHEDULE
240	if (i >= 0)
241	{
242	    SmartScheduleIdle = FALSE;
243	    SmartScheduleIdleCount = 0;
244	    if (SmartScheduleTimerStopped)
245		(void) SmartScheduleStartTimer ();
246	}
247#endif
248	if (i <= 0) /* An error or timeout occurred */
249	{
250	    if (dispatchException)
251		return 0;
252	    if (i < 0)
253	    {
254		if (selecterr == EBADF)    /* Some client disconnected */
255		{
256		    CheckConnections ();
257		    if (! XFD_ANYSET (&AllClients))
258			return 0;
259		}
260		else if (selecterr == EINVAL)
261		{
262		    FatalError("WaitForSomething(): select: errno=%d\n",
263			selecterr);
264            }
265		else if (selecterr != EINTR && selecterr != EAGAIN)
266		{
267		    ErrorF("WaitForSomething(): select: errno=%d\n",
268			selecterr);
269		}
270	    }
271#ifdef SMART_SCHEDULE
272	    else if (someReady)
273	    {
274		/*
275		 * If no-one else is home, bail quickly
276		 */
277		XFD_COPYSET(&ClientsWithInput, &LastSelectMask);
278		XFD_COPYSET(&ClientsWithInput, &clientsReadable);
279		break;
280	    }
281#endif
282	    if (*checkForInput[0] != *checkForInput[1])
283		return 0;
284
285	    if (timers)
286	    {
287                int expired = 0;
288		now = GetTimeInMillis();
289		if ((int) (timers->expires - now) <= 0)
290		    expired = 1;
291
292		while (timers && (int) (timers->expires - now) <= 0)
293		    DoTimer(timers, now, &timers);
294
295                if (expired)
296                    return 0;
297	    }
298	}
299	else
300	{
301	    fd_set tmp_set;
302
303	    if (*checkForInput[0] == *checkForInput[1]) {
304	        if (timers)
305	        {
306                    int expired = 0;
307		    now = GetTimeInMillis();
308		    if ((int) (timers->expires - now) <= 0)
309		        expired = 1;
310
311		    while (timers && (int) (timers->expires - now) <= 0)
312		        DoTimer(timers, now, &timers);
313
314                    if (expired)
315                        return 0;
316	        }
317	    }
318#ifdef SMART_SCHEDULE
319	    if (someReady)
320		XFD_ORSET(&LastSelectMask, &ClientsWithInput, &LastSelectMask);
321#endif
322	    if (AnyClientsWriteBlocked && XFD_ANYSET (&clientsWritable))
323	    {
324		NewOutputPending = TRUE;
325		XFD_ORSET(&OutputPending, &clientsWritable, &OutputPending);
326		XFD_UNSET(&ClientsWriteBlocked, &clientsWritable);
327		if (! XFD_ANYSET(&ClientsWriteBlocked))
328		    AnyClientsWriteBlocked = FALSE;
329	    }
330
331	    XFD_ANDSET(&devicesReadable, &LastSelectMask, &EnabledDevices);
332	    XFD_ANDSET(&clientsReadable, &LastSelectMask, &AllClients);
333	    XFD_ANDSET(&tmp_set, &LastSelectMask, &WellKnownConnections);
334	    if (XFD_ANYSET(&tmp_set))
335		QueueWorkProc(EstablishNewConnections, NULL,
336			      (pointer)&LastSelectMask);
337
338	    if (XFD_ANYSET (&devicesReadable) || XFD_ANYSET (&clientsReadable))
339		break;
340#ifdef WIN32
341	    /* Windows keyboard and mouse events are added to the input queue
342	       in Block- and WakupHandlers. There is no device to check if
343	       data is ready. So check here if new input is available */
344	    if (*checkForInput[0] != *checkForInput[1])
345		return 0;
346#endif
347	}
348    }
349
350    nready = 0;
351    if (XFD_ANYSET (&clientsReadable))
352    {
353#ifndef WIN32
354	for (i=0; i<howmany(XFD_SETSIZE, NFDBITS); i++)
355	{
356	    int highest_priority = 0;
357
358	    while (clientsReadable.fds_bits[i])
359	    {
360	        int client_priority, client_index;
361
362		curclient = ffs (clientsReadable.fds_bits[i]) - 1;
363		client_index = /* raphael: modified */
364			ConnectionTranslation[curclient + (i * (sizeof(fd_mask) * 8))];
365#else
366	int highest_priority = 0;
367	fd_set savedClientsReadable;
368	XFD_COPYSET(&clientsReadable, &savedClientsReadable);
369	for (i = 0; i < XFD_SETCOUNT(&savedClientsReadable); i++)
370	{
371	    int client_priority, client_index;
372
373	    curclient = XFD_FD(&savedClientsReadable, i);
374	    client_index = GetConnectionTranslation(curclient);
375#endif
376#ifdef XSYNC
377		/*  We implement "strict" priorities.
378		 *  Only the highest priority client is returned to
379		 *  dix.  If multiple clients at the same priority are
380		 *  ready, they are all returned.  This means that an
381		 *  aggressive client could take over the server.
382		 *  This was not considered a big problem because
383		 *  aggressive clients can hose the server in so many
384		 *  other ways :)
385		 */
386		client_priority = clients[client_index]->priority;
387		if (nready == 0 || client_priority > highest_priority)
388		{
389		    /*  Either we found the first client, or we found
390		     *  a client whose priority is greater than all others
391		     *  that have been found so far.  Either way, we want
392		     *  to initialize the list of clients to contain just
393		     *  this client.
394		     */
395		    pClientsReady[0] = client_index;
396		    highest_priority = client_priority;
397		    nready = 1;
398		}
399		/*  the following if makes sure that multiple same-priority
400		 *  clients get batched together
401		 */
402		else if (client_priority == highest_priority)
403#endif
404		{
405		    pClientsReady[nready++] = client_index;
406		}
407#ifndef WIN32
408		clientsReadable.fds_bits[i] &= ~(((fd_mask)1L) << curclient);
409	    }
410#else
411	    FD_CLR(curclient, &clientsReadable);
412#endif
413	}
414    }
415    return nready;
416}
417
418#if 0
419/*
420 * This is not always a macro.
421 */
422ANYSET(FdMask *src)
423{
424    int i;
425
426    for (i=0; i<mskcnt; i++)
427	if (src[ i ])
428	    return (TRUE);
429    return (FALSE);
430}
431#endif
432
433/* If time has rewound, re-run every affected timer.
434 * Timers might drop out of the list, so we have to restart every time. */
435static void
436CheckAllTimers(void)
437{
438    OsTimerPtr timer;
439    CARD32 now;
440
441start:
442    now = GetTimeInMillis();
443
444    for (timer = timers; timer; timer = timer->next) {
445        if (timer->expires - now > timer->delta + 250) {
446            TimerForce(timer);
447            goto start;
448        }
449    }
450}
451
452static void
453DoTimer(OsTimerPtr timer, CARD32 now, OsTimerPtr *prev)
454{
455    CARD32 newTime;
456
457    *prev = timer->next;
458    timer->next = NULL;
459    newTime = (*timer->callback)(timer, now, timer->arg);
460    if (newTime)
461	TimerSet(timer, 0, newTime, timer->callback, timer->arg);
462}
463
464_X_EXPORT OsTimerPtr
465TimerSet(OsTimerPtr timer, int flags, CARD32 millis,
466    OsTimerCallback func, pointer arg)
467{
468    register OsTimerPtr *prev;
469    CARD32 now = GetTimeInMillis();
470
471    if (!timer)
472    {
473	timer = (OsTimerPtr)xalloc(sizeof(struct _OsTimerRec));
474	if (!timer)
475	    return NULL;
476    }
477    else
478    {
479	for (prev = &timers; *prev; prev = &(*prev)->next)
480	{
481	    if (*prev == timer)
482	    {
483		*prev = timer->next;
484		if (flags & TimerForceOld)
485		    (void)(*timer->callback)(timer, now, timer->arg);
486		break;
487	    }
488	}
489    }
490    if (!millis)
491	return timer;
492    if (flags & TimerAbsolute) {
493        timer->delta = millis - now;
494    }
495    else {
496        timer->delta = millis;
497	millis += now;
498    }
499    timer->expires = millis;
500    timer->callback = func;
501    timer->arg = arg;
502    if ((int) (millis - now) <= 0)
503    {
504	timer->next = NULL;
505	millis = (*timer->callback)(timer, now, timer->arg);
506	if (!millis)
507	    return timer;
508    }
509    for (prev = &timers;
510	 *prev && (int) ((*prev)->expires - millis) <= 0;
511	 prev = &(*prev)->next)
512        ;
513    timer->next = *prev;
514    *prev = timer;
515    return timer;
516}
517
518Bool
519TimerForce(OsTimerPtr timer)
520{
521    OsTimerPtr *prev;
522
523    for (prev = &timers; *prev; prev = &(*prev)->next)
524    {
525	if (*prev == timer)
526	{
527	    DoTimer(timer, GetTimeInMillis(), prev);
528	    return TRUE;
529	}
530    }
531    return FALSE;
532}
533
534
535_X_EXPORT void
536TimerCancel(OsTimerPtr timer)
537{
538    OsTimerPtr *prev;
539
540    if (!timer)
541	return;
542    for (prev = &timers; *prev; prev = &(*prev)->next)
543    {
544	if (*prev == timer)
545	{
546	    *prev = timer->next;
547	    break;
548	}
549    }
550}
551
552_X_EXPORT void
553TimerFree(OsTimerPtr timer)
554{
555    if (!timer)
556	return;
557    TimerCancel(timer);
558    xfree(timer);
559}
560
561void
562TimerCheck(void)
563{
564    CARD32 now = GetTimeInMillis();
565
566    while (timers && (int) (timers->expires - now) <= 0)
567	DoTimer(timers, now, &timers);
568}
569
570void
571TimerInit(void)
572{
573    OsTimerPtr timer;
574
575    while ((timer = timers))
576    {
577	timers = timer->next;
578	xfree(timer);
579    }
580}
581
582#ifdef DPMSExtension
583
584#define DPMS_CHECK_MODE(mode,time)\
585    if (time > 0 && DPMSPowerLevel < mode && timeout >= time)\
586	DPMSSet(mode);
587
588#define DPMS_CHECK_TIMEOUT(time)\
589    if (time > 0 && (time - timeout) > 0)\
590	return time - timeout;
591
592static CARD32
593NextDPMSTimeout(INT32 timeout)
594{
595    /*
596     * Return the amount of time remaining until we should set
597     * the next power level. Fallthroughs are intentional.
598     */
599    switch (DPMSPowerLevel)
600    {
601	case DPMSModeOn:
602	    DPMS_CHECK_TIMEOUT(DPMSStandbyTime)
603
604	case DPMSModeStandby:
605	    DPMS_CHECK_TIMEOUT(DPMSSuspendTime)
606
607	case DPMSModeSuspend:
608	    DPMS_CHECK_TIMEOUT(DPMSOffTime)
609
610	default: /* DPMSModeOff */
611	    return 0;
612    }
613}
614#endif /* DPMSExtension */
615
616static CARD32
617ScreenSaverTimeoutExpire(OsTimerPtr timer,CARD32 now,pointer arg)
618{
619    INT32 timeout      = now - lastDeviceEventTime.milliseconds;
620    CARD32 nextTimeout = 0;
621
622#ifdef DPMSExtension
623    /*
624     * Check each mode lowest to highest, since a lower mode can
625     * have the same timeout as a higher one.
626     */
627    if (DPMSEnabled)
628    {
629	DPMS_CHECK_MODE(DPMSModeOff,     DPMSOffTime)
630	DPMS_CHECK_MODE(DPMSModeSuspend, DPMSSuspendTime)
631	DPMS_CHECK_MODE(DPMSModeStandby, DPMSStandbyTime)
632
633	nextTimeout = NextDPMSTimeout(timeout);
634    }
635
636    /*
637     * Only do the screensaver checks if we're not in a DPMS
638     * power saving mode
639     */
640    if (DPMSPowerLevel != DPMSModeOn)
641	return nextTimeout;
642#endif /* DPMSExtension */
643
644    if (!ScreenSaverTime)
645	return nextTimeout;
646
647    if (timeout < ScreenSaverTime)
648    {
649	return nextTimeout > 0 ?
650		min(ScreenSaverTime - timeout, nextTimeout) :
651		ScreenSaverTime - timeout;
652    }
653
654    ResetOsBuffers(); /* not ideal, but better than nothing */
655    SaveScreens(SCREEN_SAVER_ON, ScreenSaverActive);
656
657    if (ScreenSaverInterval > 0)
658    {
659	nextTimeout = nextTimeout > 0 ?
660		min(ScreenSaverInterval, nextTimeout) :
661		ScreenSaverInterval;
662    }
663
664    return nextTimeout;
665}
666
667static OsTimerPtr ScreenSaverTimer = NULL;
668
669void
670FreeScreenSaverTimer(void)
671{
672    if (ScreenSaverTimer) {
673	TimerFree(ScreenSaverTimer);
674	ScreenSaverTimer = NULL;
675    }
676}
677
678void
679SetScreenSaverTimer(void)
680{
681    CARD32 timeout = 0;
682
683#ifdef DPMSExtension
684    if (DPMSEnabled)
685    {
686	/*
687	 * A higher DPMS level has a timeout that's either less
688	 * than or equal to that of a lower DPMS level.
689	 */
690	if (DPMSStandbyTime > 0)
691	    timeout = DPMSStandbyTime;
692
693	else if (DPMSSuspendTime > 0)
694	    timeout = DPMSSuspendTime;
695
696	else if (DPMSOffTime > 0)
697	    timeout = DPMSOffTime;
698    }
699#endif
700
701    if (ScreenSaverTime > 0)
702    {
703	timeout = timeout > 0 ?
704		min(ScreenSaverTime, timeout) :
705		ScreenSaverTime;
706    }
707
708#ifdef SCREENSAVER
709    if (timeout && !screenSaverSuspended) {
710#else
711    if (timeout) {
712#endif
713	ScreenSaverTimer = TimerSet(ScreenSaverTimer, 0, timeout,
714	                            ScreenSaverTimeoutExpire, NULL);
715    }
716    else if (ScreenSaverTimer) {
717	FreeScreenSaverTimer();
718    }
719}
720
721