Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtk/generic/tkEvent.c
1810 views
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
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
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
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
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
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
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
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 *
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
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
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
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
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
1052
Tk_MainLoop()
1053
{
1054
while (Tk_GetNumMainWindows() > 0) {
1055
Tcl_DoOneEvent(0);
1056
}
1057
}
1058
1059