1/* 2 * tkError.c -- 3 * 4 * This file provides a high-performance mechanism for 5 * selectively dealing with errors that occur in talking 6 * to the X server. This is useful, for example, when 7 * communicating with a window that may not exist. 8 * 9 * Copyright (c) 1990-1994 The Regents of the University of California. 10 * Copyright (c) 1994-1995 Sun Microsystems, Inc. 11 * 12 * See the file "license.terms" for information on usage and redistribution 13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 14 * 15 * RCS: @(#) $Id: tkError.c,v 1.2 1998/09/14 18:23:09 stanton Exp $ 16 */ 17 18#include "tkPort.h" 19#include "tkInt.h" 20 21/* 22 * The default X error handler gets saved here, so that it can 23 * be invoked if an error occurs that we can't handle. 24 */ 25 26static int (*defaultHandler) _ANSI_ARGS_((Display *display, 27 XErrorEvent *eventPtr)) = NULL; 28 29 30/* 31 * Forward references to procedures declared later in this file: 32 */ 33 34static int ErrorProc _ANSI_ARGS_((Display *display, 35 XErrorEvent *errEventPtr)); 36 37/* 38 *-------------------------------------------------------------- 39 * 40 * Tk_CreateErrorHandler -- 41 * 42 * Arrange for all a given procedure to be invoked whenever 43 * certain errors occur. 44 * 45 * Results: 46 * The return value is a token identifying the handler; 47 * it must be passed to Tk_DeleteErrorHandler to delete the 48 * handler. 49 * 50 * Side effects: 51 * If an X error occurs that matches the error, request, 52 * and minor arguments, then errorProc will be invoked. 53 * ErrorProc should have the following structure: 54 * 55 * int 56 * errorProc(clientData, errorEventPtr) 57 * caddr_t clientData; 58 * XErrorEvent *errorEventPtr; 59 * { 60 * } 61 * 62 * The clientData argument will be the same as the clientData 63 * argument to this procedure, and errorEvent will describe 64 * the error. If errorProc returns 0, it means that it 65 * completely "handled" the error: no further processing 66 * should be done. If errorProc returns 1, it means that it 67 * didn't know how to deal with the error, so we should look 68 * for other error handlers, or invoke the default error 69 * handler if no other handler returns zero. Handlers are 70 * invoked in order of age: youngest handler first. 71 * 72 * Note: errorProc will only be called for errors associated 73 * with X requests made AFTER this call, but BEFORE the handler 74 * is deleted by calling Tk_DeleteErrorHandler. 75 * 76 *-------------------------------------------------------------- 77 */ 78 79Tk_ErrorHandler 80Tk_CreateErrorHandler(display, error, request, minorCode, errorProc, clientData) 81 Display *display; /* Display for which to handle 82 * errors. */ 83 int error; /* Consider only errors with this 84 * error_code (-1 means consider 85 * all errors). */ 86 int request; /* Consider only errors with this 87 * major request code (-1 means 88 * consider all major codes). */ 89 int minorCode; /* Consider only errors with this 90 * minor request code (-1 means 91 * consider all minor codes). */ 92 Tk_ErrorProc *errorProc; /* Procedure to invoke when a 93 * matching error occurs. NULL means 94 * just ignore matching errors. */ 95 ClientData clientData; /* Arbitrary value to pass to 96 * errorProc. */ 97{ 98 register TkErrorHandler *errorPtr; 99 register TkDisplay *dispPtr; 100 101 /* 102 * Find the display. If Tk doesn't know about this display then 103 * it's an error: panic. 104 */ 105 106 dispPtr = TkGetDisplay(display); 107 if (dispPtr == NULL) { 108 panic("Unknown display passed to Tk_CreateErrorHandler"); 109 } 110 111 /* 112 * Make sure that X calls us whenever errors occur. 113 */ 114 115 if (defaultHandler == NULL) { 116 defaultHandler = XSetErrorHandler(ErrorProc); 117 } 118 119 /* 120 * Create the handler record. 121 */ 122 123 errorPtr = (TkErrorHandler *) ckalloc(sizeof(TkErrorHandler)); 124 errorPtr->dispPtr = dispPtr; 125 errorPtr->firstRequest = NextRequest(display); 126 errorPtr->lastRequest = (unsigned) -1; 127 errorPtr->error = error; 128 errorPtr->request = request; 129 errorPtr->minorCode = minorCode; 130 errorPtr->errorProc = errorProc; 131 errorPtr->clientData = clientData; 132 errorPtr->nextPtr = dispPtr->errorPtr; 133 dispPtr->errorPtr = errorPtr; 134 135 return (Tk_ErrorHandler) errorPtr; 136} 137 138/* 139 *-------------------------------------------------------------- 140 * 141 * Tk_DeleteErrorHandler -- 142 * 143 * Do not use an error handler anymore. 144 * 145 * Results: 146 * None. 147 * 148 * Side effects: 149 * The handler denoted by the "handler" argument will not 150 * be invoked for any X errors associated with requests 151 * made after this call. However, if errors arrive later 152 * for requests made BEFORE this call, then the handler 153 * will still be invoked. Call XSync if you want to be 154 * sure that all outstanding errors have been received 155 * and processed. 156 * 157 *-------------------------------------------------------------- 158 */ 159 160void 161Tk_DeleteErrorHandler(handler) 162 Tk_ErrorHandler handler; /* Token for handler to delete; 163 * was previous return value from 164 * Tk_CreateErrorHandler. */ 165{ 166 register TkErrorHandler *errorPtr = (TkErrorHandler *) handler; 167 register TkDisplay *dispPtr = errorPtr->dispPtr; 168 169 errorPtr->lastRequest = NextRequest(dispPtr->display) - 1; 170 171 /* 172 * Every once-in-a-while, cleanup handlers that are no longer 173 * active. We probably won't be able to free the handler that 174 * was just deleted (need to wait for any outstanding requests to 175 * be processed by server), but there may be previously-deleted 176 * handlers that are now ready for garbage collection. To reduce 177 * the cost of the cleanup, let a few dead handlers pile up, then 178 * clean them all at once. This adds a bit of overhead to errors 179 * that might occur while the dead handlers are hanging around, 180 * but reduces the overhead of scanning the list to clean up 181 * (particularly if there are many handlers that stay around 182 * forever). 183 */ 184 185 dispPtr->deleteCount += 1; 186 if (dispPtr->deleteCount >= 10) { 187 register TkErrorHandler *prevPtr; 188 TkErrorHandler *nextPtr; 189 int lastSerial; 190 191 dispPtr->deleteCount = 0; 192 lastSerial = LastKnownRequestProcessed(dispPtr->display); 193 errorPtr = dispPtr->errorPtr; 194 for (prevPtr = NULL; errorPtr != NULL; errorPtr = nextPtr) { 195 nextPtr = errorPtr->nextPtr; 196 if ((errorPtr->lastRequest != (unsigned long) -1) 197 && (errorPtr->lastRequest <= (unsigned long) lastSerial)) { 198 if (prevPtr == NULL) { 199 dispPtr->errorPtr = nextPtr; 200 } else { 201 prevPtr->nextPtr = nextPtr; 202 } 203 ckfree((char *) errorPtr); 204 continue; 205 } 206 prevPtr = errorPtr; 207 } 208 } 209} 210 211/* 212 *-------------------------------------------------------------- 213 * 214 * ErrorProc -- 215 * 216 * This procedure is invoked by the X system when error 217 * events arrive. 218 * 219 * Results: 220 * If it returns, the return value is zero. However, 221 * it is possible that one of the error handlers may 222 * just exit. 223 * 224 * Side effects: 225 * This procedure does two things. First, it uses the 226 * serial # in the error event to eliminate handlers whose 227 * expiration serials are now in the past. Second, it 228 * invokes any handlers that want to deal with the error. 229 * 230 *-------------------------------------------------------------- 231 */ 232 233static int 234ErrorProc(display, errEventPtr) 235 Display *display; /* Display for which error 236 * occurred. */ 237 register XErrorEvent *errEventPtr; /* Information about error. */ 238{ 239 register TkDisplay *dispPtr; 240 register TkErrorHandler *errorPtr; 241 242 /* 243 * See if we know anything about the display. If not, then 244 * invoke the default error handler. 245 */ 246 247 dispPtr = TkGetDisplay(display); 248 if (dispPtr == NULL) { 249 goto couldntHandle; 250 } 251 252 /* 253 * Otherwise invoke any relevant handlers for the error, in order. 254 */ 255 256 for (errorPtr = dispPtr->errorPtr; errorPtr != NULL; 257 errorPtr = errorPtr->nextPtr) { 258 if ((errorPtr->firstRequest > errEventPtr->serial) 259 || ((errorPtr->error != -1) 260 && (errorPtr->error != errEventPtr->error_code)) 261 || ((errorPtr->request != -1) 262 && (errorPtr->request != errEventPtr->request_code)) 263 || ((errorPtr->minorCode != -1) 264 && (errorPtr->minorCode != errEventPtr->minor_code)) 265 || ((errorPtr->lastRequest != (unsigned long) -1) 266 && (errorPtr->lastRequest < errEventPtr->serial))) { 267 continue; 268 } 269 if (errorPtr->errorProc == NULL) { 270 return 0; 271 } else { 272 if ((*errorPtr->errorProc)(errorPtr->clientData, 273 errEventPtr) == 0) { 274 return 0; 275 } 276 } 277 } 278 279 /* 280 * See if the error is a BadWindow error. If so, and it refers 281 * to a window that still exists in our window table, then ignore 282 * the error. Errors like this can occur if a window owned by us 283 * is deleted by someone externally, like a window manager. We'll 284 * ignore the errors at least long enough to clean up internally and 285 * remove the entry from the window table. 286 * 287 * NOTE: For embedding, we must also check whether the window was 288 * recently deleted. If so, it may be that Tk generated operations on 289 * windows that were deleted by the container. Now we are getting 290 * the errors (BadWindow) after Tk already deleted the window itself. 291 */ 292 293 if ((errEventPtr->error_code == BadWindow) && 294 ((Tk_IdToWindow(display, (Window) errEventPtr->resourceid) != 295 NULL) || 296 (TkpWindowWasRecentlyDeleted((Window) errEventPtr->resourceid, 297 dispPtr)))) { 298 return 0; 299 } 300 301 /* 302 * We couldn't handle the error. Use the default handler. 303 */ 304 305 couldntHandle: 306 return (*defaultHandler)(display, errEventPtr); 307} 308