home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkXId.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  14KB  |  496 lines

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