OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tk/] [unix/] [tkUnixXId.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.