1996-09-27 06:29:02 -04:00
|
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
1998-04-10 06:59:06 -04:00
|
|
|
|
* SCCS: @(#) tkError.c 1.23 97/04/25 16:51:27
|
1996-09-27 06:29:02 -04:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "tkPort.h"
|
|
|
|
|
#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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int (*defaultHandler) _ANSI_ARGS_((Display *display,
|
|
|
|
|
XErrorEvent *eventPtr)) = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Forward references to procedures declared later in this file:
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int ErrorProc _ANSI_ARGS_((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(clientData, errorEventPtr)
|
|
|
|
|
* 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, error, request, minorCode, errorProc, clientData)
|
|
|
|
|
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) {
|
|
|
|
|
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 = (TkErrorHandler *) 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(handler)
|
|
|
|
|
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;
|
1998-04-10 06:59:06 -04:00
|
|
|
|
for (prevPtr = NULL; errorPtr != NULL; errorPtr = nextPtr) {
|
1996-09-27 06:29:02 -04:00
|
|
|
|
nextPtr = errorPtr->nextPtr;
|
1998-04-10 06:59:06 -04:00
|
|
|
|
if ((errorPtr->lastRequest != (unsigned long) -1)
|
|
|
|
|
&& (errorPtr->lastRequest <= (unsigned long) lastSerial)) {
|
1996-09-27 06:29:02 -04:00
|
|
|
|
if (prevPtr == NULL) {
|
|
|
|
|
dispPtr->errorPtr = nextPtr;
|
|
|
|
|
} else {
|
|
|
|
|
prevPtr->nextPtr = nextPtr;
|
|
|
|
|
}
|
|
|
|
|
ckfree((char *) 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, errEventPtr)
|
|
|
|
|
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))
|
1998-04-10 06:59:06 -04:00
|
|
|
|
|| ((errorPtr->lastRequest != (unsigned long) -1)
|
1996-09-27 06:29:02 -04:00
|
|
|
|
&& (errorPtr->lastRequest < errEventPtr->serial))) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (errorPtr->errorProc == NULL) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
if ((*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.
|
1998-04-10 06:59:06 -04:00
|
|
|
|
*
|
|
|
|
|
* 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.
|
1996-09-27 06:29:02 -04:00
|
|
|
|
*/
|
|
|
|
|
|
1998-04-10 06:59:06 -04:00
|
|
|
|
if ((errEventPtr->error_code == BadWindow) &&
|
|
|
|
|
((Tk_IdToWindow(display, (Window) errEventPtr->resourceid) !=
|
|
|
|
|
NULL) ||
|
|
|
|
|
(TkpWindowWasRecentlyDeleted((Window) errEventPtr->resourceid,
|
|
|
|
|
dispPtr)))) {
|
1996-09-27 06:29:02 -04:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We couldn't handle the error. Use the default handler.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
couldntHandle:
|
|
|
|
|
return (*defaultHandler)(display, errEventPtr);
|
|
|
|
|
}
|