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