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