1706f2543Smrg/***********************************************************
2706f2543Smrg
3706f2543SmrgCopyright 1987, 1998  The Open Group
4706f2543Smrg
5706f2543SmrgPermission to use, copy, modify, distribute, and sell this software and its
6706f2543Smrgdocumentation for any purpose is hereby granted without fee, provided that
7706f2543Smrgthe above copyright notice appear in all copies and that both that
8706f2543Smrgcopyright notice and this permission notice appear in supporting
9706f2543Smrgdocumentation.
10706f2543Smrg
11706f2543SmrgThe above copyright notice and this permission notice shall be included in
12706f2543Smrgall copies or substantial portions of the Software.
13706f2543Smrg
14706f2543SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15706f2543SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16706f2543SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17706f2543SmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18706f2543SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19706f2543SmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20706f2543Smrg
21706f2543SmrgExcept as contained in this notice, the name of The Open Group shall not be
22706f2543Smrgused in advertising or otherwise to promote the sale, use or other dealings
23706f2543Smrgin this Software without prior written authorization from The Open Group.
24706f2543Smrg
25706f2543Smrg
26706f2543SmrgCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
27706f2543Smrg
28706f2543Smrg                        All Rights Reserved
29706f2543Smrg
30706f2543SmrgPermission to use, copy, modify, and distribute this software and its
31706f2543Smrgdocumentation for any purpose and without fee is hereby granted,
32706f2543Smrgprovided that the above copyright notice appear in all copies and that
33706f2543Smrgboth that copyright notice and this permission notice appear in
34706f2543Smrgsupporting documentation, and that the name of Digital not be
35706f2543Smrgused in advertising or publicity pertaining to distribution of the
36706f2543Smrgsoftware without specific, written prior permission.
37706f2543Smrg
38706f2543SmrgDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39706f2543SmrgALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40706f2543SmrgDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41706f2543SmrgANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42706f2543SmrgWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43706f2543SmrgARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44706f2543SmrgSOFTWARE.
45706f2543Smrg
46706f2543Smrg******************************************************************/
47706f2543Smrg
48706f2543Smrg
49706f2543Smrg/*****************************************************************
50706f2543Smrg * OS Dependent input routines:
51706f2543Smrg *
52706f2543Smrg *  WaitForSomething
53706f2543Smrg *  TimerForce, TimerSet, TimerCheck, TimerFree
54706f2543Smrg *
55706f2543Smrg *****************************************************************/
56706f2543Smrg
57706f2543Smrg#include <X11/Xpoll.h>
58706f2543Smrg
59706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
60706f2543Smrg#include <dix-config.h>
61706f2543Smrg#endif
62706f2543Smrg
63706f2543Smrg#ifdef WIN32
64706f2543Smrg#include <X11/Xwinsock.h>
65706f2543Smrg#endif
66706f2543Smrg#include <X11/Xos.h>			/* for strings, fcntl, time */
67706f2543Smrg#include <errno.h>
68706f2543Smrg#include <stdio.h>
69706f2543Smrg#include <X11/X.h>
70706f2543Smrg#include "misc.h"
71706f2543Smrg
72706f2543Smrg#include "osdep.h"
73706f2543Smrg#include "dixstruct.h"
74706f2543Smrg#include "opaque.h"
75706f2543Smrg#ifdef DPMSExtension
76706f2543Smrg#include "dpmsproc.h"
77706f2543Smrg#endif
78706f2543Smrg
79706f2543Smrg#ifdef WIN32
80706f2543Smrg/* Error codes from windows sockets differ from fileio error codes  */
81706f2543Smrg#undef EINTR
82706f2543Smrg#define EINTR WSAEINTR
83706f2543Smrg#undef EINVAL
84706f2543Smrg#define EINVAL WSAEINVAL
85706f2543Smrg#undef EBADF
86706f2543Smrg#define EBADF WSAENOTSOCK
87706f2543Smrg/* Windows select does not set errno. Use GetErrno as wrapper for
88706f2543Smrg   WSAGetLastError */
89706f2543Smrg#define GetErrno WSAGetLastError
90706f2543Smrg#else
91706f2543Smrg/* This is just a fallback to errno to hide the differences between unix and
92706f2543Smrg   Windows in the code */
93706f2543Smrg#define GetErrno() errno
94706f2543Smrg#endif
95706f2543Smrg
96706f2543Smrg/* like ffs, but uses fd_mask instead of int as argument, so it works
97706f2543Smrg   when fd_mask is longer than an int, such as common 64-bit platforms */
98706f2543Smrg/* modifications by raphael */
99706f2543Smrgint
100706f2543Smrgmffs(fd_mask mask)
101706f2543Smrg{
102706f2543Smrg    int i;
103706f2543Smrg
104706f2543Smrg    if (!mask) return 0;
105706f2543Smrg    i = 1;
106706f2543Smrg    while (!(mask & 1))
107706f2543Smrg    {
108706f2543Smrg	i++;
109706f2543Smrg	mask >>= 1;
110706f2543Smrg    }
111706f2543Smrg    return i;
112706f2543Smrg}
113706f2543Smrg
114706f2543Smrg#ifdef DPMSExtension
115706f2543Smrg#include <X11/extensions/dpmsconst.h>
116706f2543Smrg#endif
117706f2543Smrg
118706f2543Smrgstruct _OsTimerRec {
119706f2543Smrg    OsTimerPtr		next;
120706f2543Smrg    CARD32		expires;
121706f2543Smrg    CARD32              delta;
122706f2543Smrg    OsTimerCallback	callback;
123706f2543Smrg    pointer		arg;
124706f2543Smrg};
125706f2543Smrg
126706f2543Smrgstatic void DoTimer(OsTimerPtr timer, CARD32 now, OsTimerPtr *prev);
127706f2543Smrgstatic void CheckAllTimers(void);
128706f2543Smrgstatic OsTimerPtr timers = NULL;
129706f2543Smrg
130706f2543Smrg/*****************
131706f2543Smrg * WaitForSomething:
132706f2543Smrg *     Make the server suspend until there is
133706f2543Smrg *	1. data from clients or
134706f2543Smrg *	2. input events available or
135706f2543Smrg *	3. ddx notices something of interest (graphics
136706f2543Smrg *	   queue ready, etc.) or
137706f2543Smrg *	4. clients that have buffered replies/events are ready
138706f2543Smrg *
139706f2543Smrg *     If the time between INPUT events is
140706f2543Smrg *     greater than ScreenSaverTime, the display is turned off (or
141706f2543Smrg *     saved, depending on the hardware).  So, WaitForSomething()
142706f2543Smrg *     has to handle this also (that's why the select() has a timeout.
143706f2543Smrg *     For more info on ClientsWithInput, see ReadRequestFromClient().
144706f2543Smrg *     pClientsReady is an array to store ready client->index values into.
145706f2543Smrg *****************/
146706f2543Smrg
147706f2543Smrgint
148706f2543SmrgWaitForSomething(int *pClientsReady)
149706f2543Smrg{
150706f2543Smrg    int i;
151706f2543Smrg    struct timeval waittime, *wt;
152706f2543Smrg    INT32 timeout = 0;
153706f2543Smrg    fd_set clientsReadable;
154706f2543Smrg    fd_set clientsWritable;
155706f2543Smrg    int curclient;
156706f2543Smrg    int selecterr;
157706f2543Smrg    static int nready;
158706f2543Smrg    fd_set devicesReadable;
159706f2543Smrg    CARD32 now = 0;
160706f2543Smrg    Bool    someReady = FALSE;
161706f2543Smrg
162706f2543Smrg    FD_ZERO(&clientsReadable);
163706f2543Smrg
164706f2543Smrg    if (nready)
165706f2543Smrg        SmartScheduleStopTimer();
166706f2543Smrg    nready = 0;
167706f2543Smrg
168706f2543Smrg    /* We need a while loop here to handle
169706f2543Smrg       crashed connections and the screen saver timeout */
170706f2543Smrg    while (1)
171706f2543Smrg    {
172706f2543Smrg	/* deal with any blocked jobs */
173706f2543Smrg	if (workQueue)
174706f2543Smrg	    ProcessWorkQueue();
175706f2543Smrg	if (XFD_ANYSET (&ClientsWithInput))
176706f2543Smrg	{
177706f2543Smrg	    if (!SmartScheduleDisable)
178706f2543Smrg	    {
179706f2543Smrg		someReady = TRUE;
180706f2543Smrg		waittime.tv_sec = 0;
181706f2543Smrg		waittime.tv_usec = 0;
182706f2543Smrg		wt = &waittime;
183706f2543Smrg	    }
184706f2543Smrg	    else
185706f2543Smrg	    {
186706f2543Smrg		XFD_COPYSET (&ClientsWithInput, &clientsReadable);
187706f2543Smrg		break;
188706f2543Smrg	    }
189706f2543Smrg	}
190706f2543Smrg	if (someReady)
191706f2543Smrg	{
192706f2543Smrg	    XFD_COPYSET(&AllSockets, &LastSelectMask);
193706f2543Smrg	    XFD_UNSET(&LastSelectMask, &ClientsWithInput);
194706f2543Smrg	}
195706f2543Smrg	else
196706f2543Smrg	{
197706f2543Smrg        wt = NULL;
198706f2543Smrg	if (timers)
199706f2543Smrg        {
200706f2543Smrg            now = GetTimeInMillis();
201706f2543Smrg	    timeout = timers->expires - now;
202706f2543Smrg            if (timeout > 0 && timeout > timers->delta + 250) {
203706f2543Smrg                /* time has rewound.  reset the timers. */
204706f2543Smrg                CheckAllTimers();
205706f2543Smrg            }
206706f2543Smrg
207706f2543Smrg	    if (timers) {
208706f2543Smrg		timeout = timers->expires - now;
209706f2543Smrg		if (timeout < 0)
210706f2543Smrg		    timeout = 0;
211706f2543Smrg		waittime.tv_sec = timeout / MILLI_PER_SECOND;
212706f2543Smrg		waittime.tv_usec = (timeout % MILLI_PER_SECOND) *
213706f2543Smrg				   (1000000 / MILLI_PER_SECOND);
214706f2543Smrg		wt = &waittime;
215706f2543Smrg	    }
216706f2543Smrg	}
217706f2543Smrg	XFD_COPYSET(&AllSockets, &LastSelectMask);
218706f2543Smrg	}
219706f2543Smrg
220706f2543Smrg	BlockHandler((pointer)&wt, (pointer)&LastSelectMask);
221706f2543Smrg	if (NewOutputPending)
222706f2543Smrg	    FlushAllOutput();
223706f2543Smrg	/* keep this check close to select() call to minimize race */
224706f2543Smrg	if (dispatchException)
225706f2543Smrg	    i = -1;
226706f2543Smrg	else if (AnyClientsWriteBlocked)
227706f2543Smrg	{
228706f2543Smrg	    XFD_COPYSET(&ClientsWriteBlocked, &clientsWritable);
229706f2543Smrg	    i = Select (MaxClients, &LastSelectMask, &clientsWritable, NULL, wt);
230706f2543Smrg	}
231706f2543Smrg	else
232706f2543Smrg	{
233706f2543Smrg	    i = Select (MaxClients, &LastSelectMask, NULL, NULL, wt);
234706f2543Smrg	}
235706f2543Smrg	selecterr = GetErrno();
236706f2543Smrg	WakeupHandler(i, (pointer)&LastSelectMask);
237706f2543Smrg	if (i <= 0) /* An error or timeout occurred */
238706f2543Smrg	{
239706f2543Smrg	    if (dispatchException)
240706f2543Smrg		return 0;
241706f2543Smrg	    if (i < 0)
242706f2543Smrg	    {
243706f2543Smrg		if (selecterr == EBADF)    /* Some client disconnected */
244706f2543Smrg		{
245706f2543Smrg		    CheckConnections ();
246706f2543Smrg		    if (! XFD_ANYSET (&AllClients))
247706f2543Smrg			return 0;
248706f2543Smrg		}
249706f2543Smrg		else if (selecterr == EINVAL)
250706f2543Smrg		{
251706f2543Smrg		    FatalError("WaitForSomething(): select: %s\n",
252706f2543Smrg			strerror(selecterr));
253706f2543Smrg            }
254706f2543Smrg		else if (selecterr != EINTR && selecterr != EAGAIN)
255706f2543Smrg		{
256706f2543Smrg		    ErrorF("WaitForSomething(): select: %s\n",
257706f2543Smrg			strerror(selecterr));
258706f2543Smrg		}
259706f2543Smrg	    }
260706f2543Smrg	    else if (someReady)
261706f2543Smrg	    {
262706f2543Smrg		/*
263706f2543Smrg		 * If no-one else is home, bail quickly
264706f2543Smrg		 */
265706f2543Smrg		XFD_COPYSET(&ClientsWithInput, &LastSelectMask);
266706f2543Smrg		XFD_COPYSET(&ClientsWithInput, &clientsReadable);
267706f2543Smrg		break;
268706f2543Smrg	    }
269706f2543Smrg	    if (*checkForInput[0] != *checkForInput[1])
270706f2543Smrg		return 0;
271706f2543Smrg
272706f2543Smrg	    if (timers)
273706f2543Smrg	    {
274706f2543Smrg                int expired = 0;
275706f2543Smrg		now = GetTimeInMillis();
276706f2543Smrg		if ((int) (timers->expires - now) <= 0)
277706f2543Smrg		    expired = 1;
278706f2543Smrg
279706f2543Smrg		while (timers && (int) (timers->expires - now) <= 0)
280706f2543Smrg		    DoTimer(timers, now, &timers);
281706f2543Smrg
282706f2543Smrg                if (expired)
283706f2543Smrg                    return 0;
284706f2543Smrg	    }
285706f2543Smrg	}
286706f2543Smrg	else
287706f2543Smrg	{
288706f2543Smrg	    fd_set tmp_set;
289706f2543Smrg
290706f2543Smrg	    if (*checkForInput[0] == *checkForInput[1]) {
291706f2543Smrg	        if (timers)
292706f2543Smrg	        {
293706f2543Smrg                    int expired = 0;
294706f2543Smrg		    now = GetTimeInMillis();
295706f2543Smrg		    if ((int) (timers->expires - now) <= 0)
296706f2543Smrg		        expired = 1;
297706f2543Smrg
298706f2543Smrg		    while (timers && (int) (timers->expires - now) <= 0)
299706f2543Smrg		        DoTimer(timers, now, &timers);
300706f2543Smrg
301706f2543Smrg                    if (expired)
302706f2543Smrg                        return 0;
303706f2543Smrg	        }
304706f2543Smrg	    }
305706f2543Smrg	    if (someReady)
306706f2543Smrg		XFD_ORSET(&LastSelectMask, &ClientsWithInput, &LastSelectMask);
307706f2543Smrg	    if (AnyClientsWriteBlocked && XFD_ANYSET (&clientsWritable))
308706f2543Smrg	    {
309706f2543Smrg		NewOutputPending = TRUE;
310706f2543Smrg		XFD_ORSET(&OutputPending, &clientsWritable, &OutputPending);
311706f2543Smrg		XFD_UNSET(&ClientsWriteBlocked, &clientsWritable);
312706f2543Smrg		if (! XFD_ANYSET(&ClientsWriteBlocked))
313706f2543Smrg		    AnyClientsWriteBlocked = FALSE;
314706f2543Smrg	    }
315706f2543Smrg
316706f2543Smrg	    XFD_ANDSET(&devicesReadable, &LastSelectMask, &EnabledDevices);
317706f2543Smrg	    XFD_ANDSET(&clientsReadable, &LastSelectMask, &AllClients);
318706f2543Smrg	    XFD_ANDSET(&tmp_set, &LastSelectMask, &WellKnownConnections);
319706f2543Smrg	    if (XFD_ANYSET(&tmp_set))
320706f2543Smrg		QueueWorkProc(EstablishNewConnections, NULL,
321706f2543Smrg			      (pointer)&LastSelectMask);
322706f2543Smrg
323706f2543Smrg	    if (XFD_ANYSET (&devicesReadable) || XFD_ANYSET (&clientsReadable))
324706f2543Smrg		break;
325706f2543Smrg	    /* check here for DDXes that queue events during Block/Wakeup */
326706f2543Smrg	    if (*checkForInput[0] != *checkForInput[1])
327706f2543Smrg		return 0;
328706f2543Smrg	}
329706f2543Smrg    }
330706f2543Smrg
331706f2543Smrg    nready = 0;
332706f2543Smrg    if (XFD_ANYSET (&clientsReadable))
333706f2543Smrg    {
334706f2543Smrg#ifndef WIN32
335706f2543Smrg	for (i=0; i<howmany(XFD_SETSIZE, NFDBITS); i++)
336706f2543Smrg	{
337706f2543Smrg	    int highest_priority = 0;
338706f2543Smrg
339706f2543Smrg	    while (clientsReadable.fds_bits[i])
340706f2543Smrg	    {
341706f2543Smrg	        int client_priority, client_index;
342706f2543Smrg
343706f2543Smrg		curclient = mffs (clientsReadable.fds_bits[i]) - 1;
344706f2543Smrg		client_index = /* raphael: modified */
345706f2543Smrg			ConnectionTranslation[curclient + (i * (sizeof(fd_mask) * 8))];
346706f2543Smrg#else
347706f2543Smrg	int highest_priority = 0;
348706f2543Smrg	fd_set savedClientsReadable;
349706f2543Smrg	XFD_COPYSET(&clientsReadable, &savedClientsReadable);
350706f2543Smrg	for (i = 0; i < XFD_SETCOUNT(&savedClientsReadable); i++)
351706f2543Smrg	{
352706f2543Smrg	    int client_priority, client_index;
353706f2543Smrg
354706f2543Smrg	    curclient = XFD_FD(&savedClientsReadable, i);
355706f2543Smrg	    client_index = GetConnectionTranslation(curclient);
356706f2543Smrg#endif
357706f2543Smrg		/*  We implement "strict" priorities.
358706f2543Smrg		 *  Only the highest priority client is returned to
359706f2543Smrg		 *  dix.  If multiple clients at the same priority are
360706f2543Smrg		 *  ready, they are all returned.  This means that an
361706f2543Smrg		 *  aggressive client could take over the server.
362706f2543Smrg		 *  This was not considered a big problem because
363706f2543Smrg		 *  aggressive clients can hose the server in so many
364706f2543Smrg		 *  other ways :)
365706f2543Smrg		 */
366706f2543Smrg		client_priority = clients[client_index]->priority;
367706f2543Smrg		if (nready == 0 || client_priority > highest_priority)
368706f2543Smrg		{
369706f2543Smrg		    /*  Either we found the first client, or we found
370706f2543Smrg		     *  a client whose priority is greater than all others
371706f2543Smrg		     *  that have been found so far.  Either way, we want
372706f2543Smrg		     *  to initialize the list of clients to contain just
373706f2543Smrg		     *  this client.
374706f2543Smrg		     */
375706f2543Smrg		    pClientsReady[0] = client_index;
376706f2543Smrg		    highest_priority = client_priority;
377706f2543Smrg		    nready = 1;
378706f2543Smrg		}
379706f2543Smrg		/*  the following if makes sure that multiple same-priority
380706f2543Smrg		 *  clients get batched together
381706f2543Smrg		 */
382706f2543Smrg		else if (client_priority == highest_priority)
383706f2543Smrg		{
384706f2543Smrg		    pClientsReady[nready++] = client_index;
385706f2543Smrg		}
386706f2543Smrg#ifndef WIN32
387706f2543Smrg		clientsReadable.fds_bits[i] &= ~(((fd_mask)1L) << curclient);
388706f2543Smrg	    }
389706f2543Smrg#else
390706f2543Smrg	    FD_CLR(curclient, &clientsReadable);
391706f2543Smrg#endif
392706f2543Smrg	}
393706f2543Smrg    }
394706f2543Smrg
395706f2543Smrg    if (nready)
396706f2543Smrg        SmartScheduleStartTimer();
397706f2543Smrg
398706f2543Smrg    return nready;
399706f2543Smrg}
400706f2543Smrg
401706f2543Smrg/* If time has rewound, re-run every affected timer.
402706f2543Smrg * Timers might drop out of the list, so we have to restart every time. */
403706f2543Smrgstatic void
404706f2543SmrgCheckAllTimers(void)
405706f2543Smrg{
406706f2543Smrg    OsTimerPtr timer;
407706f2543Smrg    CARD32 now;
408706f2543Smrg
409706f2543Smrgstart:
410706f2543Smrg    now = GetTimeInMillis();
411706f2543Smrg
412706f2543Smrg    for (timer = timers; timer; timer = timer->next) {
413706f2543Smrg        if (timer->expires - now > timer->delta + 250) {
414706f2543Smrg            TimerForce(timer);
415706f2543Smrg            goto start;
416706f2543Smrg        }
417706f2543Smrg    }
418706f2543Smrg}
419706f2543Smrg
420706f2543Smrgstatic void
421706f2543SmrgDoTimer(OsTimerPtr timer, CARD32 now, OsTimerPtr *prev)
422706f2543Smrg{
423706f2543Smrg    CARD32 newTime;
424706f2543Smrg
425706f2543Smrg    *prev = timer->next;
426706f2543Smrg    timer->next = NULL;
427706f2543Smrg    newTime = (*timer->callback)(timer, now, timer->arg);
428706f2543Smrg    if (newTime)
429706f2543Smrg	TimerSet(timer, 0, newTime, timer->callback, timer->arg);
430706f2543Smrg}
431706f2543Smrg
432706f2543SmrgOsTimerPtr
433706f2543SmrgTimerSet(OsTimerPtr timer, int flags, CARD32 millis,
434706f2543Smrg    OsTimerCallback func, pointer arg)
435706f2543Smrg{
436706f2543Smrg    register OsTimerPtr *prev;
437706f2543Smrg    CARD32 now = GetTimeInMillis();
438706f2543Smrg
439706f2543Smrg    if (!timer)
440706f2543Smrg    {
441706f2543Smrg	timer = malloc(sizeof(struct _OsTimerRec));
442706f2543Smrg	if (!timer)
443706f2543Smrg	    return NULL;
444706f2543Smrg    }
445706f2543Smrg    else
446706f2543Smrg    {
447706f2543Smrg	for (prev = &timers; *prev; prev = &(*prev)->next)
448706f2543Smrg	{
449706f2543Smrg	    if (*prev == timer)
450706f2543Smrg	    {
451706f2543Smrg		*prev = timer->next;
452706f2543Smrg		if (flags & TimerForceOld)
453706f2543Smrg		    (void)(*timer->callback)(timer, now, timer->arg);
454706f2543Smrg		break;
455706f2543Smrg	    }
456706f2543Smrg	}
457706f2543Smrg    }
458706f2543Smrg    if (!millis)
459706f2543Smrg	return timer;
460706f2543Smrg    if (flags & TimerAbsolute) {
461706f2543Smrg        timer->delta = millis - now;
462706f2543Smrg    }
463706f2543Smrg    else {
464706f2543Smrg        timer->delta = millis;
465706f2543Smrg	millis += now;
466706f2543Smrg    }
467706f2543Smrg    timer->expires = millis;
468706f2543Smrg    timer->callback = func;
469706f2543Smrg    timer->arg = arg;
470706f2543Smrg    if ((int) (millis - now) <= 0)
471706f2543Smrg    {
472706f2543Smrg	timer->next = NULL;
473706f2543Smrg	millis = (*timer->callback)(timer, now, timer->arg);
474706f2543Smrg	if (!millis)
475706f2543Smrg	    return timer;
476706f2543Smrg    }
477706f2543Smrg    for (prev = &timers;
478706f2543Smrg	 *prev && (int) ((*prev)->expires - millis) <= 0;
479706f2543Smrg	 prev = &(*prev)->next)
480706f2543Smrg        ;
481706f2543Smrg    timer->next = *prev;
482706f2543Smrg    *prev = timer;
483706f2543Smrg    return timer;
484706f2543Smrg}
485706f2543Smrg
486706f2543SmrgBool
487706f2543SmrgTimerForce(OsTimerPtr timer)
488706f2543Smrg{
489706f2543Smrg    OsTimerPtr *prev;
490706f2543Smrg
491706f2543Smrg    for (prev = &timers; *prev; prev = &(*prev)->next)
492706f2543Smrg    {
493706f2543Smrg	if (*prev == timer)
494706f2543Smrg	{
495706f2543Smrg	    DoTimer(timer, GetTimeInMillis(), prev);
496706f2543Smrg	    return TRUE;
497706f2543Smrg	}
498706f2543Smrg    }
499706f2543Smrg    return FALSE;
500706f2543Smrg}
501706f2543Smrg
502706f2543Smrg
503706f2543Smrgvoid
504706f2543SmrgTimerCancel(OsTimerPtr timer)
505706f2543Smrg{
506706f2543Smrg    OsTimerPtr *prev;
507706f2543Smrg
508706f2543Smrg    if (!timer)
509706f2543Smrg	return;
510706f2543Smrg    for (prev = &timers; *prev; prev = &(*prev)->next)
511706f2543Smrg    {
512706f2543Smrg	if (*prev == timer)
513706f2543Smrg	{
514706f2543Smrg	    *prev = timer->next;
515706f2543Smrg	    break;
516706f2543Smrg	}
517706f2543Smrg    }
518706f2543Smrg}
519706f2543Smrg
520706f2543Smrgvoid
521706f2543SmrgTimerFree(OsTimerPtr timer)
522706f2543Smrg{
523706f2543Smrg    if (!timer)
524706f2543Smrg	return;
525706f2543Smrg    TimerCancel(timer);
526706f2543Smrg    free(timer);
527706f2543Smrg}
528706f2543Smrg
529706f2543Smrgvoid
530706f2543SmrgTimerCheck(void)
531706f2543Smrg{
532706f2543Smrg    CARD32 now = GetTimeInMillis();
533706f2543Smrg
534706f2543Smrg    while (timers && (int) (timers->expires - now) <= 0)
535706f2543Smrg	DoTimer(timers, now, &timers);
536706f2543Smrg}
537706f2543Smrg
538706f2543Smrgvoid
539706f2543SmrgTimerInit(void)
540706f2543Smrg{
541706f2543Smrg    OsTimerPtr timer;
542706f2543Smrg
543706f2543Smrg    while ((timer = timers))
544706f2543Smrg    {
545706f2543Smrg	timers = timer->next;
546706f2543Smrg	free(timer);
547706f2543Smrg    }
548706f2543Smrg}
549706f2543Smrg
550706f2543Smrg#ifdef DPMSExtension
551706f2543Smrg
552706f2543Smrg#define DPMS_CHECK_MODE(mode,time)\
553706f2543Smrg    if (time > 0 && DPMSPowerLevel < mode && timeout >= time)\
554706f2543Smrg	DPMSSet(serverClient, mode);
555706f2543Smrg
556706f2543Smrg#define DPMS_CHECK_TIMEOUT(time)\
557706f2543Smrg    if (time > 0 && (time - timeout) > 0)\
558706f2543Smrg	return time - timeout;
559706f2543Smrg
560706f2543Smrgstatic CARD32
561706f2543SmrgNextDPMSTimeout(INT32 timeout)
562706f2543Smrg{
563706f2543Smrg    /*
564706f2543Smrg     * Return the amount of time remaining until we should set
565706f2543Smrg     * the next power level. Fallthroughs are intentional.
566706f2543Smrg     */
567706f2543Smrg    switch (DPMSPowerLevel)
568706f2543Smrg    {
569706f2543Smrg	case DPMSModeOn:
570706f2543Smrg	    DPMS_CHECK_TIMEOUT(DPMSStandbyTime)
571706f2543Smrg
572706f2543Smrg	case DPMSModeStandby:
573706f2543Smrg	    DPMS_CHECK_TIMEOUT(DPMSSuspendTime)
574706f2543Smrg
575706f2543Smrg	case DPMSModeSuspend:
576706f2543Smrg	    DPMS_CHECK_TIMEOUT(DPMSOffTime)
577706f2543Smrg
578706f2543Smrg	default: /* DPMSModeOff */
579706f2543Smrg	    return 0;
580706f2543Smrg    }
581706f2543Smrg}
582706f2543Smrg#endif /* DPMSExtension */
583706f2543Smrg
584706f2543Smrgstatic CARD32
585706f2543SmrgScreenSaverTimeoutExpire(OsTimerPtr timer,CARD32 now,pointer arg)
586706f2543Smrg{
587706f2543Smrg    INT32 timeout      = now - lastDeviceEventTime.milliseconds;
588706f2543Smrg    CARD32 nextTimeout = 0;
589706f2543Smrg
590706f2543Smrg#ifdef DPMSExtension
591706f2543Smrg    /*
592706f2543Smrg     * Check each mode lowest to highest, since a lower mode can
593706f2543Smrg     * have the same timeout as a higher one.
594706f2543Smrg     */
595706f2543Smrg    if (DPMSEnabled)
596706f2543Smrg    {
597706f2543Smrg	DPMS_CHECK_MODE(DPMSModeOff,     DPMSOffTime)
598706f2543Smrg	DPMS_CHECK_MODE(DPMSModeSuspend, DPMSSuspendTime)
599706f2543Smrg	DPMS_CHECK_MODE(DPMSModeStandby, DPMSStandbyTime)
600706f2543Smrg
601706f2543Smrg	nextTimeout = NextDPMSTimeout(timeout);
602706f2543Smrg    }
603706f2543Smrg
604706f2543Smrg    /*
605706f2543Smrg     * Only do the screensaver checks if we're not in a DPMS
606706f2543Smrg     * power saving mode
607706f2543Smrg     */
608706f2543Smrg    if (DPMSPowerLevel != DPMSModeOn)
609706f2543Smrg	return nextTimeout;
610706f2543Smrg#endif /* DPMSExtension */
611706f2543Smrg
612706f2543Smrg    if (!ScreenSaverTime)
613706f2543Smrg	return nextTimeout;
614706f2543Smrg
615706f2543Smrg    if (timeout < ScreenSaverTime)
616706f2543Smrg    {
617706f2543Smrg	return nextTimeout > 0 ?
618706f2543Smrg		min(ScreenSaverTime - timeout, nextTimeout) :
619706f2543Smrg		ScreenSaverTime - timeout;
620706f2543Smrg    }
621706f2543Smrg
622706f2543Smrg    ResetOsBuffers(); /* not ideal, but better than nothing */
623706f2543Smrg    dixSaveScreens(serverClient, SCREEN_SAVER_ON, ScreenSaverActive);
624706f2543Smrg
625706f2543Smrg    if (ScreenSaverInterval > 0)
626706f2543Smrg    {
627706f2543Smrg	nextTimeout = nextTimeout > 0 ?
628706f2543Smrg		min(ScreenSaverInterval, nextTimeout) :
629706f2543Smrg		ScreenSaverInterval;
630706f2543Smrg    }
631706f2543Smrg
632706f2543Smrg    return nextTimeout;
633706f2543Smrg}
634706f2543Smrg
635706f2543Smrgstatic OsTimerPtr ScreenSaverTimer = NULL;
636706f2543Smrg
637706f2543Smrgvoid
638706f2543SmrgFreeScreenSaverTimer(void)
639706f2543Smrg{
640706f2543Smrg    if (ScreenSaverTimer) {
641706f2543Smrg	TimerFree(ScreenSaverTimer);
642706f2543Smrg	ScreenSaverTimer = NULL;
643706f2543Smrg    }
644706f2543Smrg}
645706f2543Smrg
646706f2543Smrgvoid
647706f2543SmrgSetScreenSaverTimer(void)
648706f2543Smrg{
649706f2543Smrg    CARD32 timeout = 0;
650706f2543Smrg
651706f2543Smrg#ifdef DPMSExtension
652706f2543Smrg    if (DPMSEnabled)
653706f2543Smrg    {
654706f2543Smrg	/*
655706f2543Smrg	 * A higher DPMS level has a timeout that's either less
656706f2543Smrg	 * than or equal to that of a lower DPMS level.
657706f2543Smrg	 */
658706f2543Smrg	if (DPMSStandbyTime > 0)
659706f2543Smrg	    timeout = DPMSStandbyTime;
660706f2543Smrg
661706f2543Smrg	else if (DPMSSuspendTime > 0)
662706f2543Smrg	    timeout = DPMSSuspendTime;
663706f2543Smrg
664706f2543Smrg	else if (DPMSOffTime > 0)
665706f2543Smrg	    timeout = DPMSOffTime;
666706f2543Smrg    }
667706f2543Smrg#endif
668706f2543Smrg
669706f2543Smrg    if (ScreenSaverTime > 0)
670706f2543Smrg    {
671706f2543Smrg	timeout = timeout > 0 ?
672706f2543Smrg		min(ScreenSaverTime, timeout) :
673706f2543Smrg		ScreenSaverTime;
674706f2543Smrg    }
675706f2543Smrg
676706f2543Smrg#ifdef SCREENSAVER
677706f2543Smrg    if (timeout && !screenSaverSuspended) {
678706f2543Smrg#else
679706f2543Smrg    if (timeout) {
680706f2543Smrg#endif
681706f2543Smrg	ScreenSaverTimer = TimerSet(ScreenSaverTimer, 0, timeout,
682706f2543Smrg	                            ScreenSaverTimeoutExpire, NULL);
683706f2543Smrg    }
684706f2543Smrg    else if (ScreenSaverTimer) {
685706f2543Smrg	FreeScreenSaverTimer();
686706f2543Smrg    }
687706f2543Smrg}
688706f2543Smrg
689