summaryrefslogtreecommitdiffstats
path: root/generic/tkError.c
blob: 648d440ab3a662fae0caa12bfb3cde503edda0d6 (plain)
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
/* 
 * 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.3 2004/01/13 02:06:00 davygrvy 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) {
	Tcl_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 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:
    return (*defaultHandler)(display, errEventPtr);
}