1 /*
2  *
3 Copyright 1992, 1998  The Open Group
4 
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10 
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in 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 
43 typedef 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 
55 static SertafiedPtr pPending;
56 static RESTYPE SertafiedResType;
57 static Bool BlockHandlerRegistered;
58 static int SertafiedGeneration;
59 
60 static void ClientAwaken(ClientPtr /* client */ ,
61                          void *    /* closure */
62     );
63 static int SertafiedDelete(void *  /* value */ ,
64                            XID     /* id */
65     );
66 static void SertafiedBlockHandler(void *data,
67                                   void *timeout);
68 
69 static void SertafiedWakeupHandler(void *data,
70                                    int i);
71 
72 int
ClientSleepUntil(ClientPtr client,TimeStamp * revive,void (* notifyFunc)(ClientPtr,void *),void * closure)73 ClientSleepUntil(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 
125 static void
ClientAwaken(ClientPtr client,void * closure)126 ClientAwaken(ClientPtr client, void *closure)
127 {
128     AttendClient(client);
129 }
130 
131 static int
SertafiedDelete(void * value,XID id)132 SertafiedDelete(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 
152 static void
SertafiedBlockHandler(void * data,void * wt)153 SertafiedBlockHandler(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 
184 static void
SertafiedWakeupHandler(void * data,int i)185 SertafiedWakeupHandler(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