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