diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-10-18 17:31:55 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-10-18 17:31:55 (GMT) |
commit | 39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb (patch) | |
tree | 8e5374666c7f0b3017176ec9d6e6b6eae0dcabac /tk8.6/generic/tkError.c | |
parent | 066971b1e6e77991d9161bb0216a63ba94ea04f9 (diff) | |
parent | 6b095f3c8521ca7215e6ff5dcbada52b197ef7d0 (diff) | |
download | blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.zip blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.gz blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.bz2 |
Merge commit '6b095f3c8521ca7215e6ff5dcbada52b197ef7d0' as 'tk8.6'
Diffstat (limited to 'tk8.6/generic/tkError.c')
-rw-r--r-- | tk8.6/generic/tkError.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/tk8.6/generic/tkError.c b/tk8.6/generic/tkError.c new file mode 100644 index 0000000..fc223e6 --- /dev/null +++ b/tk8.6/generic/tkError.c @@ -0,0 +1,291 @@ +/* + * tkError.c -- + * + * This file provides a high-performance mechanism for selectively + * dealing with errors that occur in talking to the X server. This is + * useful, for example, when communicating with a window that may not + * exist. + * + * Copyright (c) 1990-1994 The Regents of the University of California. + * Copyright (c) 1994-1995 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkInt.h" + +/* + * The default X error handler gets saved here, so that it can be invoked if + * an error occurs that we can't handle. + */ + +typedef int (*TkXErrorHandler)(Display *display, XErrorEvent *eventPtr); +static TkXErrorHandler defaultHandler = NULL; + +/* + * Forward references to procedures declared later in this file: + */ + +static int ErrorProc(Display *display, XErrorEvent *errEventPtr); + +/* + *-------------------------------------------------------------- + * + * Tk_CreateErrorHandler -- + * + * Arrange for all a given procedure to be invoked whenever certain + * errors occur. + * + * Results: + * The return value is a token identifying the handler; it must be passed + * to Tk_DeleteErrorHandler to delete the handler. + * + * Side effects: + * If an X error occurs that matches the error, request, and minor + * arguments, then errorProc will be invoked. ErrorProc should have the + * following structure: + * + * int + * errorProc(caddr_t clientData, XErrorEvent *errorEventPtr) { + * } + * + * The clientData argument will be the same as the clientData argument to + * this procedure, and errorEvent will describe the error. If errorProc + * returns 0, it means that it completely "handled" the error: no further + * processing should be done. If errorProc returns 1, it means that it + * didn't know how to deal with the error, so we should look for other + * error handlers, or invoke the default error handler if no other + * handler returns zero. Handlers are invoked in order of age: youngest + * handler first. + * + * Note: errorProc will only be called for errors associated with X + * requests made AFTER this call, but BEFORE the handler is deleted by + * calling Tk_DeleteErrorHandler. + * + *-------------------------------------------------------------- + */ + +Tk_ErrorHandler +Tk_CreateErrorHandler( + Display *display, /* Display for which to handle errors. */ + int error, /* Consider only errors with this error_code + * (-1 means consider all errors). */ + int request, /* Consider only errors with this major + * request code (-1 means consider all major + * codes). */ + int minorCode, /* Consider only errors with this minor + * request code (-1 means consider all minor + * codes). */ + Tk_ErrorProc *errorProc, /* Procedure to invoke when a matching error + * occurs. NULL means just ignore matching + * errors. */ + ClientData clientData) /* Arbitrary value to pass to errorProc. */ +{ + register TkErrorHandler *errorPtr; + register TkDisplay *dispPtr; + + /* + * Find the display. If Tk doesn't know about this display then it's an + * error: panic. + */ + + dispPtr = TkGetDisplay(display); + if (dispPtr == NULL) { + Tcl_Panic("Unknown display passed to Tk_CreateErrorHandler"); + } + + /* + * Make sure that X calls us whenever errors occur. + */ + + if (defaultHandler == NULL) { + defaultHandler = XSetErrorHandler(ErrorProc); + } + + /* + * Create the handler record. + */ + + errorPtr = ckalloc(sizeof(TkErrorHandler)); + errorPtr->dispPtr = dispPtr; + errorPtr->firstRequest = NextRequest(display); + errorPtr->lastRequest = (unsigned) -1; + errorPtr->error = error; + errorPtr->request = request; + errorPtr->minorCode = minorCode; + errorPtr->errorProc = errorProc; + errorPtr->clientData = clientData; + errorPtr->nextPtr = dispPtr->errorPtr; + dispPtr->errorPtr = errorPtr; + + return (Tk_ErrorHandler) errorPtr; +} + +/* + *-------------------------------------------------------------- + * + * Tk_DeleteErrorHandler -- + * + * Do not use an error handler anymore. + * + * Results: + * None. + * + * Side effects: + * The handler denoted by the "handler" argument will not be invoked for + * any X errors associated with requests made after this call. However, + * if errors arrive later for requests made BEFORE this call, then the + * handler will still be invoked. Call XSync if you want to be sure that + * all outstanding errors have been received and processed. + * + *-------------------------------------------------------------- + */ + +void +Tk_DeleteErrorHandler( + Tk_ErrorHandler handler) /* Token for handler to delete; was previous + * return value from Tk_CreateErrorHandler. */ +{ + register TkErrorHandler *errorPtr = (TkErrorHandler *) handler; + register TkDisplay *dispPtr = errorPtr->dispPtr; + + errorPtr->lastRequest = NextRequest(dispPtr->display) - 1; + + /* + * Every once-in-a-while, cleanup handlers that are no longer active. We + * probably won't be able to free the handler that was just deleted (need + * to wait for any outstanding requests to be processed by server), but + * there may be previously-deleted handlers that are now ready for garbage + * collection. To reduce the cost of the cleanup, let a few dead handlers + * pile up, then clean them all at once. This adds a bit of overhead to + * errors that might occur while the dead handlers are hanging around, but + * reduces the overhead of scanning the list to clean up (particularly if + * there are many handlers that stay around forever). + */ + + dispPtr->deleteCount += 1; + if (dispPtr->deleteCount >= 10) { + register TkErrorHandler *prevPtr; + TkErrorHandler *nextPtr; + int lastSerial; + + dispPtr->deleteCount = 0; + lastSerial = LastKnownRequestProcessed(dispPtr->display); + errorPtr = dispPtr->errorPtr; + for (prevPtr = NULL; errorPtr != NULL; errorPtr = nextPtr) { + nextPtr = errorPtr->nextPtr; + if ((errorPtr->lastRequest != (unsigned long) -1) + && (errorPtr->lastRequest <= (unsigned long) lastSerial)) { + if (prevPtr == NULL) { + dispPtr->errorPtr = nextPtr; + } else { + prevPtr->nextPtr = nextPtr; + } + ckfree(errorPtr); + continue; + } + prevPtr = errorPtr; + } + } +} + +/* + *-------------------------------------------------------------- + * + * ErrorProc -- + * + * This procedure is invoked by the X system when error events arrive. + * + * Results: + * If it returns, the return value is zero. However, it is possible that + * one of the error handlers may just exit. + * + * Side effects: + * This procedure does two things. First, it uses the serial # in the + * error event to eliminate handlers whose expiration serials are now in + * the past. Second, it invokes any handlers that want to deal with the + * error. + * + *-------------------------------------------------------------- + */ + +static int +ErrorProc( + Display *display, /* Display for which error occurred. */ + register XErrorEvent *errEventPtr) + /* Information about error. */ +{ + register TkDisplay *dispPtr; + register TkErrorHandler *errorPtr; + + /* + * See if we know anything about the display. If not, then invoke the + * default error handler. + */ + + dispPtr = TkGetDisplay(display); + if (dispPtr == NULL) { + goto couldntHandle; + } + + /* + * Otherwise invoke any relevant handlers for the error, in order. + */ + + for (errorPtr = dispPtr->errorPtr; errorPtr != NULL; + errorPtr = errorPtr->nextPtr) { + if ((errorPtr->firstRequest > errEventPtr->serial) + || ((errorPtr->error != -1) + && (errorPtr->error != errEventPtr->error_code)) + || ((errorPtr->request != -1) + && (errorPtr->request != errEventPtr->request_code)) + || ((errorPtr->minorCode != -1) + && (errorPtr->minorCode != errEventPtr->minor_code)) + || ((errorPtr->lastRequest != (unsigned long) -1) + && (errorPtr->lastRequest < errEventPtr->serial))) { + continue; + } + if (errorPtr->errorProc == NULL || + errorPtr->errorProc(errorPtr->clientData, errEventPtr) == 0) { + return 0; + } + } + + /* + * See if the error is a BadWindow error. If so, and it refers to a window + * that still exists in our window table, then ignore the error. Errors + * like this can occur if a window owned by us is deleted by someone + * externally, like a window manager. We'll ignore the errors at least + * long enough to clean up internally and remove the entry from the window + * table. + * + * NOTE: For embedding, we must also check whether the window was recently + * deleted. If so, it may be that Tk generated operations on windows that + * were deleted by the container. Now we are getting the errors + * (BadWindow) after Tk already deleted the window itself. + */ + + if (errEventPtr->error_code == BadWindow) { + Window w = (Window) errEventPtr->resourceid; + + if (Tk_IdToWindow(display, w) != NULL) { + return 0; + } + } + + /* + * We couldn't handle the error. Use the default handler. + */ + + couldntHandle: + return defaultHandler(display, errEventPtr); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |