/*1* tkError.c --2*3* This file provides a high-performance mechanism for4* selectively dealing with errors that occur in talking5* to the X server. This is useful, for example, when6* communicating with a window that may not exist.7*8* Copyright (c) 1990-1994 The Regents of the University of California.9* Copyright (c) 1994-1995 Sun Microsystems, Inc.10*11* See the file "license.terms" for information on usage and redistribution12* of this file, and for a DISCLAIMER OF ALL WARRANTIES.13*14* SCCS: @(#) tkError.c 1.20 96/02/15 18:53:1715*/1617#include "tkInt.h"1819/*20* The default X error handler gets saved here, so that it can21* be invoked if an error occurs that we can't handle.22*/2324static int (*defaultHandler) _ANSI_ARGS_((Display *display,25XErrorEvent *eventPtr)) = NULL;262728/*29* Forward references to procedures declared later in this file:30*/3132static int ErrorProc _ANSI_ARGS_((Display *display,33XErrorEvent *errEventPtr));3435/*36*--------------------------------------------------------------37*38* Tk_CreateErrorHandler --39*40* Arrange for all a given procedure to be invoked whenever41* certain errors occur.42*43* Results:44* The return value is a token identifying the handler;45* it must be passed to Tk_DeleteErrorHandler to delete the46* handler.47*48* Side effects:49* If an X error occurs that matches the error, request,50* and minor arguments, then errorProc will be invoked.51* ErrorProc should have the following structure:52*53* int54* errorProc(clientData, errorEventPtr)55* caddr_t clientData;56* XErrorEvent *errorEventPtr;57* {58* }59*60* The clientData argument will be the same as the clientData61* argument to this procedure, and errorEvent will describe62* the error. If errorProc returns 0, it means that it63* completely "handled" the error: no further processing64* should be done. If errorProc returns 1, it means that it65* didn't know how to deal with the error, so we should look66* for other error handlers, or invoke the default error67* handler if no other handler returns zero. Handlers are68* invoked in order of age: youngest handler first.69*70* Note: errorProc will only be called for errors associated71* with X requests made AFTER this call, but BEFORE the handler72* is deleted by calling Tk_DeleteErrorHandler.73*74*--------------------------------------------------------------75*/7677Tk_ErrorHandler78Tk_CreateErrorHandler(display, error, request, minorCode, errorProc, clientData)79Display *display; /* Display for which to handle80* errors. */81int error; /* Consider only errors with this82* error_code (-1 means consider83* all errors). */84int request; /* Consider only errors with this85* major request code (-1 means86* consider all major codes). */87int minorCode; /* Consider only errors with this88* minor request code (-1 means89* consider all minor codes). */90Tk_ErrorProc *errorProc; /* Procedure to invoke when a91* matching error occurs. NULL means92* just ignore matching errors. */93ClientData clientData; /* Arbitrary value to pass to94* errorProc. */95{96register TkErrorHandler *errorPtr;97register TkDisplay *dispPtr;9899/*100* Find the display. If Tk doesn't know about this display then101* it's an error: panic.102*/103104dispPtr = TkGetDisplay(display);105if (dispPtr == NULL) {106panic("Unknown display passed to Tk_CreateErrorHandler");107}108109/*110* Make sure that X calls us whenever errors occur.111*/112113if (defaultHandler == NULL) {114defaultHandler = XSetErrorHandler(ErrorProc);115}116117/*118* Create the handler record.119*/120121errorPtr = (TkErrorHandler *) ckalloc(sizeof(TkErrorHandler));122errorPtr->dispPtr = dispPtr;123errorPtr->firstRequest = NextRequest(display);124errorPtr->lastRequest = (unsigned) -1;125errorPtr->error = error;126errorPtr->request = request;127errorPtr->minorCode = minorCode;128errorPtr->errorProc = errorProc;129errorPtr->clientData = clientData;130errorPtr->nextPtr = dispPtr->errorPtr;131dispPtr->errorPtr = errorPtr;132133return (Tk_ErrorHandler) errorPtr;134}135136/*137*--------------------------------------------------------------138*139* Tk_DeleteErrorHandler --140*141* Do not use an error handler anymore.142*143* Results:144* None.145*146* Side effects:147* The handler denoted by the "handler" argument will not148* be invoked for any X errors associated with requests149* made after this call. However, if errors arrive later150* for requests made BEFORE this call, then the handler151* will still be invoked. Call XSync if you want to be152* sure that all outstanding errors have been received153* and processed.154*155*--------------------------------------------------------------156*/157158void159Tk_DeleteErrorHandler(handler)160Tk_ErrorHandler handler; /* Token for handler to delete;161* was previous return value from162* Tk_CreateErrorHandler. */163{164register TkErrorHandler *errorPtr = (TkErrorHandler *) handler;165register TkDisplay *dispPtr = errorPtr->dispPtr;166167errorPtr->lastRequest = NextRequest(dispPtr->display) - 1;168169/*170* Every once-in-a-while, cleanup handlers that are no longer171* active. We probably won't be able to free the handler that172* was just deleted (need to wait for any outstanding requests to173* be processed by server), but there may be previously-deleted174* handlers that are now ready for garbage collection. To reduce175* the cost of the cleanup, let a few dead handlers pile up, then176* clean them all at once. This adds a bit of overhead to errors177* that might occur while the dead handlers are hanging around,178* but reduces the overhead of scanning the list to clean up179* (particularly if there are many handlers that stay around180* forever).181*/182183dispPtr->deleteCount += 1;184if (dispPtr->deleteCount >= 10) {185register TkErrorHandler *prevPtr;186TkErrorHandler *nextPtr;187int lastSerial;188189dispPtr->deleteCount = 0;190lastSerial = LastKnownRequestProcessed(dispPtr->display);191errorPtr = dispPtr->errorPtr;192for (errorPtr = dispPtr->errorPtr, prevPtr = NULL;193errorPtr != NULL; errorPtr = nextPtr) {194nextPtr = errorPtr->nextPtr;195if ((errorPtr->lastRequest != -1)196&& (errorPtr->lastRequest <= lastSerial)) {197if (prevPtr == NULL) {198dispPtr->errorPtr = nextPtr;199} else {200prevPtr->nextPtr = nextPtr;201}202ckfree((char *) errorPtr);203continue;204}205prevPtr = errorPtr;206}207}208}209210/*211*--------------------------------------------------------------212*213* ErrorProc --214*215* This procedure is invoked by the X system when error216* events arrive.217*218* Results:219* If it returns, the return value is zero. However,220* it is possible that one of the error handlers may221* just exit.222*223* Side effects:224* This procedure does two things. First, it uses the225* serial # in the error event to eliminate handlers whose226* expiration serials are now in the past. Second, it227* invokes any handlers that want to deal with the error.228*229*--------------------------------------------------------------230*/231232static int233ErrorProc(display, errEventPtr)234Display *display; /* Display for which error235* occurred. */236register XErrorEvent *errEventPtr; /* Information about error. */237{238register TkDisplay *dispPtr;239register TkErrorHandler *errorPtr;240241/*242* See if we know anything about the display. If not, then243* invoke the default error handler.244*/245246dispPtr = TkGetDisplay(display);247if (dispPtr == NULL) {248goto couldntHandle;249}250251/*252* Otherwise invoke any relevant handlers for the error, in order.253*/254255for (errorPtr = dispPtr->errorPtr; errorPtr != NULL;256errorPtr = errorPtr->nextPtr) {257if ((errorPtr->firstRequest > errEventPtr->serial)258|| ((errorPtr->error != -1)259&& (errorPtr->error != errEventPtr->error_code))260|| ((errorPtr->request != -1)261&& (errorPtr->request != errEventPtr->request_code))262|| ((errorPtr->minorCode != -1)263&& (errorPtr->minorCode != errEventPtr->minor_code))264|| ((errorPtr->lastRequest != -1)265&& (errorPtr->lastRequest < errEventPtr->serial))) {266continue;267}268if (errorPtr->errorProc == NULL) {269return 0;270} else {271if ((*errorPtr->errorProc)(errorPtr->clientData,272errEventPtr) == 0) {273return 0;274}275}276}277278/*279* See if the error is a BadWindow error. If so, and it refers280* to a window that still exists in our window table, then ignore281* the error. Errors like this can occur if a window owned by us282* is deleted by someone externally, like a window manager. We'll283* ignore the errors at least long enough to clean up internally and284* remove the entry from the window table.285*/286287if ((errEventPtr->error_code == BadWindow) && (Tk_IdToWindow(display,288(Window) errEventPtr->resourceid) != NULL)) {289return 0;290}291292/*293* We couldn't handle the error. Use the default handler.294*/295296couldntHandle:297return (*defaultHandler)(display, errEventPtr);298}299300301