| 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 1990 Regents of the University of California. |
| 10 | * Permission to use, copy, modify, and distribute this |
| 11 | * software and its documentation for any purpose and without |
| 12 | * fee is hereby granted, provided that the above copyright |
| 13 | * notice appear in all copies. The University of California |
| 14 | * makes no representations about the suitability of this |
| 15 | * software for any purpose. It is provided "as is" without |
| 16 | * express or implied warranty. |
| 17 | */ |
| 18 | |
| 19 | #ifndef lint |
| 20 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkError.c,v 1.10 92/04/12 17:02:08 ouster Exp $ SPRITE (Berkeley)"; |
| 21 | #endif |
| 22 | |
| 23 | #include "tkconfig.h" |
| 24 | #include "tkint.h" |
| 25 | |
| 26 | static initialized = 0; |
| 27 | |
| 28 | /* |
| 29 | * Forward references to procedures declared later in this file: |
| 30 | */ |
| 31 | |
| 32 | static int ErrorProc _ANSI_ARGS_((Display *display, |
| 33 | XErrorEvent *errEventPtr)); |
| 34 | \f |
| 35 | /* |
| 36 | *-------------------------------------------------------------- |
| 37 | * |
| 38 | * Tk_CreateErrorHandler -- |
| 39 | * |
| 40 | * Arrange for all a given procedure to be invoked whenever |
| 41 | * 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 the |
| 46 | * 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 | * int |
| 54 | * errorProc(clientData, errorEventPtr) |
| 55 | * caddr_t clientData; |
| 56 | * XErrorEvent *errorEventPtr; |
| 57 | * { |
| 58 | * } |
| 59 | * |
| 60 | * The clientData argument will be the same as the clientData |
| 61 | * argument to this procedure, and errorEvent will describe |
| 62 | * the error. If errorProc returns 0, it means that it |
| 63 | * completely "handled" the error: no further processing |
| 64 | * should be done. If errorProc returns 1, it means that it |
| 65 | * didn't know how to deal with the error, so we should look |
| 66 | * for other error handlers, or invoke the default error |
| 67 | * handler if no other handler returns zero. Handlers are |
| 68 | * invoked in order of age: youngest handler first. |
| 69 | * |
| 70 | * Note: errorProc will only be called for errors associated |
| 71 | * with X requests made AFTER this call, but BEFORE the handler |
| 72 | * is deleted by calling Tk_DeleteErrorHandler. |
| 73 | * |
| 74 | *-------------------------------------------------------------- |
| 75 | */ |
| 76 | |
| 77 | Tk_ErrorHandler |
| 78 | Tk_CreateErrorHandler(display, error, request, minorCode, errorProc, clientData) |
| 79 | Display *display; /* Display for which to handle |
| 80 | * errors. */ |
| 81 | int error; /* Consider only errors with this |
| 82 | * error_code (-1 means consider |
| 83 | * all errors). */ |
| 84 | int request; /* Consider only errors with this |
| 85 | * major request code (-1 means |
| 86 | * consider all major codes). */ |
| 87 | int minorCode; /* Consider only errors with this |
| 88 | * minor request code (-1 means |
| 89 | * consider all minor codes). */ |
| 90 | Tk_ErrorProc *errorProc; /* Procedure to invoke when a |
| 91 | * matching error occurs. NULL means |
| 92 | * just ignore matching errors. */ |
| 93 | ClientData clientData; /* Arbitrary value to pass to |
| 94 | * errorProc. */ |
| 95 | { |
| 96 | register TkErrorHandler *errorPtr; |
| 97 | register TkDisplay *dispPtr; |
| 98 | |
| 99 | /* |
| 100 | * Make sure that X calls us whenever errors occur. |
| 101 | */ |
| 102 | |
| 103 | if (!initialized) { |
| 104 | XSetErrorHandler(ErrorProc); |
| 105 | initialized = 1; |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | * Find the display. If Tk doesn't know about this display, |
| 110 | * it's an error: panic. |
| 111 | */ |
| 112 | |
| 113 | for (dispPtr = tkDisplayList; ; dispPtr = dispPtr->nextPtr) { |
| 114 | if (dispPtr->display == display) { |
| 115 | break; |
| 116 | } |
| 117 | if (dispPtr == NULL) { |
| 118 | panic("Unknown display passed to Tk_CreateErrorHandler"); |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | /* |
| 123 | * Create the handler record. |
| 124 | */ |
| 125 | |
| 126 | errorPtr = (TkErrorHandler *) ckalloc(sizeof(TkErrorHandler)); |
| 127 | errorPtr->dispPtr = dispPtr; |
| 128 | errorPtr->firstRequest = NextRequest(display); |
| 129 | errorPtr->lastRequest = -1; |
| 130 | errorPtr->error = error; |
| 131 | errorPtr->request = request; |
| 132 | errorPtr->minorCode = minorCode; |
| 133 | errorPtr->errorProc = errorProc; |
| 134 | errorPtr->clientData = clientData; |
| 135 | errorPtr->nextPtr = dispPtr->errorPtr; |
| 136 | dispPtr->errorPtr = errorPtr; |
| 137 | |
| 138 | return (Tk_ErrorHandler) errorPtr; |
| 139 | } |
| 140 | \f |
| 141 | /* |
| 142 | *-------------------------------------------------------------- |
| 143 | * |
| 144 | * Tk_DeleteErrorHandler -- |
| 145 | * |
| 146 | * Do not use an error handler anymore. |
| 147 | * |
| 148 | * Results: |
| 149 | * None. |
| 150 | * |
| 151 | * Side effects: |
| 152 | * The handler denoted by the "handler" argument will not |
| 153 | * be invoked for any X errors associated with requests |
| 154 | * made after this call. However, if errors arrive later |
| 155 | * for requests made BEFORE this call, then the handler |
| 156 | * will still be invoked. Call XSync if you want to be |
| 157 | * sure that all outstanding errors have been received |
| 158 | * and processed. |
| 159 | * |
| 160 | *-------------------------------------------------------------- |
| 161 | */ |
| 162 | |
| 163 | void |
| 164 | Tk_DeleteErrorHandler(handler) |
| 165 | Tk_ErrorHandler handler; /* Token for handler to delete; |
| 166 | * was previous return value from |
| 167 | * Tk_CreateErrorHandler. */ |
| 168 | { |
| 169 | register TkErrorHandler *errorPtr = (TkErrorHandler *) handler; |
| 170 | register TkDisplay *dispPtr = errorPtr->dispPtr; |
| 171 | |
| 172 | errorPtr->lastRequest = NextRequest(dispPtr->display) - 1; |
| 173 | |
| 174 | /* |
| 175 | * Every once-in-a-while, cleanup handlers that are no longer |
| 176 | * active. We probably won't be able to free the handler that |
| 177 | * was just deleted (need to wait for any outstanding requests to |
| 178 | * be processed by server), but there may be previously-deleted |
| 179 | * handlers that are now ready for garbage collection. To reduce |
| 180 | * the cost of the cleanup, let a few dead handlers pile up, then |
| 181 | * clean them all at once. This adds a bit of overhead to errors |
| 182 | * that might occur while the dead handlers are hanging around, |
| 183 | * but reduces the overhead of scanning the list to clean up |
| 184 | * (particularly if there are many handlers that stay around |
| 185 | * forever). |
| 186 | */ |
| 187 | |
| 188 | dispPtr->deleteCount += 1; |
| 189 | if (dispPtr->deleteCount >= 10) { |
| 190 | register TkErrorHandler *prevPtr; |
| 191 | TkErrorHandler *nextPtr; |
| 192 | int lastSerial; |
| 193 | |
| 194 | dispPtr->deleteCount = 0; |
| 195 | lastSerial = LastKnownRequestProcessed(dispPtr->display); |
| 196 | errorPtr = dispPtr->errorPtr; |
| 197 | for (errorPtr = dispPtr->errorPtr, prevPtr = NULL; |
| 198 | errorPtr != NULL; errorPtr = nextPtr) { |
| 199 | nextPtr = errorPtr->nextPtr; |
| 200 | if ((errorPtr->lastRequest != -1) |
| 201 | && (errorPtr->lastRequest <= lastSerial)) { |
| 202 | if (prevPtr == NULL) { |
| 203 | dispPtr->errorPtr = nextPtr; |
| 204 | } else { |
| 205 | prevPtr->nextPtr = nextPtr; |
| 206 | } |
| 207 | ckfree((char *) errorPtr); |
| 208 | continue; |
| 209 | } |
| 210 | prevPtr = errorPtr; |
| 211 | } |
| 212 | } |
| 213 | } |
| 214 | \f |
| 215 | /* |
| 216 | *-------------------------------------------------------------- |
| 217 | * |
| 218 | * ErrorProc -- |
| 219 | * |
| 220 | * This procedure is invoked by the X system when error |
| 221 | * events arrive. |
| 222 | * |
| 223 | * Results: |
| 224 | * If it returns, the return value is zero. However, |
| 225 | * it is possible that one of the error handlers may |
| 226 | * just exit. |
| 227 | * |
| 228 | * Side effects: |
| 229 | * This procedure does two things. First, it uses the |
| 230 | * serial # in the error event to eliminate handlers whose |
| 231 | * expiration serials are now in the past. Second, it |
| 232 | * invokes any handlers that want to deal with the error. |
| 233 | * |
| 234 | *-------------------------------------------------------------- |
| 235 | */ |
| 236 | |
| 237 | static int |
| 238 | ErrorProc(display, errEventPtr) |
| 239 | Display *display; /* Display for which error |
| 240 | * occurred. */ |
| 241 | register XErrorEvent *errEventPtr; /* Information about error. */ |
| 242 | { |
| 243 | register TkDisplay *dispPtr; |
| 244 | register TkErrorHandler *errorPtr; |
| 245 | extern int _XDefaultError(); |
| 246 | |
| 247 | /* |
| 248 | * See if we know anything about the display. If not, then |
| 249 | * invoke the default error handler. |
| 250 | */ |
| 251 | |
| 252 | for (dispPtr = tkDisplayList; ; dispPtr = dispPtr->nextPtr) { |
| 253 | if (dispPtr == NULL) { |
| 254 | goto couldntHandle; |
| 255 | } |
| 256 | if (dispPtr->display == display) { |
| 257 | break; |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | /* |
| 262 | * Otherwise invoke any relevant handlers for the error, in order. |
| 263 | */ |
| 264 | |
| 265 | for (errorPtr = dispPtr->errorPtr; errorPtr != NULL; |
| 266 | errorPtr = errorPtr->nextPtr) { |
| 267 | if ((errorPtr->firstRequest > errEventPtr->serial) |
| 268 | || ((errorPtr->error != -1) |
| 269 | && (errorPtr->error != errEventPtr->error_code)) |
| 270 | || ((errorPtr->request != -1) |
| 271 | && (errorPtr->request != errEventPtr->request_code)) |
| 272 | || ((errorPtr->minorCode != -1) |
| 273 | && (errorPtr->minorCode != errEventPtr->minor_code)) |
| 274 | || ((errorPtr->lastRequest != -1) |
| 275 | && (errorPtr->lastRequest < errEventPtr->serial))) { |
| 276 | continue; |
| 277 | } |
| 278 | if (errorPtr->errorProc == NULL) { |
| 279 | return 0; |
| 280 | } else { |
| 281 | if ((*errorPtr->errorProc)(errorPtr->clientData, |
| 282 | errEventPtr) == 0) { |
| 283 | return 0; |
| 284 | } |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | /* |
| 289 | * We couldn't handle the error. Use the default handler. |
| 290 | */ |
| 291 | |
| 292 | couldntHandle: |
| 293 | return _XDefaultError(display, errEventPtr); |
| 294 | } |