OSDN Git Service

Updated to tk 8.4.1
[pf3gnuchains/pf3gnuchains3x.git] / tk / generic / tkError.c
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  * RCS: @(#) $Id$
16  */
17
18 #include "tkPort.h"
19 #include "tkInt.h"
20
21 /*
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.
24  */
25
26 static int      (*defaultHandler) _ANSI_ARGS_((Display *display,
27                     XErrorEvent *eventPtr)) = NULL;
28
29
30 /*
31  * Forward references to procedures declared later in this file:
32  */
33
34 static int      ErrorProc _ANSI_ARGS_((Display *display,
35                     XErrorEvent *errEventPtr));
36 \f
37 /*
38  *--------------------------------------------------------------
39  *
40  * Tk_CreateErrorHandler --
41  *
42  *      Arrange for all a given procedure to be invoked whenever
43  *      certain errors occur.
44  *
45  * Results:
46  *      The return value is a token identifying the handler;
47  *      it must be passed to Tk_DeleteErrorHandler to delete the
48  *      handler.
49  *
50  * Side effects:
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:
54  *
55  *      int
56  *      errorProc(clientData, errorEventPtr)
57  *          caddr_t clientData;
58  *          XErrorEvent *errorEventPtr;
59  *      {
60  *      }
61  *
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.
71  *
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.
75  *
76  *--------------------------------------------------------------
77  */
78
79 Tk_ErrorHandler
80 Tk_CreateErrorHandler(display, error, request, minorCode, errorProc, clientData)
81     Display *display;           /* Display for which to handle
82                                  * errors. */
83     int error;                  /* Consider only errors with this
84                                  * error_code (-1 means consider
85                                  * all errors). */
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
96                                  * errorProc. */
97 {
98     register TkErrorHandler *errorPtr;
99     register TkDisplay *dispPtr;
100
101     /*
102      * Find the display.  If Tk doesn't know about this display then
103      * it's an error:  panic.
104      */
105
106     dispPtr = TkGetDisplay(display);
107     if (dispPtr == NULL) {
108         panic("Unknown display passed to Tk_CreateErrorHandler");
109     }
110
111     /*
112      * Make sure that X calls us whenever errors occur.
113      */
114
115     if (defaultHandler == NULL) {
116         defaultHandler = XSetErrorHandler(ErrorProc);
117     }
118
119     /*
120      * Create the handler record.
121      */
122
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;
134
135     return (Tk_ErrorHandler) errorPtr;
136 }
137 \f
138 /*
139  *--------------------------------------------------------------
140  *
141  * Tk_DeleteErrorHandler --
142  *
143  *      Do not use an error handler anymore.
144  *
145  * Results:
146  *      None.
147  *
148  * Side effects:
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
155  *      and processed.
156  *
157  *--------------------------------------------------------------
158  */
159
160 void
161 Tk_DeleteErrorHandler(handler)
162     Tk_ErrorHandler handler;    /* Token for handler to delete;
163                                  * was previous return value from
164                                  * Tk_CreateErrorHandler. */
165 {
166     register TkErrorHandler *errorPtr = (TkErrorHandler *) handler;
167     register TkDisplay *dispPtr = errorPtr->dispPtr;
168
169     errorPtr->lastRequest = NextRequest(dispPtr->display) - 1;
170
171     /*
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
182      * forever).
183      */
184
185     dispPtr->deleteCount += 1;
186     if (dispPtr->deleteCount >= 10) {
187         register TkErrorHandler *prevPtr;
188         TkErrorHandler *nextPtr;
189         int lastSerial;
190
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;
200                 } else {
201                     prevPtr->nextPtr = nextPtr;
202                 }
203                 ckfree((char *) errorPtr);
204                 continue;
205             }
206             prevPtr = errorPtr;
207         }
208     }
209 }
210 \f
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 != (unsigned long) -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      * 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.
291      */
292
293     if ((errEventPtr->error_code == BadWindow) &&
294             ((Tk_IdToWindow(display, (Window) errEventPtr->resourceid) !=
295                     NULL) ||
296                 (TkpWindowWasRecentlyDeleted((Window) errEventPtr->resourceid,
297                         dispPtr)))) {
298         return 0;
299     }
300
301     /*
302      * We couldn't handle the error.  Use the default handler.
303      */
304
305     couldntHandle:
306     return (*defaultHandler)(display, errEventPtr);
307 }