1 |
/* $Header$ */
|
2 |
|
3 |
/*
|
4 |
* tkEvent.c --
|
5 |
*
|
6 |
* This file provides basic low-level facilities for managing
|
7 |
* X events in Tk.
|
8 |
*
|
9 |
* Copyright (c) 1990-1994 The Regents of the University of California.
|
10 |
* Copyright (c) 1994-1995 Sun Microsystems, Inc.
|
11 |
* Copyright (c) 1998 by Scriptics Corporation.
|
12 |
*
|
13 |
* See the file "license.terms" for information on usage and redistribution
|
14 |
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
15 |
*
|
16 |
* RCS: @(#) $Id: tkevent.c,v 1.1.1.1 2001/06/13 05:00:10 dtashley Exp $
|
17 |
*/
|
18 |
|
19 |
#include "tkPort.h"
|
20 |
#include "tkInt.h"
|
21 |
#include <signal.h>
|
22 |
|
23 |
/*
|
24 |
* There's a potential problem if a handler is deleted while it's
|
25 |
* current (i.e. its procedure is executing), since Tk_HandleEvent
|
26 |
* will need to read the handler's "nextPtr" field when the procedure
|
27 |
* returns. To handle this problem, structures of the type below
|
28 |
* indicate the next handler to be processed for any (recursively
|
29 |
* nested) dispatches in progress. The nextHandler fields get
|
30 |
* updated if the handlers pointed to are deleted. Tk_HandleEvent
|
31 |
* also needs to know if the entire window gets deleted; the winPtr
|
32 |
* field is set to zero if that particular window gets deleted.
|
33 |
*/
|
34 |
|
35 |
typedef struct InProgress {
|
36 |
XEvent *eventPtr; /* Event currently being handled. */
|
37 |
TkWindow *winPtr; /* Window for event. Gets set to None if
|
38 |
* window is deleted while event is being
|
39 |
* handled. */
|
40 |
TkEventHandler *nextHandler; /* Next handler in search. */
|
41 |
struct InProgress *nextPtr; /* Next higher nested search. */
|
42 |
} InProgress;
|
43 |
|
44 |
/*
|
45 |
* For each call to Tk_CreateGenericHandler, an instance of the following
|
46 |
* structure will be created. All of the active handlers are linked into a
|
47 |
* list.
|
48 |
*/
|
49 |
|
50 |
typedef struct GenericHandler {
|
51 |
Tk_GenericProc *proc; /* Procedure to dispatch on all X events. */
|
52 |
ClientData clientData; /* Client data to pass to procedure. */
|
53 |
int deleteFlag; /* Flag to set when this handler is deleted. */
|
54 |
struct GenericHandler *nextPtr;
|
55 |
/* Next handler in list of all generic
|
56 |
* handlers, or NULL for end of list. */
|
57 |
} GenericHandler;
|
58 |
|
59 |
/*
|
60 |
* There's a potential problem if Tk_HandleEvent is entered recursively.
|
61 |
* A handler cannot be deleted physically until we have returned from
|
62 |
* calling it. Otherwise, we're looking at unallocated memory in advancing to
|
63 |
* its `next' entry. We deal with the problem by using the `delete flag' and
|
64 |
* deleting handlers only when it's known that there's no handler active.
|
65 |
*
|
66 |
*/
|
67 |
|
68 |
/*
|
69 |
* The following structure is used for queueing X-style events on the
|
70 |
* Tcl event queue.
|
71 |
*/
|
72 |
|
73 |
typedef struct TkWindowEvent {
|
74 |
Tcl_Event header; /* Standard information for all events. */
|
75 |
XEvent event; /* The X event. */
|
76 |
} TkWindowEvent;
|
77 |
|
78 |
/*
|
79 |
* Array of event masks corresponding to each X event:
|
80 |
*/
|
81 |
|
82 |
static unsigned long eventMasks[TK_LASTEVENT] = {
|
83 |
0,
|
84 |
0,
|
85 |
KeyPressMask, /* KeyPress */
|
86 |
KeyReleaseMask, /* KeyRelease */
|
87 |
ButtonPressMask, /* ButtonPress */
|
88 |
ButtonReleaseMask, /* ButtonRelease */
|
89 |
PointerMotionMask|PointerMotionHintMask|ButtonMotionMask
|
90 |
|Button1MotionMask|Button2MotionMask|Button3MotionMask
|
91 |
|Button4MotionMask|Button5MotionMask,
|
92 |
/* MotionNotify */
|
93 |
EnterWindowMask, /* EnterNotify */
|
94 |
LeaveWindowMask, /* LeaveNotify */
|
95 |
FocusChangeMask, /* FocusIn */
|
96 |
FocusChangeMask, /* FocusOut */
|
97 |
KeymapStateMask, /* KeymapNotify */
|
98 |
ExposureMask, /* Expose */
|
99 |
ExposureMask, /* GraphicsExpose */
|
100 |
ExposureMask, /* NoExpose */
|
101 |
VisibilityChangeMask, /* VisibilityNotify */
|
102 |
SubstructureNotifyMask, /* CreateNotify */
|
103 |
StructureNotifyMask, /* DestroyNotify */
|
104 |
StructureNotifyMask, /* UnmapNotify */
|
105 |
StructureNotifyMask, /* MapNotify */
|
106 |
SubstructureRedirectMask, /* MapRequest */
|
107 |
StructureNotifyMask, /* ReparentNotify */
|
108 |
StructureNotifyMask, /* ConfigureNotify */
|
109 |
SubstructureRedirectMask, /* ConfigureRequest */
|
110 |
StructureNotifyMask, /* GravityNotify */
|
111 |
ResizeRedirectMask, /* ResizeRequest */
|
112 |
StructureNotifyMask, /* CirculateNotify */
|
113 |
SubstructureRedirectMask, /* CirculateRequest */
|
114 |
PropertyChangeMask, /* PropertyNotify */
|
115 |
0, /* SelectionClear */
|
116 |
0, /* SelectionRequest */
|
117 |
0, /* SelectionNotify */
|
118 |
ColormapChangeMask, /* ColormapNotify */
|
119 |
0, /* ClientMessage */
|
120 |
0, /* Mapping Notify */
|
121 |
VirtualEventMask, /* VirtualEvents */
|
122 |
ActivateMask, /* ActivateNotify */
|
123 |
ActivateMask, /* DeactivateNotify */
|
124 |
MouseWheelMask /* MouseWheelEvent */
|
125 |
};
|
126 |
|
127 |
|
128 |
/*
|
129 |
* The structure below is used to store Data for the Event module that
|
130 |
* must be kept thread-local. The "dataKey" is used to fetch the
|
131 |
* thread-specific storage for the current thread.
|
132 |
*/
|
133 |
|
134 |
typedef struct ThreadSpecificData {
|
135 |
|
136 |
int genericHandlersActive;
|
137 |
/* The following variable has a non-zero
|
138 |
* value when a handler is active. */
|
139 |
InProgress *pendingPtr;
|
140 |
/* Topmost search in progress, or
|
141 |
* NULL if none. */
|
142 |
GenericHandler *genericList;
|
143 |
/* First handler in the list, or NULL. */
|
144 |
GenericHandler *lastGenericPtr;
|
145 |
/* Last handler in list. */
|
146 |
|
147 |
/*
|
148 |
* If someone has called Tk_RestrictEvents, the information below
|
149 |
* keeps track of it.
|
150 |
*/
|
151 |
|
152 |
Tk_RestrictProc *restrictProc;
|
153 |
/* Procedure to call. NULL means no
|
154 |
* restrictProc is currently in effect. */
|
155 |
ClientData restrictArg; /* Argument to pass to restrictProc. */
|
156 |
} ThreadSpecificData;
|
157 |
static Tcl_ThreadDataKey dataKey;
|
158 |
|
159 |
/*
|
160 |
* Prototypes for procedures that are only referenced locally within
|
161 |
* this file.
|
162 |
*/
|
163 |
|
164 |
static void DelayedMotionProc _ANSI_ARGS_((ClientData clientData));
|
165 |
static int WindowEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
|
166 |
int flags));
|
167 |
|
168 |
/*
|
169 |
*--------------------------------------------------------------
|
170 |
*
|
171 |
* Tk_CreateEventHandler --
|
172 |
*
|
173 |
* Arrange for a given procedure to be invoked whenever
|
174 |
* events from a given class occur in a given window.
|
175 |
*
|
176 |
* Results:
|
177 |
* None.
|
178 |
*
|
179 |
* Side effects:
|
180 |
* From now on, whenever an event of the type given by
|
181 |
* mask occurs for token and is processed by Tk_HandleEvent,
|
182 |
* proc will be called. See the manual entry for details
|
183 |
* of the calling sequence and return value for proc.
|
184 |
*
|
185 |
*--------------------------------------------------------------
|
186 |
*/
|
187 |
|
188 |
void
|
189 |
Tk_CreateEventHandler(token, mask, proc, clientData)
|
190 |
Tk_Window token; /* Token for window in which to
|
191 |
* create handler. */
|
192 |
unsigned long mask; /* Events for which proc should
|
193 |
* be called. */
|
194 |
Tk_EventProc *proc; /* Procedure to call for each
|
195 |
* selected event */
|
196 |
ClientData clientData; /* Arbitrary data to pass to proc. */
|
197 |
{
|
198 |
register TkEventHandler *handlerPtr;
|
199 |
register TkWindow *winPtr = (TkWindow *) token;
|
200 |
int found;
|
201 |
|
202 |
/*
|
203 |
* Skim through the list of existing handlers to (a) compute the
|
204 |
* overall event mask for the window (so we can pass this new
|
205 |
* value to the X system) and (b) see if there's already a handler
|
206 |
* declared with the same callback and clientData (if so, just
|
207 |
* change the mask). If no existing handler matches, then create
|
208 |
* a new handler.
|
209 |
*/
|
210 |
|
211 |
found = 0;
|
212 |
if (winPtr->handlerList == NULL) {
|
213 |
handlerPtr = (TkEventHandler *) ckalloc(
|
214 |
(unsigned) sizeof(TkEventHandler));
|
215 |
winPtr->handlerList = handlerPtr;
|
216 |
goto initHandler;
|
217 |
} else {
|
218 |
for (handlerPtr = winPtr->handlerList; ;
|
219 |
handlerPtr = handlerPtr->nextPtr) {
|
220 |
if ((handlerPtr->proc == proc)
|
221 |
&& (handlerPtr->clientData == clientData)) {
|
222 |
handlerPtr->mask = mask;
|
223 |
found = 1;
|
224 |
}
|
225 |
if (handlerPtr->nextPtr == NULL) {
|
226 |
break;
|
227 |
}
|
228 |
}
|
229 |
}
|
230 |
|
231 |
/*
|
232 |
* Create a new handler if no matching old handler was found.
|
233 |
*/
|
234 |
|
235 |
if (!found) {
|
236 |
handlerPtr->nextPtr = (TkEventHandler *)
|
237 |
ckalloc(sizeof(TkEventHandler));
|
238 |
handlerPtr = handlerPtr->nextPtr;
|
239 |
initHandler:
|
240 |
handlerPtr->mask = mask;
|
241 |
handlerPtr->proc = proc;
|
242 |
handlerPtr->clientData = clientData;
|
243 |
handlerPtr->nextPtr = NULL;
|
244 |
}
|
245 |
|
246 |
/*
|
247 |
* No need to call XSelectInput: Tk always selects on all events
|
248 |
* for all windows (needed to support bindings on classes and "all").
|
249 |
*/
|
250 |
}
|
251 |
|
252 |
/*
|
253 |
*--------------------------------------------------------------
|
254 |
*
|
255 |
* Tk_DeleteEventHandler --
|
256 |
*
|
257 |
* Delete a previously-created handler.
|
258 |
*
|
259 |
* Results:
|
260 |
* None.
|
261 |
*
|
262 |
* Side effects:
|
263 |
* If there existed a handler as described by the
|
264 |
* parameters, the handler is deleted so that proc
|
265 |
* will not be invoked again.
|
266 |
*
|
267 |
*--------------------------------------------------------------
|
268 |
*/
|
269 |
|
270 |
void
|
271 |
Tk_DeleteEventHandler(token, mask, proc, clientData)
|
272 |
Tk_Window token; /* Same as corresponding arguments passed */
|
273 |
unsigned long mask; /* previously to Tk_CreateEventHandler. */
|
274 |
Tk_EventProc *proc;
|
275 |
ClientData clientData;
|
276 |
{
|
277 |
register TkEventHandler *handlerPtr;
|
278 |
register InProgress *ipPtr;
|
279 |
TkEventHandler *prevPtr;
|
280 |
register TkWindow *winPtr = (TkWindow *) token;
|
281 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
|
282 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
|
283 |
|
284 |
/*
|
285 |
* Find the event handler to be deleted, or return
|
286 |
* immediately if it doesn't exist.
|
287 |
*/
|
288 |
|
289 |
for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ;
|
290 |
prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) {
|
291 |
if (handlerPtr == NULL) {
|
292 |
return;
|
293 |
}
|
294 |
if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc)
|
295 |
&& (handlerPtr->clientData == clientData)) {
|
296 |
break;
|
297 |
}
|
298 |
}
|
299 |
|
300 |
/*
|
301 |
* If Tk_HandleEvent is about to process this handler, tell it to
|
302 |
* process the next one instead.
|
303 |
*/
|
304 |
|
305 |
for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
|
306 |
if (ipPtr->nextHandler == handlerPtr) {
|
307 |
ipPtr->nextHandler = handlerPtr->nextPtr;
|
308 |
}
|
309 |
}
|
310 |
|
311 |
/*
|
312 |
* Free resources associated with the handler.
|
313 |
*/
|
314 |
|
315 |
if (prevPtr == NULL) {
|
316 |
winPtr->handlerList = handlerPtr->nextPtr;
|
317 |
} else {
|
318 |
prevPtr->nextPtr = handlerPtr->nextPtr;
|
319 |
}
|
320 |
ckfree((char *) handlerPtr);
|
321 |
|
322 |
|
323 |
/*
|
324 |
* No need to call XSelectInput: Tk always selects on all events
|
325 |
* for all windows (needed to support bindings on classes and "all").
|
326 |
*/
|
327 |
}
|
328 |
|
329 |
/*--------------------------------------------------------------
|
330 |
*
|
331 |
* Tk_CreateGenericHandler --
|
332 |
*
|
333 |
* Register a procedure to be called on each X event, regardless
|
334 |
* of display or window. Generic handlers are useful for capturing
|
335 |
* events that aren't associated with windows, or events for windows
|
336 |
* not managed by Tk.
|
337 |
*
|
338 |
* Results:
|
339 |
* None.
|
340 |
*
|
341 |
* Side Effects:
|
342 |
* From now on, whenever an X event is given to Tk_HandleEvent,
|
343 |
* invoke proc, giving it clientData and the event as arguments.
|
344 |
*
|
345 |
*--------------------------------------------------------------
|
346 |
*/
|
347 |
|
348 |
void
|
349 |
Tk_CreateGenericHandler(proc, clientData)
|
350 |
Tk_GenericProc *proc; /* Procedure to call on every event. */
|
351 |
ClientData clientData; /* One-word value to pass to proc. */
|
352 |
{
|
353 |
GenericHandler *handlerPtr;
|
354 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
|
355 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
|
356 |
|
357 |
handlerPtr = (GenericHandler *) ckalloc (sizeof (GenericHandler));
|
358 |
|
359 |
handlerPtr->proc = proc;
|
360 |
handlerPtr->clientData = clientData;
|
361 |
handlerPtr->deleteFlag = 0;
|
362 |
handlerPtr->nextPtr = NULL;
|
363 |
if (tsdPtr->genericList == NULL) {
|
364 |
tsdPtr->genericList = handlerPtr;
|
365 |
} else {
|
366 |
tsdPtr->lastGenericPtr->nextPtr = handlerPtr;
|
367 |
}
|
368 |
tsdPtr->lastGenericPtr = handlerPtr;
|
369 |
}
|
370 |
|
371 |
/*
|
372 |
*--------------------------------------------------------------
|
373 |
*
|
374 |
* Tk_DeleteGenericHandler --
|
375 |
*
|
376 |
* Delete a previously-created generic handler.
|
377 |
*
|
378 |
* Results:
|
379 |
* None.
|
380 |
*
|
381 |
* Side Effects:
|
382 |
* If there existed a handler as described by the parameters,
|
383 |
* that handler is logically deleted so that proc will not be
|
384 |
* invoked again. The physical deletion happens in the event
|
385 |
* loop in Tk_HandleEvent.
|
386 |
*
|
387 |
*--------------------------------------------------------------
|
388 |
*/
|
389 |
|
390 |
void
|
391 |
Tk_DeleteGenericHandler(proc, clientData)
|
392 |
Tk_GenericProc *proc;
|
393 |
ClientData clientData;
|
394 |
{
|
395 |
GenericHandler * handler;
|
396 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
|
397 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
|
398 |
|
399 |
for (handler = tsdPtr->genericList; handler; handler = handler->nextPtr) {
|
400 |
if ((handler->proc == proc) && (handler->clientData == clientData)) {
|
401 |
handler->deleteFlag = 1;
|
402 |
}
|
403 |
}
|
404 |
}
|
405 |
|
406 |
/*
|
407 |
*--------------------------------------------------------------
|
408 |
*
|
409 |
* TkEventInit --
|
410 |
*
|
411 |
* This procedures initializes all the event module
|
412 |
* structures used by the current thread. It must be
|
413 |
* called before any other procedure in this file is
|
414 |
* called.
|
415 |
*
|
416 |
* Results:
|
417 |
* None.
|
418 |
*
|
419 |
* Side Effects:
|
420 |
* None.
|
421 |
*
|
422 |
*--------------------------------------------------------------
|
423 |
*/
|
424 |
|
425 |
void
|
426 |
TkEventInit _ANSI_ARGS_((void))
|
427 |
{
|
428 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
|
429 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
|
430 |
|
431 |
tsdPtr->genericHandlersActive = 0;
|
432 |
tsdPtr->pendingPtr = NULL;
|
433 |
tsdPtr->genericList = NULL;
|
434 |
tsdPtr->lastGenericPtr = NULL;
|
435 |
tsdPtr->restrictProc = NULL;
|
436 |
tsdPtr->restrictArg = NULL;
|
437 |
}
|
438 |
|
439 |
/*
|
440 |
*--------------------------------------------------------------
|
441 |
*
|
442 |
* Tk_HandleEvent --
|
443 |
*
|
444 |
* Given an event, invoke all the handlers that have
|
445 |
* been registered for the event.
|
446 |
*
|
447 |
* Results:
|
448 |
* None.
|
449 |
*
|
450 |
* Side effects:
|
451 |
* Depends on the handlers.
|
452 |
*
|
453 |
*--------------------------------------------------------------
|
454 |
*/
|
455 |
|
456 |
void
|
457 |
Tk_HandleEvent(eventPtr)
|
458 |
XEvent *eventPtr; /* Event to dispatch. */
|
459 |
{
|
460 |
register TkEventHandler *handlerPtr;
|
461 |
register GenericHandler *genericPtr;
|
462 |
register GenericHandler *genPrevPtr;
|
463 |
TkWindow *winPtr;
|
464 |
unsigned long mask;
|
465 |
InProgress ip;
|
466 |
Window handlerWindow;
|
467 |
TkDisplay *dispPtr;
|
468 |
Tcl_Interp *interp = (Tcl_Interp *) NULL;
|
469 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
|
470 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
|
471 |
|
472 |
/*
|
473 |
* Hack for simulated X-events: Correct the state field
|
474 |
* of the event record to match with the ButtonPress
|
475 |
* and ButtonRelease events.
|
476 |
*/
|
477 |
|
478 |
if (eventPtr->type==ButtonPress) {
|
479 |
dispPtr = TkGetDisplay(eventPtr->xbutton.display);
|
480 |
eventPtr->xbutton.state |= dispPtr->mouseButtonState;
|
481 |
switch (eventPtr->xbutton.button) {
|
482 |
case 1: dispPtr->mouseButtonState |= Button1Mask; break;
|
483 |
case 2: dispPtr->mouseButtonState |= Button2Mask; break;
|
484 |
case 3: dispPtr->mouseButtonState |= Button3Mask; break;
|
485 |
}
|
486 |
} else if (eventPtr->type==ButtonRelease) {
|
487 |
dispPtr = TkGetDisplay(eventPtr->xbutton.display);
|
488 |
switch (eventPtr->xbutton.button) {
|
489 |
case 1: dispPtr->mouseButtonState &= ~Button1Mask; break;
|
490 |
case 2: dispPtr->mouseButtonState &= ~Button2Mask; break;
|
491 |
case 3: dispPtr->mouseButtonState &= ~Button3Mask; break;
|
492 |
}
|
493 |
eventPtr->xbutton.state |= dispPtr->mouseButtonState;
|
494 |
} else if (eventPtr->type==MotionNotify) {
|
495 |
dispPtr = TkGetDisplay(eventPtr->xmotion.display);
|
496 |
eventPtr->xmotion.state |= dispPtr->mouseButtonState;
|
497 |
}
|
498 |
|
499 |
/*
|
500 |
* Next, invoke all the generic event handlers (those that are
|
501 |
* invoked for all events). If a generic event handler reports that
|
502 |
* an event is fully processed, go no further.
|
503 |
*/
|
504 |
|
505 |
for (genPrevPtr = NULL, genericPtr = tsdPtr->genericList;
|
506 |
genericPtr != NULL; ) {
|
507 |
if (genericPtr->deleteFlag) {
|
508 |
if (!tsdPtr->genericHandlersActive) {
|
509 |
GenericHandler *tmpPtr;
|
510 |
|
511 |
/*
|
512 |
* This handler needs to be deleted and there are no
|
513 |
* calls pending through the handler, so now is a safe
|
514 |
* time to delete it.
|
515 |
*/
|
516 |
|
517 |
tmpPtr = genericPtr->nextPtr;
|
518 |
if (genPrevPtr == NULL) {
|
519 |
tsdPtr->genericList = tmpPtr;
|
520 |
} else {
|
521 |
genPrevPtr->nextPtr = tmpPtr;
|
522 |
}
|
523 |
if (tmpPtr == NULL) {
|
524 |
tsdPtr->lastGenericPtr = genPrevPtr;
|
525 |
}
|
526 |
(void) ckfree((char *) genericPtr);
|
527 |
genericPtr = tmpPtr;
|
528 |
continue;
|
529 |
}
|
530 |
} else {
|
531 |
int done;
|
532 |
|
533 |
tsdPtr->genericHandlersActive++;
|
534 |
done = (*genericPtr->proc)(genericPtr->clientData, eventPtr);
|
535 |
tsdPtr->genericHandlersActive--;
|
536 |
if (done) {
|
537 |
return;
|
538 |
}
|
539 |
}
|
540 |
genPrevPtr = genericPtr;
|
541 |
genericPtr = genPrevPtr->nextPtr;
|
542 |
}
|
543 |
|
544 |
/*
|
545 |
* If the event is a MappingNotify event, find its display and
|
546 |
* refresh the keyboard mapping information for the display.
|
547 |
* After that there's nothing else to do with the event, so just
|
548 |
* quit.
|
549 |
*/
|
550 |
|
551 |
if (eventPtr->type == MappingNotify) {
|
552 |
dispPtr = TkGetDisplay(eventPtr->xmapping.display);
|
553 |
if (dispPtr != NULL) {
|
554 |
XRefreshKeyboardMapping(&eventPtr->xmapping);
|
555 |
dispPtr->bindInfoStale = 1;
|
556 |
}
|
557 |
return;
|
558 |
}
|
559 |
|
560 |
/*
|
561 |
* Events selected by StructureNotify require special handling.
|
562 |
* They look the same as those selected by SubstructureNotify.
|
563 |
* The only difference is whether the "event" and "window" fields
|
564 |
* are the same. Compare the two fields and convert StructureNotify
|
565 |
* to SubstructureNotify if necessary.
|
566 |
*/
|
567 |
|
568 |
handlerWindow = eventPtr->xany.window;
|
569 |
mask = eventMasks[eventPtr->xany.type];
|
570 |
if (mask == StructureNotifyMask) {
|
571 |
if (eventPtr->xmap.event != eventPtr->xmap.window) {
|
572 |
mask = SubstructureNotifyMask;
|
573 |
handlerWindow = eventPtr->xmap.event;
|
574 |
}
|
575 |
}
|
576 |
winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, handlerWindow);
|
577 |
if (winPtr == NULL) {
|
578 |
|
579 |
/*
|
580 |
* There isn't a TkWindow structure for this window.
|
581 |
* However, if the event is a PropertyNotify event then call
|
582 |
* the selection manager (it deals beneath-the-table with
|
583 |
* certain properties).
|
584 |
*/
|
585 |
|
586 |
if (eventPtr->type == PropertyNotify) {
|
587 |
TkSelPropProc(eventPtr);
|
588 |
}
|
589 |
return;
|
590 |
}
|
591 |
|
592 |
/*
|
593 |
* Once a window has started getting deleted, don't process any more
|
594 |
* events for it except for the DestroyNotify event. This check is
|
595 |
* needed because a DestroyNotify handler could re-invoke the event
|
596 |
* loop, causing other pending events to be handled for the window
|
597 |
* (the window doesn't get totally expunged from our tables until
|
598 |
* after the DestroyNotify event has been completely handled).
|
599 |
*/
|
600 |
|
601 |
if ((winPtr->flags & TK_ALREADY_DEAD)
|
602 |
&& (eventPtr->type != DestroyNotify)) {
|
603 |
return;
|
604 |
}
|
605 |
|
606 |
if (winPtr->mainPtr != NULL) {
|
607 |
|
608 |
/*
|
609 |
* Protect interpreter for this window from possible deletion
|
610 |
* while we are dealing with the event for this window. Thus,
|
611 |
* widget writers do not have to worry about protecting the
|
612 |
* interpreter in their own code.
|
613 |
*/
|
614 |
|
615 |
interp = winPtr->mainPtr->interp;
|
616 |
Tcl_Preserve((ClientData) interp);
|
617 |
|
618 |
/*
|
619 |
* Call focus-related code to look at FocusIn, FocusOut, Enter,
|
620 |
* and Leave events; depending on its return value, ignore the
|
621 |
* event.
|
622 |
*/
|
623 |
|
624 |
if ((mask & (FocusChangeMask|EnterWindowMask|LeaveWindowMask))
|
625 |
&& !TkFocusFilterEvent(winPtr, eventPtr)) {
|
626 |
Tcl_Release((ClientData) interp);
|
627 |
return;
|
628 |
}
|
629 |
|
630 |
/*
|
631 |
* Redirect KeyPress and KeyRelease events to the focus window,
|
632 |
* or ignore them entirely if there is no focus window. We also
|
633 |
* route the MouseWheel event to the focus window. The MouseWheel
|
634 |
* event is an extension to the X event set. Currently, it is only
|
635 |
* available on the Windows version of Tk.
|
636 |
*/
|
637 |
|
638 |
if (mask & (KeyPressMask|KeyReleaseMask|MouseWheelMask)) {
|
639 |
winPtr->dispPtr->lastEventTime = eventPtr->xkey.time;
|
640 |
winPtr = TkFocusKeyEvent(winPtr, eventPtr);
|
641 |
if (winPtr == NULL) {
|
642 |
Tcl_Release((ClientData) interp);
|
643 |
return;
|
644 |
}
|
645 |
}
|
646 |
|
647 |
/*
|
648 |
* Call a grab-related procedure to do special processing on
|
649 |
* pointer events.
|
650 |
*/
|
651 |
|
652 |
if (mask & (ButtonPressMask|ButtonReleaseMask|PointerMotionMask
|
653 |
|EnterWindowMask|LeaveWindowMask)) {
|
654 |
if (mask & (ButtonPressMask|ButtonReleaseMask)) {
|
655 |
winPtr->dispPtr->lastEventTime = eventPtr->xbutton.time;
|
656 |
} else if (mask & PointerMotionMask) {
|
657 |
winPtr->dispPtr->lastEventTime = eventPtr->xmotion.time;
|
658 |
} else {
|
659 |
winPtr->dispPtr->lastEventTime = eventPtr->xcrossing.time;
|
660 |
}
|
661 |
if (TkPointerEvent(eventPtr, winPtr) == 0) {
|
662 |
goto done;
|
663 |
}
|
664 |
}
|
665 |
}
|
666 |
|
667 |
#ifdef TK_USE_INPUT_METHODS
|
668 |
/*
|
669 |
* Pass the event to the input method(s), if there are any, and
|
670 |
* discard the event if the input method(s) insist. Create the
|
671 |
* input context for the window if it hasn't already been done
|
672 |
* (XFilterEvent needs this context).
|
673 |
*/
|
674 |
if (winPtr->dispPtr->useInputMethods) {
|
675 |
if (!(winPtr->flags & TK_CHECKED_IC)) {
|
676 |
if (winPtr->dispPtr->inputMethod != NULL) {
|
677 |
winPtr->inputContext = XCreateIC(
|
678 |
winPtr->dispPtr->inputMethod, XNInputStyle,
|
679 |
XIMPreeditNothing|XIMStatusNothing,
|
680 |
XNClientWindow, winPtr->window,
|
681 |
XNFocusWindow, winPtr->window, NULL);
|
682 |
}
|
683 |
winPtr->flags |= TK_CHECKED_IC;
|
684 |
}
|
685 |
if (XFilterEvent(eventPtr, None)) {
|
686 |
goto done;
|
687 |
}
|
688 |
}
|
689 |
#endif /* TK_USE_INPUT_METHODS */
|
690 |
|
691 |
/*
|
692 |
* For events where it hasn't already been done, update the current
|
693 |
* time in the display.
|
694 |
*/
|
695 |
|
696 |
if (eventPtr->type == PropertyNotify) {
|
697 |
winPtr->dispPtr->lastEventTime = eventPtr->xproperty.time;
|
698 |
}
|
699 |
|
700 |
/*
|
701 |
* There's a potential interaction here with Tk_DeleteEventHandler.
|
702 |
* Read the documentation for pendingPtr.
|
703 |
*/
|
704 |
|
705 |
ip.eventPtr = eventPtr;
|
706 |
ip.winPtr = winPtr;
|
707 |
ip.nextHandler = NULL;
|
708 |
ip.nextPtr = tsdPtr->pendingPtr;
|
709 |
tsdPtr->pendingPtr = &ip;
|
710 |
if (mask == 0) {
|
711 |
if ((eventPtr->type == SelectionClear)
|
712 |
|| (eventPtr->type == SelectionRequest)
|
713 |
|| (eventPtr->type == SelectionNotify)) {
|
714 |
TkSelEventProc((Tk_Window) winPtr, eventPtr);
|
715 |
} else if ((eventPtr->type == ClientMessage)
|
716 |
&& (eventPtr->xclient.message_type ==
|
717 |
Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"))) {
|
718 |
TkWmProtocolEventProc(winPtr, eventPtr);
|
719 |
}
|
720 |
} else {
|
721 |
for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) {
|
722 |
if ((handlerPtr->mask & mask) != 0) {
|
723 |
ip.nextHandler = handlerPtr->nextPtr;
|
724 |
(*(handlerPtr->proc))(handlerPtr->clientData, eventPtr);
|
725 |
handlerPtr = ip.nextHandler;
|
726 |
} else {
|
727 |
handlerPtr = handlerPtr->nextPtr;
|
728 |
}
|
729 |
}
|
730 |
|
731 |
/*
|
732 |
* Pass the event to the "bind" command mechanism. But, don't
|
733 |
* do this for SubstructureNotify events. The "bind" command
|
734 |
* doesn't support them anyway, and it's easier to filter out
|
735 |
* these events here than in the lower-level procedures.
|
736 |
*/
|
737 |
|
738 |
if ((ip.winPtr != None) && (mask != SubstructureNotifyMask)) {
|
739 |
TkBindEventProc(winPtr, eventPtr);
|
740 |
}
|
741 |
}
|
742 |
tsdPtr->pendingPtr = ip.nextPtr;
|
743 |
done:
|
744 |
|
745 |
/*
|
746 |
* Release the interpreter for this window so that it can be potentially
|
747 |
* deleted if requested.
|
748 |
*/
|
749 |
|
750 |
if (interp != (Tcl_Interp *) NULL) {
|
751 |
Tcl_Release((ClientData) interp);
|
752 |
}
|
753 |
}
|
754 |
|
755 |
/*
|
756 |
*--------------------------------------------------------------
|
757 |
*
|
758 |
* TkEventDeadWindow --
|
759 |
*
|
760 |
* This procedure is invoked when it is determined that
|
761 |
* a window is dead. It cleans up event-related information
|
762 |
* about the window.
|
763 |
*
|
764 |
* Results:
|
765 |
* None.
|
766 |
*
|
767 |
* Side effects:
|
768 |
* Various things get cleaned up and recycled.
|
769 |
*
|
770 |
*--------------------------------------------------------------
|
771 |
*/
|
772 |
|
773 |
void
|
774 |
TkEventDeadWindow(winPtr)
|
775 |
TkWindow *winPtr; /* Information about the window
|
776 |
* that is being deleted. */
|
777 |
{
|
778 |
register TkEventHandler *handlerPtr;
|
779 |
register InProgress *ipPtr;
|
780 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
|
781 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
|
782 |
|
783 |
/*
|
784 |
* While deleting all the handlers, be careful to check for
|
785 |
* Tk_HandleEvent being about to process one of the deleted
|
786 |
* handlers. If it is, tell it to quit (all of the handlers
|
787 |
* are being deleted).
|
788 |
*/
|
789 |
|
790 |
while (winPtr->handlerList != NULL) {
|
791 |
handlerPtr = winPtr->handlerList;
|
792 |
winPtr->handlerList = handlerPtr->nextPtr;
|
793 |
for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
|
794 |
ipPtr = ipPtr->nextPtr) {
|
795 |
if (ipPtr->nextHandler == handlerPtr) {
|
796 |
ipPtr->nextHandler = NULL;
|
797 |
}
|
798 |
if (ipPtr->winPtr == winPtr) {
|
799 |
ipPtr->winPtr = None;
|
800 |
}
|
801 |
}
|
802 |
ckfree((char *) handlerPtr);
|
803 |
}
|
804 |
}
|
805 |
|
806 |
/*
|
807 |
*----------------------------------------------------------------------
|
808 |
*
|
809 |
* TkCurrentTime --
|
810 |
*
|
811 |
* Try to deduce the current time. "Current time" means the time
|
812 |
* of the event that led to the current code being executed, which
|
813 |
* means the time in the most recently-nested invocation of
|
814 |
* Tk_HandleEvent.
|
815 |
*
|
816 |
* Results:
|
817 |
* The return value is the time from the current event, or
|
818 |
* CurrentTime if there is no current event or if the current
|
819 |
* event contains no time.
|
820 |
*
|
821 |
* Side effects:
|
822 |
* None.
|
823 |
*
|
824 |
*----------------------------------------------------------------------
|
825 |
*/
|
826 |
|
827 |
Time
|
828 |
TkCurrentTime(dispPtr)
|
829 |
TkDisplay *dispPtr; /* Display for which the time is desired. */
|
830 |
{
|
831 |
register XEvent *eventPtr;
|
832 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
|
833 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
|
834 |
|
835 |
if (tsdPtr->pendingPtr == NULL) {
|
836 |
return dispPtr->lastEventTime;
|
837 |
}
|
838 |
eventPtr = tsdPtr->pendingPtr->eventPtr;
|
839 |
switch (eventPtr->type) {
|
840 |
case ButtonPress:
|
841 |
case ButtonRelease:
|
842 |
return eventPtr->xbutton.time;
|
843 |
case KeyPress:
|
844 |
case KeyRelease:
|
845 |
return eventPtr->xkey.time;
|
846 |
case MotionNotify:
|
847 |
return eventPtr->xmotion.time;
|
848 |
case EnterNotify:
|
849 |
case LeaveNotify:
|
850 |
return eventPtr->xcrossing.time;
|
851 |
case PropertyNotify:
|
852 |
return eventPtr->xproperty.time;
|
853 |
}
|
854 |
return dispPtr->lastEventTime;
|
855 |
}
|
856 |
|
857 |
/*
|
858 |
*----------------------------------------------------------------------
|
859 |
*
|
860 |
* Tk_RestrictEvents --
|
861 |
*
|
862 |
* This procedure is used to globally restrict the set of events
|
863 |
* that will be dispatched. The restriction is done by filtering
|
864 |
* all incoming X events through a procedure that determines
|
865 |
* whether they are to be processed immediately, deferred, or
|
866 |
* discarded.
|
867 |
*
|
868 |
* Results:
|
869 |
* The return value is the previous restriction procedure in effect,
|
870 |
* if there was one, or NULL if there wasn't.
|
871 |
*
|
872 |
* Side effects:
|
873 |
* From now on, proc will be called to determine whether to process,
|
874 |
* defer or discard each incoming X event.
|
875 |
*
|
876 |
*----------------------------------------------------------------------
|
877 |
*/
|
878 |
|
879 |
Tk_RestrictProc *
|
880 |
Tk_RestrictEvents(proc, arg, prevArgPtr)
|
881 |
Tk_RestrictProc *proc; /* Procedure to call for each incoming
|
882 |
* event. */
|
883 |
ClientData arg; /* Arbitrary argument to pass to proc. */
|
884 |
ClientData *prevArgPtr; /* Place to store information about previous
|
885 |
* argument. */
|
886 |
{
|
887 |
Tk_RestrictProc *prev;
|
888 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
|
889 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
|
890 |
|
891 |
prev = tsdPtr->restrictProc;
|
892 |
*prevArgPtr = tsdPtr->restrictArg;
|
893 |
tsdPtr->restrictProc = proc;
|
894 |
tsdPtr->restrictArg = arg;
|
895 |
return prev;
|
896 |
}
|
897 |
|
898 |
/*
|
899 |
*----------------------------------------------------------------------
|
900 |
*
|
901 |
* Tk_QueueWindowEvent --
|
902 |
*
|
903 |
* Given an X-style window event, this procedure adds it to the
|
904 |
* Tcl event queue at the given position. This procedure also
|
905 |
* performs mouse motion event collapsing if possible.
|
906 |
*
|
907 |
* Results:
|
908 |
* None.
|
909 |
*
|
910 |
* Side effects:
|
911 |
* Adds stuff to the event queue, which will eventually be
|
912 |
* processed.
|
913 |
*
|
914 |
*----------------------------------------------------------------------
|
915 |
*/
|
916 |
|
917 |
void
|
918 |
Tk_QueueWindowEvent(eventPtr, position)
|
919 |
XEvent *eventPtr; /* Event to add to queue. This
|
920 |
* procedures copies it before adding
|
921 |
* it to the queue. */
|
922 |
Tcl_QueuePosition position; /* Where to put it on the queue:
|
923 |
* TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
|
924 |
* or TCL_QUEUE_MARK. */
|
925 |
{
|
926 |
TkWindowEvent *wevPtr;
|
927 |
TkDisplay *dispPtr;
|
928 |
|
929 |
/*
|
930 |
* Find our display structure for the event's display.
|
931 |
*/
|
932 |
|
933 |
for (dispPtr = TkGetDisplayList(); ; dispPtr = dispPtr->nextPtr) {
|
934 |
if (dispPtr == NULL) {
|
935 |
return;
|
936 |
}
|
937 |
if (dispPtr->display == eventPtr->xany.display) {
|
938 |
break;
|
939 |
}
|
940 |
}
|
941 |
|
942 |
if ((dispPtr->delayedMotionPtr != NULL) && (position == TCL_QUEUE_TAIL)) {
|
943 |
if ((eventPtr->type == MotionNotify) && (eventPtr->xmotion.window
|
944 |
== dispPtr->delayedMotionPtr->event.xmotion.window)) {
|
945 |
/*
|
946 |
* The new event is a motion event in the same window as the
|
947 |
* saved motion event. Just replace the saved event with the
|
948 |
* new one.
|
949 |
*/
|
950 |
|
951 |
dispPtr->delayedMotionPtr->event = *eventPtr;
|
952 |
return;
|
953 |
} else if ((eventPtr->type != GraphicsExpose)
|
954 |
&& (eventPtr->type != NoExpose)
|
955 |
&& (eventPtr->type != Expose)) {
|
956 |
/*
|
957 |
* The new event may conflict with the saved motion event. Queue
|
958 |
* the saved motion event now so that it will be processed before
|
959 |
* the new event.
|
960 |
*/
|
961 |
|
962 |
Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, position);
|
963 |
dispPtr->delayedMotionPtr = NULL;
|
964 |
Tcl_CancelIdleCall(DelayedMotionProc, (ClientData) dispPtr);
|
965 |
}
|
966 |
}
|
967 |
|
968 |
wevPtr = (TkWindowEvent *) ckalloc(sizeof(TkWindowEvent));
|
969 |
wevPtr->header.proc = WindowEventProc;
|
970 |
wevPtr->event = *eventPtr;
|
971 |
if ((eventPtr->type == MotionNotify) && (position == TCL_QUEUE_TAIL)) {
|
972 |
/*
|
973 |
* The new event is a motion event so don't queue it immediately;
|
974 |
* save it around in case another motion event arrives that it can
|
975 |
* be collapsed with.
|
976 |
*/
|
977 |
|
978 |
if (dispPtr->delayedMotionPtr != NULL) {
|
979 |
panic("Tk_QueueWindowEvent found unexpected delayed motion event");
|
980 |
}
|
981 |
dispPtr->delayedMotionPtr = wevPtr;
|
982 |
Tcl_DoWhenIdle(DelayedMotionProc, (ClientData) dispPtr);
|
983 |
} else {
|
984 |
Tcl_QueueEvent(&wevPtr->header, position);
|
985 |
}
|
986 |
}
|
987 |
|
988 |
/*
|
989 |
*---------------------------------------------------------------------------
|
990 |
*
|
991 |
* TkQueueEventForAllChildren --
|
992 |
*
|
993 |
* Given an XEvent, recursively queue the event for this window and
|
994 |
* all non-toplevel children of the given window.
|
995 |
*
|
996 |
* Results:
|
997 |
* None.
|
998 |
*
|
999 |
* Side effects:
|
1000 |
* Events queued.
|
1001 |
*
|
1002 |
*---------------------------------------------------------------------------
|
1003 |
*/
|
1004 |
|
1005 |
void
|
1006 |
TkQueueEventForAllChildren(winPtr, eventPtr)
|
1007 |
TkWindow *winPtr; /* Window to which event is sent. */
|
1008 |
XEvent *eventPtr; /* The event to be sent. */
|
1009 |
{
|
1010 |
TkWindow *childPtr;
|
1011 |
|
1012 |
eventPtr->xany.window = winPtr->window;
|
1013 |
Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL);
|
1014 |
|
1015 |
childPtr = winPtr->childList;
|
1016 |
while (childPtr != NULL) {
|
1017 |
if (!Tk_IsTopLevel(childPtr)) {
|
1018 |
TkQueueEventForAllChildren(childPtr, eventPtr);
|
1019 |
}
|
1020 |
childPtr = childPtr->nextPtr;
|
1021 |
}
|
1022 |
}
|
1023 |
|
1024 |
/*
|
1025 |
*----------------------------------------------------------------------
|
1026 |
*
|
1027 |
* WindowEventProc --
|
1028 |
*
|
1029 |
* This procedure is called by Tcl_DoOneEvent when a window event
|
1030 |
* reaches the front of the event queue. This procedure is responsible
|
1031 |
* for actually handling the event.
|
1032 |
*
|
1033 |
* Results:
|
1034 |
* Returns 1 if the event was handled, meaning it should be removed
|
1035 |
* from the queue. Returns 0 if the event was not handled, meaning
|
1036 |
* it should stay on the queue. The event isn't handled if the
|
1037 |
* TCL_WINDOW_EVENTS bit isn't set in flags, if a restrict proc
|
1038 |
* prevents the event from being handled.
|
1039 |
*
|
1040 |
* Side effects:
|
1041 |
* Whatever the event handlers for the event do.
|
1042 |
*
|
1043 |
*----------------------------------------------------------------------
|
1044 |
*/
|
1045 |
|
1046 |
static int
|
1047 |
WindowEventProc(evPtr, flags)
|
1048 |
Tcl_Event *evPtr; /* Event to service. */
|
1049 |
int flags; /* Flags that indicate what events to
|
1050 |
* handle, such as TCL_WINDOW_EVENTS. */
|
1051 |
{
|
1052 |
TkWindowEvent *wevPtr = (TkWindowEvent *) evPtr;
|
1053 |
Tk_RestrictAction result;
|
1054 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
|
1055 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
|
1056 |
|
1057 |
if (!(flags & TCL_WINDOW_EVENTS)) {
|
1058 |
return 0;
|
1059 |
}
|
1060 |
if (tsdPtr->restrictProc != NULL) {
|
1061 |
result = (*tsdPtr->restrictProc)(tsdPtr->restrictArg, &wevPtr->event);
|
1062 |
if (result != TK_PROCESS_EVENT) {
|
1063 |
if (result == TK_DEFER_EVENT) {
|
1064 |
return 0;
|
1065 |
} else {
|
1066 |
/*
|
1067 |
* TK_DELETE_EVENT: return and say we processed the event,
|
1068 |
* even though we didn't do anything at all.
|
1069 |
*/
|
1070 |
return 1;
|
1071 |
}
|
1072 |
}
|
1073 |
}
|
1074 |
Tk_HandleEvent(&wevPtr->event);
|
1075 |
return 1;
|
1076 |
}
|
1077 |
|
1078 |
/*
|
1079 |
*----------------------------------------------------------------------
|
1080 |
*
|
1081 |
* DelayedMotionProc --
|
1082 |
*
|
1083 |
* This procedure is invoked as an idle handler when a mouse motion
|
1084 |
* event has been delayed. It queues the delayed event so that it
|
1085 |
* will finally be serviced.
|
1086 |
*
|
1087 |
* Results:
|
1088 |
* None.
|
1089 |
*
|
1090 |
* Side effects:
|
1091 |
* The delayed mouse motion event gets added to the Tcl event
|
1092 |
* queue for servicing.
|
1093 |
*
|
1094 |
*----------------------------------------------------------------------
|
1095 |
*/
|
1096 |
|
1097 |
static void
|
1098 |
DelayedMotionProc(clientData)
|
1099 |
ClientData clientData; /* Pointer to display containing a delayed
|
1100 |
* motion event to be serviced. */
|
1101 |
{
|
1102 |
TkDisplay *dispPtr = (TkDisplay *) clientData;
|
1103 |
|
1104 |
if (dispPtr->delayedMotionPtr == NULL) {
|
1105 |
panic("DelayedMotionProc found no delayed mouse motion event");
|
1106 |
}
|
1107 |
Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, TCL_QUEUE_TAIL);
|
1108 |
dispPtr->delayedMotionPtr = NULL;
|
1109 |
}
|
1110 |
|
1111 |
/*
|
1112 |
*--------------------------------------------------------------
|
1113 |
*
|
1114 |
* Tk_MainLoop --
|
1115 |
*
|
1116 |
* Call Tcl_DoOneEvent over and over again in an infinite
|
1117 |
* loop as long as there exist any main windows.
|
1118 |
*
|
1119 |
* Results:
|
1120 |
* None.
|
1121 |
*
|
1122 |
* Side effects:
|
1123 |
* Arbitrary; depends on handlers for events.
|
1124 |
*
|
1125 |
*--------------------------------------------------------------
|
1126 |
*/
|
1127 |
|
1128 |
void
|
1129 |
Tk_MainLoop()
|
1130 |
{
|
1131 |
while (Tk_GetNumMainWindows() > 0) {
|
1132 |
Tcl_DoOneEvent(0);
|
1133 |
}
|
1134 |
}
|
1135 |
|
1136 |
/* End of tkevent.c */
|