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