|
|
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.