]> git.zerfleddert.de Git - micropolis/blob - src/tk/tkevent.c
XINCLUDE: use /usr/X11R6/include everywhere
[micropolis] / src / tk / tkevent.c
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"
24 #include "tclxtend.h"
25 #include "tkwm.h"
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(&copy);
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(&copy);
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 }
Impressum, Datenschutz