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