|
|
1.1 root 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 <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:
36: typedef 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:
47: static 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:
54: static int readCount; /* Number of files for which we */
55: static int writeCount; /* care about each event type. */
56: static int exceptCount;
57: #define MASK_SIZE ((OPEN_MAX+(8*sizeof(int))-1)/(8*sizeof(int)))
58: static int masks[3*MASK_SIZE]; /* Integer array containing official
59: * copies of the three sets of
60: * masks. */
61: static 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. */
65: static int *readPtr; /* Pointers to the portions of */
66: static int *writePtr; /* *readyPtr for reading, writing, */
67: static int *exceptPtr; /* and excepting. Will be NULL if
68: * corresponding count (e.g. readCount
69: * is zero. */
70: static 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:
81: typedef 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:
99: static 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:
107: typedef 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:
114: static IdleHandler *idleList = NULL;
115: /* First in list of all idle handlers. */
116: static 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:
131: typedef 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:
140: static 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:
150: typedef 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:
159: static GenericHandler *genericList = NULL;
160: /* First handler in the list, or NULL. */
161: static 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:
174: static int genericHandlersActive = 0;
175:
176: /*
177: * Array of event masks corresponding to each X event:
178: */
179:
180: static 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:
226: static Bool (*restrictProc) _ANSI_ARGS_((Display *display, XEvent *eventPtr,
227: char *arg)); /* Procedure to call. NULL means no
228: * restrictProc is currently in effect. */
229: static 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
238: static XEvent eventTrace[TK_NEVENTS];
239: static int traceIndex = 0;
240: int tkEventDebug = 0;
241:
242: int tkCollapseMotion = 1;
243: int tkMustExit = 0;
244:
245:
246: #define DefPool(type) \
247: type *Unused##type = NULL; \
248: \
249: type *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: \
259: void Free##type(type *ptr) { \
260: ptr->nextPtr = Unused##type; \
261: Unused##type = ptr; \
262: }
263:
264: DefPool(TkEventHandler)
265: DefPool(GenericHandler)
266: DefPool(FileEvent)
267: DefPool(TimerEvent)
268: DefPool(IdleHandler)
269:
270:
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:
291: void
292: Tk_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:
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:
371: void
372: Tk_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:
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:
447: void
448: Tk_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:
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:
487: void
488: Tk_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:
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:
518: void
519: Tk_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:
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:
731: void
732: Tk_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:
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:
842: void
843: Tk_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:
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:
925: Tk_TimerToken
926: Tk_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:
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:
1001: Tk_TimerToken
1002: Tk_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:
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:
1079: void
1080: Tk_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:
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:
1125: void
1126: Tk_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:
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:
1162: void
1163: Tk_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:
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:
1215: int
1216: Tk_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(©);
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(©);
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:
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:
1535: void
1536: Tk_MainLoop()
1537: {
1538: while (!tkMustExit &&
1539: tk_NumMainWindows > 0) {
1540: Tk_DoOneEvent(0);
1541: }
1542: }
1543:
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:
1560: void
1561: Tk_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:
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:
1593: Tk_RestrictProc *
1594: Tk_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:
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:
1642: void
1643: Tk_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:
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:
1672: int
1673: Tk_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:
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:
1775: void
1776: TkFocusEventProc(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:
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:
1824: void
1825: TkEventDeadWindow(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:
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:
1878: Time
1879: TkCurrentTime(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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.