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