sleepuntil.c revision 1b5d61b8
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    if (!client->clientGone)
129        AttendClient(client);
130}
131
132static int
133SertafiedDelete(void *value, XID id)
134{
135    SertafiedPtr pRequest = (SertafiedPtr) value;
136    SertafiedPtr pReq, pPrev;
137
138    pPrev = 0;
139    for (pReq = pPending; pReq; pPrev = pReq, pReq = pReq->next)
140        if (pReq == pRequest) {
141            if (pPrev)
142                pPrev->next = pReq->next;
143            else
144                pPending = pReq->next;
145            break;
146        }
147    if (pRequest->notifyFunc)
148        (*pRequest->notifyFunc) (pRequest->pClient, pRequest->closure);
149    free(pRequest);
150    return TRUE;
151}
152
153static void
154SertafiedBlockHandler(void *data, void *wt)
155{
156    SertafiedPtr pReq, pNext;
157    unsigned long delay;
158    TimeStamp now;
159
160    if (!pPending)
161        return;
162    now.milliseconds = GetTimeInMillis();
163    now.months = currentTime.months;
164    if ((int) (now.milliseconds - currentTime.milliseconds) < 0)
165        now.months++;
166    for (pReq = pPending; pReq; pReq = pNext) {
167        pNext = pReq->next;
168        if (CompareTimeStamps(pReq->revive, now) == LATER)
169            break;
170        FreeResource(pReq->id, RT_NONE);
171
172        /* AttendClient() may have been called via the resource delete
173         * function so a client may have input to be processed and so
174         *  set delay to 0 to prevent blocking in WaitForSomething().
175         */
176        AdjustWaitForDelay(wt, 0);
177    }
178    pReq = pPending;
179    if (!pReq)
180        return;
181    delay = pReq->revive.milliseconds - now.milliseconds;
182    AdjustWaitForDelay(wt, delay);
183}
184
185static void
186SertafiedWakeupHandler(void *data, int i)
187{
188    SertafiedPtr pReq, pNext;
189    TimeStamp now;
190
191    now.milliseconds = GetTimeInMillis();
192    now.months = currentTime.months;
193    if ((int) (now.milliseconds - currentTime.milliseconds) < 0)
194        now.months++;
195    for (pReq = pPending; pReq; pReq = pNext) {
196        pNext = pReq->next;
197        if (CompareTimeStamps(pReq->revive, now) == LATER)
198            break;
199        FreeResource(pReq->id, RT_NONE);
200    }
201    if (!pPending) {
202        RemoveBlockAndWakeupHandlers(SertafiedBlockHandler,
203                                     SertafiedWakeupHandler, (void *) 0);
204        BlockHandlerRegistered = FALSE;
205    }
206}
207