Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtk/unix/tkUnixXId.c
1811 views
1
/*
2
* tkUnixXId.c --
3
*
4
* This file provides a replacement function for the default X
5
* resource allocator (_XAllocID). The problem with the default
6
* allocator is that it never re-uses ids, which causes long-lived
7
* applications to crash when X resource identifiers wrap around.
8
* The replacement functions in this file re-use old identifiers
9
* to prevent this problem.
10
*
11
* The code in this file is based on similar implementations by
12
* George C. Kaplan and Michael Hoegeman.
13
*
14
* Copyright (c) 1993 The Regents of the University of California.
15
* Copyright (c) 1994-1995 Sun Microsystems, Inc.
16
*
17
* See the file "license.terms" for information on usage and redistribution
18
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
19
*
20
* SCCS: @(#) tkUnixXId.c 1.17 96/07/23 16:56:39
21
*/
22
23
/*
24
* The definition below is needed on some systems so that we can access
25
* the resource_alloc field of Display structures in order to replace
26
* the resource allocator.
27
*/
28
29
#define XLIB_ILLEGAL_ACCESS 1
30
31
#include "tkInt.h"
32
33
/*
34
* A structure of the following type is used to hold one or more
35
* available resource identifiers. There is a list of these structures
36
* for each display.
37
*/
38
39
#define IDS_PER_STACK 10
40
typedef struct TkIdStack {
41
XID ids[IDS_PER_STACK]; /* Array of free identifiers. */
42
int numUsed; /* Indicates how many of the entries
43
* in ids are currently in use. */
44
TkDisplay *dispPtr; /* Display to which ids belong. */
45
struct TkIdStack *nextPtr; /* Next bunch of free identifiers
46
* for the same display. */
47
} TkIdStack;
48
49
/*
50
* Forward declarations for procedures defined in this file:
51
*/
52
53
static XID AllocXId _ANSI_ARGS_((Display *display));
54
static Tk_RestrictAction CheckRestrictProc _ANSI_ARGS_((
55
ClientData clientData, XEvent *eventPtr));
56
static void WindowIdCleanup _ANSI_ARGS_((ClientData clientData));
57
static void WindowIdCleanup2 _ANSI_ARGS_((ClientData clientData));
58
59
/*
60
*----------------------------------------------------------------------
61
*
62
* TkInitXId --
63
*
64
* This procedure is called to initialize the id allocator for
65
* a given display.
66
*
67
* Results:
68
* None.
69
*
70
* Side effects:
71
* The official allocator for the display is set up to be Tk_AllocXID.
72
*
73
*----------------------------------------------------------------------
74
*/
75
76
void
77
TkInitXId(dispPtr)
78
TkDisplay *dispPtr; /* Tk's information about the
79
* display. */
80
{
81
dispPtr->idStackPtr = NULL;
82
dispPtr->defaultAllocProc = dispPtr->display->resource_alloc;
83
dispPtr->display->resource_alloc = AllocXId;
84
dispPtr->windowStackPtr = NULL;
85
dispPtr->idCleanupScheduled = 0;
86
}
87
88
/*
89
*----------------------------------------------------------------------
90
*
91
* AllocXId --
92
*
93
* This procedure is invoked by Xlib as the resource allocator
94
* for a display.
95
*
96
* Results:
97
* The return value is an X resource identifier that isn't currently
98
* in use.
99
*
100
* Side effects:
101
* The identifier is removed from the stack of free identifiers,
102
* if it was previously on the stack.
103
*
104
*----------------------------------------------------------------------
105
*/
106
107
static XID
108
AllocXId(display)
109
Display *display; /* Display for which to allocate. */
110
{
111
TkDisplay *dispPtr;
112
TkIdStack *stackPtr;
113
114
/*
115
* Find Tk's information about the display.
116
*/
117
118
dispPtr = TkGetDisplay(display);
119
120
/*
121
* If the topmost chunk on the stack is empty then free it. Then
122
* check for a free id on the stack and return it if it exists.
123
*/
124
125
stackPtr = dispPtr->idStackPtr;
126
if (stackPtr != NULL) {
127
while (stackPtr->numUsed == 0) {
128
dispPtr->idStackPtr = stackPtr->nextPtr;
129
ckfree((char *) stackPtr);
130
stackPtr = dispPtr->idStackPtr;
131
if (stackPtr == NULL) {
132
goto defAlloc;
133
}
134
}
135
stackPtr->numUsed--;
136
return stackPtr->ids[stackPtr->numUsed];
137
}
138
139
/*
140
* No free ids in the stack: just get one from the default
141
* allocator.
142
*/
143
144
defAlloc:
145
return (*dispPtr->defaultAllocProc)(display);
146
}
147
148
/*
149
*----------------------------------------------------------------------
150
*
151
* Tk_FreeXId --
152
*
153
* This procedure is called to indicate that an X resource identifier
154
* is now free.
155
*
156
* Results:
157
* None.
158
*
159
* Side effects:
160
* The identifier is added to the stack of free identifiers for its
161
* display, so that it can be re-used.
162
*
163
*----------------------------------------------------------------------
164
*/
165
166
void
167
Tk_FreeXId(display, xid)
168
Display *display; /* Display for which xid was
169
* allocated. */
170
XID xid; /* Identifier that is no longer
171
* in use. */
172
{
173
TkDisplay *dispPtr;
174
TkIdStack *stackPtr;
175
176
/*
177
* Find Tk's information about the display.
178
*/
179
180
dispPtr = TkGetDisplay(display);
181
182
/*
183
* Add a new chunk to the stack if the current chunk is full.
184
*/
185
186
stackPtr = dispPtr->idStackPtr;
187
if ((stackPtr == NULL) || (stackPtr->numUsed >= IDS_PER_STACK)) {
188
stackPtr = (TkIdStack *) ckalloc(sizeof(TkIdStack));
189
stackPtr->numUsed = 0;
190
stackPtr->dispPtr = dispPtr;
191
stackPtr->nextPtr = dispPtr->idStackPtr;
192
dispPtr->idStackPtr = stackPtr;
193
}
194
195
/*
196
* Add the id to the current chunk.
197
*/
198
199
stackPtr->ids[stackPtr->numUsed] = xid;
200
stackPtr->numUsed++;
201
}
202
203
/*
204
*----------------------------------------------------------------------
205
*
206
* TkFreeWindowId --
207
*
208
* This procedure is invoked instead of TkFreeXId for window ids.
209
* See below for the reason why.
210
*
211
* Results:
212
* None.
213
*
214
* Side effects:
215
* The id given by w will eventually be freed, so that it can be
216
* reused for other resources.
217
*
218
* Design:
219
* Freeing window ids is very tricky because there could still be
220
* events pending for a window in the event queue (or even in the
221
* server) at the time the window is destroyed. If the window
222
* id were to get reused immediately for another window, old
223
* events could "drop in" on the new window, causing unexpected
224
* behavior.
225
*
226
* Thus we have to wait to re-use a window id until we know that
227
* there are no events left for it. Right now this is done in
228
* two steps. First, we wait until we know that the server
229
* has seen the XDestroyWindow request, so we can be sure that
230
* it won't generate more events for the window and that any
231
* existing events are in our queue. Second, we make sure that
232
* there are no events whatsoever in our queue (this is conservative
233
* but safe).
234
*
235
* The first step is done by remembering the request id of the
236
* XDestroyWindow request and using LastKnownRequestProcessed to
237
* see what events the server has processed. If multiple windows
238
* get destroyed at about the same time, we just remember the
239
* most recent request number for any of them (again, conservative
240
* but safe).
241
*
242
* There are a few other complications as well. When Tk destroys a
243
* sub-tree of windows, it only issues a single XDestroyWindow call,
244
* at the very end for the root of the subtree. We can't free any of
245
* the window ids until the final XDestroyWindow call. To make sure
246
* that this happens, we have to keep track of deletions in progress,
247
* hence the need for the "destroyCount" field of the display.
248
*
249
* One final problem. Some servers, like Sun X11/News servers still
250
* seem to have problems with ids getting reused too quickly. I'm
251
* not completely sure why this is a problem, but delaying the
252
* recycling of ids appears to eliminate it. Therefore, we wait
253
* an additional few seconds, even after "the coast is clear"
254
* before reusing the ids.
255
*
256
*----------------------------------------------------------------------
257
*/
258
259
void
260
TkFreeWindowId(dispPtr, w)
261
TkDisplay *dispPtr; /* Display that w belongs to. */
262
Window w; /* X identifier for window on dispPtr. */
263
{
264
TkIdStack *stackPtr;
265
266
/*
267
* Put the window id on a separate stack of window ids, rather
268
* than the main stack, so it won't get reused right away. Add
269
* a new chunk to the stack if the current chunk is full.
270
*/
271
272
stackPtr = dispPtr->windowStackPtr;
273
if ((stackPtr == NULL) || (stackPtr->numUsed >= IDS_PER_STACK)) {
274
stackPtr = (TkIdStack *) ckalloc(sizeof(TkIdStack));
275
stackPtr->numUsed = 0;
276
stackPtr->dispPtr = dispPtr;
277
stackPtr->nextPtr = dispPtr->windowStackPtr;
278
dispPtr->windowStackPtr = stackPtr;
279
}
280
281
/*
282
* Add the id to the current chunk.
283
*/
284
285
stackPtr->ids[stackPtr->numUsed] = w;
286
stackPtr->numUsed++;
287
288
/*
289
* Schedule a call to WindowIdCleanup if one isn't already
290
* scheduled.
291
*/
292
293
if (!dispPtr->idCleanupScheduled) {
294
dispPtr->idCleanupScheduled = 1;
295
Tcl_CreateTimerHandler(100, WindowIdCleanup, (ClientData *) dispPtr);
296
}
297
}
298
299
/*
300
*----------------------------------------------------------------------
301
*
302
* WindowIdCleanup --
303
*
304
* See if we can now free up all the accumulated ids of
305
* deleted windows.
306
*
307
* Results:
308
* None.
309
*
310
* Side effects:
311
* If it's safe to move the window ids back to the main free
312
* list, we schedule this to happen after a few mores seconds
313
* of delay. If it's not safe to move them yet, a timer handler
314
* gets invoked to try again later.
315
*
316
*----------------------------------------------------------------------
317
*/
318
319
static void
320
WindowIdCleanup(clientData)
321
ClientData clientData; /* Pointer to TkDisplay for display */
322
{
323
TkDisplay *dispPtr = (TkDisplay *) clientData;
324
int anyEvents, delta;
325
Tk_RestrictProc *oldProc;
326
ClientData oldData;
327
328
dispPtr->idCleanupScheduled = 0;
329
330
/*
331
* See if it's safe to recycle the window ids. It's safe if:
332
* (a) no deletions are in progress.
333
* (b) the server has seen all of the requests up to the last
334
* XDestroyWindow request.
335
* (c) there are no events in the event queue; the only way to
336
* test for this right now is to create a restrict proc that
337
* will filter the events, then call Tcl_DoOneEvent to see if
338
* the procedure gets invoked.
339
*/
340
341
if (dispPtr->destroyCount > 0) {
342
goto tryAgain;
343
}
344
delta = LastKnownRequestProcessed(dispPtr->display)
345
- dispPtr->lastDestroyRequest;
346
if (delta < 0) {
347
XSync(dispPtr->display, False);
348
}
349
anyEvents = 0;
350
oldProc = Tk_RestrictEvents(CheckRestrictProc, (ClientData) &anyEvents,
351
&oldData);
352
Tcl_DoOneEvent(TCL_DONT_WAIT|TCL_WINDOW_EVENTS);
353
Tk_RestrictEvents(oldProc, oldData, &oldData);
354
if (anyEvents) {
355
goto tryAgain;
356
}
357
358
/*
359
* These ids look safe to recycle, but we still need to delay a bit
360
* more (see comments for TkFreeWindowId). Schedule the final freeing.
361
*/
362
363
if (dispPtr->windowStackPtr != NULL) {
364
Tcl_CreateTimerHandler(5000, WindowIdCleanup2,
365
(ClientData) dispPtr->windowStackPtr);
366
dispPtr->windowStackPtr = NULL;
367
}
368
return;
369
370
/*
371
* It's still not safe to free up the ids. Try again a bit later.
372
*/
373
374
tryAgain:
375
dispPtr->idCleanupScheduled = 1;
376
Tcl_CreateTimerHandler(500, WindowIdCleanup, (ClientData *) dispPtr);
377
}
378
379
/*
380
*----------------------------------------------------------------------
381
*
382
* WindowIdCleanup2 --
383
*
384
* This procedure is the last one in the chain that recycles
385
* window ids. It takes all of the ids indicated by its
386
* argument and adds them back to the main id free list.
387
*
388
* Results:
389
* None.
390
*
391
* Side effects:
392
* Window ids get added to the main free list for their display.
393
*
394
*----------------------------------------------------------------------
395
*/
396
397
static void
398
WindowIdCleanup2(clientData)
399
ClientData clientData; /* Pointer to TkIdStack list. */
400
{
401
TkIdStack *stackPtr = (TkIdStack *) clientData;
402
TkIdStack *lastPtr;
403
404
lastPtr = stackPtr;
405
while (lastPtr->nextPtr != NULL) {
406
lastPtr = lastPtr->nextPtr;
407
}
408
lastPtr->nextPtr = stackPtr->dispPtr->idStackPtr;
409
stackPtr->dispPtr->idStackPtr = stackPtr;
410
}
411
412
/*
413
*----------------------------------------------------------------------
414
*
415
* CheckRestrictProc --
416
*
417
* This procedure is a restrict procedure, called by Tcl_DoOneEvent
418
* to filter X events. All it does is to set a flag to indicate
419
* that there are X events present.
420
*
421
* Results:
422
* Sets the integer pointed to by the argument, then returns
423
* TK_DEFER_EVENT.
424
*
425
* Side effects:
426
* None.
427
*
428
*----------------------------------------------------------------------
429
*/
430
431
static Tk_RestrictAction
432
CheckRestrictProc(clientData, eventPtr)
433
ClientData clientData; /* Pointer to flag to set. */
434
XEvent *eventPtr; /* Event to filter; not used. */
435
{
436
int *flag = (int *) clientData;
437
*flag = 1;
438
return TK_DEFER_EVENT;
439
}
440
441
/*
442
*----------------------------------------------------------------------
443
*
444
* Tk_GetPixmap --
445
*
446
* Same as the XCreatePixmap procedure except that it manages
447
* resource identifiers better.
448
*
449
* Results:
450
* Returns a new pixmap.
451
*
452
* Side effects:
453
* None.
454
*
455
*----------------------------------------------------------------------
456
*/
457
458
Pixmap
459
Tk_GetPixmap(display, d, width, height, depth)
460
Display *display; /* Display for new pixmap. */
461
Drawable d; /* Drawable where pixmap will be used. */
462
int width, height; /* Dimensions of pixmap. */
463
int depth; /* Bits per pixel for pixmap. */
464
{
465
return XCreatePixmap(display, d, (unsigned) width, (unsigned) height,
466
(unsigned) depth);
467
}
468
469
/*
470
*----------------------------------------------------------------------
471
*
472
* Tk_FreePixmap --
473
*
474
* Same as the XFreePixmap procedure except that it also marks
475
* the resource identifier as free.
476
*
477
* Results:
478
* None.
479
*
480
* Side effects:
481
* The pixmap is freed in the X server and its resource identifier
482
* is saved for re-use.
483
*
484
*----------------------------------------------------------------------
485
*/
486
487
void
488
Tk_FreePixmap(display, pixmap)
489
Display *display; /* Display for which pixmap was allocated. */
490
Pixmap pixmap; /* Identifier for pixmap. */
491
{
492
XFreePixmap(display, pixmap);
493
Tk_FreeXId(display, (XID) pixmap);
494
}
495
496