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.
9 * Copyright (c) 1990-1994 The Regents of the University of California.
10 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
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.
26 static int (*defaultHandler) _ANSI_ARGS_((Display *display,
27 XErrorEvent *eventPtr)) = NULL;
31 * Forward references to procedures declared later in this file:
34 static int ErrorProc _ANSI_ARGS_((Display *display,
35 XErrorEvent *errEventPtr));
38 *--------------------------------------------------------------
40 * Tk_CreateErrorHandler --
42 * Arrange for all a given procedure to be invoked whenever
43 * certain errors occur.
46 * The return value is a token identifying the handler;
47 * it must be passed to Tk_DeleteErrorHandler to delete the
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:
56 * errorProc(clientData, errorEventPtr)
58 * XErrorEvent *errorEventPtr;
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.
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.
76 *--------------------------------------------------------------
80 Tk_CreateErrorHandler(display, error, request, minorCode, errorProc, clientData)
81 Display *display; /* Display for which to handle
83 int error; /* Consider only errors with this
84 * error_code (-1 means consider
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
98 register TkErrorHandler *errorPtr;
99 register TkDisplay *dispPtr;
102 * Find the display. If Tk doesn't know about this display then
103 * it's an error: panic.
106 dispPtr = TkGetDisplay(display);
107 if (dispPtr == NULL) {
108 panic("Unknown display passed to Tk_CreateErrorHandler");
112 * Make sure that X calls us whenever errors occur.
115 if (defaultHandler == NULL) {
116 defaultHandler = XSetErrorHandler(ErrorProc);
120 * Create the handler record.
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;
135 return (Tk_ErrorHandler) errorPtr;
139 *--------------------------------------------------------------
141 * Tk_DeleteErrorHandler --
143 * Do not use an error handler anymore.
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
157 *--------------------------------------------------------------
161 Tk_DeleteErrorHandler(handler)
162 Tk_ErrorHandler handler; /* Token for handler to delete;
163 * was previous return value from
164 * Tk_CreateErrorHandler. */
166 register TkErrorHandler *errorPtr = (TkErrorHandler *) handler;
167 register TkDisplay *dispPtr = errorPtr->dispPtr;
169 errorPtr->lastRequest = NextRequest(dispPtr->display) - 1;
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
185 dispPtr->deleteCount += 1;
186 if (dispPtr->deleteCount >= 10) {
187 register TkErrorHandler *prevPtr;
188 TkErrorHandler *nextPtr;
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;
201 prevPtr->nextPtr = nextPtr;
203 ckfree((char *) errorPtr);
212 *--------------------------------------------------------------
216 * This procedure is invoked by the X system when error
220 * If it returns, the return value is zero. However,
221 * it is possible that one of the error handlers may
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.
230 *--------------------------------------------------------------
234 ErrorProc(display, errEventPtr)
235 Display *display; /* Display for which error
237 register XErrorEvent *errEventPtr; /* Information about error. */
239 register TkDisplay *dispPtr;
240 register TkErrorHandler *errorPtr;
243 * See if we know anything about the display. If not, then
244 * invoke the default error handler.
247 dispPtr = TkGetDisplay(display);
248 if (dispPtr == NULL) {
253 * Otherwise invoke any relevant handlers for the error, in order.
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))) {
269 if (errorPtr->errorProc == NULL) {
272 if ((*errorPtr->errorProc)(errorPtr->clientData,
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.
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.
293 if ((errEventPtr->error_code == BadWindow) &&
294 ((Tk_IdToWindow(display, (Window) errEventPtr->resourceid) !=
296 (TkpWindowWasRecentlyDeleted((Window) errEventPtr->resourceid,
302 * We couldn't handle the error. Use the default handler.
306 return (*defaultHandler)(display, errEventPtr);