1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
|
/*
* 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.
*
* RCS: @(#) $Id: tkError.c,v 1.2 1998/09/14 18:23:09 stanton Exp $
*/
#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;
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((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 void
UnhandledXError()
{
}
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))
|| ((errorPtr->lastRequest != (unsigned long) -1)
&& (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.
*
* 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) &&
((Tk_IdToWindow(display, (Window) errEventPtr->resourceid) !=
NULL) ||
(TkpWindowWasRecentlyDeleted((Window) errEventPtr->resourceid,
dispPtr)))) {
return 0;
}
/*
* We couldn't handle the error. Use the default handler.
*/
couldntHandle:
UnhandledXError();
return (*defaultHandler)(display, errEventPtr);
}
|