]>
Commit | Line | Data |
---|---|---|
6a5fa4e0 MG |
1 | /* |
2 | * tkEvent.c -- | |
3 | * | |
4 | * This file provides basic event-managing facilities, | |
5 | * whereby procedure callbacks may be attached to | |
6 | * certain events. | |
7 | * | |
8 | * Copyright 1990-1992 Regents of the University of California. | |
9 | * Permission to use, copy, modify, and distribute this | |
10 | * software and its documentation for any purpose and without | |
11 | * fee is hereby granted, provided that the above copyright | |
12 | * notice appear in all copies. The University of California | |
13 | * makes no representations about the suitability of this | |
14 | * software for any purpose. It is provided "as is" without | |
15 | * express or implied warranty. | |
16 | */ | |
17 | ||
18 | #ifndef lint | |
19 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkEvent.c,v 1.60 92/08/21 16:15:57 ouster Exp $ SPRITE (Berkeley)"; | |
20 | #endif | |
21 | ||
22 | #include "tkconfig.h" | |
23 | #include "tkint.h" | |
753d72ec RS |
24 | #include "tclxtend.h" |
25 | #include "tkwm.h" | |
6a5fa4e0 MG |
26 | #include <errno.h> |
27 | #include <signal.h> | |
28 | #include <sys/time.h> | |
29 | #include <assert.h> | |
30 | ||
31 | ||
32 | /* | |
33 | * For each timer callback that's pending, there is one record | |
34 | * of the following type, chained together in a list sorted by | |
35 | * time (earliest event first). | |
36 | */ | |
37 | ||
38 | typedef struct TimerEvent { | |
39 | struct timeval time; /* When timer is to fire. */ | |
40 | void (*proc) _ANSI_ARGS_((ClientData clientData)); | |
41 | /* Procedure to call. */ | |
42 | ClientData clientData; /* Argument to pass to proc. */ | |
43 | Tk_TimerToken token; /* Identifies event so it can be | |
44 | * deleted. */ | |
45 | struct TimerEvent *nextPtr; /* Next event in queue, or NULL for | |
46 | * end of queue. */ | |
47 | } TimerEvent; | |
48 | ||
49 | static TimerEvent *timerQueue; /* First event in queue. */ | |
50 | ||
51 | /* | |
52 | * The information below is used to provide read, write, and | |
53 | * exception masks to select during calls to Tk_DoOneEvent. | |
54 | */ | |
55 | ||
56 | static int readCount; /* Number of files for which we */ | |
57 | static int writeCount; /* care about each event type. */ | |
58 | static int exceptCount; | |
59 | #define MASK_SIZE ((OPEN_MAX+(8*sizeof(int))-1)/(8*sizeof(int))) | |
60 | static int masks[3*MASK_SIZE]; /* Integer array containing official | |
61 | * copies of the three sets of | |
62 | * masks. */ | |
63 | static int ready[3*MASK_SIZE]; /* Temporary copy of masks, passed | |
64 | * to select and modified by kernel | |
65 | * to indicate which files are | |
66 | * actually ready. */ | |
67 | static int *readPtr; /* Pointers to the portions of */ | |
68 | static int *writePtr; /* *readyPtr for reading, writing, */ | |
69 | static int *exceptPtr; /* and excepting. Will be NULL if | |
70 | * corresponding count (e.g. readCount | |
71 | * is zero. */ | |
72 | static int numFds = 0; /* Number of valid bits in mask | |
73 | * arrays (this value is passed | |
74 | * to select). */ | |
75 | ||
76 | /* | |
77 | * For each file registered in a call to Tk_CreateFileHandler, | |
78 | * and for each display that's currently active, there is one | |
79 | * record of the following type. All of these records are | |
80 | * chained together into a single list. | |
81 | */ | |
82 | ||
83 | typedef struct FileEvent { | |
84 | int fd; /* Descriptor number for this file. */ | |
85 | int *readPtr; /* Pointer to word in ready array | |
86 | * for this file's read mask bit. */ | |
87 | int *writePtr; /* Same for write mask bit. */ | |
88 | int *exceptPtr; /* Same for except mask bit. */ | |
89 | int mask; /* Value to AND with mask word to | |
90 | * select just this file's bit. */ | |
91 | void (*proc) _ANSI_ARGS_((ClientData clientData, int mask)); | |
92 | /* Procedure to call. NULL means | |
93 | * this is a display. */ | |
94 | ClientData clientData; /* Argument to pass to proc. For | |
95 | * displays, this is a (Display *). */ | |
96 | struct FileEvent *nextPtr; /* Next in list of all files we | |
97 | * care about (NULL for end of | |
98 | * list). */ | |
99 | } FileEvent; | |
100 | ||
101 | static FileEvent *fileList; /* List of all file events. */ | |
102 | ||
103 | /* | |
104 | * There is one of the following structures for each of the | |
105 | * handlers declared in a call to Tk_DoWhenIdle. All of the | |
106 | * currently-active handlers are linked together into a list. | |
107 | */ | |
108 | ||
109 | typedef struct IdleHandler { | |
110 | void (*proc) _ANSI_ARGS_((ClientData clientData)); | |
111 | /* Procedure to call. */ | |
112 | ClientData clientData; /* Value to pass to proc. */ | |
113 | struct IdleHandler *nextPtr;/* Next in list of active handlers. */ | |
114 | } IdleHandler; | |
115 | ||
116 | static IdleHandler *idleList = NULL; | |
117 | /* First in list of all idle handlers. */ | |
118 | static IdleHandler *lastIdlePtr = NULL; | |
119 | /* Last in list (or NULL for empty list). */ | |
120 | ||
121 | /* | |
122 | * There's a potential problem if a handler is deleted while it's | |
123 | * current (i.e. its procedure is executing), since Tk_HandleEvent | |
124 | * will need to read the handler's "nextPtr" field when the procedure | |
125 | * returns. To handle this problem, structures of the type below | |
126 | * indicate the next handler to be processed for any (recursively | |
127 | * nested) dispatches in progress. The nextHandler fields get | |
128 | * updated if the handlers pointed to are deleted. Tk_HandleEvent | |
129 | * also needs to know if the entire window gets deleted; the winPtr | |
130 | * field is set to zero if that particular window gets deleted. | |
131 | */ | |
132 | ||
133 | typedef struct InProgress { | |
134 | XEvent *eventPtr; /* Event currently being handled. */ | |
135 | TkWindow *winPtr; /* Window for event. Gets set to None if | |
136 | * window is deleted while event is being | |
137 | * handled. */ | |
138 | TkEventHandler *nextHandler; /* Next handler in search. */ | |
139 | struct InProgress *nextPtr; /* Next higher nested search. */ | |
140 | } InProgress; | |
141 | ||
142 | static InProgress *pendingPtr = NULL; | |
143 | /* Topmost search in progress, or | |
144 | * NULL if none. */ | |
145 | ||
146 | /* | |
147 | * For each call to Tk_CreateGenericHandler, an instance of the following | |
148 | * structure will be created. All of the active handlers are linked into a | |
149 | * list. | |
150 | */ | |
151 | ||
152 | typedef struct GenericHandler { | |
153 | Tk_GenericProc *proc; /* Procedure to dispatch on all X events. */ | |
154 | ClientData clientData; /* Client data to pass to procedure. */ | |
155 | int deleteFlag; /* Flag to set when this handler is deleted. */ | |
156 | struct GenericHandler *nextPtr; | |
157 | /* Next handler in list of all generic | |
158 | * handlers, or NULL for end of list. */ | |
159 | } GenericHandler; | |
160 | ||
161 | static GenericHandler *genericList = NULL; | |
162 | /* First handler in the list, or NULL. */ | |
163 | static GenericHandler *lastGenericPtr = NULL; | |
164 | /* Last handler in list. */ | |
165 | ||
166 | /* | |
167 | * There's a potential problem if Tk_HandleEvent is entered recursively. | |
168 | * A handler cannot be deleted physically until we have returned from | |
169 | * calling it. Otherwise, we're looking at unallocated memory in advancing to | |
170 | * its `next' entry. We deal with the problem by using the `delete flag' and | |
171 | * deleting handlers only when it's known that there's no handler active. | |
172 | * | |
173 | * The following variable has a non-zero value when a handler is active. | |
174 | */ | |
175 | ||
176 | static int genericHandlersActive = 0; | |
177 | ||
178 | /* | |
179 | * Array of event masks corresponding to each X event: | |
180 | */ | |
181 | ||
182 | static unsigned long eventMasks[] = { | |
183 | 0, | |
184 | 0, | |
185 | KeyPressMask, /* KeyPress */ | |
186 | KeyReleaseMask, /* KeyRelease */ | |
187 | ButtonPressMask, /* ButtonPress */ | |
188 | ButtonReleaseMask, /* ButtonRelease */ | |
189 | PointerMotionMask|PointerMotionHintMask|ButtonMotionMask | |
190 | |Button1MotionMask|Button2MotionMask|Button3MotionMask | |
191 | |Button4MotionMask|Button5MotionMask, | |
192 | /* MotionNotify */ | |
193 | EnterWindowMask, /* EnterNotify */ | |
194 | LeaveWindowMask, /* LeaveNotify */ | |
195 | FocusChangeMask, /* FocusIn */ | |
196 | FocusChangeMask, /* FocusOut */ | |
197 | KeymapStateMask, /* KeymapNotify */ | |
198 | ExposureMask, /* Expose */ | |
199 | ExposureMask, /* GraphicsExpose */ | |
200 | ExposureMask, /* NoExpose */ | |
201 | VisibilityChangeMask, /* VisibilityNotify */ | |
202 | SubstructureNotifyMask, /* CreateNotify */ | |
203 | StructureNotifyMask, /* DestroyNotify */ | |
204 | StructureNotifyMask, /* UnmapNotify */ | |
205 | StructureNotifyMask, /* MapNotify */ | |
206 | SubstructureRedirectMask, /* MapRequest */ | |
207 | StructureNotifyMask, /* ReparentNotify */ | |
208 | StructureNotifyMask, /* ConfigureNotify */ | |
209 | SubstructureRedirectMask, /* ConfigureRequest */ | |
210 | StructureNotifyMask, /* GravityNotify */ | |
211 | ResizeRedirectMask, /* ResizeRequest */ | |
212 | StructureNotifyMask, /* CirculateNotify */ | |
213 | SubstructureRedirectMask, /* CirculateRequest */ | |
214 | PropertyChangeMask, /* PropertyNotify */ | |
215 | 0, /* SelectionClear */ | |
216 | 0, /* SelectionRequest */ | |
217 | 0, /* SelectionNotify */ | |
218 | ColormapChangeMask, /* ColormapNotify */ | |
219 | 0, /* ClientMessage */ | |
220 | 0, /* Mapping Notify */ | |
221 | }; | |
222 | ||
223 | /* | |
224 | * If someone has called Tk_RestrictEvents, the information below | |
225 | * keeps track of it. | |
226 | */ | |
227 | ||
228 | static Bool (*restrictProc) _ANSI_ARGS_((Display *display, XEvent *eventPtr, | |
229 | char *arg)); /* Procedure to call. NULL means no | |
230 | * restrictProc is currently in effect. */ | |
231 | static char *restrictArg; /* Argument to pass to restrictProc. */ | |
232 | ||
233 | /* | |
234 | * The following array keeps track of the last TK_NEVENTS X events, for | |
235 | * memory dump analysis. The tracing is only done if tkEventDebug is set | |
236 | * to 1. | |
237 | */ | |
238 | ||
239 | #define TK_NEVENTS 32 | |
240 | static XEvent eventTrace[TK_NEVENTS]; | |
241 | static int traceIndex = 0; | |
242 | int tkEventDebug = 0; | |
243 | ||
244 | int tkCollapseMotion = 1; | |
245 | int tkMustExit = 0; | |
246 | \f | |
247 | ||
248 | #define DefPool(type) \ | |
249 | type *Unused##type = NULL; \ | |
250 | \ | |
251 | type *New##type() { \ | |
252 | if (Unused##type == NULL) { \ | |
253 | return (type *)ckalloc(sizeof (type)); \ | |
254 | } else { \ | |
255 | type *ptr = Unused##type; \ | |
256 | Unused##type = ptr->nextPtr; \ | |
257 | return (ptr); \ | |
258 | } \ | |
259 | } \ | |
260 | \ | |
261 | void Free##type(type *ptr) { \ | |
262 | ptr->nextPtr = Unused##type; \ | |
263 | Unused##type = ptr; \ | |
264 | } | |
265 | ||
266 | DefPool(TkEventHandler) | |
267 | DefPool(GenericHandler) | |
268 | DefPool(FileEvent) | |
269 | DefPool(TimerEvent) | |
270 | DefPool(IdleHandler) | |
271 | ||
272 | \f | |
273 | /* | |
274 | *-------------------------------------------------------------- | |
275 | * | |
276 | * Tk_CreateEventHandler -- | |
277 | * | |
278 | * Arrange for a given procedure to be invoked whenever | |
279 | * events from a given class occur in a given window. | |
280 | * | |
281 | * Results: | |
282 | * None. | |
283 | * | |
284 | * Side effects: | |
285 | * From now on, whenever an event of the type given by | |
286 | * mask occurs for token and is processed by Tk_HandleEvent, | |
287 | * proc will be called. See the manual entry for details | |
288 | * of the calling sequence and return value for proc. | |
289 | * | |
290 | *-------------------------------------------------------------- | |
291 | */ | |
292 | ||
293 | void | |
294 | Tk_CreateEventHandler(token, mask, proc, clientData) | |
295 | Tk_Window token; /* Token for window in which to | |
296 | * create handler. */ | |
297 | unsigned long mask; /* Events for which proc should | |
298 | * be called. */ | |
299 | Tk_EventProc *proc; /* Procedure to call for each | |
300 | * selected event */ | |
301 | ClientData clientData; /* Arbitrary data to pass to proc. */ | |
302 | { | |
303 | register TkEventHandler *handlerPtr; | |
304 | register TkWindow *winPtr = (TkWindow *) token; | |
305 | int found; | |
306 | ||
307 | /* | |
308 | * Skim through the list of existing handlers to (a) compute the | |
309 | * overall event mask for the window (so we can pass this new | |
310 | * value to the X system) and (b) see if there's already a handler | |
311 | * declared with the same callback and clientData (if so, just | |
312 | * change the mask). If no existing handler matches, then create | |
313 | * a new handler. | |
314 | */ | |
315 | ||
316 | found = 0; | |
317 | if (winPtr->handlerList == NULL) { | |
318 | handlerPtr = (TkEventHandler *) NewTkEventHandler(); | |
319 | winPtr->handlerList = handlerPtr; | |
320 | goto initHandler; | |
321 | } else { | |
322 | for (handlerPtr = winPtr->handlerList; ; | |
323 | handlerPtr = handlerPtr->nextPtr) { | |
324 | if ((handlerPtr->proc == proc) | |
325 | && (handlerPtr->clientData == clientData)) { | |
326 | handlerPtr->mask = mask; | |
327 | found = 1; | |
328 | } | |
329 | if (handlerPtr->nextPtr == NULL) { | |
330 | break; | |
331 | } | |
332 | } | |
333 | } | |
334 | ||
335 | /* | |
336 | * Create a new handler if no matching old handler was found. | |
337 | */ | |
338 | ||
339 | if (!found) { | |
340 | handlerPtr->nextPtr = NewTkEventHandler(); | |
341 | handlerPtr = handlerPtr->nextPtr; | |
342 | initHandler: | |
343 | handlerPtr->mask = mask; | |
344 | handlerPtr->proc = proc; | |
345 | handlerPtr->clientData = clientData; | |
346 | handlerPtr->nextPtr = NULL; | |
347 | } | |
348 | ||
349 | /* | |
350 | * No need to call XSelectInput: Tk always selects on all events | |
351 | * for all windows (needed to support bindings on classes and "all"). | |
352 | */ | |
353 | } | |
354 | \f | |
355 | /* | |
356 | *-------------------------------------------------------------- | |
357 | * | |
358 | * Tk_DeleteEventHandler -- | |
359 | * | |
360 | * Delete a previously-created handler. | |
361 | * | |
362 | * Results: | |
363 | * None. | |
364 | * | |
365 | * Side effects: | |
366 | * If there existed a handler as described by the | |
367 | * parameters, the handler is deleted so that proc | |
368 | * will not be invoked again. | |
369 | * | |
370 | *-------------------------------------------------------------- | |
371 | */ | |
372 | ||
373 | void | |
374 | Tk_DeleteEventHandler(token, mask, proc, clientData) | |
375 | Tk_Window token; /* Same as corresponding arguments passed */ | |
376 | unsigned long mask; /* previously to Tk_CreateEventHandler. */ | |
377 | Tk_EventProc *proc; | |
378 | ClientData clientData; | |
379 | { | |
380 | register TkEventHandler *handlerPtr; | |
381 | register InProgress *ipPtr; | |
382 | TkEventHandler *prevPtr; | |
383 | register TkWindow *winPtr = (TkWindow *) token; | |
384 | ||
385 | /* | |
386 | * Find the event handler to be deleted, or return | |
387 | * immediately if it doesn't exist. | |
388 | */ | |
389 | ||
390 | for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ; | |
391 | prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) { | |
392 | if (handlerPtr == NULL) { | |
393 | return; | |
394 | } | |
395 | if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc) | |
396 | && (handlerPtr->clientData == clientData)) { | |
397 | break; | |
398 | } | |
399 | } | |
400 | ||
401 | /* | |
402 | * If Tk_HandleEvent is about to process this handler, tell it to | |
403 | * process the next one instead. | |
404 | */ | |
405 | ||
406 | for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) { | |
407 | if (ipPtr->nextHandler == handlerPtr) { | |
408 | ipPtr->nextHandler = handlerPtr->nextPtr; | |
409 | } | |
410 | } | |
411 | ||
412 | /* | |
413 | * Free resources associated with the handler. | |
414 | */ | |
415 | ||
416 | if (prevPtr == NULL) { | |
417 | winPtr->handlerList = handlerPtr->nextPtr; | |
418 | } else { | |
419 | prevPtr->nextPtr = handlerPtr->nextPtr; | |
420 | } | |
421 | (void) FreeTkEventHandler(handlerPtr); | |
422 | ||
423 | ||
424 | /* | |
425 | * No need to call XSelectInput: Tk always selects on all events | |
426 | * for all windows (needed to support bindings on classes and "all"). | |
427 | */ | |
428 | } | |
429 | \f | |
430 | /*-------------------------------------------------------------- | |
431 | * | |
432 | * Tk_CreateGenericHandler -- | |
433 | * | |
434 | * Register a procedure to be called on each X event, regardless | |
435 | * of display or window. Generic handlers are useful for capturing | |
436 | * events that aren't associated with windows, or events for windows | |
437 | * not managed by Tk. | |
438 | * | |
439 | * Results: | |
440 | * None. | |
441 | * | |
442 | * Side Effects: | |
443 | * From now on, whenever an X event is given to Tk_HandleEvent, | |
444 | * invoke proc, giving it clientData and the event as arguments. | |
445 | * | |
446 | *-------------------------------------------------------------- | |
447 | */ | |
448 | ||
449 | void | |
450 | Tk_CreateGenericHandler(proc, clientData) | |
451 | Tk_GenericProc *proc; /* Procedure to call on every event. */ | |
452 | ClientData clientData; /* One-word value to pass to proc. */ | |
453 | { | |
454 | GenericHandler *handlerPtr; | |
455 | ||
456 | handlerPtr = NewGenericHandler(); | |
457 | ||
458 | handlerPtr->proc = proc; | |
459 | handlerPtr->clientData = clientData; | |
460 | handlerPtr->deleteFlag = 0; | |
461 | handlerPtr->nextPtr = NULL; | |
462 | if (genericList == NULL) { | |
463 | genericList = handlerPtr; | |
464 | } else { | |
465 | lastGenericPtr->nextPtr = handlerPtr; | |
466 | } | |
467 | lastGenericPtr = handlerPtr; | |
468 | } | |
469 | \f | |
470 | /* | |
471 | *-------------------------------------------------------------- | |
472 | * | |
473 | * Tk_DeleteGenericHandler -- | |
474 | * | |
475 | * Delete a previously-created generic handler. | |
476 | * | |
477 | * Results: | |
478 | * None. | |
479 | * | |
480 | * Side Effects: | |
481 | * If there existed a handler as described by the parameters, | |
482 | * that handler is logically deleted so that proc will not be | |
483 | * invoked again. The physical deletion happens in the event | |
484 | * loop in Tk_HandleEvent. | |
485 | * | |
486 | *-------------------------------------------------------------- | |
487 | */ | |
488 | ||
489 | void | |
490 | Tk_DeleteGenericHandler(proc, clientData) | |
491 | Tk_GenericProc *proc; | |
492 | ClientData clientData; | |
493 | { | |
494 | GenericHandler * handler; | |
495 | ||
496 | for (handler = genericList; handler; handler = handler->nextPtr) { | |
497 | if ((handler->proc == proc) && (handler->clientData == clientData)) { | |
498 | handler->deleteFlag = 1; | |
499 | } | |
500 | } | |
501 | } | |
502 | \f | |
503 | /* | |
504 | *-------------------------------------------------------------- | |
505 | * | |
506 | * Tk_HandleEvent -- | |
507 | * | |
508 | * Given an event, invoke all the handlers that have | |
509 | * been registered for the event. | |
510 | * | |
511 | * Results: | |
512 | * None. | |
513 | * | |
514 | * Side effects: | |
515 | * Depends on the handlers. | |
516 | * | |
517 | *-------------------------------------------------------------- | |
518 | */ | |
519 | ||
520 | void | |
521 | Tk_HandleEvent(eventPtr) | |
522 | XEvent *eventPtr; /* Event to dispatch. */ | |
523 | { | |
524 | register TkEventHandler *handlerPtr; | |
525 | register GenericHandler *genericPtr; | |
526 | register GenericHandler *genPrevPtr; | |
527 | TkWindow *winPtr; | |
528 | register unsigned long mask; | |
529 | InProgress ip; | |
530 | Window handlerWindow; | |
531 | ||
532 | /* | |
533 | * First off, invoke all the generic event handlers (those that are | |
534 | * invoked for all events). If a generic event handler reports that | |
535 | * an event is fully processed, go no further. | |
536 | */ | |
537 | ||
538 | for (genPrevPtr = NULL, genericPtr = genericList; genericPtr != NULL; ) { | |
539 | if (genericPtr->deleteFlag) { | |
540 | if (!genericHandlersActive) { | |
541 | GenericHandler *tmpPtr; | |
542 | ||
543 | /* | |
544 | * This handler needs to be deleted and there are no | |
545 | * calls pending through the handler, so now is a safe | |
546 | * time to delete it. | |
547 | */ | |
548 | ||
549 | tmpPtr = genericPtr->nextPtr; | |
550 | if (genPrevPtr == NULL) { | |
551 | genericList = tmpPtr; | |
552 | } else { | |
553 | genPrevPtr->nextPtr = tmpPtr; | |
554 | } | |
555 | (void) FreeGenericHandler(genericPtr); | |
556 | genericPtr = tmpPtr; | |
557 | continue; | |
558 | } | |
559 | } else { | |
560 | int done; | |
561 | ||
562 | genericHandlersActive++; | |
563 | done = (*genericPtr->proc)(genericPtr->clientData, eventPtr); | |
564 | genericHandlersActive--; | |
565 | if (done) { | |
566 | return; | |
567 | } | |
568 | } | |
569 | genPrevPtr = genericPtr; | |
570 | genericPtr = genPrevPtr->nextPtr; | |
571 | } | |
572 | ||
573 | /* | |
574 | * Events selected by StructureNotify look the same as those | |
575 | * selected by SubstructureNotify; the only difference is | |
576 | * whether the "event" and "window" fields are the same. | |
577 | * Check it out and convert StructureNotify to | |
578 | * SubstructureNotify if necessary. | |
579 | */ | |
580 | ||
581 | handlerWindow = eventPtr->xany.window; | |
582 | mask = eventMasks[eventPtr->xany.type]; | |
583 | if (mask == StructureNotifyMask) { | |
584 | if (eventPtr->xmap.event != eventPtr->xmap.window) { | |
585 | mask = SubstructureNotifyMask; | |
586 | handlerWindow = eventPtr->xmap.event; | |
587 | } | |
588 | } | |
589 | if (XFindContext(eventPtr->xany.display, handlerWindow, | |
590 | tkWindowContext, (void *) &winPtr) != 0) { | |
591 | ||
592 | /* | |
593 | * There isn't a TkWindow structure for this window. | |
594 | * However, if the event is a PropertyNotify event then call | |
595 | * the selection manager (it deals beneath-the-table with | |
596 | * certain properties). | |
597 | */ | |
598 | ||
599 | if (eventPtr->type == PropertyNotify) { | |
600 | TkSelPropProc(eventPtr); | |
601 | } | |
602 | return; | |
603 | } | |
604 | ||
605 | /* | |
606 | * Redirect KeyPress and KeyRelease events if input focussing | |
607 | * is happening. Map the x and y coordinates between the two | |
608 | * windows, if possible (make both -1 if the map-from and map-to | |
609 | * windows don't share the same top-level window). | |
610 | */ | |
611 | ||
612 | if (mask & (KeyPressMask|KeyReleaseMask)) { | |
613 | winPtr->dispPtr->lastEventTime = eventPtr->xkey.time; | |
614 | /* XXX: FOCUS */ | |
615 | if (winPtr->dispPtr->focusPtr != NULL) { | |
616 | TkWindow *focusPtr; | |
617 | int winX, winY, focusX, focusY; | |
618 | ||
619 | focusPtr = winPtr->dispPtr->focusPtr; | |
620 | if ((focusPtr->display != winPtr->display) | |
621 | || (focusPtr->screenNum != winPtr->screenNum)) { | |
622 | eventPtr->xkey.x = -1; | |
623 | eventPtr->xkey.y = -1; | |
624 | } else { | |
625 | Tk_GetRootCoords((Tk_Window) winPtr, &winX, &winY); | |
626 | Tk_GetRootCoords((Tk_Window) focusPtr, &focusX, &focusY); | |
627 | eventPtr->xkey.x -= focusX - winX; | |
628 | eventPtr->xkey.y -= focusY - winY; | |
629 | } | |
630 | eventPtr->xkey.window = focusPtr->window; | |
631 | winPtr = focusPtr; | |
632 | } | |
633 | } | |
634 | ||
635 | /* | |
636 | * Call a grab-related procedure to do special processing on | |
637 | * pointer events. | |
638 | */ | |
639 | ||
640 | if (mask & (ButtonPressMask|ButtonReleaseMask|PointerMotionMask | |
641 | |EnterWindowMask|LeaveWindowMask)) { | |
642 | if (mask & (ButtonPressMask|ButtonReleaseMask)) { | |
643 | winPtr->dispPtr->lastEventTime = eventPtr->xbutton.time; | |
644 | } else if (mask & PointerMotionMask) { | |
645 | winPtr->dispPtr->lastEventTime = eventPtr->xmotion.time; | |
646 | } else { | |
647 | winPtr->dispPtr->lastEventTime = eventPtr->xcrossing.time; | |
648 | } | |
649 | if (TkPointerEvent(eventPtr, winPtr) == 0) { | |
650 | return; | |
651 | } | |
652 | } | |
653 | ||
654 | /* | |
655 | * For events where it hasn't already been done, update the current | |
656 | * time in the display. | |
657 | */ | |
658 | ||
659 | if (eventPtr->type == PropertyNotify) { | |
660 | winPtr->dispPtr->lastEventTime = eventPtr->xproperty.time; | |
661 | } | |
662 | ||
663 | /* | |
664 | * There's a potential interaction here with Tk_DeleteEventHandler. | |
665 | * Read the documentation for pendingPtr. | |
666 | */ | |
667 | ||
668 | ip.eventPtr = eventPtr; | |
669 | ip.winPtr = winPtr; | |
670 | ip.nextHandler = NULL; | |
671 | ip.nextPtr = pendingPtr; | |
672 | pendingPtr = &ip; | |
673 | if (mask == 0) { | |
674 | if ((eventPtr->type == SelectionClear) | |
675 | || (eventPtr->type == SelectionRequest) | |
676 | || (eventPtr->type == SelectionNotify)) { | |
677 | TkSelEventProc((Tk_Window) winPtr, eventPtr); | |
678 | } else if ((eventPtr->type == ClientMessage) | |
679 | && (eventPtr->xclient.message_type == | |
680 | Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"))) { | |
681 | /* | |
682 | * this is a ICCCM WM_PROTOCOL ClientMessage | |
683 | */ | |
684 | TkWmProtocolEventProc(winPtr, eventPtr); | |
685 | } | |
686 | } else { | |
687 | for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) { | |
688 | if ((handlerPtr->mask & mask) != 0) { | |
689 | ip.nextHandler = handlerPtr->nextPtr; | |
690 | (*(handlerPtr->proc))(handlerPtr->clientData, eventPtr); | |
691 | handlerPtr = ip.nextHandler; | |
692 | } else { | |
693 | handlerPtr = handlerPtr->nextPtr; | |
694 | } | |
695 | } | |
696 | ||
697 | /* | |
698 | * Pass the event to the "bind" command mechanism. But, don't | |
699 | * do this for SubstructureNotify events. The "bind" command | |
700 | * doesn't support them anyway, and it's easier to filter out | |
701 | * these events here than in the lower-level procedures. | |
702 | */ | |
703 | ||
704 | if ((ip.winPtr != None) && (mask != SubstructureNotifyMask)) { | |
705 | TkBindEventProc(winPtr, eventPtr); | |
706 | } | |
707 | } | |
708 | pendingPtr = ip.nextPtr; | |
709 | } | |
710 | \f | |
711 | /* | |
712 | *-------------------------------------------------------------- | |
713 | * | |
714 | * Tk_CreateFileHandler -- | |
715 | * | |
716 | * Arrange for a given procedure to be invoked whenever | |
717 | * a given file becomes readable or writable. | |
718 | * | |
719 | * Results: | |
720 | * None. | |
721 | * | |
722 | * Side effects: | |
723 | * From now on, whenever the I/O channel given by fd becomes | |
724 | * ready in the way indicated by mask, proc will be invoked. | |
725 | * See the manual entry for details on the calling sequence | |
726 | * to proc. If fd is already registered then the old mask | |
727 | * and proc and clientData values will be replaced with | |
728 | * new ones. | |
729 | * | |
730 | *-------------------------------------------------------------- | |
731 | */ | |
732 | ||
733 | void | |
734 | Tk_CreateFileHandler(fd, mask, proc, clientData) | |
735 | int fd; /* Integer identifier for stream. */ | |
736 | int mask; /* OR'ed combination of TK_READABLE, | |
737 | * TK_WRITABLE, and TK_EXCEPTION: | |
738 | * indicates conditions under which | |
739 | * proc should be called. */ | |
740 | Tk_FileProc *proc; /* Procedure to call for each | |
741 | * selected event. NULL means that | |
742 | * this is a display, and that | |
743 | * clientData is the (Display *) | |
744 | * for it, and that events should | |
745 | * be handled automatically. */ | |
746 | ClientData clientData; /* Arbitrary data to pass to proc. */ | |
747 | { | |
748 | register FileEvent *filePtr; | |
749 | int index; | |
750 | ||
751 | if (fd >= OPEN_MAX) { | |
752 | panic("Tk_CreatefileHandler can't handle file id %d", fd); | |
753 | } | |
754 | ||
755 | /* | |
756 | * Make sure the file isn't already registered. Create a | |
757 | * new record in the normal case where there's no existing | |
758 | * record. | |
759 | */ | |
760 | ||
761 | for (filePtr = fileList; filePtr != NULL; | |
762 | filePtr = filePtr->nextPtr) { | |
763 | if (filePtr->fd == fd) { | |
764 | break; | |
765 | } | |
766 | } | |
767 | index = fd/(8*sizeof(int)); | |
768 | if (filePtr == NULL) { | |
769 | filePtr = NewFileEvent(); | |
770 | filePtr->fd = fd; | |
771 | filePtr->readPtr = &ready[index]; | |
772 | filePtr->writePtr = &ready[index+MASK_SIZE]; | |
773 | filePtr->exceptPtr = &ready[index+2*MASK_SIZE]; | |
774 | filePtr->mask = 1 << (fd%(8*sizeof(int))); | |
775 | filePtr->nextPtr = fileList; | |
776 | fileList = filePtr; | |
777 | } else { | |
778 | if (masks[index] & filePtr->mask) { | |
779 | readCount--; | |
780 | *filePtr->readPtr &= ~filePtr->mask; | |
781 | masks[index] &= ~filePtr->mask; | |
782 | } | |
783 | if (masks[index+MASK_SIZE] & filePtr->mask) { | |
784 | writeCount--; | |
785 | *filePtr->writePtr &= ~filePtr->mask; | |
786 | masks[index+MASK_SIZE] &= ~filePtr->mask; | |
787 | } | |
788 | if (masks[index+2*MASK_SIZE] & filePtr->mask) { | |
789 | exceptCount--; | |
790 | *filePtr->exceptPtr &= ~filePtr->mask; | |
791 | masks[index+2*MASK_SIZE] &= ~filePtr->mask; | |
792 | } | |
793 | } | |
794 | ||
795 | /* | |
796 | * The remainder of the initialization below is done | |
797 | * regardless of whether or not this is a new record | |
798 | * or a modification of an old one. | |
799 | */ | |
800 | ||
801 | if (mask & TK_READABLE) { | |
802 | masks[index] |= filePtr->mask; | |
803 | readCount++; | |
804 | } | |
805 | readPtr = (readCount == 0 ? NULL : &ready[0]); | |
806 | ||
807 | if (mask & TK_WRITABLE) { | |
808 | masks[index+MASK_SIZE] |= filePtr->mask; | |
809 | writeCount++; | |
810 | } | |
811 | writePtr = (writeCount == 0 ? NULL : &ready[MASK_SIZE]); | |
812 | ||
813 | if (mask & TK_EXCEPTION) { | |
814 | masks[index+2*MASK_SIZE] |= filePtr->mask; | |
815 | exceptCount++; | |
816 | } | |
817 | exceptPtr = (exceptCount == 0 ? NULL : &ready[2*MASK_SIZE]); | |
818 | ||
819 | filePtr->proc = proc; | |
820 | filePtr->clientData = clientData; | |
821 | ||
822 | if (numFds <= fd) { | |
823 | numFds = fd+1; | |
824 | } | |
825 | } | |
826 | \f | |
827 | /* | |
828 | *-------------------------------------------------------------- | |
829 | * | |
830 | * Tk_DeleteFileHandler -- | |
831 | * | |
832 | * Cancel a previously-arranged callback arrangement for | |
833 | * a file. | |
834 | * | |
835 | * Results: | |
836 | * None. | |
837 | * | |
838 | * Side effects: | |
839 | * If a callback was previously registered on fd, remove it. | |
840 | * | |
841 | *-------------------------------------------------------------- | |
842 | */ | |
843 | ||
844 | void | |
845 | Tk_DeleteFileHandler(fd) | |
846 | int fd; /* Stream id for which to remove | |
847 | * callback procedure. */ | |
848 | { | |
849 | register FileEvent *filePtr; | |
850 | FileEvent *prevPtr; | |
851 | int index; | |
852 | ||
853 | /* | |
854 | * Find the entry for the given file (and return if there | |
855 | * isn't one). | |
856 | */ | |
857 | ||
858 | for (prevPtr = NULL, filePtr = fileList; ; | |
859 | prevPtr = filePtr, filePtr = filePtr->nextPtr) { | |
860 | if (filePtr == NULL) { | |
861 | return; | |
862 | } | |
863 | if (filePtr->fd == fd) { | |
864 | break; | |
865 | } | |
866 | } | |
867 | ||
868 | /* | |
869 | * Clean up information in the callback record. | |
870 | */ | |
871 | ||
872 | index = filePtr->fd/(8*sizeof(int)); | |
873 | if (masks[index] & filePtr->mask) { | |
874 | readCount--; | |
875 | *filePtr->readPtr &= ~filePtr->mask; | |
876 | masks[index] &= ~filePtr->mask; | |
877 | } | |
878 | if (masks[index+MASK_SIZE] & filePtr->mask) { | |
879 | writeCount--; | |
880 | *filePtr->writePtr &= ~filePtr->mask; | |
881 | masks[index+MASK_SIZE] &= ~filePtr->mask; | |
882 | } | |
883 | if (masks[index+2*MASK_SIZE] & filePtr->mask) { | |
884 | exceptCount--; | |
885 | *filePtr->exceptPtr &= ~filePtr->mask; | |
886 | masks[index+2*MASK_SIZE] &= ~filePtr->mask; | |
887 | } | |
888 | if (prevPtr == NULL) { | |
889 | fileList = filePtr->nextPtr; | |
890 | } else { | |
891 | prevPtr->nextPtr = filePtr->nextPtr; | |
892 | } | |
893 | FreeFileEvent(filePtr); | |
894 | ||
895 | /* | |
896 | * Recompute numFds. | |
897 | */ | |
898 | ||
899 | numFds = 0; | |
900 | for (filePtr = fileList; filePtr != NULL; | |
901 | filePtr = filePtr->nextPtr) { | |
902 | if (numFds <= filePtr->fd) { | |
903 | numFds = filePtr->fd+1; | |
904 | } | |
905 | } | |
906 | } | |
907 | \f | |
908 | /* | |
909 | *-------------------------------------------------------------- | |
910 | * | |
911 | * Tk_CreateTimerHandler -- | |
912 | * | |
913 | * Arrange for a given procedure to be invoked at a particular | |
914 | * time in the future. | |
915 | * | |
916 | * Results: | |
917 | * The return value is a token for the timer event, which | |
918 | * may be used to delete the event before it fires. | |
919 | * | |
920 | * Side effects: | |
921 | * When milliseconds have elapsed, proc will be invoked | |
922 | * exactly once. | |
923 | * | |
924 | *-------------------------------------------------------------- | |
925 | */ | |
926 | ||
927 | Tk_TimerToken | |
928 | Tk_CreateTimerHandler(milliseconds, proc, clientData) | |
929 | int milliseconds; /* How many milliseconds to wait | |
930 | * before invoking proc. */ | |
931 | Tk_TimerProc *proc; /* Procedure to invoke. */ | |
932 | ClientData clientData; /* Arbitrary data to pass to proc. */ | |
933 | { | |
934 | register TimerEvent *timerPtr, *tPtr2, *prevPtr; | |
935 | static int id = 0; | |
936 | ||
937 | timerPtr = NewTimerEvent(); | |
938 | ||
939 | /* | |
940 | * Compute when the event should fire. | |
941 | */ | |
942 | ||
943 | (void) gettimeofday(&timerPtr->time, (struct timezone *) NULL); | |
944 | timerPtr->time.tv_sec += milliseconds/1000; | |
945 | timerPtr->time.tv_usec += (milliseconds%1000)*1000; | |
946 | if (timerPtr->time.tv_usec > 1000000) { | |
947 | timerPtr->time.tv_usec -= 1000000; | |
948 | timerPtr->time.tv_sec += 1; | |
949 | } | |
950 | ||
951 | /* | |
952 | * Fill in other fields for the event. | |
953 | */ | |
954 | ||
955 | timerPtr->proc = proc; | |
956 | timerPtr->clientData = clientData; | |
957 | id++; | |
958 | timerPtr->token = (Tk_TimerToken) id; | |
959 | ||
960 | /* | |
961 | * Add the event to the queue in the correct position | |
962 | * (ordered by event firing time). | |
963 | */ | |
964 | ||
965 | for (tPtr2 = timerQueue, prevPtr = NULL; tPtr2 != NULL; | |
966 | prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) { | |
967 | if ((tPtr2->time.tv_sec > timerPtr->time.tv_sec) | |
968 | || ((tPtr2->time.tv_sec == timerPtr->time.tv_sec) | |
969 | && (tPtr2->time.tv_usec > timerPtr->time.tv_usec))) { | |
970 | break; | |
971 | } | |
972 | } | |
973 | if (prevPtr == NULL) { | |
974 | timerPtr->nextPtr = timerQueue; | |
975 | timerQueue = timerPtr; | |
976 | } else { | |
977 | timerPtr->nextPtr = prevPtr->nextPtr; | |
978 | prevPtr->nextPtr = timerPtr; | |
979 | } | |
980 | return timerPtr->token; | |
981 | } | |
982 | \f | |
983 | // Added by Don to support finer timer resolution. | |
984 | /* | |
985 | *-------------------------------------------------------------- | |
986 | * | |
987 | * Tk_CreateMicroTimerHandler -- | |
988 | * | |
989 | * Arrange for a given procedure to be invoked at a particular | |
990 | * time in the future. | |
991 | * | |
992 | * Results: | |
993 | * The return value is a token for the timer event, which | |
994 | * may be used to delete the event before it fires. | |
995 | * | |
996 | * Side effects: | |
997 | * When seconds and seconds have elapsed, proc will be invoked | |
998 | * exactly once. | |
999 | * | |
1000 | *-------------------------------------------------------------- | |
1001 | */ | |
1002 | ||
1003 | Tk_TimerToken | |
1004 | Tk_CreateMicroTimerHandler(seconds, microseconds, proc, clientData) | |
1005 | int seconds; /* How many seconds to wait | |
1006 | * before invoking proc. */ | |
1007 | int microseconds; /* How many microseconds to wait | |
1008 | * before invoking proc. */ | |
1009 | Tk_TimerProc *proc; /* Procedure to invoke. */ | |
1010 | ClientData clientData; /* Arbitrary data to pass to proc. */ | |
1011 | { | |
1012 | register TimerEvent *timerPtr, *tPtr2, *prevPtr; | |
1013 | static int id = 0; | |
1014 | ||
1015 | timerPtr = NewTimerEvent(); | |
1016 | ||
1017 | /* | |
1018 | * Compute when the event should fire. | |
1019 | */ | |
1020 | ||
1021 | (void) gettimeofday(&timerPtr->time, (struct timezone *) NULL); | |
1022 | timerPtr->time.tv_sec += seconds; | |
1023 | timerPtr->time.tv_usec += microseconds; | |
1024 | while (timerPtr->time.tv_usec > 1000000) { | |
1025 | timerPtr->time.tv_usec -= 1000000; | |
1026 | timerPtr->time.tv_sec += 1; | |
1027 | } | |
1028 | ||
1029 | /* | |
1030 | * Fill in other fields for the event. | |
1031 | */ | |
1032 | ||
1033 | timerPtr->proc = proc; | |
1034 | timerPtr->clientData = clientData; | |
1035 | id++; | |
1036 | timerPtr->token = (Tk_TimerToken) id; | |
1037 | ||
1038 | /* | |
1039 | * Add the event to the queue in the correct position | |
1040 | * (ordered by event firing time). | |
1041 | */ | |
1042 | ||
1043 | for (tPtr2 = timerQueue, prevPtr = NULL; tPtr2 != NULL; | |
1044 | prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) { | |
1045 | if ((tPtr2->time.tv_sec > timerPtr->time.tv_sec) | |
1046 | || ((tPtr2->time.tv_sec == timerPtr->time.tv_sec) | |
1047 | && (tPtr2->time.tv_usec > timerPtr->time.tv_usec))) { | |
1048 | break; | |
1049 | } | |
1050 | } | |
1051 | if (prevPtr == NULL) { | |
1052 | timerPtr->nextPtr = timerQueue; | |
1053 | timerQueue = timerPtr; | |
1054 | } else { | |
1055 | timerPtr->nextPtr = prevPtr->nextPtr; | |
1056 | prevPtr->nextPtr = timerPtr; | |
1057 | } | |
1058 | return timerPtr->token; | |
1059 | } | |
1060 | ||
1061 | \f | |
1062 | /* | |
1063 | *-------------------------------------------------------------- | |
1064 | * | |
1065 | * Tk_DeleteTimerHandler -- | |
1066 | * | |
1067 | * Delete a previously-registered timer handler. | |
1068 | * | |
1069 | * Results: | |
1070 | * None. | |
1071 | * | |
1072 | * Side effects: | |
1073 | * Destroy the timer callback identified by TimerToken, | |
1074 | * so that its associated procedure will not be called. | |
1075 | * If the callback has already fired, or if the given | |
1076 | * token doesn't exist, then nothing happens. | |
1077 | * | |
1078 | *-------------------------------------------------------------- | |
1079 | */ | |
1080 | ||
1081 | void | |
1082 | Tk_DeleteTimerHandler(token) | |
1083 | Tk_TimerToken token; /* Result previously returned by | |
1084 | * Tk_DeleteTimerHandler. */ | |
1085 | { | |
1086 | register TimerEvent *timerPtr, *prevPtr; | |
1087 | ||
1088 | if (token == 0) return; | |
1089 | ||
1090 | for (timerPtr = timerQueue, prevPtr = NULL; timerPtr != NULL; | |
1091 | prevPtr = timerPtr, timerPtr = timerPtr->nextPtr) { | |
1092 | if (timerPtr->token != token) { | |
1093 | continue; | |
1094 | } | |
1095 | if (prevPtr == NULL) { | |
1096 | timerQueue = timerPtr->nextPtr; | |
1097 | } else { | |
1098 | prevPtr->nextPtr = timerPtr->nextPtr; | |
1099 | } | |
1100 | FreeTimerEvent(timerPtr); | |
1101 | return; | |
1102 | } | |
1103 | ||
1104 | // fprintf(stderr, "Tk_DeleteTimerHandler called on bogus timer %d\n", token); | |
1105 | } | |
1106 | \f | |
1107 | /* | |
1108 | *-------------------------------------------------------------- | |
1109 | * | |
1110 | * Tk_DoWhenIdle -- | |
1111 | * | |
1112 | * Arrange for proc to be invoked the next time the | |
1113 | * system is idle (i.e., just before the next time | |
1114 | * that Tk_DoOneEvent would have to wait for something | |
1115 | * to happen). | |
1116 | * | |
1117 | * Results: | |
1118 | * None. | |
1119 | * | |
1120 | * Side effects: | |
1121 | * Proc will eventually be called, with clientData | |
1122 | * as argument. See the manual entry for details. | |
1123 | * | |
1124 | *-------------------------------------------------------------- | |
1125 | */ | |
1126 | ||
1127 | void | |
1128 | Tk_DoWhenIdle(proc, clientData) | |
1129 | Tk_IdleProc *proc; /* Procedure to invoke. */ | |
1130 | ClientData clientData; /* Arbitrary value to pass to proc. */ | |
1131 | { | |
1132 | register IdleHandler *idlePtr; | |
1133 | ||
1134 | idlePtr = NewIdleHandler(); | |
1135 | idlePtr->proc = proc; | |
1136 | idlePtr->clientData = clientData; | |
1137 | idlePtr->nextPtr = NULL; | |
1138 | if (lastIdlePtr == NULL) { | |
1139 | idleList = idlePtr; | |
1140 | } else { | |
1141 | lastIdlePtr->nextPtr = idlePtr; | |
1142 | } | |
1143 | lastIdlePtr = idlePtr; | |
1144 | } | |
1145 | \f | |
1146 | /* | |
1147 | *---------------------------------------------------------------------- | |
1148 | * | |
1149 | * Tk_CancelIdleCall -- | |
1150 | * | |
1151 | * If there are any when-idle calls requested to a given procedure | |
1152 | * with given clientData, cancel all of them. | |
1153 | * | |
1154 | * Results: | |
1155 | * None. | |
1156 | * | |
1157 | * Side effects: | |
1158 | * If the proc/clientData combination were on the when-idle list, | |
1159 | * they are removed so that they will never be called. | |
1160 | * | |
1161 | *---------------------------------------------------------------------- | |
1162 | */ | |
1163 | ||
1164 | void | |
1165 | Tk_CancelIdleCall(proc, clientData) | |
1166 | Tk_IdleProc *proc; /* Procedure that was previously registered. */ | |
1167 | ClientData clientData; /* Arbitrary value to pass to proc. */ | |
1168 | { | |
1169 | register IdleHandler *idlePtr, *prevPtr; | |
1170 | IdleHandler *nextPtr; | |
1171 | ||
1172 | for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL; | |
1173 | prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) { | |
1174 | while ((idlePtr->proc == proc) | |
1175 | && (idlePtr->clientData == clientData)) { | |
1176 | nextPtr = idlePtr->nextPtr; | |
1177 | FreeIdleHandler(idlePtr); | |
1178 | idlePtr = nextPtr; | |
1179 | if (prevPtr == NULL) { | |
1180 | idleList = idlePtr; | |
1181 | } else { | |
1182 | prevPtr->nextPtr = idlePtr; | |
1183 | } | |
1184 | if (idlePtr == NULL) { | |
1185 | lastIdlePtr = prevPtr; | |
1186 | return; | |
1187 | } | |
1188 | } | |
1189 | } | |
1190 | } | |
1191 | \f | |
1192 | /* | |
1193 | *-------------------------------------------------------------- | |
1194 | * | |
1195 | * Tk_DoOneEvent -- | |
1196 | * | |
1197 | * Process a single event of some sort. If there's no | |
1198 | * work to do, wait for an event to occur, then process | |
1199 | * it. | |
1200 | * | |
1201 | * Results: | |
1202 | * The return value is 1 if the procedure actually found | |
1203 | * an event to process. If no event was found then 0 is | |
1204 | * returned. | |
1205 | * | |
1206 | * Side effects: | |
1207 | * May delay execution of process while waiting for an | |
1208 | * X event, X error, file-ready event, or timer event. | |
1209 | * The handling of the event could cause additional | |
1210 | * side effects. Collapses sequences of mouse-motion | |
1211 | * events for the same window into a single event by | |
1212 | * delaying motion event processing. | |
1213 | * | |
1214 | *-------------------------------------------------------------- | |
1215 | */ | |
1216 | ||
1217 | int | |
1218 | Tk_DoOneEvent(flags) | |
1219 | int flags; /* Miscellaneous flag values: may be any | |
1220 | * combination of TK_DONT_WAIT, TK_X_EVENTS, | |
1221 | * TK_FILE_EVENTS, TK_TIMER_EVENTS, and | |
1222 | * TK_IDLE_EVENTS. */ | |
1223 | { | |
1224 | register FileEvent *filePtr; | |
1225 | struct timeval curTime, timeout, *timeoutPtr; | |
1226 | int numFound; | |
1227 | static XEvent delayedMotionEvent; /* Used to hold motion events that | |
1228 | * are being saved until later. */ | |
1229 | static int eventDelayed = 0; /* Non-zero means there is an event | |
1230 | * in delayedMotionEvent. */ | |
1231 | ||
1232 | if ((flags & TK_ALL_EVENTS) == 0) { | |
1233 | flags |= TK_ALL_EVENTS; | |
1234 | } | |
1235 | ||
1236 | /* | |
1237 | * Phase One: see if there's already something ready | |
1238 | * (either a file or a display) that was left over | |
1239 | * from before (i.e don't do a select, just check the | |
1240 | * bits from the last select). | |
1241 | */ | |
1242 | ||
1243 | checkFiles: | |
1244 | for (filePtr = fileList; filePtr != NULL; | |
1245 | filePtr = filePtr->nextPtr) { | |
1246 | int mask; | |
1247 | ||
1248 | /* | |
1249 | * Displays: flush output, check for queued events, | |
1250 | * and read events from the server if display is ready. | |
1251 | * If there are any events, process one and then | |
1252 | * return. | |
1253 | */ | |
1254 | ||
1255 | if ((filePtr->proc == NULL) && (flags & TK_X_EVENTS)) { | |
1256 | Display *display = (Display *) filePtr->clientData; | |
1257 | XEvent event; | |
1258 | ||
1259 | XFlush(display); | |
1260 | if ((*filePtr->readPtr) & filePtr->mask) { | |
1261 | *filePtr->readPtr &= ~filePtr->mask; | |
1262 | if (XEventsQueued(display, QueuedAfterReading) == 0) { | |
1263 | ||
1264 | /* | |
1265 | * Things are very tricky if there aren't any events | |
1266 | * readable at this point (after all, there was | |
1267 | * supposedly data available on the connection). | |
1268 | * A couple of things could have occurred: | |
1269 | * | |
1270 | * One possibility is that there were only error events | |
1271 | * in the input from the server. If this happens, | |
1272 | * we should return (we don't want to go to sleep | |
1273 | * in XNextEvent below, since this would block out | |
1274 | * other sources of input to the process). | |
1275 | * | |
1276 | * Another possibility is that our connection to the | |
1277 | * server has been closed. This will not necessarily | |
1278 | * be detected in XEventsQueued (!!), so if we just | |
1279 | * return then there will be an infinite loop. To | |
1280 | * detect such an error, generate a NoOp protocol | |
1281 | * request to exercise the connection to the server, | |
1282 | * then return. However, must disable SIGPIPE while | |
1283 | * sending the event, or else the process will die | |
1284 | * from the signal and won't invoke the X error | |
1285 | * function to print a nice message. | |
1286 | */ | |
1287 | ||
1288 | void (*oldHandler)(); | |
1289 | ||
1290 | oldHandler = (void (*)()) signal(SIGPIPE, SIG_IGN); | |
1291 | XNoOp(display); | |
1292 | XFlush(display); | |
1293 | (void) signal(SIGPIPE, oldHandler); | |
1294 | return 1; | |
1295 | } | |
1296 | if (restrictProc != NULL) { | |
1297 | if (!XCheckIfEvent(display, &event, restrictProc, | |
1298 | restrictArg)) { | |
1299 | return 1; | |
1300 | } | |
1301 | } else { | |
1302 | XNextEvent(display, &event); | |
1303 | } | |
1304 | } else { | |
1305 | if (QLength(display) == 0) { | |
1306 | continue; | |
1307 | } | |
1308 | if (restrictProc != NULL) { | |
1309 | if (!XCheckIfEvent(display, &event, restrictProc, | |
1310 | restrictArg)) { | |
1311 | continue; | |
1312 | } | |
1313 | } else { | |
1314 | XNextEvent(display, &event); | |
1315 | } | |
1316 | } | |
1317 | ||
1318 | /* | |
1319 | * Got an event. Deal with mouse-motion-collapsing and | |
1320 | * event-delaying here. If there's already an event delayed, | |
1321 | * then process that event if it's incompatible with the new | |
1322 | * event (new event not mouse motion, or window changed, or | |
1323 | * state changed). If the new event is mouse motion, then | |
1324 | * don't process it now; delay it until later in the hopes | |
1325 | * that it can be merged with other mouse motion events | |
1326 | * immediately following. | |
1327 | */ | |
1328 | ||
1329 | if (tkEventDebug) { | |
1330 | eventTrace[traceIndex] = event; | |
1331 | traceIndex = (traceIndex+1) % TK_NEVENTS; | |
1332 | } | |
1333 | ||
1334 | if (eventDelayed) { | |
1335 | if (((event.type != MotionNotify) | |
1336 | && (event.type != GraphicsExpose) | |
1337 | && (event.type != NoExpose) | |
1338 | && (event.type != Expose)) | |
1339 | || (event.xmotion.display | |
1340 | != delayedMotionEvent.xmotion.display) | |
1341 | || (event.xmotion.window | |
1342 | != delayedMotionEvent.xmotion.window)) { | |
1343 | XEvent copy; | |
1344 | ||
1345 | /* | |
1346 | * Must copy the event out of delayedMotionEvent before | |
1347 | * processing it, in order to allow recursive calls to | |
1348 | * Tk_DoOneEvent as part of the handler. | |
1349 | */ | |
1350 | ||
1351 | copy = delayedMotionEvent; | |
1352 | eventDelayed = 0; | |
1353 | Tk_HandleEvent(©); | |
1354 | } | |
1355 | } | |
1356 | if (tkCollapseMotion && event.type == MotionNotify) { | |
1357 | delayedMotionEvent = event; | |
1358 | eventDelayed = 1; | |
1359 | } else { | |
1360 | Tk_HandleEvent(&event); | |
1361 | } | |
1362 | return 1; | |
1363 | } | |
1364 | ||
1365 | /* | |
1366 | * Not a display: if the file is ready, call the | |
1367 | * appropriate handler. | |
1368 | */ | |
1369 | ||
1370 | if (((*filePtr->readPtr | *filePtr->writePtr | |
1371 | | *filePtr->exceptPtr) & filePtr->mask) == 0) { | |
1372 | continue; | |
1373 | } | |
1374 | if (!(flags & TK_FILE_EVENTS)) { | |
1375 | continue; | |
1376 | } | |
1377 | mask = 0; | |
1378 | if (*filePtr->readPtr & filePtr->mask) { | |
1379 | mask |= TK_READABLE; | |
1380 | *filePtr->readPtr &= ~filePtr->mask; | |
1381 | } | |
1382 | if (*filePtr->writePtr & filePtr->mask) { | |
1383 | mask |= TK_WRITABLE; | |
1384 | *filePtr->writePtr &= ~filePtr->mask; | |
1385 | } | |
1386 | if (*filePtr->exceptPtr & filePtr->mask) { | |
1387 | mask |= TK_EXCEPTION; | |
1388 | *filePtr->exceptPtr &= ~filePtr->mask; | |
1389 | } | |
1390 | (*filePtr->proc)(filePtr->clientData, mask); | |
1391 | return 1; | |
1392 | } | |
1393 | ||
1394 | /* | |
1395 | * Phase Two: get the current time and see if any timer | |
1396 | * events are ready to fire. If so, fire one and return. | |
1397 | */ | |
1398 | ||
1399 | checkTime: | |
1400 | if ((timerQueue != NULL) && (flags & TK_TIMER_EVENTS)) { | |
1401 | register TimerEvent *timerPtr = timerQueue; | |
1402 | ||
1403 | (void) gettimeofday(&curTime, (struct timezone *) NULL); | |
1404 | if ((timerPtr->time.tv_sec < curTime.tv_sec) | |
1405 | || ((timerPtr->time.tv_sec == curTime.tv_sec) | |
1406 | && (timerPtr->time.tv_usec < curTime.tv_usec))) { | |
1407 | timerQueue = timerPtr->nextPtr; | |
1408 | (*timerPtr->proc)(timerPtr->clientData); | |
1409 | FreeTimerEvent(timerPtr); | |
1410 | return 1; | |
1411 | } | |
1412 | } | |
1413 | ||
1414 | ||
1415 | /* | |
1416 | * Phase Three: if there is a delayed motion event, process it | |
1417 | * now, before any DoWhenIdle handlers. Better to process before | |
1418 | * idle handlers than after, because the goal of idle handlers is | |
1419 | * to delay until after all pending events have been processed. | |
1420 | * Must free up delayedMotionEvent *before* calling Tk_HandleEvent, | |
1421 | * so that the event handler can call Tk_DoOneEvent recursively | |
1422 | * without infinite looping. | |
1423 | */ | |
1424 | ||
1425 | if ((eventDelayed) && (flags & TK_X_EVENTS)) { | |
1426 | XEvent copy; | |
1427 | ||
1428 | copy = delayedMotionEvent; | |
1429 | eventDelayed = 0; | |
1430 | Tk_HandleEvent(©); | |
1431 | return 1; | |
1432 | } | |
1433 | ||
1434 | /* | |
1435 | * Phase Four: if there are DoWhenIdle requests pending (or | |
1436 | * if we're not allowed to block), then do a select with an | |
1437 | * instantaneous timeout. If a ready file is found, then go | |
1438 | * back to process it. | |
1439 | */ | |
1440 | ||
1441 | if (((idleList != NULL) && (flags & TK_IDLE_EVENTS)) | |
1442 | || (flags & TK_DONT_WAIT)) { | |
1443 | if (flags & (TK_X_EVENTS|TK_FILE_EVENTS)) { | |
1444 | memcpy((VOID *) ready, (VOID *) masks, 3*MASK_SIZE*sizeof(int)); | |
1445 | timeout.tv_sec = timeout.tv_usec = 0; | |
1446 | do { | |
1447 | numFound = select(numFds, (SELECT_MASK *) readPtr, | |
1448 | (SELECT_MASK *) writePtr, (SELECT_MASK *) exceptPtr, | |
1449 | &timeout); | |
1450 | } while ((numFound == -1) && (errno == EINTR)); | |
1451 | if (numFound > 0) { | |
1452 | goto checkFiles; | |
1453 | } | |
1454 | } | |
1455 | } | |
1456 | ||
1457 | /* | |
1458 | * Phase Five: process all pending DoWhenIdle requests. | |
1459 | */ | |
1460 | ||
1461 | if ((idleList != NULL) && (flags & TK_IDLE_EVENTS)) { | |
1462 | register IdleHandler *idlePtr; | |
1463 | ||
1464 | /* | |
1465 | * If you change the code below, be aware that new handlers | |
1466 | * can get added to the list while the current one is being | |
1467 | * processed. | |
1468 | * | |
1469 | * NOTE! Must remove the entry from the list before calling | |
1470 | * it, in case the idle handler calls Tk_DoOneEvent: don't | |
1471 | * want to loop infinitely. Must also be careful because | |
1472 | * Tk_CancelIdleCall could change the list during the call. | |
1473 | */ | |
1474 | ||
1475 | while (idleList != NULL) { | |
1476 | idlePtr = idleList; | |
1477 | idleList = idlePtr->nextPtr; | |
1478 | if (idleList == NULL) { | |
1479 | lastIdlePtr = NULL; | |
1480 | } | |
1481 | (*idlePtr->proc)(idlePtr->clientData); | |
1482 | FreeIdleHandler(idlePtr); | |
1483 | } | |
1484 | return 1; | |
1485 | } | |
1486 | ||
1487 | /* | |
1488 | * Phase Six: do a select to wait for either one of the | |
1489 | * files to become ready or for the first timer event to | |
1490 | * fire. Then go back to process the event. | |
1491 | */ | |
1492 | ||
1493 | if ((flags & TK_DONT_WAIT) | |
1494 | || !(flags & (TK_TIMER_EVENTS|TK_FILE_EVENTS|TK_X_EVENTS))) { | |
1495 | return 0; | |
1496 | } | |
1497 | if ((timerQueue == NULL) || !(flags & TK_TIMER_EVENTS)) { | |
1498 | timeoutPtr = NULL; | |
1499 | } else { | |
1500 | timeoutPtr = &timeout; | |
1501 | timeout.tv_sec = timerQueue->time.tv_sec - curTime.tv_sec; | |
1502 | timeout.tv_usec = timerQueue->time.tv_usec - curTime.tv_usec; | |
1503 | if (timeout.tv_usec < 0) { | |
1504 | timeout.tv_sec -= 1; | |
1505 | timeout.tv_usec += 1000000; | |
1506 | } | |
1507 | } | |
1508 | memcpy((VOID *) ready, (VOID *) masks, 3*MASK_SIZE*sizeof(int)); | |
1509 | do { | |
1510 | numFound = select(numFds, (SELECT_MASK *) readPtr, | |
1511 | (SELECT_MASK *) writePtr, (SELECT_MASK *) exceptPtr, | |
1512 | timeoutPtr); | |
1513 | } while ((numFound == -1) && (errno == EINTR)); | |
1514 | if (numFound == 0) { | |
1515 | goto checkTime; | |
1516 | } | |
1517 | goto checkFiles; | |
1518 | } | |
1519 | \f | |
1520 | /* | |
1521 | *-------------------------------------------------------------- | |
1522 | * | |
1523 | * Tk_MainLoop -- | |
1524 | * | |
1525 | * Call Tk_DoOneEvent over and over again in an infinite | |
1526 | * loop as long as there exist any main windows. | |
1527 | * | |
1528 | * Results: | |
1529 | * None. | |
1530 | * | |
1531 | * Side effects: | |
1532 | * Arbitrary; depends on handlers for events. | |
1533 | * | |
1534 | *-------------------------------------------------------------- | |
1535 | */ | |
1536 | ||
1537 | void | |
1538 | Tk_MainLoop() | |
1539 | { | |
1540 | while (!tkMustExit && | |
1541 | tk_NumMainWindows > 0) { | |
1542 | Tk_DoOneEvent(0); | |
1543 | } | |
1544 | } | |
1545 | \f | |
1546 | /* | |
1547 | *---------------------------------------------------------------------- | |
1548 | * | |
1549 | * Tk_Sleep -- | |
1550 | * | |
1551 | * Delay execution for the specified number of milliseconds. | |
1552 | * | |
1553 | * Results: | |
1554 | * None. | |
1555 | * | |
1556 | * Side effects: | |
1557 | * Time passes. | |
1558 | * | |
1559 | *---------------------------------------------------------------------- | |
1560 | */ | |
1561 | ||
1562 | void | |
1563 | Tk_Sleep(ms) | |
1564 | int ms; /* Number of milliseconds to sleep. */ | |
1565 | { | |
1566 | static struct timeval delay; | |
1567 | ||
1568 | delay.tv_sec = ms/1000; | |
1569 | delay.tv_usec = (ms%1000)*1000; | |
1570 | (void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0, | |
1571 | (SELECT_MASK *) 0, &delay); | |
1572 | } | |
1573 | \f | |
1574 | /* | |
1575 | *---------------------------------------------------------------------- | |
1576 | * | |
1577 | * Tk_RestrictEvents -- | |
1578 | * | |
1579 | * This procedure is used to globally restrict the set of events | |
1580 | * that will be dispatched. The restriction is done by filtering | |
1581 | * all incoming X events through a procedure that determines | |
1582 | * whether they are to be processed immediately or deferred. | |
1583 | * | |
1584 | * Results: | |
1585 | * The return value is the previous restriction procedure in effect, | |
1586 | * if there was one, or NULL if there wasn't. | |
1587 | * | |
1588 | * Side effects: | |
1589 | * From now on, proc will be called to determine whether to process | |
1590 | * or defer each incoming X event. | |
1591 | * | |
1592 | *---------------------------------------------------------------------- | |
1593 | */ | |
1594 | ||
1595 | Tk_RestrictProc * | |
1596 | Tk_RestrictEvents(proc, arg, prevArgPtr) | |
1597 | Tk_RestrictProc *proc; /* X "if" procedure to call for each | |
1598 | * incoming event. See "XIfEvent" doc. | |
1599 | * for details. */ | |
1600 | char *arg; /* Arbitrary argument to pass to proc. */ | |
1601 | char **prevArgPtr; /* Place to store information about previous | |
1602 | * argument. */ | |
1603 | { | |
1604 | Bool (*prev) _ANSI_ARGS_((Display *display, XEvent *eventPtr, char *arg)); | |
1605 | ||
1606 | prev = restrictProc; | |
1607 | *prevArgPtr = restrictArg; | |
1608 | restrictProc = proc; | |
1609 | restrictArg = arg; | |
1610 | return prev; | |
1611 | } | |
1612 | \f | |
1613 | /* | |
1614 | *-------------------------------------------------------------- | |
1615 | * | |
1616 | * Tk_CreateFocusHandler -- | |
1617 | * | |
1618 | * Arrange for a procedure to be called whenever the focus | |
1619 | * enters or leaves a given window. | |
1620 | * | |
1621 | * Results: | |
1622 | * None. | |
1623 | * | |
1624 | * Side effects: | |
1625 | * After this procedure has been invoked, whenever tkwin gets | |
1626 | * or loses the input focus, proc will be called. It should have | |
1627 | * the following structure: | |
1628 | * | |
1629 | * void | |
1630 | * proc(clientData, gotFocus) | |
1631 | * ClientData clientData; | |
1632 | * int gotFocus; | |
1633 | * { | |
1634 | * } | |
1635 | * | |
1636 | * The clientData argument to "proc" will be the same as the | |
1637 | * clientData argument to this procedure. GotFocus will be | |
1638 | * 1 if tkwin is getting the focus, and 0 if it's losing the | |
1639 | * focus. | |
1640 | * | |
1641 | *-------------------------------------------------------------- | |
1642 | */ | |
1643 | ||
1644 | void | |
1645 | Tk_CreateFocusHandler(tkwin, proc, clientData) | |
1646 | Tk_Window tkwin; /* Token for window. */ | |
1647 | Tk_FocusProc *proc; /* Procedure to call when tkwin gets | |
1648 | * or loses the input focus. */ | |
1649 | ClientData clientData; /* Arbitrary value to pass to proc. */ | |
1650 | { | |
1651 | register TkWindow *winPtr = (TkWindow *) tkwin; | |
1652 | ||
1653 | winPtr->focusProc = proc; | |
1654 | winPtr->focusData = clientData; | |
1655 | } | |
1656 | \f | |
1657 | /* | |
1658 | *-------------------------------------------------------------- | |
1659 | * | |
1660 | * Tk_FocusCmd -- | |
1661 | * | |
1662 | * This procedure is invoked to process the "focus" Tcl command. | |
1663 | * See the user documentation for details on what it does. | |
1664 | * | |
1665 | * Results: | |
1666 | * A standard Tcl result. | |
1667 | * | |
1668 | * Side effects: | |
1669 | * See the user documentation. | |
1670 | * | |
1671 | *-------------------------------------------------------------- | |
1672 | */ | |
1673 | ||
1674 | int | |
1675 | Tk_FocusCmd(clientData, interp, argc, argv) | |
1676 | ClientData clientData; /* Main window associated with | |
1677 | * interpreter. */ | |
1678 | Tcl_Interp *interp; /* Current interpreter. */ | |
1679 | int argc; /* Number of arguments. */ | |
1680 | char **argv; /* Argument strings. */ | |
1681 | { | |
1682 | Tk_Window tkwin = (Tk_Window) clientData; | |
1683 | register TkWindow *winPtr = (TkWindow *) clientData; | |
1684 | register TkWindow *newPtr; | |
1685 | ||
1686 | if (argc > 3) { | |
1687 | focusSyntax: | |
1688 | Tcl_AppendResult(interp, "too many args: should be \"", | |
1689 | argv[0], " ?-query? ?window?\"", (char *) NULL); | |
1690 | return TCL_ERROR; | |
1691 | } | |
1692 | ||
1693 | if (argc == 1) { | |
1694 | if (winPtr->dispPtr->focusPtr == NULL) { | |
1695 | interp->result = "none"; | |
1696 | } else { | |
1697 | interp->result = winPtr->dispPtr->focusPtr->pathName; | |
1698 | } | |
1699 | return TCL_OK; | |
1700 | } | |
1701 | ||
1702 | if (argv[1][0] == '-') { | |
1703 | int switchLength; | |
1704 | ||
1705 | switchLength = strlen(argv[1]); | |
1706 | if ((switchLength >= 2) | |
1707 | && (strncmp(argv[1], "-query", switchLength) == 0)) { | |
1708 | ||
1709 | if (argc != 3) { | |
1710 | goto focusSyntax; | |
1711 | } | |
1712 | ||
1713 | newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin); | |
1714 | if (newPtr == NULL) { | |
1715 | return TCL_ERROR; | |
1716 | } | |
1717 | if (newPtr->dispPtr->focusPtr == NULL) { | |
1718 | interp->result = "none"; | |
1719 | } else { | |
1720 | interp->result = newPtr->dispPtr->focusPtr->pathName; | |
1721 | } | |
1722 | return TCL_OK; | |
1723 | } | |
1724 | } | |
1725 | ||
1726 | if (argc != 2) { | |
1727 | goto focusSyntax; | |
1728 | } | |
1729 | ||
1730 | if (strcmp(argv[1], "none") == 0) { | |
1731 | newPtr = NULL; | |
1732 | } else { | |
1733 | newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin); | |
1734 | if (newPtr == NULL) { | |
1735 | return TCL_ERROR; | |
1736 | } | |
1737 | } | |
1738 | /* XXX: mumble frotz */ | |
1739 | /* if (newPtr->dispPtr->focusPtr == newPtr) { */ | |
1740 | if ((!newPtr) || (newPtr->dispPtr->focusPtr == newPtr)) { | |
1741 | return TCL_OK; | |
1742 | } | |
1743 | if (winPtr == newPtr->dispPtr->mouseMainPtr) { /* XXX: ??? presumably */ | |
1744 | if ((newPtr->dispPtr->focusPtr != NULL) | |
1745 | && (newPtr->dispPtr->focusPtr->focusProc != NULL)) { | |
1746 | (*newPtr->dispPtr->focusPtr->focusProc)( | |
1747 | newPtr->dispPtr->focusPtr->focusData, 0); | |
1748 | } | |
1749 | newPtr->dispPtr->focusPtr = newPtr; | |
1750 | if ((newPtr != NULL) && (newPtr->focusProc != NULL)) { | |
1751 | (*newPtr->focusProc)(newPtr->focusData, 1); | |
1752 | } | |
1753 | } else { | |
1754 | newPtr->dispPtr->focusPtr = newPtr; | |
1755 | } | |
1756 | return TCL_OK; | |
1757 | } | |
1758 | \f | |
1759 | /* | |
1760 | *-------------------------------------------------------------- | |
1761 | * | |
1762 | * TkFocusEventProc -- | |
1763 | * | |
1764 | * This procedure is invoked whenever the pointer enters | |
1765 | * or leaves a top-level window. It notifies the current | |
1766 | * owner of the focus, if any. | |
1767 | * | |
1768 | * Results: | |
1769 | * None. | |
1770 | * | |
1771 | * Side effects: | |
1772 | * None. | |
1773 | * | |
1774 | *-------------------------------------------------------------- | |
1775 | */ | |
1776 | ||
1777 | void | |
1778 | TkFocusEventProc(winPtr, eventPtr) | |
1779 | register TkWindow *winPtr; /* Top-level window just entered or left. */ | |
1780 | XEvent *eventPtr; /* EnterWindow or LeaveWindow event. */ | |
1781 | { | |
1782 | register TkWindow *focusPtr; | |
1783 | TkWindow *newMouseMainPtr = NULL; | |
1784 | ||
1785 | if (eventPtr->type == EnterNotify) { | |
1786 | newMouseMainPtr = winPtr->mainPtr->winPtr; | |
1787 | } | |
1788 | if (winPtr->dispPtr->mouseMainPtr == newMouseMainPtr) { | |
1789 | return; | |
1790 | } | |
1791 | if (winPtr->dispPtr->mouseMainPtr != NULL) { | |
1792 | focusPtr = winPtr->dispPtr->focusPtr; | |
1793 | if ((focusPtr != NULL) | |
1794 | && (focusPtr->focusProc != NULL)) { | |
1795 | (*focusPtr->focusProc)(focusPtr->focusData, 0); | |
1796 | } | |
1797 | } | |
1798 | winPtr->dispPtr->mouseMainPtr = newMouseMainPtr; | |
1799 | if (newMouseMainPtr != NULL) { | |
1800 | focusPtr = newMouseMainPtr->dispPtr->focusPtr; | |
1801 | if ((focusPtr != NULL) | |
1802 | && (focusPtr->focusProc != NULL)) { | |
1803 | (*focusPtr->focusProc)(focusPtr->focusData, 1); | |
1804 | } | |
1805 | } | |
1806 | } | |
1807 | \f | |
1808 | /* | |
1809 | *-------------------------------------------------------------- | |
1810 | * | |
1811 | * TkEventDeadWindow -- | |
1812 | * | |
1813 | * This procedure is invoked when it is determined that | |
1814 | * a window is dead. It cleans up event-related information | |
1815 | * about the window. | |
1816 | * | |
1817 | * Results: | |
1818 | * None. | |
1819 | * | |
1820 | * Side effects: | |
1821 | * Various things get cleaned up and recycled. | |
1822 | * | |
1823 | *-------------------------------------------------------------- | |
1824 | */ | |
1825 | ||
1826 | void | |
1827 | TkEventDeadWindow(winPtr) | |
1828 | TkWindow *winPtr; /* Information about the window | |
1829 | * that is being deleted. */ | |
1830 | { | |
1831 | register TkEventHandler *handlerPtr; | |
1832 | register InProgress *ipPtr; | |
1833 | ||
1834 | /* | |
1835 | * While deleting all the handlers, be careful to check for | |
1836 | * Tk_HandleEvent being about to process one of the deleted | |
1837 | * handlers. If it is, tell it to quit (all of the handlers | |
1838 | * are being deleted). | |
1839 | */ | |
1840 | ||
1841 | while (winPtr->handlerList != NULL) { | |
1842 | handlerPtr = winPtr->handlerList; | |
1843 | winPtr->handlerList = handlerPtr->nextPtr; | |
1844 | for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) { | |
1845 | if (ipPtr->nextHandler == handlerPtr) { | |
1846 | ipPtr->nextHandler = NULL; | |
1847 | } | |
1848 | if (ipPtr->winPtr == winPtr) { | |
1849 | ipPtr->winPtr = None; | |
1850 | } | |
1851 | } | |
1852 | ckfree((char *) handlerPtr); | |
1853 | } | |
1854 | if ((winPtr->dispPtr != NULL) && (winPtr->dispPtr->focusPtr == winPtr)) { | |
1855 | winPtr->dispPtr->focusPtr = NULL; | |
1856 | } | |
1857 | } | |
1858 | \f | |
1859 | /* | |
1860 | *---------------------------------------------------------------------- | |
1861 | * | |
1862 | * TkCurrentTime -- | |
1863 | * | |
1864 | * Try to deduce the current time. "Current time" means the time | |
1865 | * of the event that led to the current code being executed, which | |
1866 | * means the time in the most recently-nested invocation of | |
1867 | * Tk_HandleEvent. | |
1868 | * | |
1869 | * Results: | |
1870 | * The return value is the time from the current event, or | |
1871 | * CurrentTime if there is no current event or if the current | |
1872 | * event contains no time. | |
1873 | * | |
1874 | * Side effects: | |
1875 | * None. | |
1876 | * | |
1877 | *---------------------------------------------------------------------- | |
1878 | */ | |
1879 | ||
1880 | Time | |
1881 | TkCurrentTime(dispPtr) | |
1882 | TkDisplay *dispPtr; /* Display for which the time is desired. */ | |
1883 | { | |
1884 | register XEvent *eventPtr; | |
1885 | ||
1886 | if (pendingPtr == NULL) { | |
1887 | return dispPtr->lastEventTime; | |
1888 | } | |
1889 | eventPtr = pendingPtr->eventPtr; | |
1890 | switch (eventPtr->type) { | |
1891 | case ButtonPress: | |
1892 | case ButtonRelease: | |
1893 | return eventPtr->xbutton.time; | |
1894 | case KeyPress: | |
1895 | case KeyRelease: | |
1896 | return eventPtr->xkey.time; | |
1897 | case MotionNotify: | |
1898 | return eventPtr->xmotion.time; | |
1899 | case EnterNotify: | |
1900 | case LeaveNotify: | |
1901 | return eventPtr->xcrossing.time; | |
1902 | case PropertyNotify: | |
1903 | return eventPtr->xproperty.time; | |
1904 | } | |
1905 | return dispPtr->lastEventTime; | |
1906 | } |