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 */ |