]>
Commit | Line | Data |
---|---|---|
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 | } |