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