sleepuntil.c revision 35c4bbdf
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                                  OSTimePtr /* wt */ ,
68                                  void *    /* LastSelectMask */
69    );
70static void SertafiedWakeupHandler(void *   /* data */ ,
71                                   int      /* i */ ,
72                                   void *   /* LastSelectMask */
73    );
74
75int
76ClientSleepUntil(ClientPtr client,
77                 TimeStamp *revive,
78                 void (*notifyFunc) (ClientPtr, void *), void *closure)
79{
80    SertafiedPtr pRequest, pReq, pPrev;
81
82    if (SertafiedGeneration != serverGeneration) {
83        SertafiedResType = CreateNewResourceType(SertafiedDelete,
84                                                 "ClientSleep");
85        if (!SertafiedResType)
86            return FALSE;
87        SertafiedGeneration = serverGeneration;
88        BlockHandlerRegistered = FALSE;
89    }
90    pRequest = malloc(sizeof(SertafiedRec));
91    if (!pRequest)
92        return FALSE;
93    pRequest->pClient = client;
94    pRequest->revive = *revive;
95    pRequest->id = FakeClientID(client->index);
96    pRequest->closure = closure;
97    if (!BlockHandlerRegistered) {
98        if (!RegisterBlockAndWakeupHandlers(SertafiedBlockHandler,
99                                            SertafiedWakeupHandler,
100                                            (void *) 0)) {
101            free(pRequest);
102            return FALSE;
103        }
104        BlockHandlerRegistered = TRUE;
105    }
106    pRequest->notifyFunc = 0;
107    if (!AddResource(pRequest->id, SertafiedResType, (void *) pRequest))
108        return FALSE;
109    if (!notifyFunc)
110        notifyFunc = ClientAwaken;
111    pRequest->notifyFunc = notifyFunc;
112    /* Insert into time-ordered queue, with earliest activation time coming first. */
113    pPrev = 0;
114    for (pReq = pPending; pReq; pReq = pReq->next) {
115        if (CompareTimeStamps(pReq->revive, *revive) == LATER)
116            break;
117        pPrev = pReq;
118    }
119    if (pPrev)
120        pPrev->next = pRequest;
121    else
122        pPending = pRequest;
123    pRequest->next = pReq;
124    IgnoreClient(client);
125    return TRUE;
126}
127
128static void
129ClientAwaken(ClientPtr client, void *closure)
130{
131    if (!client->clientGone)
132        AttendClient(client);
133}
134
135static int
136SertafiedDelete(void *value, XID id)
137{
138    SertafiedPtr pRequest = (SertafiedPtr) value;
139    SertafiedPtr pReq, pPrev;
140
141    pPrev = 0;
142    for (pReq = pPending; pReq; pPrev = pReq, pReq = pReq->next)
143        if (pReq == pRequest) {
144            if (pPrev)
145                pPrev->next = pReq->next;
146            else
147                pPending = pReq->next;
148            break;
149        }
150    if (pRequest->notifyFunc)
151        (*pRequest->notifyFunc) (pRequest->pClient, pRequest->closure);
152    free(pRequest);
153    return TRUE;
154}
155
156static void
157SertafiedBlockHandler(void *data, OSTimePtr wt, void *LastSelectMask)
158{
159    SertafiedPtr pReq, pNext;
160    unsigned long delay;
161    TimeStamp now;
162
163    if (!pPending)
164        return;
165    now.milliseconds = GetTimeInMillis();
166    now.months = currentTime.months;
167    if ((int) (now.milliseconds - currentTime.milliseconds) < 0)
168        now.months++;
169    for (pReq = pPending; pReq; pReq = pNext) {
170        pNext = pReq->next;
171        if (CompareTimeStamps(pReq->revive, now) == LATER)
172            break;
173        FreeResource(pReq->id, RT_NONE);
174
175        /* AttendClient() may have been called via the resource delete
176         * function so a client may have input to be processed and so
177         *  set delay to 0 to prevent blocking in WaitForSomething().
178         */
179        AdjustWaitForDelay(wt, 0);
180    }
181    pReq = pPending;
182    if (!pReq)
183        return;
184    delay = pReq->revive.milliseconds - now.milliseconds;
185    AdjustWaitForDelay(wt, delay);
186}
187
188static void
189SertafiedWakeupHandler(void *data, int i, void *LastSelectMask)
190{
191    SertafiedPtr pReq, pNext;
192    TimeStamp now;
193
194    now.milliseconds = GetTimeInMillis();
195    now.months = currentTime.months;
196    if ((int) (now.milliseconds - currentTime.milliseconds) < 0)
197        now.months++;
198    for (pReq = pPending; pReq; pReq = pNext) {
199        pNext = pReq->next;
200        if (CompareTimeStamps(pReq->revive, now) == LATER)
201            break;
202        FreeResource(pReq->id, RT_NONE);
203    }
204    if (!pPending) {
205        RemoveBlockAndWakeupHandlers(SertafiedBlockHandler,
206                                     SertafiedWakeupHandler, (void *) 0);
207        BlockHandlerRegistered = FALSE;
208    }
209}
210