tkUnixXId.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:16k
源码类别:

通讯编程

开发平台:

Visual C++

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