Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /*********************************************************** |
2 | ||
3 | Copyright 1987, 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 | Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. | |
26 | ||
27 | All Rights Reserved | |
28 | ||
29 | Permission to use, copy, modify, and distribute this software and its | |
30 | documentation for any purpose and without fee is hereby granted, | |
31 | provided that the above copyright notice appear in all copies and that | |
32 | both that copyright notice and this permission notice appear in | |
33 | supporting documentation, and that the name of Digital not be | |
34 | used in advertising or publicity pertaining to distribution of the | |
35 | software without specific, written prior permission. | |
36 | ||
37 | DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | |
38 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL | |
39 | DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | |
40 | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
41 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
42 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
43 | SOFTWARE. | |
44 | ||
45 | ******************************************************************/ | |
46 | ||
47 | /* | |
48 | ||
49 | (c)Copyright 1988,1991 Adobe Systems Incorporated. All rights reserved. | |
50 | ||
51 | Permission to use, copy, modify, distribute, and sublicense this software and its | |
52 | documentation for any purpose and without fee is hereby granted, provided that | |
53 | the above copyright notices appear in all copies and that both those copyright | |
54 | notices and this permission notice appear in supporting documentation and that | |
55 | the name of Adobe Systems Incorporated not be used in advertising or publicity | |
56 | pertaining to distribution of the software without specific, written prior | |
57 | permission. No trademark license to use the Adobe trademarks is hereby | |
58 | granted. If the Adobe trademark "Display PostScript"(tm) is used to describe | |
59 | this software, its functionality or for any other purpose, such use shall be | |
60 | limited to a statement that this software works in conjunction with the Display | |
61 | PostScript system. Proper trademark attribution to reflect Adobe's ownership | |
62 | of the trademark shall be given whenever any such reference to the Display | |
63 | PostScript system is made. | |
64 | ||
65 | ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY | |
66 | PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ADOBE | |
67 | DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED | |
68 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON- | |
69 | INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL ADOBE BE LIABLE TO YOU | |
70 | OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY | |
71 | DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,NEGLIGENCE, STRICT | |
72 | LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
73 | PERFORMANCE OF THIS SOFTWARE. ADOBE WILL NOT PROVIDE ANY TRAINING OR OTHER | |
74 | SUPPORT FOR THE SOFTWARE. | |
75 | ||
76 | Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems | |
77 | Incorporated which may be registered in certain jurisdictions. | |
78 | ||
79 | Author: Adobe Systems Incorporated | |
80 | ||
81 | */ | |
82 | ||
83 | #ifdef HAVE_DIX_CONFIG_H | |
84 | #include <dix-config.h> | |
85 | #endif | |
86 | ||
87 | #include <X11/X.h> | |
88 | #include <X11/Xmd.h> | |
89 | #include "misc.h" | |
90 | #include "windowstr.h" | |
91 | #include "dixstruct.h" | |
92 | #include "pixmapstr.h" | |
93 | #include "gcstruct.h" | |
94 | #include "scrnintstr.h" | |
95 | #define XK_LATIN1 | |
96 | #include <X11/keysymdef.h> | |
97 | #include "xace.h" | |
98 | ||
99 | /* | |
100 | * CompareTimeStamps returns -1, 0, or +1 depending on if the first | |
101 | * argument is less than, equal to or greater than the second argument. | |
102 | */ | |
103 | ||
104 | int | |
105 | CompareTimeStamps(TimeStamp a, TimeStamp b) | |
106 | { | |
107 | if (a.months < b.months) | |
108 | return EARLIER; | |
109 | if (a.months > b.months) | |
110 | return LATER; | |
111 | if (a.milliseconds < b.milliseconds) | |
112 | return EARLIER; | |
113 | if (a.milliseconds > b.milliseconds) | |
114 | return LATER; | |
115 | return SAMETIME; | |
116 | } | |
117 | ||
118 | /* | |
119 | * convert client times to server TimeStamps | |
120 | */ | |
121 | ||
122 | #define HALFMONTH ((unsigned long) 1<<31) | |
123 | TimeStamp | |
124 | ClientTimeToServerTime(CARD32 c) | |
125 | { | |
126 | TimeStamp ts; | |
127 | ||
128 | if (c == CurrentTime) | |
129 | return currentTime; | |
130 | ts.months = currentTime.months; | |
131 | ts.milliseconds = c; | |
132 | if (c > currentTime.milliseconds) { | |
133 | if (((unsigned long) c - currentTime.milliseconds) > HALFMONTH) | |
134 | ts.months -= 1; | |
135 | } | |
136 | else if (c < currentTime.milliseconds) { | |
137 | if (((unsigned long) currentTime.milliseconds - c) > HALFMONTH) | |
138 | ts.months += 1; | |
139 | } | |
140 | return ts; | |
141 | } | |
142 | ||
143 | /* | |
144 | * ISO Latin-1 case conversion routine | |
145 | * | |
146 | * this routine always null-terminates the result, so | |
147 | * beware of too-small buffers | |
148 | */ | |
149 | ||
150 | static unsigned char | |
151 | ISOLatin1ToLower(unsigned char source) | |
152 | { | |
153 | unsigned char dest; | |
154 | ||
155 | if ((source >= XK_A) && (source <= XK_Z)) | |
156 | dest = source + (XK_a - XK_A); | |
157 | else if ((source >= XK_Agrave) && (source <= XK_Odiaeresis)) | |
158 | dest = source + (XK_agrave - XK_Agrave); | |
159 | else if ((source >= XK_Ooblique) && (source <= XK_Thorn)) | |
160 | dest = source + (XK_oslash - XK_Ooblique); | |
161 | else | |
162 | dest = source; | |
163 | return dest; | |
164 | } | |
165 | ||
166 | int | |
167 | CompareISOLatin1Lowered(const unsigned char *s1, int s1len, | |
168 | const unsigned char *s2, int s2len) | |
169 | { | |
170 | unsigned char c1, c2; | |
171 | ||
172 | for (;;) { | |
173 | /* note -- compare against zero so that -1 ignores len */ | |
174 | c1 = s1len-- ? *s1++ : '\0'; | |
175 | c2 = s2len-- ? *s2++ : '\0'; | |
176 | if (!c1 || | |
177 | (c1 != c2 && | |
178 | (c1 = ISOLatin1ToLower(c1)) != (c2 = ISOLatin1ToLower(c2)))) | |
179 | break; | |
180 | } | |
181 | return (int) c1 - (int) c2; | |
182 | } | |
183 | ||
184 | /* | |
185 | * dixLookupWindow and dixLookupDrawable: | |
186 | * Look up the window/drawable taking into account the client doing the | |
187 | * lookup, the type of drawable desired, and the type of access desired. | |
188 | * Return Success with *pDraw set if the window/drawable exists and the client | |
189 | * is allowed access, else return an error code with *pDraw set to NULL. The | |
190 | * access mask values are defined in resource.h. The type mask values are | |
191 | * defined in pixmap.h, with zero equivalent to M_DRAWABLE. | |
192 | */ | |
193 | int | |
194 | dixLookupDrawable(DrawablePtr *pDraw, XID id, ClientPtr client, | |
195 | Mask type, Mask access) | |
196 | { | |
197 | DrawablePtr pTmp; | |
198 | int rc; | |
199 | ||
200 | *pDraw = NULL; | |
201 | ||
202 | rc = dixLookupResourceByClass((pointer *) &pTmp, id, RC_DRAWABLE, client, | |
203 | access); | |
204 | ||
205 | if (rc != Success) | |
206 | client->errorValue = id; | |
207 | ||
208 | if (rc == BadValue) | |
209 | return BadDrawable; | |
210 | if (rc != Success) | |
211 | return rc; | |
212 | if (!((1 << pTmp->type) & (type ? type : M_DRAWABLE))) | |
213 | return BadMatch; | |
214 | ||
215 | *pDraw = pTmp; | |
216 | return Success; | |
217 | } | |
218 | ||
219 | int | |
220 | dixLookupWindow(WindowPtr *pWin, XID id, ClientPtr client, Mask access) | |
221 | { | |
222 | int rc; | |
223 | ||
224 | rc = dixLookupDrawable((DrawablePtr *) pWin, id, client, M_WINDOW, access); | |
225 | /* dixLookupDrawable returns BadMatch iff id is a valid Drawable | |
226 | but is not a Window. Users of dixLookupWindow expect a BadWindow | |
227 | error in this case; they don't care that it's a valid non-Window XID */ | |
228 | if (rc == BadMatch) | |
229 | rc = BadWindow; | |
230 | /* Similarly, users of dixLookupWindow don't want BadDrawable. */ | |
231 | if (rc == BadDrawable) | |
232 | rc = BadWindow; | |
233 | return rc; | |
234 | } | |
235 | ||
236 | int | |
237 | dixLookupGC(GCPtr *pGC, XID id, ClientPtr client, Mask access) | |
238 | { | |
239 | return dixLookupResourceByType((pointer *) pGC, id, RT_GC, client, access); | |
240 | } | |
241 | ||
242 | int | |
243 | dixLookupFontable(FontPtr *pFont, XID id, ClientPtr client, Mask access) | |
244 | { | |
245 | int rc; | |
246 | GC *pGC; | |
247 | ||
248 | client->errorValue = id; /* EITHER font or gc */ | |
249 | rc = dixLookupResourceByType((pointer *) pFont, id, RT_FONT, client, | |
250 | access); | |
251 | if (rc != BadFont) | |
252 | return rc; | |
253 | rc = dixLookupResourceByType((pointer *) &pGC, id, RT_GC, client, access); | |
254 | if (rc == BadGC) | |
255 | return BadFont; | |
256 | if (rc == Success) | |
257 | *pFont = pGC->font; | |
258 | return rc; | |
259 | } | |
260 | ||
261 | int | |
262 | dixLookupClient(ClientPtr *pClient, XID rid, ClientPtr client, Mask access) | |
263 | { | |
264 | pointer pRes; | |
265 | int rc = BadValue, clientIndex = CLIENT_ID(rid); | |
266 | ||
267 | if (!clientIndex || !clients[clientIndex] || (rid & SERVER_BIT)) | |
268 | goto bad; | |
269 | ||
270 | rc = dixLookupResourceByClass(&pRes, rid, RC_ANY, client, DixGetAttrAccess); | |
271 | if (rc != Success) | |
272 | goto bad; | |
273 | ||
274 | rc = XaceHook(XACE_CLIENT_ACCESS, client, clients[clientIndex], access); | |
275 | if (rc != Success) | |
276 | goto bad; | |
277 | ||
278 | *pClient = clients[clientIndex]; | |
279 | return Success; | |
280 | bad: | |
281 | if (client) | |
282 | client->errorValue = rid; | |
283 | *pClient = NULL; | |
284 | return rc; | |
285 | } | |
286 | ||
287 | int | |
288 | AlterSaveSetForClient(ClientPtr client, WindowPtr pWin, unsigned mode, | |
289 | Bool toRoot, Bool map) | |
290 | { | |
291 | int numnow; | |
292 | SaveSetElt *pTmp = NULL; | |
293 | int j; | |
294 | ||
295 | numnow = client->numSaved; | |
296 | j = 0; | |
297 | if (numnow) { | |
298 | pTmp = client->saveSet; | |
299 | while ((j < numnow) && (SaveSetWindow(pTmp[j]) != (pointer) pWin)) | |
300 | j++; | |
301 | } | |
302 | if (mode == SetModeInsert) { | |
303 | if (j < numnow) /* duplicate */ | |
304 | return Success; | |
305 | numnow++; | |
306 | pTmp = (SaveSetElt *) realloc(client->saveSet, sizeof(*pTmp) * numnow); | |
307 | if (!pTmp) | |
308 | return BadAlloc; | |
309 | client->saveSet = pTmp; | |
310 | client->numSaved = numnow; | |
311 | SaveSetAssignWindow(client->saveSet[numnow - 1], pWin); | |
312 | SaveSetAssignToRoot(client->saveSet[numnow - 1], toRoot); | |
313 | SaveSetAssignMap(client->saveSet[numnow - 1], map); | |
314 | return Success; | |
315 | } | |
316 | else if ((mode == SetModeDelete) && (j < numnow)) { | |
317 | while (j < numnow - 1) { | |
318 | pTmp[j] = pTmp[j + 1]; | |
319 | j++; | |
320 | } | |
321 | numnow--; | |
322 | if (numnow) { | |
323 | pTmp = | |
324 | (SaveSetElt *) realloc(client->saveSet, sizeof(*pTmp) * numnow); | |
325 | if (pTmp) | |
326 | client->saveSet = pTmp; | |
327 | } | |
328 | else { | |
329 | free(client->saveSet); | |
330 | client->saveSet = (SaveSetElt *) NULL; | |
331 | } | |
332 | client->numSaved = numnow; | |
333 | return Success; | |
334 | } | |
335 | return Success; | |
336 | } | |
337 | ||
338 | void | |
339 | DeleteWindowFromAnySaveSet(WindowPtr pWin) | |
340 | { | |
341 | int i; | |
342 | ClientPtr client; | |
343 | ||
344 | for (i = 0; i < currentMaxClients; i++) { | |
345 | client = clients[i]; | |
346 | if (client && client->numSaved) | |
347 | (void) AlterSaveSetForClient(client, pWin, SetModeDelete, FALSE, | |
348 | TRUE); | |
349 | } | |
350 | } | |
351 | ||
352 | /* No-op Don't Do Anything : sometimes we need to be able to call a procedure | |
353 | * that doesn't do anything. For example, on screen with only static | |
354 | * colormaps, if someone calls install colormap, it's easier to have a dummy | |
355 | * procedure to call than to check if there's a procedure | |
356 | */ | |
357 | void | |
358 | NoopDDA(void) | |
359 | { | |
360 | } | |
361 | ||
362 | typedef struct _BlockHandler { | |
363 | BlockHandlerProcPtr BlockHandler; | |
364 | WakeupHandlerProcPtr WakeupHandler; | |
365 | pointer blockData; | |
366 | Bool deleted; | |
367 | } BlockHandlerRec, *BlockHandlerPtr; | |
368 | ||
369 | static BlockHandlerPtr handlers; | |
370 | static int numHandlers; | |
371 | static int sizeHandlers; | |
372 | static Bool inHandler; | |
373 | static Bool handlerDeleted; | |
374 | ||
375 | /** | |
376 | * | |
377 | * \param pTimeout DIX doesn't want to know how OS represents time | |
378 | * \param pReadMask nor how it represents the det of descriptors | |
379 | */ | |
380 | void | |
381 | BlockHandler(pointer pTimeout, pointer pReadmask) | |
382 | { | |
383 | int i, j; | |
384 | ||
385 | ++inHandler; | |
386 | for (i = 0; i < screenInfo.numScreens; i++) | |
387 | (*screenInfo.screens[i]->BlockHandler) (screenInfo.screens[i], | |
388 | pTimeout, pReadmask); | |
389 | for (i = 0; i < screenInfo.numGPUScreens; i++) | |
390 | (*screenInfo.gpuscreens[i]->BlockHandler) (screenInfo.gpuscreens[i], | |
391 | pTimeout, pReadmask); | |
392 | for (i = 0; i < numHandlers; i++) | |
393 | if (!handlers[i].deleted) | |
394 | (*handlers[i].BlockHandler) (handlers[i].blockData, | |
395 | pTimeout, pReadmask); | |
396 | if (handlerDeleted) { | |
397 | for (i = 0; i < numHandlers;) | |
398 | if (handlers[i].deleted) { | |
399 | for (j = i; j < numHandlers - 1; j++) | |
400 | handlers[j] = handlers[j + 1]; | |
401 | numHandlers--; | |
402 | } | |
403 | else | |
404 | i++; | |
405 | handlerDeleted = FALSE; | |
406 | } | |
407 | --inHandler; | |
408 | } | |
409 | ||
410 | /** | |
411 | * | |
412 | * \param result 32 bits of undefined result from the wait | |
413 | * \param pReadmask the resulting descriptor mask | |
414 | */ | |
415 | void | |
416 | WakeupHandler(int result, pointer pReadmask) | |
417 | { | |
418 | int i, j; | |
419 | ||
420 | ++inHandler; | |
421 | for (i = numHandlers - 1; i >= 0; i--) | |
422 | if (!handlers[i].deleted) | |
423 | (*handlers[i].WakeupHandler) (handlers[i].blockData, | |
424 | result, pReadmask); | |
425 | for (i = 0; i < screenInfo.numScreens; i++) | |
426 | (*screenInfo.screens[i]->WakeupHandler) (screenInfo.screens[i], | |
427 | result, pReadmask); | |
428 | for (i = 0; i < screenInfo.numGPUScreens; i++) | |
429 | (*screenInfo.gpuscreens[i]->WakeupHandler) (screenInfo.gpuscreens[i], | |
430 | result, pReadmask); | |
431 | if (handlerDeleted) { | |
432 | for (i = 0; i < numHandlers;) | |
433 | if (handlers[i].deleted) { | |
434 | for (j = i; j < numHandlers - 1; j++) | |
435 | handlers[j] = handlers[j + 1]; | |
436 | numHandlers--; | |
437 | } | |
438 | else | |
439 | i++; | |
440 | handlerDeleted = FALSE; | |
441 | } | |
442 | --inHandler; | |
443 | } | |
444 | ||
445 | /** | |
446 | * Reentrant with BlockHandler and WakeupHandler, except wakeup won't | |
447 | * get called until next time | |
448 | */ | |
449 | Bool | |
450 | RegisterBlockAndWakeupHandlers(BlockHandlerProcPtr blockHandler, | |
451 | WakeupHandlerProcPtr wakeupHandler, | |
452 | pointer blockData) | |
453 | { | |
454 | BlockHandlerPtr new; | |
455 | ||
456 | if (numHandlers >= sizeHandlers) { | |
457 | new = (BlockHandlerPtr) realloc(handlers, (numHandlers + 1) * | |
458 | sizeof(BlockHandlerRec)); | |
459 | if (!new) | |
460 | return FALSE; | |
461 | handlers = new; | |
462 | sizeHandlers = numHandlers + 1; | |
463 | } | |
464 | handlers[numHandlers].BlockHandler = blockHandler; | |
465 | handlers[numHandlers].WakeupHandler = wakeupHandler; | |
466 | handlers[numHandlers].blockData = blockData; | |
467 | handlers[numHandlers].deleted = FALSE; | |
468 | numHandlers = numHandlers + 1; | |
469 | return TRUE; | |
470 | } | |
471 | ||
472 | void | |
473 | RemoveBlockAndWakeupHandlers(BlockHandlerProcPtr blockHandler, | |
474 | WakeupHandlerProcPtr wakeupHandler, | |
475 | pointer blockData) | |
476 | { | |
477 | int i; | |
478 | ||
479 | for (i = 0; i < numHandlers; i++) | |
480 | if (handlers[i].BlockHandler == blockHandler && | |
481 | handlers[i].WakeupHandler == wakeupHandler && | |
482 | handlers[i].blockData == blockData) { | |
483 | if (inHandler) { | |
484 | handlerDeleted = TRUE; | |
485 | handlers[i].deleted = TRUE; | |
486 | } | |
487 | else { | |
488 | for (; i < numHandlers - 1; i++) | |
489 | handlers[i] = handlers[i + 1]; | |
490 | numHandlers--; | |
491 | } | |
492 | break; | |
493 | } | |
494 | } | |
495 | ||
496 | void | |
497 | InitBlockAndWakeupHandlers(void) | |
498 | { | |
499 | free(handlers); | |
500 | handlers = (BlockHandlerPtr) 0; | |
501 | numHandlers = 0; | |
502 | sizeHandlers = 0; | |
503 | } | |
504 | ||
505 | /* | |
506 | * A general work queue. Perform some task before the server | |
507 | * sleeps for input. | |
508 | */ | |
509 | ||
510 | WorkQueuePtr workQueue; | |
511 | static WorkQueuePtr *workQueueLast = &workQueue; | |
512 | ||
513 | void | |
514 | ProcessWorkQueue(void) | |
515 | { | |
516 | WorkQueuePtr q, *p; | |
517 | ||
518 | p = &workQueue; | |
519 | /* | |
520 | * Scan the work queue once, calling each function. Those | |
521 | * which return TRUE are removed from the queue, otherwise | |
522 | * they will be called again. This must be reentrant with | |
523 | * QueueWorkProc. | |
524 | */ | |
525 | while ((q = *p)) { | |
526 | if ((*q->function) (q->client, q->closure)) { | |
527 | /* remove q from the list */ | |
528 | *p = q->next; /* don't fetch until after func called */ | |
529 | free(q); | |
530 | } | |
531 | else { | |
532 | p = &q->next; /* don't fetch until after func called */ | |
533 | } | |
534 | } | |
535 | workQueueLast = p; | |
536 | } | |
537 | ||
538 | void | |
539 | ProcessWorkQueueZombies(void) | |
540 | { | |
541 | WorkQueuePtr q, *p; | |
542 | ||
543 | p = &workQueue; | |
544 | while ((q = *p)) { | |
545 | if (q->client && q->client->clientGone) { | |
546 | (void) (*q->function) (q->client, q->closure); | |
547 | /* remove q from the list */ | |
548 | *p = q->next; /* don't fetch until after func called */ | |
549 | free(q); | |
550 | } | |
551 | else { | |
552 | p = &q->next; /* don't fetch until after func called */ | |
553 | } | |
554 | } | |
555 | workQueueLast = p; | |
556 | } | |
557 | ||
558 | Bool | |
559 | QueueWorkProc(Bool (*function) | |
560 | (ClientPtr /* pClient */ , pointer /* closure */ ), | |
561 | ClientPtr client, pointer closure) | |
562 | { | |
563 | WorkQueuePtr q; | |
564 | ||
565 | q = malloc(sizeof *q); | |
566 | if (!q) | |
567 | return FALSE; | |
568 | q->function = function; | |
569 | q->client = client; | |
570 | q->closure = closure; | |
571 | q->next = NULL; | |
572 | *workQueueLast = q; | |
573 | workQueueLast = &q->next; | |
574 | return TRUE; | |
575 | } | |
576 | ||
577 | /* | |
578 | * Manage a queue of sleeping clients, awakening them | |
579 | * when requested, by using the OS functions IgnoreClient | |
580 | * and AttendClient. Note that this *ignores* the troubles | |
581 | * with request data interleaving itself with events, but | |
582 | * we'll leave that until a later time. | |
583 | */ | |
584 | ||
585 | typedef struct _SleepQueue { | |
586 | struct _SleepQueue *next; | |
587 | ClientPtr client; | |
588 | ClientSleepProcPtr function; | |
589 | pointer closure; | |
590 | } SleepQueueRec, *SleepQueuePtr; | |
591 | ||
592 | static SleepQueuePtr sleepQueue = NULL; | |
593 | ||
594 | Bool | |
595 | ClientSleep(ClientPtr client, ClientSleepProcPtr function, pointer closure) | |
596 | { | |
597 | SleepQueuePtr q; | |
598 | ||
599 | q = malloc(sizeof *q); | |
600 | if (!q) | |
601 | return FALSE; | |
602 | ||
603 | IgnoreClient(client); | |
604 | q->next = sleepQueue; | |
605 | q->client = client; | |
606 | q->function = function; | |
607 | q->closure = closure; | |
608 | sleepQueue = q; | |
609 | return TRUE; | |
610 | } | |
611 | ||
612 | Bool | |
613 | ClientSignal(ClientPtr client) | |
614 | { | |
615 | SleepQueuePtr q; | |
616 | ||
617 | for (q = sleepQueue; q; q = q->next) | |
618 | if (q->client == client) { | |
619 | return QueueWorkProc(q->function, q->client, q->closure); | |
620 | } | |
621 | return FALSE; | |
622 | } | |
623 | ||
624 | void | |
625 | ClientWakeup(ClientPtr client) | |
626 | { | |
627 | SleepQueuePtr q, *prev; | |
628 | ||
629 | prev = &sleepQueue; | |
630 | while ((q = *prev)) { | |
631 | if (q->client == client) { | |
632 | *prev = q->next; | |
633 | free(q); | |
634 | if (client->clientGone) | |
635 | /* Oops -- new zombie cleanup code ensures this only | |
636 | * happens from inside CloseDownClient; don't want to | |
637 | * recurse here... | |
638 | */ | |
639 | /* CloseDownClient(client) */ ; | |
640 | else | |
641 | AttendClient(client); | |
642 | break; | |
643 | } | |
644 | prev = &q->next; | |
645 | } | |
646 | } | |
647 | ||
648 | Bool | |
649 | ClientIsAsleep(ClientPtr client) | |
650 | { | |
651 | SleepQueuePtr q; | |
652 | ||
653 | for (q = sleepQueue; q; q = q->next) | |
654 | if (q->client == client) | |
655 | return TRUE; | |
656 | return FALSE; | |
657 | } | |
658 | ||
659 | /* | |
660 | * Generic Callback Manager | |
661 | */ | |
662 | ||
663 | /* ===== Private Procedures ===== */ | |
664 | ||
665 | static int numCallbackListsToCleanup = 0; | |
666 | static CallbackListPtr **listsToCleanup = NULL; | |
667 | ||
668 | static Bool | |
669 | _AddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, pointer data) | |
670 | { | |
671 | CallbackPtr cbr; | |
672 | ||
673 | cbr = malloc(sizeof(CallbackRec)); | |
674 | if (!cbr) | |
675 | return FALSE; | |
676 | cbr->proc = callback; | |
677 | cbr->data = data; | |
678 | cbr->next = (*pcbl)->list; | |
679 | cbr->deleted = FALSE; | |
680 | (*pcbl)->list = cbr; | |
681 | return TRUE; | |
682 | } | |
683 | ||
684 | static Bool | |
685 | _DeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, pointer data) | |
686 | { | |
687 | CallbackListPtr cbl = *pcbl; | |
688 | CallbackPtr cbr, pcbr; | |
689 | ||
690 | for (pcbr = NULL, cbr = cbl->list; cbr != NULL; pcbr = cbr, cbr = cbr->next) { | |
691 | if ((cbr->proc == callback) && (cbr->data == data)) | |
692 | break; | |
693 | } | |
694 | if (cbr != NULL) { | |
695 | if (cbl->inCallback) { | |
696 | ++(cbl->numDeleted); | |
697 | cbr->deleted = TRUE; | |
698 | } | |
699 | else { | |
700 | if (pcbr == NULL) | |
701 | cbl->list = cbr->next; | |
702 | else | |
703 | pcbr->next = cbr->next; | |
704 | free(cbr); | |
705 | } | |
706 | return TRUE; | |
707 | } | |
708 | return FALSE; | |
709 | } | |
710 | ||
711 | void | |
712 | _CallCallbacks(CallbackListPtr *pcbl, pointer call_data) | |
713 | { | |
714 | CallbackListPtr cbl = *pcbl; | |
715 | CallbackPtr cbr, pcbr; | |
716 | ||
717 | ++(cbl->inCallback); | |
718 | for (cbr = cbl->list; cbr != NULL; cbr = cbr->next) { | |
719 | (*(cbr->proc)) (pcbl, cbr->data, call_data); | |
720 | } | |
721 | --(cbl->inCallback); | |
722 | ||
723 | if (cbl->inCallback) | |
724 | return; | |
725 | ||
726 | /* Was the entire list marked for deletion? */ | |
727 | ||
728 | if (cbl->deleted) { | |
729 | DeleteCallbackList(pcbl); | |
730 | return; | |
731 | } | |
732 | ||
733 | /* Were some individual callbacks on the list marked for deletion? | |
734 | * If so, do the deletions. | |
735 | */ | |
736 | ||
737 | if (cbl->numDeleted) { | |
738 | for (pcbr = NULL, cbr = cbl->list; (cbr != NULL) && cbl->numDeleted;) { | |
739 | if (cbr->deleted) { | |
740 | if (pcbr) { | |
741 | cbr = cbr->next; | |
742 | free(pcbr->next); | |
743 | pcbr->next = cbr; | |
744 | } | |
745 | else { | |
746 | cbr = cbr->next; | |
747 | free(cbl->list); | |
748 | cbl->list = cbr; | |
749 | } | |
750 | cbl->numDeleted--; | |
751 | } | |
752 | else { /* this one wasn't deleted */ | |
753 | ||
754 | pcbr = cbr; | |
755 | cbr = cbr->next; | |
756 | } | |
757 | } | |
758 | } | |
759 | } | |
760 | ||
761 | static void | |
762 | _DeleteCallbackList(CallbackListPtr *pcbl) | |
763 | { | |
764 | CallbackListPtr cbl = *pcbl; | |
765 | CallbackPtr cbr, nextcbr; | |
766 | int i; | |
767 | ||
768 | if (cbl->inCallback) { | |
769 | cbl->deleted = TRUE; | |
770 | return; | |
771 | } | |
772 | ||
773 | for (i = 0; i < numCallbackListsToCleanup; i++) { | |
774 | if (listsToCleanup[i] == pcbl) { | |
775 | listsToCleanup[i] = NULL; | |
776 | break; | |
777 | } | |
778 | } | |
779 | ||
780 | for (cbr = cbl->list; cbr != NULL; cbr = nextcbr) { | |
781 | nextcbr = cbr->next; | |
782 | free(cbr); | |
783 | } | |
784 | free(cbl); | |
785 | *pcbl = NULL; | |
786 | } | |
787 | ||
788 | static Bool | |
789 | CreateCallbackList(CallbackListPtr *pcbl) | |
790 | { | |
791 | CallbackListPtr cbl; | |
792 | int i; | |
793 | ||
794 | if (!pcbl) | |
795 | return FALSE; | |
796 | cbl = malloc(sizeof(CallbackListRec)); | |
797 | if (!cbl) | |
798 | return FALSE; | |
799 | cbl->inCallback = 0; | |
800 | cbl->deleted = FALSE; | |
801 | cbl->numDeleted = 0; | |
802 | cbl->list = NULL; | |
803 | *pcbl = cbl; | |
804 | ||
805 | for (i = 0; i < numCallbackListsToCleanup; i++) { | |
806 | if (!listsToCleanup[i]) { | |
807 | listsToCleanup[i] = pcbl; | |
808 | return TRUE; | |
809 | } | |
810 | } | |
811 | ||
812 | listsToCleanup = (CallbackListPtr **) xnfrealloc(listsToCleanup, | |
813 | sizeof(CallbackListPtr *) * | |
814 | (numCallbackListsToCleanup | |
815 | + 1)); | |
816 | listsToCleanup[numCallbackListsToCleanup] = pcbl; | |
817 | numCallbackListsToCleanup++; | |
818 | return TRUE; | |
819 | } | |
820 | ||
821 | /* ===== Public Procedures ===== */ | |
822 | ||
823 | Bool | |
824 | AddCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, pointer data) | |
825 | { | |
826 | if (!pcbl) | |
827 | return FALSE; | |
828 | if (!*pcbl) { /* list hasn't been created yet; go create it */ | |
829 | if (!CreateCallbackList(pcbl)) | |
830 | return FALSE; | |
831 | } | |
832 | return _AddCallback(pcbl, callback, data); | |
833 | } | |
834 | ||
835 | Bool | |
836 | DeleteCallback(CallbackListPtr *pcbl, CallbackProcPtr callback, pointer data) | |
837 | { | |
838 | if (!pcbl || !*pcbl) | |
839 | return FALSE; | |
840 | return _DeleteCallback(pcbl, callback, data); | |
841 | } | |
842 | ||
843 | void | |
844 | DeleteCallbackList(CallbackListPtr *pcbl) | |
845 | { | |
846 | if (!pcbl || !*pcbl) | |
847 | return; | |
848 | _DeleteCallbackList(pcbl); | |
849 | } | |
850 | ||
851 | void | |
852 | DeleteCallbackManager(void) | |
853 | { | |
854 | int i; | |
855 | ||
856 | for (i = 0; i < numCallbackListsToCleanup; i++) { | |
857 | DeleteCallbackList(listsToCleanup[i]); | |
858 | } | |
859 | free(listsToCleanup); | |
860 | ||
861 | numCallbackListsToCleanup = 0; | |
862 | listsToCleanup = NULL; | |
863 | } | |
864 | ||
865 | void | |
866 | InitCallbackManager(void) | |
867 | { | |
868 | DeleteCallbackManager(); | |
869 | } |