Import Tk 8.5.15 (as of svn r89086)
This commit is contained in:
604
unix/tkUnixXId.c
Normal file
604
unix/tkUnixXId.c
Normal file
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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"
|
||||
|
||||
/*
|
||||
* 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 functions defined in this file:
|
||||
*/
|
||||
|
||||
static XID AllocXId(Display *display);
|
||||
static Tk_RestrictAction CheckRestrictProc(ClientData clientData,
|
||||
XEvent *eventPtr);
|
||||
static void WindowIdCleanup(ClientData clientData);
|
||||
static void WindowIdCleanup2(ClientData clientData);
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TkInitXId --
|
||||
*
|
||||
* This function 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(
|
||||
TkDisplay *dispPtr) /* Tk's information about the display. */
|
||||
{
|
||||
dispPtr->idStackPtr = NULL;
|
||||
dispPtr->defaultAllocProc = (XID (*) (Display *display))
|
||||
dispPtr->display->resource_alloc;
|
||||
dispPtr->display->resource_alloc = AllocXId;
|
||||
dispPtr->windowStackPtr = NULL;
|
||||
dispPtr->idCleanupScheduled = (Tcl_TimerToken) 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TkFreeXId --
|
||||
*
|
||||
* This function 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(
|
||||
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 function 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 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 function 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 *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 function 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(
|
||||
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) /* 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 function 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 function 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) /* 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 function is a restrict function, 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 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 function except that it manages resource
|
||||
* identifiers better.
|
||||
*
|
||||
* Results:
|
||||
* Returns a new pixmap.
|
||||
*
|
||||
* Side effects:
|
||||
* None.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
Pixmap
|
||||
Tk_GetPixmap(
|
||||
Display *display, /* Display for new pixmap. */
|
||||
Drawable d, /* Drawable where pixmap will be used. */
|
||||
int width, int 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 function 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 *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(
|
||||
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(
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* c-basic-offset: 4
|
||||
* fill-column: 78
|
||||
* End:
|
||||
*/
|
||||
Reference in New Issue
Block a user