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
25Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
26
27                        All Rights Reserved
28
29Permission to use, copy, modify, and distribute this software and its
30documentation for any purpose and without fee is hereby granted,
31provided that the above copyright notice appear in all copies and that
32both that copyright notice and this permission notice appear in
33supporting documentation, and that the name of Digital not be
34used in advertising or publicity pertaining to distribution of the
35software without specific, written prior permission.
36
37DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
39DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
40ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
41WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
42ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43SOFTWARE.
44
45******************************************************************/
46
47/*****************************************************************
48 * OS Dependent input routines:
49 *
50 *  WaitForSomething
51 *  TimerForce, TimerSet, TimerCheck, TimerFree
52 *
53 *****************************************************************/
54
55#include <X11/Xpoll.h>
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 "dixstruct.h"
72#include "opaque.h"
73#ifdef DPMSExtension
74#include "dpmsproc.h"
75#endif
76#include "busfault.h"
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#ifdef DPMSExtension
96#include <X11/extensions/dpmsconst.h>
97#endif
98
99struct _OsTimerRec {
100    struct xorg_list list;
101    CARD32 expires;
102    CARD32 delta;
103    OsTimerCallback callback;
104    void *arg;
105};
106
107static void DoTimer(OsTimerPtr timer, CARD32 now);
108static void DoTimers(CARD32 now);
109static void CheckAllTimers(void);
110static volatile struct xorg_list timers;
111
112static inline OsTimerPtr
113first_timer(void)
114{
115    /* inline xorg_list_is_empty which can't handle volatile */
116    if (timers.next == &timers)
117        return NULL;
118    return xorg_list_first_entry(&timers, struct _OsTimerRec, list);
119}
120
121/*
122 * Compute timeout until next timer, running
123 * any expired timers
124 */
125static int
126check_timers(void)
127{
128    OsTimerPtr timer;
129
130    if ((timer = first_timer()) != NULL) {
131        CARD32 now = GetTimeInMillis();
132        int timeout = timer->expires - now;
133
134        if (timeout <= 0) {
135            DoTimers(now);
136        } else {
137            /* Make sure the timeout is sane */
138            if (timeout < timer->delta + 250)
139                return timeout;
140
141            /* time has rewound.  reset the timers. */
142            CheckAllTimers();
143        }
144
145        return 0;
146    }
147    return -1;
148}
149
150/*****************
151 * WaitForSomething:
152 *     Make the server suspend until there is
153 *	1. data from clients or
154 *	2. input events available or
155 *	3. ddx notices something of interest (graphics
156 *	   queue ready, etc.) or
157 *	4. clients that have buffered replies/events are ready
158 *
159 *     If the time between INPUT events is
160 *     greater than ScreenSaverTime, the display is turned off (or
161 *     saved, depending on the hardware).  So, WaitForSomething()
162 *     has to handle this also (that's why the select() has a timeout.
163 *     For more info on ClientsWithInput, see ReadRequestFromClient().
164 *     pClientsReady is an array to store ready client->index values into.
165 *****************/
166
167Bool
168WaitForSomething(Bool are_ready)
169{
170    int i;
171    int timeout;
172    int pollerr;
173    static Bool were_ready;
174    Bool timer_is_running;
175
176    timer_is_running = were_ready;
177
178    if (were_ready && !are_ready) {
179        timer_is_running = FALSE;
180        SmartScheduleStopTimer();
181    }
182
183    were_ready = FALSE;
184
185#ifdef BUSFAULT
186    busfault_check();
187#endif
188
189    /* We need a while loop here to handle
190       crashed connections and the screen saver timeout */
191    while (1) {
192        /* deal with any blocked jobs */
193        if (workQueue) {
194            ProcessWorkQueue();
195        }
196
197        timeout = check_timers();
198        are_ready = clients_are_ready();
199
200        if (are_ready)
201            timeout = 0;
202
203        BlockHandler(&timeout);
204        if (NewOutputPending)
205            FlushAllOutput();
206        /* keep this check close to select() call to minimize race */
207        if (dispatchException)
208            i = -1;
209        else
210            i = ospoll_wait(server_poll, timeout);
211        pollerr = GetErrno();
212        WakeupHandler(i);
213        if (i <= 0) {           /* An error or timeout occurred */
214            if (dispatchException)
215                return FALSE;
216            if (i < 0) {
217                if (pollerr != EINTR && !ETEST(pollerr)) {
218                    ErrorF("WaitForSomething(): poll: %s\n",
219                           strerror(pollerr));
220                }
221            }
222        } else
223            are_ready = clients_are_ready();
224
225        if (InputCheckPending())
226            return FALSE;
227
228        if (are_ready) {
229            were_ready = TRUE;
230            if (!timer_is_running)
231                SmartScheduleStartTimer();
232            return TRUE;
233        }
234    }
235}
236
237void
238AdjustWaitForDelay(void *waitTime, int newdelay)
239{
240    int *timeoutp = waitTime;
241    int timeout = *timeoutp;
242
243    if (timeout < 0 || newdelay < timeout)
244        *timeoutp = newdelay;
245}
246
247static inline Bool timer_pending(OsTimerPtr timer) {
248    return !xorg_list_is_empty(&timer->list);
249}
250
251/* If time has rewound, re-run every affected timer.
252 * Timers might drop out of the list, so we have to restart every time. */
253static void
254CheckAllTimers(void)
255{
256    OsTimerPtr timer;
257    CARD32 now;
258
259    input_lock();
260 start:
261    now = GetTimeInMillis();
262
263    xorg_list_for_each_entry(timer, &timers, list) {
264        if (timer->expires - now > timer->delta + 250) {
265            DoTimer(timer, now);
266            goto start;
267        }
268    }
269    input_unlock();
270}
271
272static void
273DoTimer(OsTimerPtr timer, CARD32 now)
274{
275    CARD32 newTime;
276
277    xorg_list_del(&timer->list);
278    newTime = (*timer->callback) (timer, now, timer->arg);
279    if (newTime)
280        TimerSet(timer, 0, newTime, timer->callback, timer->arg);
281}
282
283static void
284DoTimers(CARD32 now)
285{
286    OsTimerPtr  timer;
287
288    input_lock();
289    while ((timer = first_timer())) {
290        if ((int) (timer->expires - now) > 0)
291            break;
292        DoTimer(timer, now);
293    }
294    input_unlock();
295}
296
297OsTimerPtr
298TimerSet(OsTimerPtr timer, int flags, CARD32 millis,
299         OsTimerCallback func, void *arg)
300{
301    OsTimerPtr existing;
302    CARD32 now = GetTimeInMillis();
303
304    if (!timer) {
305        timer = calloc(1, sizeof(struct _OsTimerRec));
306        if (!timer)
307            return NULL;
308        xorg_list_init(&timer->list);
309    }
310    else {
311        input_lock();
312        if (timer_pending(timer)) {
313            xorg_list_del(&timer->list);
314            if (flags & TimerForceOld)
315                (void) (*timer->callback) (timer, now, timer->arg);
316        }
317        input_unlock();
318    }
319    if (!millis)
320        return timer;
321    if (flags & TimerAbsolute) {
322        timer->delta = millis - now;
323    }
324    else {
325        timer->delta = millis;
326        millis += now;
327    }
328    timer->expires = millis;
329    timer->callback = func;
330    timer->arg = arg;
331    input_lock();
332
333    /* Sort into list */
334    xorg_list_for_each_entry(existing, &timers, list)
335        if ((int) (existing->expires - millis) > 0)
336            break;
337    /* This even works at the end of the list -- existing->list will be timers */
338    xorg_list_append(&timer->list, &existing->list);
339
340    /* Check to see if the timer is ready to run now */
341    if ((int) (millis - now) <= 0)
342        DoTimer(timer, now);
343
344    input_unlock();
345    return timer;
346}
347
348Bool
349TimerForce(OsTimerPtr timer)
350{
351    int pending;
352
353    input_lock();
354    pending = timer_pending(timer);
355    if (pending)
356        DoTimer(timer, GetTimeInMillis());
357    input_unlock();
358    return pending;
359}
360
361void
362TimerCancel(OsTimerPtr timer)
363{
364    if (!timer)
365        return;
366    input_lock();
367    xorg_list_del(&timer->list);
368    input_unlock();
369}
370
371void
372TimerFree(OsTimerPtr timer)
373{
374    if (!timer)
375        return;
376    TimerCancel(timer);
377    free(timer);
378}
379
380void
381TimerCheck(void)
382{
383    DoTimers(GetTimeInMillis());
384}
385
386void
387TimerInit(void)
388{
389    static Bool been_here;
390    OsTimerPtr timer, tmp;
391
392    if (!been_here) {
393        been_here = TRUE;
394        xorg_list_init((struct xorg_list*) &timers);
395    }
396
397    xorg_list_for_each_entry_safe(timer, tmp, &timers, list) {
398        xorg_list_del(&timer->list);
399        free(timer);
400    }
401}
402
403#ifdef DPMSExtension
404
405#define DPMS_CHECK_MODE(mode,time)\
406    if (time > 0 && DPMSPowerLevel < mode && timeout >= time)\
407	DPMSSet(serverClient, mode);
408
409#define DPMS_CHECK_TIMEOUT(time)\
410    if (time > 0 && (time - timeout) > 0)\
411	return time - timeout;
412
413static CARD32
414NextDPMSTimeout(INT32 timeout)
415{
416    /*
417     * Return the amount of time remaining until we should set
418     * the next power level. Fallthroughs are intentional.
419     */
420    switch (DPMSPowerLevel) {
421    case DPMSModeOn:
422        DPMS_CHECK_TIMEOUT(DPMSStandbyTime)
423        /* fallthrough */
424    case DPMSModeStandby:
425        DPMS_CHECK_TIMEOUT(DPMSSuspendTime)
426        /* fallthrough */
427    case DPMSModeSuspend:
428        DPMS_CHECK_TIMEOUT(DPMSOffTime)
429        /* fallthrough */
430    default:                   /* DPMSModeOff */
431        return 0;
432    }
433}
434#endif                          /* DPMSExtension */
435
436static CARD32
437ScreenSaverTimeoutExpire(OsTimerPtr timer, CARD32 now, void *arg)
438{
439    INT32 timeout = now - LastEventTime(XIAllDevices).milliseconds;
440    CARD32 nextTimeout = 0;
441
442#ifdef DPMSExtension
443    /*
444     * Check each mode lowest to highest, since a lower mode can
445     * have the same timeout as a higher one.
446     */
447    if (DPMSEnabled) {
448        DPMS_CHECK_MODE(DPMSModeOff, DPMSOffTime)
449            DPMS_CHECK_MODE(DPMSModeSuspend, DPMSSuspendTime)
450            DPMS_CHECK_MODE(DPMSModeStandby, DPMSStandbyTime)
451
452            nextTimeout = NextDPMSTimeout(timeout);
453    }
454
455    /*
456     * Only do the screensaver checks if we're not in a DPMS
457     * power saving mode
458     */
459    if (DPMSPowerLevel != DPMSModeOn)
460        return nextTimeout;
461#endif                          /* DPMSExtension */
462
463    if (!ScreenSaverTime)
464        return nextTimeout;
465
466    if (timeout < ScreenSaverTime) {
467        return nextTimeout > 0 ?
468            min(ScreenSaverTime - timeout, nextTimeout) :
469            ScreenSaverTime - timeout;
470    }
471
472    ResetOsBuffers();           /* not ideal, but better than nothing */
473    dixSaveScreens(serverClient, SCREEN_SAVER_ON, ScreenSaverActive);
474
475    if (ScreenSaverInterval > 0) {
476        nextTimeout = nextTimeout > 0 ?
477            min(ScreenSaverInterval, nextTimeout) : ScreenSaverInterval;
478    }
479
480    return nextTimeout;
481}
482
483static OsTimerPtr ScreenSaverTimer = NULL;
484
485void
486FreeScreenSaverTimer(void)
487{
488    if (ScreenSaverTimer) {
489        TimerFree(ScreenSaverTimer);
490        ScreenSaverTimer = NULL;
491    }
492}
493
494void
495SetScreenSaverTimer(void)
496{
497    CARD32 timeout = 0;
498
499#ifdef DPMSExtension
500    if (DPMSEnabled) {
501        /*
502         * A higher DPMS level has a timeout that's either less
503         * than or equal to that of a lower DPMS level.
504         */
505        if (DPMSStandbyTime > 0)
506            timeout = DPMSStandbyTime;
507
508        else if (DPMSSuspendTime > 0)
509            timeout = DPMSSuspendTime;
510
511        else if (DPMSOffTime > 0)
512            timeout = DPMSOffTime;
513    }
514#endif
515
516    if (ScreenSaverTime > 0) {
517        timeout = timeout > 0 ? min(ScreenSaverTime, timeout) : ScreenSaverTime;
518    }
519
520#ifdef SCREENSAVER
521    if (timeout && !screenSaverSuspended) {
522#else
523    if (timeout) {
524#endif
525        ScreenSaverTimer = TimerSet(ScreenSaverTimer, 0, timeout,
526                                    ScreenSaverTimeoutExpire, NULL);
527    }
528    else if (ScreenSaverTimer) {
529        FreeScreenSaverTimer();
530    }
531}
532