1/*
2 *
3Copyright 1992, 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 * Author:  Keith Packard, MIT X Consortium
26 */
27
28/* dixsleep.c - implement millisecond timeouts for X clients */
29
30#ifdef HAVE_DIX_CONFIG_H
31#include <dix-config.h>
32#endif
33
34#include "sleepuntil.h"
35#include <X11/X.h>
36#include <X11/Xmd.h>
37#include "misc.h"
38#include "windowstr.h"
39#include "dixstruct.h"
40#include "pixmapstr.h"
41#include "scrnintstr.h"
42
43typedef struct _Sertafied {
44    struct _Sertafied *next;
45    TimeStamp revive;
46    ClientPtr pClient;
47    XID id;
48    void (*notifyFunc) (ClientPtr /* client */ ,
49                        void *    /* closure */
50        );
51
52    void *closure;
53} SertafiedRec, *SertafiedPtr;
54
55static SertafiedPtr pPending;
56static RESTYPE SertafiedResType;
57static Bool BlockHandlerRegistered;
58static int SertafiedGeneration;
59
60static void ClientAwaken(ClientPtr /* client */ ,
61                         void *    /* closure */
62    );
63static int SertafiedDelete(void *  /* value */ ,
64                           XID     /* id */
65    );
66static void SertafiedBlockHandler(void *data,
67                                  void *timeout);
68
69static void SertafiedWakeupHandler(void *data,
70                                   int i);
71
72int
73ClientSleepUntil(ClientPtr client,
74                 TimeStamp *revive,
75                 void (*notifyFunc) (ClientPtr, void *), void *closure)
76{
77    SertafiedPtr pRequest, pReq, pPrev;
78
79    if (SertafiedGeneration != serverGeneration) {
80        SertafiedResType = CreateNewResourceType(SertafiedDelete,
81                                                 "ClientSleep");
82        if (!SertafiedResType)
83            return FALSE;
84        SertafiedGeneration = serverGeneration;
85        BlockHandlerRegistered = FALSE;
86    }
87    pRequest = malloc(sizeof(SertafiedRec));
88    if (!pRequest)
89        return FALSE;
90    pRequest->pClient = client;
91    pRequest->revive = *revive;
92    pRequest->id = FakeClientID(client->index);
93    pRequest->closure = closure;
94    if (!BlockHandlerRegistered) {
95        if (!RegisterBlockAndWakeupHandlers(SertafiedBlockHandler,
96                                            SertafiedWakeupHandler,
97                                            (void *) 0)) {
98            free(pRequest);
99            return FALSE;
100        }
101        BlockHandlerRegistered = TRUE;
102    }
103    pRequest->notifyFunc = 0;
104    if (!AddResource(pRequest->id, SertafiedResType, (void *) pRequest))
105        return FALSE;
106    if (!notifyFunc)
107        notifyFunc = ClientAwaken;
108    pRequest->notifyFunc = notifyFunc;
109    /* Insert into time-ordered queue, with earliest activation time coming first. */
110    pPrev = 0;
111    for (pReq = pPending; pReq; pReq = pReq->next) {
112        if (CompareTimeStamps(pReq->revive, *revive) == LATER)
113            break;
114        pPrev = pReq;
115    }
116    if (pPrev)
117        pPrev->next = pRequest;
118    else
119        pPending = pRequest;
120    pRequest->next = pReq;
121    IgnoreClient(client);
122    return TRUE;
123}
124
125static void
126ClientAwaken(ClientPtr client, void *closure)
127{
128    AttendClient(client);
129}
130
131static int
132SertafiedDelete(void *value, XID id)
133{
134    SertafiedPtr pRequest = (SertafiedPtr) value;
135    SertafiedPtr pReq, pPrev;
136
137    pPrev = 0;
138    for (pReq = pPending; pReq; pPrev = pReq, pReq = pReq->next)
139        if (pReq == pRequest) {
140            if (pPrev)
141                pPrev->next = pReq->next;
142            else
143                pPending = pReq->next;
144            break;
145        }
146    if (pRequest->notifyFunc)
147        (*pRequest->notifyFunc) (pRequest->pClient, pRequest->closure);
148    free(pRequest);
149    return TRUE;
150}
151
152static void
153SertafiedBlockHandler(void *data, void *wt)
154{
155    SertafiedPtr pReq, pNext;
156    unsigned long delay;
157    TimeStamp now;
158
159    if (!pPending)
160        return;
161    now.milliseconds = GetTimeInMillis();
162    now.months = currentTime.months;
163    if ((int) (now.milliseconds - currentTime.milliseconds) < 0)
164        now.months++;
165    for (pReq = pPending; pReq; pReq = pNext) {
166        pNext = pReq->next;
167        if (CompareTimeStamps(pReq->revive, now) == LATER)
168            break;
169        FreeResource(pReq->id, RT_NONE);
170
171        /* AttendClient() may have been called via the resource delete
172         * function so a client may have input to be processed and so
173         *  set delay to 0 to prevent blocking in WaitForSomething().
174         */
175        AdjustWaitForDelay(wt, 0);
176    }
177    pReq = pPending;
178    if (!pReq)
179        return;
180    delay = pReq->revive.milliseconds - now.milliseconds;
181    AdjustWaitForDelay(wt, delay);
182}
183
184static void
185SertafiedWakeupHandler(void *data, int i)
186{
187    SertafiedPtr pReq, pNext;
188    TimeStamp now;
189
190    now.milliseconds = GetTimeInMillis();
191    now.months = currentTime.months;
192    if ((int) (now.milliseconds - currentTime.milliseconds) < 0)
193        now.months++;
194    for (pReq = pPending; pReq; pReq = pNext) {
195        pNext = pReq->next;
196        if (CompareTimeStamps(pReq->revive, now) == LATER)
197            break;
198        FreeResource(pReq->id, RT_NONE);
199    }
200    if (!pPending) {
201        RemoveBlockAndWakeupHandlers(SertafiedBlockHandler,
202                                     SertafiedWakeupHandler, (void *) 0);
203        BlockHandlerRegistered = FALSE;
204    }
205}
206