tkUnixXId.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:16k
- /*
- * tkUnixXId.c --
- *
- * This file provides a replacement function for the default X
- * resource allocator (_XAllocID). The problem with the default
- * allocator is that it never re-uses ids, which causes long-lived
- * applications to crash when X resource identifiers wrap around.
- * The replacement functions in this file re-use old identifiers
- * to prevent this problem.
- *
- * The code in this file is based on similar implementations by
- * George C. Kaplan and Michael Hoegeman.
- *
- * Copyright (c) 1993 The Regents of the University of California.
- * Copyright (c) 1994-1997 Sun Microsystems, Inc.
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tkUnixXId.c,v 1.7 2002/04/12 10:06:09 hobbs Exp $
- */
- /*
- * The definition below is needed on some systems so that we can access
- * the resource_alloc field of Display structures in order to replace
- * the resource allocator.
- */
- #define XLIB_ILLEGAL_ACCESS 1
- #include "tkUnixInt.h"
- #include "tkPort.h"
- /*
- * A structure of the following type is used to hold one or more
- * available resource identifiers. There is a list of these structures
- * for each display.
- */
- #define IDS_PER_STACK 10
- typedef struct TkIdStack {
- XID ids[IDS_PER_STACK]; /* Array of free identifiers. */
- int numUsed; /* Indicates how many of the entries
- * in ids are currently in use. */
- TkDisplay *dispPtr; /* Display to which ids belong. */
- struct TkIdStack *nextPtr; /* Next bunch of free identifiers
- * for the same display. */
- } TkIdStack;
- /*
- * Forward declarations for procedures defined in this file:
- */
- static XID AllocXId _ANSI_ARGS_((Display *display));
- static Tk_RestrictAction CheckRestrictProc _ANSI_ARGS_((
- ClientData clientData, XEvent *eventPtr));
- static void WindowIdCleanup _ANSI_ARGS_((ClientData clientData));
- static void WindowIdCleanup2 _ANSI_ARGS_((ClientData clientData));
- /*
- *----------------------------------------------------------------------
- *
- * TkInitXId --
- *
- * This procedure is called to initialize the id allocator for
- * a given display.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The official allocator for the display is set up to be AllocXId.
- *
- *----------------------------------------------------------------------
- */
- void
- TkInitXId(dispPtr)
- TkDisplay *dispPtr; /* Tk's information about the
- * display. */
- {
- dispPtr->idStackPtr = NULL;
- dispPtr->defaultAllocProc = (XID (*) _ANSI_ARGS_((Display *display)))
- dispPtr->display->resource_alloc;
- dispPtr->display->resource_alloc = AllocXId;
- dispPtr->windowStackPtr = NULL;
- dispPtr->idCleanupScheduled = (Tcl_TimerToken) 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TkFreeXId --
- *
- * This procedure is called to free resources for the id allocator
- * for a given display.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Frees the id and window stack pools.
- *
- *----------------------------------------------------------------------
- */
- void
- TkFreeXId(dispPtr)
- TkDisplay *dispPtr; /* Tk's information about the
- * display. */
- {
- TkIdStack *stackPtr, *freePtr;
- if (dispPtr->idCleanupScheduled) {
- Tcl_DeleteTimerHandler(dispPtr->idCleanupScheduled);
- }
- for (stackPtr = dispPtr->idStackPtr; stackPtr != NULL; ) {
- freePtr = stackPtr;
- stackPtr = stackPtr->nextPtr;
- ckfree((char *) freePtr);
- }
- dispPtr->idStackPtr = NULL;
- for (stackPtr = dispPtr->windowStackPtr; stackPtr != NULL; ) {
- freePtr = stackPtr;
- stackPtr = stackPtr->nextPtr;
- ckfree((char *) freePtr);
- }
- dispPtr->windowStackPtr = NULL;
- }
- /*
- *----------------------------------------------------------------------
- *
- * AllocXId --
- *
- * This procedure is invoked by Xlib as the resource allocator
- * for a display.
- *
- * Results:
- * The return value is an X resource identifier that isn't currently
- * in use.
- *
- * Side effects:
- * The identifier is removed from the stack of free identifiers,
- * if it was previously on the stack.
- *
- *----------------------------------------------------------------------
- */
- static XID
- AllocXId(display)
- Display *display; /* Display for which to allocate. */
- {
- TkDisplay *dispPtr;
- TkIdStack *stackPtr;
- /*
- * Find Tk's information about the display.
- */
- dispPtr = TkGetDisplay(display);
-
- /*
- * If the topmost chunk on the stack is empty then free it. Then
- * check for a free id on the stack and return it if it exists.
- */
- stackPtr = dispPtr->idStackPtr;
- if (stackPtr != NULL) {
- while (stackPtr->numUsed == 0) {
- dispPtr->idStackPtr = stackPtr->nextPtr;
- ckfree((char *) stackPtr);
- stackPtr = dispPtr->idStackPtr;
- if (stackPtr == NULL) {
- goto defAlloc;
- }
- }
- stackPtr->numUsed--;
- return stackPtr->ids[stackPtr->numUsed];
- }
- /*
- * No free ids in the stack: just get one from the default
- * allocator.
- */
- defAlloc:
- return (*dispPtr->defaultAllocProc)(display);
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tk_FreeXId --
- *
- * This procedure is called to indicate that an X resource identifier
- * is now free.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The identifier is added to the stack of free identifiers for its
- * display, so that it can be re-used.
- *
- *----------------------------------------------------------------------
- */
- void
- Tk_FreeXId(display, xid)
- Display *display; /* Display for which xid was
- * allocated. */
- XID xid; /* Identifier that is no longer
- * in use. */
- {
- TkDisplay *dispPtr;
- TkIdStack *stackPtr;
- /*
- * Find Tk's information about the display.
- */
- dispPtr = TkGetDisplay(display);
- /*
- * Add a new chunk to the stack if the current chunk is full.
- */
-
- stackPtr = dispPtr->idStackPtr;
- if ((stackPtr == NULL) || (stackPtr->numUsed >= IDS_PER_STACK)) {
- stackPtr = (TkIdStack *) ckalloc(sizeof(TkIdStack));
- stackPtr->numUsed = 0;
- stackPtr->dispPtr = dispPtr;
- stackPtr->nextPtr = dispPtr->idStackPtr;
- dispPtr->idStackPtr = stackPtr;
- }
- /*
- * Add the id to the current chunk.
- */
- stackPtr->ids[stackPtr->numUsed] = xid;
- stackPtr->numUsed++;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TkFreeWindowId --
- *
- * This procedure is invoked instead of TkFreeXId for window ids.
- * See below for the reason why.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The id given by w will eventually be freed, so that it can be
- * reused for other resources.
- *
- * Design:
- * Freeing window ids is very tricky because there could still be
- * events pending for a window in the event queue (or even in the
- * server) at the time the window is destroyed. If the window
- * id were to get reused immediately for another window, old
- * events could "drop in" on the new window, causing unexpected
- * behavior.
- *
- * Thus we have to wait to re-use a window id until we know that
- * there are no events left for it. Right now this is done in
- * two steps. First, we wait until we know that the server
- * has seen the XDestroyWindow request, so we can be sure that
- * it won't generate more events for the window and that any
- * existing events are in our queue. Second, we make sure that
- * there are no events whatsoever in our queue (this is conservative
- * but safe).
- *
- * The first step is done by remembering the request id of the
- * XDestroyWindow request and using LastKnownRequestProcessed to
- * see what events the server has processed. If multiple windows
- * get destroyed at about the same time, we just remember the
- * most recent request number for any of them (again, conservative
- * but safe).
- *
- * There are a few other complications as well. When Tk destroys a
- * sub-tree of windows, it only issues a single XDestroyWindow call,
- * at the very end for the root of the subtree. We can't free any of
- * the window ids until the final XDestroyWindow call. To make sure
- * that this happens, we have to keep track of deletions in progress,
- * hence the need for the "destroyCount" field of the display.
- *
- * One final problem. Some servers, like Sun X11/News servers still
- * seem to have problems with ids getting reused too quickly. I'm
- * not completely sure why this is a problem, but delaying the
- * recycling of ids appears to eliminate it. Therefore, we wait
- * an additional few seconds, even after "the coast is clear"
- * before reusing the ids.
- *
- *----------------------------------------------------------------------
- */
- void
- TkFreeWindowId(dispPtr, w)
- TkDisplay *dispPtr; /* Display that w belongs to. */
- Window w; /* X identifier for window on dispPtr. */
- {
- TkIdStack *stackPtr;
- /*
- * Put the window id on a separate stack of window ids, rather
- * than the main stack, so it won't get reused right away. Add
- * a new chunk to the stack if the current chunk is full.
- */
- stackPtr = dispPtr->windowStackPtr;
- if ((stackPtr == NULL) || (stackPtr->numUsed >= IDS_PER_STACK)) {
- stackPtr = (TkIdStack *) ckalloc(sizeof(TkIdStack));
- stackPtr->numUsed = 0;
- stackPtr->dispPtr = dispPtr;
- stackPtr->nextPtr = dispPtr->windowStackPtr;
- dispPtr->windowStackPtr = stackPtr;
- }
- /*
- * Add the id to the current chunk.
- */
- stackPtr->ids[stackPtr->numUsed] = w;
- stackPtr->numUsed++;
- /*
- * Schedule a call to WindowIdCleanup if one isn't already
- * scheduled.
- */
- if (!dispPtr->idCleanupScheduled) {
- dispPtr->idCleanupScheduled =
- Tcl_CreateTimerHandler(100, WindowIdCleanup, (ClientData) dispPtr);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * WindowIdCleanup --
- *
- * See if we can now free up all the accumulated ids of
- * deleted windows.
- *
- * Results:
- * None.
- *
- * Side effects:
- * If it's safe to move the window ids back to the main free
- * list, we schedule this to happen after a few mores seconds
- * of delay. If it's not safe to move them yet, a timer handler
- * gets invoked to try again later.
- *
- *----------------------------------------------------------------------
- */
- static void
- WindowIdCleanup(clientData)
- ClientData clientData; /* Pointer to TkDisplay for display */
- {
- TkDisplay *dispPtr = (TkDisplay *) clientData;
- int anyEvents, delta;
- Tk_RestrictProc *oldProc;
- ClientData oldData;
- static Tcl_Time timeout = {0, 0};
- dispPtr->idCleanupScheduled = (Tcl_TimerToken) 0;
- /*
- * See if it's safe to recycle the window ids. It's safe if:
- * (a) no deletions are in progress.
- * (b) the server has seen all of the requests up to the last
- * XDestroyWindow request.
- * (c) there are no events in the event queue; the only way to
- * test for this right now is to create a restrict proc that
- * will filter the events, then call Tcl_DoOneEvent to see if
- * the procedure gets invoked.
- */
- if (dispPtr->destroyCount > 0) {
- goto tryAgain;
- }
- delta = LastKnownRequestProcessed(dispPtr->display)
- - dispPtr->lastDestroyRequest;
- if (delta < 0) {
- XSync(dispPtr->display, False);
- }
- anyEvents = 0;
- oldProc = Tk_RestrictEvents(CheckRestrictProc, (ClientData) &anyEvents,
- &oldData);
- TkUnixDoOneXEvent(&timeout);
- Tk_RestrictEvents(oldProc, oldData, &oldData);
- if (anyEvents) {
- goto tryAgain;
- }
- /*
- * These ids look safe to recycle, but we still need to delay a bit
- * more (see comments for TkFreeWindowId). Schedule the final freeing.
- */
- if (dispPtr->windowStackPtr != NULL) {
- Tcl_CreateTimerHandler(5000, WindowIdCleanup2,
- (ClientData) dispPtr->windowStackPtr);
- dispPtr->windowStackPtr = NULL;
- }
- return;
- /*
- * It's still not safe to free up the ids. Try again a bit later.
- */
- tryAgain:
- dispPtr->idCleanupScheduled =
- Tcl_CreateTimerHandler(500, WindowIdCleanup, (ClientData) dispPtr);
- }
- /*
- *----------------------------------------------------------------------
- *
- * WindowIdCleanup2 --
- *
- * This procedure is the last one in the chain that recycles
- * window ids. It takes all of the ids indicated by its
- * argument and adds them back to the main id free list.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Window ids get added to the main free list for their display.
- *
- *----------------------------------------------------------------------
- */
- static void
- WindowIdCleanup2(clientData)
- ClientData clientData; /* Pointer to TkIdStack list. */
- {
- TkIdStack *stackPtr = (TkIdStack *) clientData;
- TkIdStack *lastPtr;
- lastPtr = stackPtr;
- while (lastPtr->nextPtr != NULL) {
- lastPtr = lastPtr->nextPtr;
- }
- lastPtr->nextPtr = stackPtr->dispPtr->idStackPtr;
- stackPtr->dispPtr->idStackPtr = stackPtr;
- }
- /*
- *----------------------------------------------------------------------
- *
- * CheckRestrictProc --
- *
- * This procedure is a restrict procedure, called by Tcl_DoOneEvent
- * to filter X events. All it does is to set a flag to indicate
- * that there are X events present.
- *
- * Results:
- * Sets the integer pointed to by the argument, then returns
- * TK_DEFER_EVENT.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static Tk_RestrictAction
- CheckRestrictProc(clientData, eventPtr)
- ClientData clientData; /* Pointer to flag to set. */
- XEvent *eventPtr; /* Event to filter; not used. */
- {
- int *flag = (int *) clientData;
- *flag = 1;
- return TK_DEFER_EVENT;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tk_GetPixmap --
- *
- * Same as the XCreatePixmap procedure except that it manages
- * resource identifiers better.
- *
- * Results:
- * Returns a new pixmap.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- Pixmap
- Tk_GetPixmap(display, d, width, height, depth)
- Display *display; /* Display for new pixmap. */
- Drawable d; /* Drawable where pixmap will be used. */
- int width, height; /* Dimensions of pixmap. */
- int depth; /* Bits per pixel for pixmap. */
- {
- return XCreatePixmap(display, d, (unsigned) width, (unsigned) height,
- (unsigned) depth);
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tk_FreePixmap --
- *
- * Same as the XFreePixmap procedure except that it also marks
- * the resource identifier as free.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The pixmap is freed in the X server and its resource identifier
- * is saved for re-use.
- *
- *----------------------------------------------------------------------
- */
- void
- Tk_FreePixmap(display, pixmap)
- Display *display; /* Display for which pixmap was allocated. */
- Pixmap pixmap; /* Identifier for pixmap. */
- {
- XFreePixmap(display, pixmap);
- Tk_FreeXId(display, (XID) pixmap);
- }
- /*
- *----------------------------------------------------------------------
- *
- * TkpWindowWasRecentlyDeleted --
- *
- * Checks whether the window was recently deleted. This is called
- * by the generic error handler to detect asynchronous notification
- * of errors due to operations by Tk on a window that was already
- * deleted by the server.
- *
- * Results:
- * 1 if the window was deleted recently, 0 otherwise.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- TkpWindowWasRecentlyDeleted(win, dispPtr)
- Window win; /* The window to check for. */
- TkDisplay *dispPtr; /* The window belongs to this display. */
- {
- TkIdStack *stackPtr;
- int i;
- for (stackPtr = dispPtr->windowStackPtr;
- stackPtr != NULL;
- stackPtr = stackPtr->nextPtr) {
- for (i = 0; i < stackPtr->numUsed; i++) {
- if ((Window) stackPtr->ids[i] == win) {
- return 1;
- }
- }
- }
- return 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TkpScanWindowId --
- *
- * Given a string, produce the corresponding Window Id.
- *
- * Results:
- * The return value is normally TCL_OK; in this case *idPtr
- * will be set to the Window value equivalent to string. If
- * string is improperly formed then TCL_ERROR is returned and
- * an error message will be left in the interp's result.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- TkpScanWindowId(interp, string, idPtr)
- Tcl_Interp *interp;
- CONST char *string;
- Window *idPtr;
- {
- int value;
- if (Tcl_GetInt(interp, string, &value) != TCL_OK) {
- return TCL_ERROR;
- }
- *idPtr = (Window) value;
- return TCL_OK;
- }