1/* 2 * tkEvent.c -- 3 * 4 * This file provides basic low-level facilities for managing X events in 5 * Tk. 6 * 7 * Copyright (c) 1990-1994 The Regents of the University of California. 8 * Copyright (c) 1994-1995 Sun Microsystems, Inc. 9 * Copyright (c) 1998-2000 Ajuba Solutions. 10 * Copyright (c) 2004 George Peter Staplin 11 * 12 * See the file "license.terms" for information on usage and redistribution of 13 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 14 * 15 * RCS: @(#) $Id$ 16 */ 17 18#include "tkInt.h" 19 20/* 21 * There's a potential problem if a handler is deleted while it's current 22 * (i.e. its function is executing), since Tk_HandleEvent will need to read 23 * the handler's "nextPtr" field when the function returns. To handle this 24 * problem, structures of the type below indicate the next handler to be 25 * processed for any (recursively nested) dispatches in progress. The 26 * nextHandler fields get updated if the handlers pointed to are deleted. 27 * Tk_HandleEvent also needs to know if the entire window gets deleted; the 28 * winPtr field is set to zero if that particular window gets deleted. 29 */ 30 31typedef struct InProgress { 32 XEvent *eventPtr; /* Event currently being handled. */ 33 TkWindow *winPtr; /* Window for event. Gets set to None if 34 * window is deleted while event is being 35 * handled. */ 36 TkEventHandler *nextHandler;/* Next handler in search. */ 37 struct InProgress *nextPtr; /* Next higher nested search. */ 38} InProgress; 39 40/* 41 * For each call to Tk_CreateGenericHandler or Tk_CreateClientMessageHandler, 42 * an instance of the following structure will be created. All of the active 43 * handlers are linked into a list. 44 */ 45 46typedef struct GenericHandler { 47 Tk_GenericProc *proc; /* Function to dispatch on all X events. */ 48 ClientData clientData; /* Client data to pass to function. */ 49 int deleteFlag; /* Flag to set when this handler is 50 * deleted. */ 51 struct GenericHandler *nextPtr; 52 /* Next handler in list of all generic 53 * handlers, or NULL for end of list. */ 54} GenericHandler; 55 56/* 57 * There's a potential problem if Tk_HandleEvent is entered recursively. A 58 * handler cannot be deleted physically until we have returned from calling 59 * it. Otherwise, we're looking at unallocated memory in advancing to its 60 * `next' entry. We deal with the problem by using the `delete flag' and 61 * deleting handlers only when it's known that there's no handler active. 62 */ 63 64/* 65 * The following structure is used for queueing X-style events on the Tcl 66 * event queue. 67 */ 68 69typedef struct TkWindowEvent { 70 Tcl_Event header; /* Standard information for all events. */ 71 XEvent event; /* The X event. */ 72} TkWindowEvent; 73 74/* 75 * Array of event masks corresponding to each X event: 76 */ 77 78static unsigned long realEventMasks[MappingNotify+1] = { 79 0, 80 0, 81 KeyPressMask, /* KeyPress */ 82 KeyReleaseMask, /* KeyRelease */ 83 ButtonPressMask, /* ButtonPress */ 84 ButtonReleaseMask, /* ButtonRelease */ 85 PointerMotionMask|PointerMotionHintMask|ButtonMotionMask 86 |Button1MotionMask|Button2MotionMask|Button3MotionMask 87 |Button4MotionMask|Button5MotionMask, 88 /* MotionNotify */ 89 EnterWindowMask, /* EnterNotify */ 90 LeaveWindowMask, /* LeaveNotify */ 91 FocusChangeMask, /* FocusIn */ 92 FocusChangeMask, /* FocusOut */ 93 KeymapStateMask, /* KeymapNotify */ 94 ExposureMask, /* Expose */ 95 ExposureMask, /* GraphicsExpose */ 96 ExposureMask, /* NoExpose */ 97 VisibilityChangeMask, /* VisibilityNotify */ 98 SubstructureNotifyMask, /* CreateNotify */ 99 StructureNotifyMask, /* DestroyNotify */ 100 StructureNotifyMask, /* UnmapNotify */ 101 StructureNotifyMask, /* MapNotify */ 102 SubstructureRedirectMask, /* MapRequest */ 103 StructureNotifyMask, /* ReparentNotify */ 104 StructureNotifyMask, /* ConfigureNotify */ 105 SubstructureRedirectMask, /* ConfigureRequest */ 106 StructureNotifyMask, /* GravityNotify */ 107 ResizeRedirectMask, /* ResizeRequest */ 108 StructureNotifyMask, /* CirculateNotify */ 109 SubstructureRedirectMask, /* CirculateRequest */ 110 PropertyChangeMask, /* PropertyNotify */ 111 0, /* SelectionClear */ 112 0, /* SelectionRequest */ 113 0, /* SelectionNotify */ 114 ColormapChangeMask, /* ColormapNotify */ 115 0, /* ClientMessage */ 116 0 /* Mapping Notify */ 117}; 118 119static unsigned long virtualEventMasks[TK_LASTEVENT-VirtualEvent] = { 120 VirtualEventMask, /* VirtualEvents */ 121 ActivateMask, /* ActivateNotify */ 122 ActivateMask, /* DeactivateNotify */ 123 MouseWheelMask /* MouseWheelEvent */ 124}; 125 126/* 127 * For each exit handler created with a call to TkCreateExitHandler or 128 * TkCreateThreadExitHandler there is a structure of the following type: 129 */ 130 131typedef struct ExitHandler { 132 Tcl_ExitProc *proc; /* Function to call when process exits. */ 133 ClientData clientData; /* One word of information to pass to proc. */ 134 struct ExitHandler *nextPtr;/* Next in list of all exit handlers for this 135 * application, or NULL for end of list. */ 136} ExitHandler; 137 138/* 139 * The structure below is used to store Data for the Event module that must be 140 * kept thread-local. The "dataKey" is used to fetch the thread-specific 141 * storage for the current thread. 142 */ 143 144typedef struct ThreadSpecificData { 145 int handlersActive; /* The following variable has a non-zero value 146 * when a handler is active. */ 147 InProgress *pendingPtr; /* Topmost search in progress, or NULL if 148 * none. */ 149 150 /* 151 * List of generic handler records. 152 */ 153 154 GenericHandler *genericList;/* First handler in the list, or NULL. */ 155 GenericHandler *lastGenericPtr; 156 /* Last handler in list. */ 157 158 /* 159 * List of client message handler records. 160 */ 161 162 GenericHandler *cmList; /* First handler in the list, or NULL. */ 163 GenericHandler *lastCmPtr; /* Last handler in list. */ 164 165 /* 166 * If someone has called Tk_RestrictEvents, the information below keeps 167 * track of it. 168 */ 169 170 Tk_RestrictProc *restrictProc; 171 /* Function to call. NULL means no 172 * restrictProc is currently in effect. */ 173 ClientData restrictArg; /* Argument to pass to restrictProc. */ 174 ExitHandler *firstExitPtr; /* First in list of all exit handlers for this 175 * thread. */ 176 int inExit; /* True when this thread is exiting. This is 177 * used as a hack to decide to close the 178 * standard channels. */ 179} ThreadSpecificData; 180static Tcl_ThreadDataKey dataKey; 181 182/* 183 * There are both per-process and per-thread exit handlers. The first list is 184 * controlled by a mutex. The other is in thread local storage. 185 */ 186 187static ExitHandler *firstExitPtr = NULL; 188 /* First in list of all exit handlers for 189 * application. */ 190TCL_DECLARE_MUTEX(exitMutex) 191 192/* 193 * Prototypes for functions that are only referenced locally within this file. 194 */ 195 196static void CleanUpTkEvent(XEvent *eventPtr); 197static void DelayedMotionProc(ClientData clientData); 198static int GetButtonMask(unsigned int Button); 199static unsigned long GetEventMaskFromXEvent(XEvent *eventPtr); 200static TkWindow * GetTkWindowFromXEvent(XEvent *eventPtr); 201static void InvokeClientMessageHandlers(ThreadSpecificData *tsdPtr, 202 Tk_Window tkwin, XEvent *eventPtr); 203static int InvokeFocusHandlers(TkWindow **winPtrPtr, 204 unsigned long mask, XEvent *eventPtr); 205static int InvokeGenericHandlers(ThreadSpecificData *tsdPtr, 206 XEvent *eventPtr); 207static int InvokeMouseHandlers(TkWindow *winPtr, 208 unsigned long mask, XEvent *eventPtr); 209static Window ParentXId(Display *display, Window w); 210static int RefreshKeyboardMappingIfNeeded(XEvent *eventPtr); 211static int TkXErrorHandler(ClientData clientData, 212 XErrorEvent *errEventPtr); 213static void UpdateButtonEventState(XEvent *eventPtr); 214static int WindowEventProc(Tcl_Event *evPtr, int flags); 215#ifdef TK_USE_INPUT_METHODS 216static void CreateXIC(TkWindow *winPtr); 217#endif /* TK_USE_INPUT_METHODS */ 218 219/* 220 *---------------------------------------------------------------------- 221 * 222 * InvokeFocusHandlers -- 223 * 224 * Call focus-related code to look at FocusIn, FocusOut, Enter, and Leave 225 * events; depending on its return value, ignore the event. 226 * 227 * Results: 228 * 0 further processing can be done on the event. 229 * 1 we are done with the event passed. 230 * 231 * Side effects: 232 * The *winPtrPtr in the caller may be changed to the TkWindow for the 233 * window with focus. 234 * 235 *---------------------------------------------------------------------- 236 */ 237 238static int 239InvokeFocusHandlers( 240 TkWindow **winPtrPtr, 241 unsigned long mask, 242 XEvent *eventPtr) 243{ 244 if ((mask & (FocusChangeMask|EnterWindowMask|LeaveWindowMask)) 245 && (TkFocusFilterEvent(*winPtrPtr, eventPtr) == 0)) { 246 return 1; 247 } 248 249 /* 250 * MouseWheel events are not focus specific on Mac OS X. 251 */ 252 253#ifdef MAC_OSX_TK 254#define FOCUS_DIRECTED_EVENT_MASK (KeyPressMask|KeyReleaseMask) 255#else 256#define FOCUS_DIRECTED_EVENT_MASK (KeyPressMask|KeyReleaseMask|MouseWheelMask) 257#endif 258 259 if (mask & FOCUS_DIRECTED_EVENT_MASK) { 260 (*winPtrPtr)->dispPtr->lastEventTime = eventPtr->xkey.time; 261 *winPtrPtr = TkFocusKeyEvent(*winPtrPtr, eventPtr); 262 if (*winPtrPtr == NULL) { 263 return 1; 264 } 265 } 266 267 return 0; 268} 269 270/* 271 *---------------------------------------------------------------------- 272 * 273 * InvokeMouseHandlers -- 274 * 275 * Call a grab-related function to do special processing on pointer 276 * events. 277 * 278 * Results: 279 * 0 further processing can be done on the event. 280 * 1 we are done with the event passed. 281 * 282 * Side effects: 283 * New events may be queued from TkPointerEvent and grabs may be added 284 * and/or removed. The eventPtr may be changed by TkPointerEvent in some 285 * cases. 286 * 287 *---------------------------------------------------------------------- 288 */ 289 290static int 291InvokeMouseHandlers( 292 TkWindow *winPtr, 293 unsigned long mask, 294 XEvent *eventPtr) 295{ 296 if (mask & (ButtonPressMask|ButtonReleaseMask|PointerMotionMask 297 |EnterWindowMask|LeaveWindowMask)) { 298 299 if (mask & (ButtonPressMask|ButtonReleaseMask)) { 300 winPtr->dispPtr->lastEventTime = eventPtr->xbutton.time; 301 } else if (mask & PointerMotionMask) { 302 winPtr->dispPtr->lastEventTime = eventPtr->xmotion.time; 303 } else { 304 winPtr->dispPtr->lastEventTime = eventPtr->xcrossing.time; 305 } 306 307 if (TkPointerEvent(eventPtr, winPtr) == 0) { 308 /* 309 * The event should be ignored to make grab work correctly (as the 310 * comment for TkPointerEvent states). 311 */ 312 313 return 1; 314 } 315 } 316 317 return 0; 318} 319 320/* 321 *---------------------------------------------------------------------- 322 * 323 * CreateXIC -- 324 * 325 * Create the X input context for our winPtr. 326 * XIM is only ever enabled on Unix. 327 * 328 *---------------------------------------------------------------------- 329 */ 330 331#if defined(TK_USE_INPUT_METHODS) 332static void 333CreateXIC( 334 TkWindow *winPtr) 335{ 336 TkDisplay *dispPtr = winPtr->dispPtr; 337 long im_event_mask = 0L; 338 const char *preedit_attname = NULL; 339 XVaNestedList preedit_attlist = NULL; 340 341 if (dispPtr->inputStyle & XIMPreeditPosition) { 342 XPoint spot = {0, 0}; 343 344 preedit_attname = XNPreeditAttributes; 345 preedit_attlist = XVaCreateNestedList(0, 346 XNSpotLocation, &spot, 347 XNFontSet, dispPtr->inputXfs, 348 NULL); 349 } 350 351 winPtr->inputContext = XCreateIC(dispPtr->inputMethod, 352 XNInputStyle, dispPtr->inputStyle, 353 XNClientWindow, winPtr->window, 354 XNFocusWindow, winPtr->window, 355 preedit_attname, preedit_attlist, 356 NULL); 357 358 if (preedit_attlist) { 359 XFree(preedit_attlist); 360 } 361 362 363 if (winPtr->inputContext == NULL) { 364 /* XCreateIC failed. */ 365 return; 366 } 367 368 /* 369 * Adjust the window's event mask if the IM requires it. 370 */ 371 XGetICValues(winPtr->inputContext, XNFilterEvents, &im_event_mask, NULL); 372 if ((winPtr->atts.event_mask & im_event_mask) != im_event_mask) { 373 winPtr->atts.event_mask |= im_event_mask; 374 XSelectInput(winPtr->display, winPtr->window, winPtr->atts.event_mask); 375 } 376} 377#endif 378 379/* 380 *---------------------------------------------------------------------- 381 * 382 * GetTkWindowFromXEvent -- 383 * 384 * Attempt to find which TkWindow is associated with an event. If it 385 * fails we attempt to get the TkWindow from the parent for a property 386 * notification. 387 * 388 * Results: 389 * The TkWindow associated with the event or NULL. 390 * 391 * Side effects: 392 * TkSelPropProc may influence selection on windows not known to Tk. 393 * 394 *---------------------------------------------------------------------- 395 */ 396 397static TkWindow * 398GetTkWindowFromXEvent( 399 XEvent *eventPtr) 400{ 401 TkWindow *winPtr; 402 Window parentXId, handlerWindow = eventPtr->xany.window; 403 404 if ((eventPtr->xany.type == StructureNotifyMask) 405 && (eventPtr->xmap.event != eventPtr->xmap.window)) { 406 handlerWindow = eventPtr->xmap.event; 407 } 408 409 winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, handlerWindow); 410 411 if (winPtr == NULL) { 412 /* 413 * There isn't a TkWindow structure for this window. However, if the 414 * event is a PropertyNotify event then call the selection manager (it 415 * deals beneath-the-table with certain properties). Also, if the 416 * window's parent is a Tk window that has the TK_PROP_PROPCHANGE flag 417 * set, then we must propagate the PropertyNotify event up to the 418 * parent. 419 */ 420 421 if (eventPtr->type != PropertyNotify) { 422 return NULL; 423 } 424 TkSelPropProc(eventPtr); 425 parentXId = ParentXId(eventPtr->xany.display, handlerWindow); 426 if (parentXId == None) { 427 return NULL; 428 } 429 winPtr = (TkWindow *) Tk_IdToWindow(eventPtr->xany.display, parentXId); 430 if (winPtr == NULL) { 431 return NULL; 432 } 433 if (!(winPtr->flags & TK_PROP_PROPCHANGE)) { 434 return NULL; 435 } 436 } 437 return winPtr; 438} 439 440/* 441 *---------------------------------------------------------------------- 442 * 443 * GetEventMaskFromXEvent -- 444 * 445 * The event type is looked up in our eventMasks tables, and may be 446 * changed to a different mask depending on the state of the event and 447 * window members. 448 * 449 * Results: 450 * The mask for the event. 451 * 452 * Side effects: 453 * None. 454 * 455 *---------------------------------------------------------------------- 456 */ 457 458static unsigned long 459GetEventMaskFromXEvent( 460 XEvent *eventPtr) 461{ 462 unsigned long mask; 463 464 /* 465 * Get the event mask from the correct table. Note that there are two 466 * tables here because that means we no longer need this code to rely on 467 * the exact value of VirtualEvent, which has caused us problems in the 468 * past when X11 changed the value of LASTEvent. [Bug ???] 469 */ 470 471 if (eventPtr->xany.type <= MappingNotify) { 472 mask = realEventMasks[eventPtr->xany.type]; 473 } else if (eventPtr->xany.type >= VirtualEvent 474 && eventPtr->xany.type<TK_LASTEVENT) { 475 mask = virtualEventMasks[eventPtr->xany.type - VirtualEvent]; 476 } else { 477 mask = 0; 478 } 479 480 /* 481 * Events selected by StructureNotify require special handling. They look 482 * the same as those selected by SubstructureNotify. The only difference 483 * is whether the "event" and "window" fields are the same. Compare the 484 * two fields and convert StructureNotify to SubstructureNotify if 485 * necessary. 486 */ 487 488 if (mask == StructureNotifyMask) { 489 if (eventPtr->xmap.event != eventPtr->xmap.window) { 490 mask = SubstructureNotifyMask; 491 } 492 } 493 return mask; 494} 495 496/* 497 *---------------------------------------------------------------------- 498 * 499 * RefreshKeyboardMappingIfNeeded -- 500 * 501 * If the event is a MappingNotify event, find its display and refresh 502 * the keyboard mapping information for the display. 503 * 504 * Results: 505 * 0 if the event was not a MappingNotify event 506 * 1 if the event was a MappingNotify event 507 * 508 * Side effects: 509 * None. 510 * 511 *---------------------------------------------------------------------- 512 */ 513 514static int 515RefreshKeyboardMappingIfNeeded( 516 XEvent *eventPtr) 517{ 518 TkDisplay *dispPtr; 519 520 if (eventPtr->type == MappingNotify) { 521 dispPtr = TkGetDisplay(eventPtr->xmapping.display); 522 if (dispPtr != NULL) { 523 XRefreshKeyboardMapping(&eventPtr->xmapping); 524 dispPtr->bindInfoStale = 1; 525 } 526 return 1; 527 } 528 return 0; 529} 530 531/* 532 *---------------------------------------------------------------------- 533 * 534 * GetButtonMask -- 535 * 536 * Return the proper Button${n}Mask for the button. 537 * 538 * Results: 539 * A button mask. 540 * 541 * Side effects: 542 * None. 543 * 544 *---------------------------------------------------------------------- 545 */ 546 547static int 548GetButtonMask( 549 unsigned int button) 550{ 551 switch (button) { 552 case 1: 553 return Button1Mask; 554 case 2: 555 return Button2Mask; 556 case 3: 557 return Button3Mask; 558 case 4: 559 return Button4Mask; 560 case 5: 561 return Button5Mask; 562 } 563 return 0; 564} 565 566/* 567 *---------------------------------------------------------------------- 568 * 569 * UpdateButtonEventState -- 570 * 571 * Update the button event state in our TkDisplay using the XEvent 572 * passed. We also may modify the the XEvent passed to fit some aspects 573 * of our TkDisplay. 574 * 575 * Results: 576 * None. 577 * 578 * Side effects: 579 * The TkDisplay's private button state may be modified. The eventPtr's 580 * state may be updated to reflect masks stored in our TkDisplay that the 581 * event doesn't contain. The eventPtr may also be modified to not 582 * contain a button state for the window in which it was not pressed in. 583 * 584 *---------------------------------------------------------------------- 585 */ 586 587static void 588UpdateButtonEventState( 589 XEvent *eventPtr) 590{ 591 TkDisplay *dispPtr; 592 int allButtonsMask = Button1Mask | Button2Mask | Button3Mask 593 | Button4Mask | Button5Mask; 594 595 switch (eventPtr->type) { 596 case ButtonPress: 597 dispPtr = TkGetDisplay(eventPtr->xbutton.display); 598 dispPtr->mouseButtonWindow = eventPtr->xbutton.window; 599 eventPtr->xbutton.state |= dispPtr->mouseButtonState; 600 601 dispPtr->mouseButtonState |= GetButtonMask(eventPtr->xbutton.button); 602 break; 603 604 case ButtonRelease: 605 dispPtr = TkGetDisplay(eventPtr->xbutton.display); 606 dispPtr->mouseButtonWindow = None; 607 dispPtr->mouseButtonState &= ~GetButtonMask(eventPtr->xbutton.button); 608 eventPtr->xbutton.state |= dispPtr->mouseButtonState; 609 break; 610 611 case MotionNotify: 612 dispPtr = TkGetDisplay(eventPtr->xmotion.display); 613 if (dispPtr->mouseButtonState & allButtonsMask) { 614 if (eventPtr->xbutton.window != dispPtr->mouseButtonWindow) { 615 /* 616 * This motion event should not be interpreted as a button 617 * press + motion event since this is not the same window the 618 * button was pressed down in. 619 */ 620 621 dispPtr->mouseButtonState &= ~allButtonsMask; 622 dispPtr->mouseButtonWindow = None; 623 } else { 624 eventPtr->xmotion.state |= dispPtr->mouseButtonState; 625 } 626 } 627 break; 628 } 629} 630 631/* 632 *---------------------------------------------------------------------- 633 * 634 * InvokeClientMessageHandlers -- 635 * 636 * Iterate the list of handlers and invoke the function pointer for each. 637 * 638 * Results: 639 * None. 640 * 641 * Side effects: 642 * Handlers may be deleted and events may be sent to handlers. 643 * 644 *---------------------------------------------------------------------- 645 */ 646 647static void 648InvokeClientMessageHandlers( 649 ThreadSpecificData *tsdPtr, 650 Tk_Window tkwin, 651 XEvent *eventPtr) 652{ 653 GenericHandler *prevPtr, *tmpPtr, *curPtr = tsdPtr->cmList; 654 655 for (prevPtr = NULL; curPtr != NULL; ) { 656 if (curPtr->deleteFlag) { 657 if (!tsdPtr->handlersActive) { 658 /* 659 * This handler needs to be deleted and there are no calls 660 * pending through any handlers, so now is a safe time to 661 * delete it. 662 */ 663 664 tmpPtr = curPtr->nextPtr; 665 if (prevPtr == NULL) { 666 tsdPtr->cmList = tmpPtr; 667 } else { 668 prevPtr->nextPtr = tmpPtr; 669 } 670 if (tmpPtr == NULL) { 671 tsdPtr->lastCmPtr = prevPtr; 672 } 673 (void) ckfree((char *) curPtr); 674 curPtr = tmpPtr; 675 continue; 676 } 677 } else { 678 int done; 679 680 tsdPtr->handlersActive++; 681 done = (*(Tk_ClientMessageProc *)curPtr->proc)(tkwin, eventPtr); 682 tsdPtr->handlersActive--; 683 if (done) { 684 break; 685 } 686 } 687 prevPtr = curPtr; 688 curPtr = curPtr->nextPtr; 689 } 690} 691 692/* 693 *---------------------------------------------------------------------- 694 * 695 * InvokeGenericHandlers -- 696 * 697 * Iterate the list of handlers and invoke the function pointer for each. 698 * If the handler invoked returns a non-zero value then we are done. 699 * 700 * Results: 701 * 0 when the event wasn't handled by a handler. Non-zero when it was 702 * processed and handled by a handler. 703 * 704 * Side effects: 705 * Handlers may be deleted and events may be sent to handlers. 706 * 707 *---------------------------------------------------------------------- 708 */ 709 710static int 711InvokeGenericHandlers( 712 ThreadSpecificData *tsdPtr, 713 XEvent *eventPtr) 714{ 715 GenericHandler *prevPtr, *tmpPtr, *curPtr = tsdPtr->genericList; 716 717 for (prevPtr = NULL; curPtr != NULL; ) { 718 if (curPtr->deleteFlag) { 719 if (!tsdPtr->handlersActive) { 720 /* 721 * This handler needs to be deleted and there are no calls 722 * pending through the handler, so now is a safe time to 723 * delete it. 724 */ 725 726 tmpPtr = curPtr->nextPtr; 727 if (prevPtr == NULL) { 728 tsdPtr->genericList = tmpPtr; 729 } else { 730 prevPtr->nextPtr = tmpPtr; 731 } 732 if (tmpPtr == NULL) { 733 tsdPtr->lastGenericPtr = prevPtr; 734 } 735 (void) ckfree((char *) curPtr); 736 curPtr = tmpPtr; 737 continue; 738 } 739 } else { 740 int done; 741 742 tsdPtr->handlersActive++; 743 done = (*curPtr->proc)(curPtr->clientData, eventPtr); 744 tsdPtr->handlersActive--; 745 if (done) { 746 return done; 747 } 748 } 749 prevPtr = curPtr; 750 curPtr = curPtr->nextPtr; 751 } 752 return 0; 753} 754 755/* 756 *---------------------------------------------------------------------- 757 * 758 * Tk_CreateEventHandler -- 759 * 760 * Arrange for a given function to be invoked whenever events from a 761 * given class occur in a given window. 762 * 763 * Results: 764 * None. 765 * 766 * Side effects: 767 * From now on, whenever an event of the type given by mask occurs for 768 * token and is processed by Tk_HandleEvent, proc will be called. See the 769 * manual entry for details of the calling sequence and return value for 770 * proc. 771 * 772 *---------------------------------------------------------------------- 773 */ 774 775void 776Tk_CreateEventHandler( 777 Tk_Window token, /* Token for window in which to create 778 * handler. */ 779 unsigned long mask, /* Events for which proc should be called. */ 780 Tk_EventProc *proc, /* Function to call for each selected event */ 781 ClientData clientData) /* Arbitrary data to pass to proc. */ 782{ 783 register TkEventHandler *handlerPtr; 784 register TkWindow *winPtr = (TkWindow *) token; 785 786 /* 787 * Skim through the list of existing handlers to (a) compute the overall 788 * event mask for the window (so we can pass this new value to the X 789 * system) and (b) see if there's already a handler declared with the same 790 * callback and clientData (if so, just change the mask). If no existing 791 * handler matches, then create a new handler. 792 */ 793 794 if (winPtr->handlerList == NULL) { 795 /* 796 * No event handlers defined at all, so must create. 797 */ 798 799 handlerPtr = (TkEventHandler *) ckalloc(sizeof(TkEventHandler)); 800 winPtr->handlerList = handlerPtr; 801 } else { 802 int found = 0; 803 804 for (handlerPtr = winPtr->handlerList; ; 805 handlerPtr = handlerPtr->nextPtr) { 806 if ((handlerPtr->proc == proc) 807 && (handlerPtr->clientData == clientData)) { 808 handlerPtr->mask = mask; 809 found = 1; 810 } 811 if (handlerPtr->nextPtr == NULL) { 812 break; 813 } 814 } 815 816 /* 817 * If we found anything, we're done because we do not need to use 818 * XSelectInput; Tk always selects on all events anyway in order to 819 * support binding on classes, 'all' and other bind-tags. 820 */ 821 822 if (found) { 823 return; 824 } 825 826 /* 827 * No event handler matched, so create a new one. 828 */ 829 830 handlerPtr->nextPtr = (TkEventHandler *) 831 ckalloc(sizeof(TkEventHandler)); 832 handlerPtr = handlerPtr->nextPtr; 833 } 834 835 /* 836 * Initialize the new event handler. 837 */ 838 839 handlerPtr->mask = mask; 840 handlerPtr->proc = proc; 841 handlerPtr->clientData = clientData; 842 handlerPtr->nextPtr = NULL; 843 844 /* 845 * No need to call XSelectInput: Tk always selects on all events for all 846 * windows (needed to support bindings on classes and "all"). 847 */ 848} 849 850/* 851 *---------------------------------------------------------------------- 852 * 853 * Tk_DeleteEventHandler -- 854 * 855 * Delete a previously-created handler. 856 * 857 * Results: 858 * None. 859 * 860 * Side effects: 861 * If there existed a handler as described by the parameters, the handler 862 * is deleted so that proc will not be invoked again. 863 * 864 *---------------------------------------------------------------------- 865 */ 866 867void 868Tk_DeleteEventHandler( 869 Tk_Window token, /* Same as corresponding arguments passed */ 870 unsigned long mask, /* previously to Tk_CreateEventHandler. */ 871 Tk_EventProc *proc, 872 ClientData clientData) 873{ 874 register TkEventHandler *handlerPtr; 875 register InProgress *ipPtr; 876 TkEventHandler *prevPtr; 877 register TkWindow *winPtr = (TkWindow *) token; 878 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 879 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 880 881 /* 882 * Find the event handler to be deleted, or return immediately if it 883 * doesn't exist. 884 */ 885 886 for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ; 887 prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) { 888 if (handlerPtr == NULL) { 889 return; 890 } 891 if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc) 892 && (handlerPtr->clientData == clientData)) { 893 break; 894 } 895 } 896 897 /* 898 * If Tk_HandleEvent is about to process this handler, tell it to process 899 * the next one instead. 900 */ 901 902 for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) { 903 if (ipPtr->nextHandler == handlerPtr) { 904 ipPtr->nextHandler = handlerPtr->nextPtr; 905 } 906 } 907 908 /* 909 * Free resources associated with the handler. 910 */ 911 912 if (prevPtr == NULL) { 913 winPtr->handlerList = handlerPtr->nextPtr; 914 } else { 915 prevPtr->nextPtr = handlerPtr->nextPtr; 916 } 917 ckfree((char *) handlerPtr); 918 919 /* 920 * No need to call XSelectInput: Tk always selects on all events for all 921 * windows (needed to support bindings on classes and "all"). 922 */ 923} 924 925/*---------------------------------------------------------------------- 926 * 927 * Tk_CreateGenericHandler -- 928 * 929 * Register a function to be called on each X event, regardless of 930 * display or window. Generic handlers are useful for capturing events 931 * that aren't associated with windows, or events for windows not managed 932 * by Tk. 933 * 934 * Results: 935 * None. 936 * 937 * Side Effects: 938 * From now on, whenever an X event is given to Tk_HandleEvent, invoke 939 * proc, giving it clientData and the event as arguments. 940 * 941 *---------------------------------------------------------------------- 942 */ 943 944void 945Tk_CreateGenericHandler( 946 Tk_GenericProc *proc, /* Function to call on every event. */ 947 ClientData clientData) /* One-word value to pass to proc. */ 948{ 949 GenericHandler *handlerPtr; 950 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 951 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 952 953 handlerPtr = (GenericHandler *) ckalloc(sizeof(GenericHandler)); 954 955 handlerPtr->proc = proc; 956 handlerPtr->clientData = clientData; 957 handlerPtr->deleteFlag = 0; 958 handlerPtr->nextPtr = NULL; 959 if (tsdPtr->genericList == NULL) { 960 tsdPtr->genericList = handlerPtr; 961 } else { 962 tsdPtr->lastGenericPtr->nextPtr = handlerPtr; 963 } 964 tsdPtr->lastGenericPtr = handlerPtr; 965} 966 967/* 968 *---------------------------------------------------------------------- 969 * 970 * Tk_DeleteGenericHandler -- 971 * 972 * Delete a previously-created generic handler. 973 * 974 * Results: 975 * None. 976 * 977 * Side Effects: 978 * If there existed a handler as described by the parameters, that 979 * handler is logically deleted so that proc will not be invoked again. 980 * The physical deletion happens in the event loop in Tk_HandleEvent. 981 * 982 *---------------------------------------------------------------------- 983 */ 984 985void 986Tk_DeleteGenericHandler( 987 Tk_GenericProc *proc, 988 ClientData clientData) 989{ 990 GenericHandler * handler; 991 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 992 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 993 994 for (handler=tsdPtr->genericList ; handler ; handler=handler->nextPtr) { 995 if ((handler->proc == proc) && (handler->clientData == clientData)) { 996 handler->deleteFlag = 1; 997 } 998 } 999} 1000 1001/*---------------------------------------------------------------------- 1002 * 1003 * Tk_CreateClientMessageHandler -- 1004 * 1005 * Register a function to be called on each ClientMessage event. 1006 * ClientMessage handlers are useful for Drag&Drop extensions. 1007 * 1008 * Results: 1009 * None. 1010 * 1011 * Side Effects: 1012 * From now on, whenever a ClientMessage event is received that isn't a 1013 * WM_PROTOCOL event or SelectionEvent, invoke proc, giving it tkwin and 1014 * the event as arguments. 1015 * 1016 *---------------------------------------------------------------------- 1017 */ 1018 1019void 1020Tk_CreateClientMessageHandler( 1021 Tk_ClientMessageProc *proc) /* Function to call on event. */ 1022{ 1023 GenericHandler *handlerPtr; 1024 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1025 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1026 1027 /* 1028 * We use a GenericHandler struct, because it's basically the same, except 1029 * with an extra clientData field we'll never use. 1030 */ 1031 1032 handlerPtr = (GenericHandler *) ckalloc(sizeof(GenericHandler)); 1033 1034 handlerPtr->proc = (Tk_GenericProc *) proc; 1035 handlerPtr->clientData = NULL; /* never used */ 1036 handlerPtr->deleteFlag = 0; 1037 handlerPtr->nextPtr = NULL; 1038 if (tsdPtr->cmList == NULL) { 1039 tsdPtr->cmList = handlerPtr; 1040 } else { 1041 tsdPtr->lastCmPtr->nextPtr = handlerPtr; 1042 } 1043 tsdPtr->lastCmPtr = handlerPtr; 1044} 1045 1046/* 1047 *---------------------------------------------------------------------- 1048 * 1049 * Tk_DeleteClientMessageHandler -- 1050 * 1051 * Delete a previously-created ClientMessage handler. 1052 * 1053 * Results: 1054 * None. 1055 * 1056 * Side Effects: 1057 * If there existed a handler as described by the parameters, that 1058 * handler is logically deleted so that proc will not be invoked again. 1059 * The physical deletion happens in the event loop in 1060 * TkClientMessageEventProc. 1061 * 1062 *---------------------------------------------------------------------- 1063 */ 1064 1065void 1066Tk_DeleteClientMessageHandler( 1067 Tk_ClientMessageProc *proc) 1068{ 1069 GenericHandler * handler; 1070 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1071 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1072 1073 for (handler=tsdPtr->cmList ; handler!=NULL ; handler=handler->nextPtr) { 1074 if (handler->proc == (Tk_GenericProc *) proc) { 1075 handler->deleteFlag = 1; 1076 } 1077 } 1078} 1079 1080/* 1081 *---------------------------------------------------------------------- 1082 * 1083 * TkEventInit -- 1084 * 1085 * This functions initializes all the event module structures used by the 1086 * current thread. It must be called before any other function in this 1087 * file is called. 1088 * 1089 * Results: 1090 * None. 1091 * 1092 * Side Effects: 1093 * None. 1094 * 1095 *---------------------------------------------------------------------- 1096 */ 1097 1098void 1099TkEventInit(void) 1100{ 1101 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1102 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1103 1104 tsdPtr->handlersActive = 0; 1105 tsdPtr->pendingPtr = NULL; 1106 tsdPtr->genericList = NULL; 1107 tsdPtr->lastGenericPtr = NULL; 1108 tsdPtr->cmList = NULL; 1109 tsdPtr->lastCmPtr = NULL; 1110 tsdPtr->restrictProc = NULL; 1111 tsdPtr->restrictArg = NULL; 1112} 1113 1114/* 1115 *---------------------------------------------------------------------- 1116 * 1117 * TkXErrorHandler -- 1118 * 1119 * TkXErrorHandler is an error handler, to be installed via 1120 * Tk_CreateErrorHandler, that will set a flag if an X error occurred. 1121 * 1122 * Results: 1123 * Always returns 0, indicating that the X error was handled. 1124 * 1125 * Side effects: 1126 * None. 1127 * 1128 *---------------------------------------------------------------------- 1129 */ 1130 1131static int 1132TkXErrorHandler( 1133 ClientData clientData, /* Pointer to flag we set. */ 1134 XErrorEvent *errEventPtr) /* X error info. */ 1135{ 1136 int *error; 1137 1138 error = (int *) clientData; 1139 *error = 1; 1140 return 0; 1141} 1142 1143/* 1144 *---------------------------------------------------------------------- 1145 * 1146 * ParentXId -- 1147 * 1148 * Returns the parent of the given window, or "None" if the window 1149 * doesn't exist. 1150 * 1151 * Results: 1152 * Returns an X window ID. 1153 * 1154 * Side effects: 1155 * None. 1156 * 1157 *---------------------------------------------------------------------- 1158 */ 1159 1160static Window 1161ParentXId( 1162 Display *display, 1163 Window w) 1164{ 1165 Tk_ErrorHandler handler; 1166 int gotXError; 1167 Status status; 1168 Window parent; 1169 Window root; 1170 Window *childList; 1171 unsigned int nChildren; 1172 1173 /* 1174 * Handle errors ourselves. 1175 */ 1176 1177 gotXError = 0; 1178 handler = Tk_CreateErrorHandler(display, -1, -1, -1, 1179 TkXErrorHandler, (ClientData) (&gotXError)); 1180 1181 /* 1182 * Get the parent window. 1183 */ 1184 1185 status = XQueryTree(display, w, &root, &parent, &childList, &nChildren); 1186 1187 /* 1188 * Do some cleanup; gotta return "None" if we got an error. 1189 */ 1190 1191 Tk_DeleteErrorHandler(handler); 1192 XSync(display, False); 1193 if (status != 0 && childList != NULL) { 1194 XFree(childList); 1195 } 1196 if (status == 0) { 1197 parent = None; 1198 } 1199 1200 return parent; 1201} 1202 1203/* 1204 *---------------------------------------------------------------------- 1205 * 1206 * Tk_HandleEvent -- 1207 * 1208 * Given an event, invoke all the handlers that have been registered for 1209 * the event. 1210 * 1211 * Results: 1212 * None. 1213 * 1214 * Side effects: 1215 * Depends on the handlers. 1216 * 1217 *---------------------------------------------------------------------- 1218 */ 1219 1220void 1221Tk_HandleEvent( 1222 XEvent *eventPtr) /* Event to dispatch. */ 1223{ 1224 register TkEventHandler *handlerPtr; 1225 TkWindow *winPtr; 1226 unsigned long mask; 1227 InProgress ip; 1228 Tcl_Interp *interp = NULL; 1229 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1230 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1231 1232 UpdateButtonEventState(eventPtr); 1233 1234 /* 1235 * If the generic handler processed this event we are done and can return. 1236 */ 1237 1238 if (InvokeGenericHandlers(tsdPtr, eventPtr)) { 1239 goto releaseEventResources; 1240 } 1241 1242 if (RefreshKeyboardMappingIfNeeded(eventPtr)) { 1243 /* 1244 * We are done with a MappingNotify event. 1245 */ 1246 1247 goto releaseEventResources; 1248 } 1249 1250 mask = GetEventMaskFromXEvent(eventPtr); 1251 winPtr = GetTkWindowFromXEvent(eventPtr); 1252 1253 if (winPtr == NULL) { 1254 goto releaseEventResources; 1255 } 1256 1257 /* 1258 * Once a window has started getting deleted, don't process any more 1259 * events for it except for the DestroyNotify event. This check is needed 1260 * because a DestroyNotify handler could re-invoke the event loop, causing 1261 * other pending events to be handled for the window (the window doesn't 1262 * get totally expunged from our tables until after the DestroyNotify 1263 * event has been completely handled). 1264 */ 1265 1266 if ((winPtr->flags & TK_ALREADY_DEAD) 1267 && (eventPtr->type != DestroyNotify)) { 1268 goto releaseEventResources; 1269 } 1270 1271 if (winPtr->mainPtr != NULL) { 1272 int result; 1273 1274 interp = winPtr->mainPtr->interp; 1275 1276 /* 1277 * Protect interpreter for this window from possible deletion while we 1278 * are dealing with the event for this window. Thus, widget writers do 1279 * not have to worry about protecting the interpreter in their own 1280 * code. 1281 */ 1282 1283 Tcl_Preserve((ClientData) interp); 1284 1285 result = ((InvokeFocusHandlers(&winPtr, mask, eventPtr)) 1286 || (InvokeMouseHandlers(winPtr, mask, eventPtr))); 1287 1288 if (result) { 1289 goto releaseInterpreter; 1290 } 1291 } 1292 1293 /* 1294 * Create the input context for the window if it hasn't already been done 1295 * (XFilterEvent needs this context). When the event is a FocusIn event, 1296 * set the input context focus to the receiving window. This code is only 1297 * ever active for X11. 1298 */ 1299 1300#ifdef TK_USE_INPUT_METHODS 1301 if ((winPtr->dispPtr->flags & TK_DISPLAY_USE_IM)) { 1302 if (!(winPtr->flags & (TK_CHECKED_IC|TK_ALREADY_DEAD))) { 1303 winPtr->flags |= TK_CHECKED_IC; 1304 if (winPtr->dispPtr->inputMethod != NULL) { 1305 CreateXIC(winPtr); 1306 } 1307 } 1308 if (eventPtr->type == FocusIn && winPtr->inputContext != NULL) { 1309 XSetICFocus(winPtr->inputContext); 1310 } 1311 } 1312#endif 1313 1314 /* 1315 * For events where it hasn't already been done, update the current time 1316 * in the display. 1317 */ 1318 1319 if (eventPtr->type == PropertyNotify) { 1320 winPtr->dispPtr->lastEventTime = eventPtr->xproperty.time; 1321 } 1322 1323 /* 1324 * There's a potential interaction here with Tk_DeleteEventHandler. Read 1325 * the documentation for pendingPtr. 1326 */ 1327 1328 ip.eventPtr = eventPtr; 1329 ip.winPtr = winPtr; 1330 ip.nextHandler = NULL; 1331 ip.nextPtr = tsdPtr->pendingPtr; 1332 tsdPtr->pendingPtr = &ip; 1333 if (mask == 0) { 1334 if ((eventPtr->type == SelectionClear) 1335 || (eventPtr->type == SelectionRequest) 1336 || (eventPtr->type == SelectionNotify)) { 1337 TkSelEventProc((Tk_Window) winPtr, eventPtr); 1338 } else if (eventPtr->type == ClientMessage) { 1339 if (eventPtr->xclient.message_type == 1340 Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS")) { 1341 TkWmProtocolEventProc(winPtr, eventPtr); 1342 } else { 1343 InvokeClientMessageHandlers(tsdPtr, (Tk_Window)winPtr, 1344 eventPtr); 1345 } 1346 } 1347 } else { 1348 for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) { 1349 if ((handlerPtr->mask & mask) != 0) { 1350 ip.nextHandler = handlerPtr->nextPtr; 1351 (*(handlerPtr->proc))(handlerPtr->clientData, eventPtr); 1352 handlerPtr = ip.nextHandler; 1353 } else { 1354 handlerPtr = handlerPtr->nextPtr; 1355 } 1356 } 1357 1358 /* 1359 * Pass the event to the "bind" command mechanism. But, don't do this 1360 * for SubstructureNotify events. The "bind" command doesn't support 1361 * them anyway, and it's easier to filter out these events here than 1362 * in the lower-level functions. 1363 */ 1364 1365 /* 1366 * ...well, except when we use the tkwm patches, in which case we DO 1367 * handle CreateNotify events, so we gotta pass 'em through. 1368 */ 1369 1370 if ((ip.winPtr != None) 1371 && ((mask != SubstructureNotifyMask) 1372 || (eventPtr->type == CreateNotify))) { 1373 TkBindEventProc(winPtr, eventPtr); 1374 } 1375 } 1376 tsdPtr->pendingPtr = ip.nextPtr; 1377 1378 /* 1379 * Release the interpreter for this window so that it can be potentially 1380 * deleted if requested. 1381 */ 1382 1383 releaseInterpreter: 1384 if (interp != NULL) { 1385 Tcl_Release((ClientData) interp); 1386 } 1387 1388 /* 1389 * Release the user_data from the event (if it is a virtual event and the 1390 * field was non-NULL in the first place.) Note that this is done using a 1391 * Tcl_Obj interface, and we set the field back to NULL afterwards out of 1392 * paranoia. Also clean up any cached %A substitutions from key events. 1393 */ 1394 1395 releaseEventResources: 1396 CleanUpTkEvent(eventPtr); 1397} 1398 1399/* 1400 *---------------------------------------------------------------------- 1401 * 1402 * TkEventDeadWindow -- 1403 * 1404 * This function is invoked when it is determined that a window is dead. 1405 * It cleans up event-related information about the window. 1406 * 1407 * Results: 1408 * None. 1409 * 1410 * Side effects: 1411 * Various things get cleaned up and recycled. 1412 * 1413 *---------------------------------------------------------------------- 1414 */ 1415 1416void 1417TkEventDeadWindow( 1418 TkWindow *winPtr) /* Information about the window that is being 1419 * deleted. */ 1420{ 1421 register TkEventHandler *handlerPtr; 1422 register InProgress *ipPtr; 1423 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1424 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1425 1426 /* 1427 * While deleting all the handlers, be careful to check for Tk_HandleEvent 1428 * being about to process one of the deleted handlers. If it is, tell it 1429 * to quit (all of the handlers are being deleted). 1430 */ 1431 1432 while (winPtr->handlerList != NULL) { 1433 handlerPtr = winPtr->handlerList; 1434 winPtr->handlerList = handlerPtr->nextPtr; 1435 for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL; 1436 ipPtr = ipPtr->nextPtr) { 1437 if (ipPtr->nextHandler == handlerPtr) { 1438 ipPtr->nextHandler = NULL; 1439 } 1440 if (ipPtr->winPtr == winPtr) { 1441 ipPtr->winPtr = None; 1442 } 1443 } 1444 ckfree((char *) handlerPtr); 1445 } 1446} 1447 1448/* 1449 *---------------------------------------------------------------------- 1450 * 1451 * TkCurrentTime -- 1452 * 1453 * Try to deduce the current time. "Current time" means the time of the 1454 * event that led to the current code being executed, which means the 1455 * time in the most recently-nested invocation of Tk_HandleEvent. 1456 * 1457 * Results: 1458 * The return value is the time from the current event, or CurrentTime if 1459 * there is no current event or if the current event contains no time. 1460 * 1461 * Side effects: 1462 * None. 1463 * 1464 *---------------------------------------------------------------------- 1465 */ 1466 1467Time 1468TkCurrentTime( 1469 TkDisplay *dispPtr) /* Display for which the time is desired. */ 1470{ 1471 register XEvent *eventPtr; 1472 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1473 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1474 1475 if (tsdPtr->pendingPtr == NULL) { 1476 return dispPtr->lastEventTime; 1477 } 1478 eventPtr = tsdPtr->pendingPtr->eventPtr; 1479 switch (eventPtr->type) { 1480 case ButtonPress: 1481 case ButtonRelease: 1482 return eventPtr->xbutton.time; 1483 case KeyPress: 1484 case KeyRelease: 1485 return eventPtr->xkey.time; 1486 case MotionNotify: 1487 return eventPtr->xmotion.time; 1488 case EnterNotify: 1489 case LeaveNotify: 1490 return eventPtr->xcrossing.time; 1491 case PropertyNotify: 1492 return eventPtr->xproperty.time; 1493 } 1494 return dispPtr->lastEventTime; 1495} 1496 1497/* 1498 *---------------------------------------------------------------------- 1499 * 1500 * Tk_RestrictEvents -- 1501 * 1502 * This function is used to globally restrict the set of events that will 1503 * be dispatched. The restriction is done by filtering all incoming X 1504 * events through a function that determines whether they are to be 1505 * processed immediately, deferred, or discarded. 1506 * 1507 * Results: 1508 * The return value is the previous restriction function in effect, if 1509 * there was one, or NULL if there wasn't. 1510 * 1511 * Side effects: 1512 * From now on, proc will be called to determine whether to process, 1513 * defer or discard each incoming X event. 1514 * 1515 *---------------------------------------------------------------------- 1516 */ 1517 1518Tk_RestrictProc * 1519Tk_RestrictEvents( 1520 Tk_RestrictProc *proc, /* Function to call for each incoming event */ 1521 ClientData arg, /* Arbitrary argument to pass to proc. */ 1522 ClientData *prevArgPtr) /* Place to store information about previous 1523 * argument. */ 1524{ 1525 Tk_RestrictProc *prev; 1526 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1527 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1528 1529 prev = tsdPtr->restrictProc; 1530 *prevArgPtr = tsdPtr->restrictArg; 1531 tsdPtr->restrictProc = proc; 1532 tsdPtr->restrictArg = arg; 1533 return prev; 1534} 1535 1536/* 1537 *---------------------------------------------------------------------- 1538 * 1539 * Tk_CollapseMotionEvents -- 1540 * 1541 * This function controls whether we collapse motion events in a 1542 * particular display or not. 1543 * 1544 * Results: 1545 * The return value is the previous collapse value in effect. 1546 * 1547 * Side effects: 1548 * Filtering of motion events may be changed after calling this. 1549 * 1550 *---------------------------------------------------------------------- 1551 */ 1552 1553int 1554Tk_CollapseMotionEvents( 1555 Display *display, /* Display handling these events. */ 1556 int collapse) /* Boolean value that specifies whether motion 1557 * events should be collapsed. */ 1558{ 1559 TkDisplay *dispPtr = (TkDisplay *) display; 1560 int prev = (dispPtr->flags & TK_DISPLAY_COLLAPSE_MOTION_EVENTS); 1561 1562 if (collapse) { 1563 dispPtr->flags |= TK_DISPLAY_COLLAPSE_MOTION_EVENTS; 1564 } else { 1565 dispPtr->flags &= ~TK_DISPLAY_COLLAPSE_MOTION_EVENTS; 1566 } 1567 return prev; 1568} 1569 1570/* 1571 *---------------------------------------------------------------------- 1572 * 1573 * Tk_QueueWindowEvent -- 1574 * 1575 * Given an X-style window event, this function adds it to the Tcl event 1576 * queue at the given position. This function also performs mouse motion 1577 * event collapsing if possible. 1578 * 1579 * Results: 1580 * None. 1581 * 1582 * Side effects: 1583 * Adds stuff to the event queue, which will eventually be processed. 1584 * 1585 *---------------------------------------------------------------------- 1586 */ 1587 1588void 1589Tk_QueueWindowEvent( 1590 XEvent *eventPtr, /* Event to add to queue. This function copies 1591 * it before adding it to the queue. */ 1592 Tcl_QueuePosition position) /* Where to put it on the queue: 1593 * TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, or 1594 * TCL_QUEUE_MARK. */ 1595{ 1596 TkWindowEvent *wevPtr; 1597 TkDisplay *dispPtr; 1598 1599 /* 1600 * Find our display structure for the event's display. 1601 */ 1602 1603 for (dispPtr = TkGetDisplayList(); ; dispPtr = dispPtr->nextPtr) { 1604 if (dispPtr == NULL) { 1605 return; 1606 } 1607 if (dispPtr->display == eventPtr->xany.display) { 1608 break; 1609 } 1610 } 1611 1612 /* 1613 * Don't filter motion events if the user defaulting to true (1), which 1614 * could be set to false (0) when the user wishes to receive all the 1615 * motion data) 1616 */ 1617 1618 if (!(dispPtr->flags & TK_DISPLAY_COLLAPSE_MOTION_EVENTS)) { 1619 wevPtr = (TkWindowEvent *) ckalloc(sizeof(TkWindowEvent)); 1620 wevPtr->header.proc = WindowEventProc; 1621 wevPtr->event = *eventPtr; 1622 Tcl_QueueEvent(&wevPtr->header, position); 1623 return; 1624 } 1625 1626 if ((dispPtr->delayedMotionPtr != NULL) && (position == TCL_QUEUE_TAIL)) { 1627 if ((eventPtr->type == MotionNotify) && (eventPtr->xmotion.window 1628 == dispPtr->delayedMotionPtr->event.xmotion.window)) { 1629 /* 1630 * The new event is a motion event in the same window as the saved 1631 * motion event. Just replace the saved event with the new one. 1632 */ 1633 1634 dispPtr->delayedMotionPtr->event = *eventPtr; 1635 return; 1636 } else if ((eventPtr->type != GraphicsExpose) 1637 && (eventPtr->type != NoExpose) 1638 && (eventPtr->type != Expose)) { 1639 /* 1640 * The new event may conflict with the saved motion event. Queue 1641 * the saved motion event now so that it will be processed before 1642 * the new event. 1643 */ 1644 1645 Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, position); 1646 dispPtr->delayedMotionPtr = NULL; 1647 Tcl_CancelIdleCall(DelayedMotionProc, (ClientData) dispPtr); 1648 } 1649 } 1650 1651 wevPtr = (TkWindowEvent *) ckalloc(sizeof(TkWindowEvent)); 1652 wevPtr->header.proc = WindowEventProc; 1653 wevPtr->event = *eventPtr; 1654 if ((eventPtr->type == MotionNotify) && (position == TCL_QUEUE_TAIL)) { 1655 /* 1656 * The new event is a motion event so don't queue it immediately; save 1657 * it around in case another motion event arrives that it can be 1658 * collapsed with. 1659 */ 1660 1661 if (dispPtr->delayedMotionPtr != NULL) { 1662 Tcl_Panic("Tk_QueueWindowEvent found unexpected delayed motion event"); 1663 } 1664 dispPtr->delayedMotionPtr = wevPtr; 1665 Tcl_DoWhenIdle(DelayedMotionProc, (ClientData) dispPtr); 1666 } else { 1667 Tcl_QueueEvent(&wevPtr->header, position); 1668 } 1669} 1670 1671/* 1672 *---------------------------------------------------------------------- 1673 * 1674 * TkQueueEventForAllChildren -- 1675 * 1676 * Given an XEvent, recursively queue the event for this window and all 1677 * non-toplevel children of the given window. 1678 * 1679 * Results: 1680 * None. 1681 * 1682 * Side effects: 1683 * Events queued. 1684 * 1685 *---------------------------------------------------------------------- 1686 */ 1687 1688void 1689TkQueueEventForAllChildren( 1690 TkWindow *winPtr, /* Window to which event is sent. */ 1691 XEvent *eventPtr) /* The event to be sent. */ 1692{ 1693 TkWindow *childPtr; 1694 1695 if (!Tk_IsMapped(winPtr)) { 1696 return; 1697 } 1698 1699 eventPtr->xany.window = winPtr->window; 1700 Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL); 1701 1702 childPtr = winPtr->childList; 1703 while (childPtr != NULL) { 1704 if (!Tk_TopWinHierarchy(childPtr)) { 1705 TkQueueEventForAllChildren(childPtr, eventPtr); 1706 } 1707 childPtr = childPtr->nextPtr; 1708 } 1709} 1710 1711/* 1712 *---------------------------------------------------------------------- 1713 * 1714 * WindowEventProc -- 1715 * 1716 * This function is called by Tcl_DoOneEvent when a window event reaches 1717 * the front of the event queue. This function is responsible for 1718 * actually handling the event. 1719 * 1720 * Results: 1721 * Returns 1 if the event was handled, meaning it should be removed from 1722 * the queue. Returns 0 if the event was not handled, meaning it should 1723 * stay on the queue. The event isn't handled if the TCL_WINDOW_EVENTS 1724 * bit isn't set in flags, if a restrict proc prevents the event from 1725 * being handled. 1726 * 1727 * Side effects: 1728 * Whatever the event handlers for the event do. 1729 * 1730 *---------------------------------------------------------------------- 1731 */ 1732 1733static int 1734WindowEventProc( 1735 Tcl_Event *evPtr, /* Event to service. */ 1736 int flags) /* Flags that indicate what events to handle, 1737 * such as TCL_WINDOW_EVENTS. */ 1738{ 1739 TkWindowEvent *wevPtr = (TkWindowEvent *) evPtr; 1740 Tk_RestrictAction result; 1741 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1742 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1743 1744 if (!(flags & TCL_WINDOW_EVENTS)) { 1745 return 0; 1746 } 1747 if (tsdPtr->restrictProc != NULL) { 1748 result = (*tsdPtr->restrictProc)(tsdPtr->restrictArg, &wevPtr->event); 1749 if (result != TK_PROCESS_EVENT) { 1750 if (result == TK_DEFER_EVENT) { 1751 return 0; 1752 } else { 1753 /* 1754 * TK_DELETE_EVENT: return and say we processed the event, 1755 * even though we didn't do anything at all. 1756 */ 1757 1758 CleanUpTkEvent(&wevPtr->event); 1759 return 1; 1760 } 1761 } 1762 } 1763 Tk_HandleEvent(&wevPtr->event); 1764 CleanUpTkEvent(&wevPtr->event); 1765 return 1; 1766} 1767 1768/* 1769 *---------------------------------------------------------------------- 1770 * 1771 * CleanUpTkEvent -- 1772 * 1773 * This function is called to remove and deallocate any information in 1774 * the event which is not directly in the event structure itself. It may 1775 * be called multiple times per event, so it takes care to set the 1776 * cleared pointer fields to NULL afterwards. 1777 * 1778 * Results: 1779 * None. 1780 * 1781 * Side effects: 1782 * Makes the event no longer have any external resources. 1783 * 1784 *---------------------------------------------------------------------- 1785 */ 1786 1787static void 1788CleanUpTkEvent( 1789 XEvent *eventPtr) 1790{ 1791 switch (eventPtr->type) { 1792 case KeyPress: 1793 case KeyRelease: { 1794 TkKeyEvent *kePtr = (TkKeyEvent *) eventPtr; 1795 1796 if (kePtr->charValuePtr != NULL) { 1797 ckfree(kePtr->charValuePtr); 1798 kePtr->charValuePtr = NULL; 1799 kePtr->charValueLen = 0; 1800 } 1801 break; 1802 } 1803 1804 case VirtualEvent: { 1805 XVirtualEvent *vePtr = (XVirtualEvent *) eventPtr; 1806 1807 if (vePtr->user_data != NULL) { 1808 Tcl_DecrRefCount(vePtr->user_data); 1809 vePtr->user_data = NULL; 1810 } 1811 break; 1812 } 1813 } 1814} 1815 1816/* 1817 *---------------------------------------------------------------------- 1818 * 1819 * DelayedMotionProc -- 1820 * 1821 * This function is invoked as an idle handler when a mouse motion event 1822 * has been delayed. It queues the delayed event so that it will finally 1823 * be serviced. 1824 * 1825 * Results: 1826 * None. 1827 * 1828 * Side effects: 1829 * The delayed mouse motion event gets added to the Tcl event queue for 1830 * servicing. 1831 * 1832 *---------------------------------------------------------------------- 1833 */ 1834 1835static void 1836DelayedMotionProc( 1837 ClientData clientData) /* Pointer to display containing a delayed 1838 * motion event to be serviced. */ 1839{ 1840 TkDisplay *dispPtr = (TkDisplay *) clientData; 1841 1842 if (dispPtr->delayedMotionPtr == NULL) { 1843 Tcl_Panic("DelayedMotionProc found no delayed mouse motion event"); 1844 } 1845 Tcl_QueueEvent(&dispPtr->delayedMotionPtr->header, TCL_QUEUE_TAIL); 1846 dispPtr->delayedMotionPtr = NULL; 1847} 1848 1849/* 1850 *---------------------------------------------------------------------- 1851 * 1852 * TkCreateExitHandler -- 1853 * 1854 * Same as Tcl_CreateExitHandler, but private to Tk. 1855 * 1856 * Results: 1857 * None. 1858 * 1859 * Side effects. 1860 * Sets a handler with Tcl_CreateExitHandler if this is the first call. 1861 * 1862 *---------------------------------------------------------------------- 1863 */ 1864 1865void 1866TkCreateExitHandler( 1867 Tcl_ExitProc *proc, /* Function to invoke. */ 1868 ClientData clientData) /* Arbitrary value to pass to proc. */ 1869{ 1870 ExitHandler *exitPtr; 1871 1872 exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler)); 1873 exitPtr->proc = proc; 1874 exitPtr->clientData = clientData; 1875 Tcl_MutexLock(&exitMutex); 1876 1877 /* 1878 * The call to TclInExit() is disabled here. That's a private Tcl routine, 1879 * and calling it is causing some trouble with portability of building Tk. 1880 * We should avoid private Tcl routines generally. 1881 * 1882 * In this case, the TclInExit() call is being used only to prevent a 1883 * Tcl_CreateExitHandler() call when Tcl finalization is in progress. 1884 * That's a situation that shouldn't happen anyway. Recent changes within 1885 * Tcl_Finalize now cause a Tcl_Panic() to happen if exit handlers get 1886 * added after exit handling is complete. By disabling the guard here, 1887 * that panic will serve to help us find the buggy conditions and correct 1888 * them. 1889 * 1890 * We can restore this guard if we find we must (hopefully getting public 1891 * access to TclInExit() if we discover extensions really do need this), 1892 * but during alpha development, this is a good time to dig in and find 1893 * the root causes of finalization bugs. 1894 */ 1895 1896 if (firstExitPtr == NULL/* && !TclInExit()*/) { 1897 Tcl_CreateExitHandler(TkFinalize, NULL); 1898 } 1899 exitPtr->nextPtr = firstExitPtr; 1900 firstExitPtr = exitPtr; 1901 Tcl_MutexUnlock(&exitMutex); 1902} 1903 1904/* 1905 *---------------------------------------------------------------------- 1906 * 1907 * TkDeleteExitHandler -- 1908 * 1909 * Same as Tcl_DeleteExitHandler, but private to Tk. 1910 * 1911 * Results: 1912 * None. 1913 * 1914 * Side effects. 1915 * None. 1916 * 1917 *---------------------------------------------------------------------- 1918 */ 1919 1920void 1921TkDeleteExitHandler( 1922 Tcl_ExitProc *proc, /* Function that was previously registered. */ 1923 ClientData clientData) /* Arbitrary value to pass to proc. */ 1924{ 1925 ExitHandler *exitPtr, *prevPtr; 1926 1927 Tcl_MutexLock(&exitMutex); 1928 for (prevPtr = NULL, exitPtr = firstExitPtr; exitPtr != NULL; 1929 prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) { 1930 if ((exitPtr->proc == proc) 1931 && (exitPtr->clientData == clientData)) { 1932 if (prevPtr == NULL) { 1933 firstExitPtr = exitPtr->nextPtr; 1934 } else { 1935 prevPtr->nextPtr = exitPtr->nextPtr; 1936 } 1937 ckfree((char *) exitPtr); 1938 break; 1939 } 1940 } 1941 Tcl_MutexUnlock(&exitMutex); 1942 return; 1943} 1944 1945/* 1946 *---------------------------------------------------------------------- 1947 * 1948 * TkCreateThreadExitHandler -- 1949 * 1950 * Same as Tcl_CreateThreadExitHandler, but private to Tk. 1951 * 1952 * Results: 1953 * None. 1954 * 1955 * Side effects: 1956 * Proc will be invoked with clientData as argument when the application 1957 * exits. 1958 * 1959 *---------------------------------------------------------------------- 1960 */ 1961 1962void 1963TkCreateThreadExitHandler( 1964 Tcl_ExitProc *proc, /* Function to invoke. */ 1965 ClientData clientData) /* Arbitrary value to pass to proc. */ 1966{ 1967 ExitHandler *exitPtr; 1968 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 1969 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 1970 1971 exitPtr = (ExitHandler *) ckalloc(sizeof(ExitHandler)); 1972 exitPtr->proc = proc; 1973 exitPtr->clientData = clientData; 1974 1975 /* 1976 * See comments in TkCreateExitHandler(). 1977 */ 1978 1979 if (tsdPtr->firstExitPtr == NULL/* && !TclInExit()*/) { 1980 Tcl_CreateThreadExitHandler(TkFinalizeThread, NULL); 1981 } 1982 exitPtr->nextPtr = tsdPtr->firstExitPtr; 1983 tsdPtr->firstExitPtr = exitPtr; 1984} 1985 1986/* 1987 *---------------------------------------------------------------------- 1988 * 1989 * TkDeleteThreadExitHandler -- 1990 * 1991 * Same as Tcl_DeleteThreadExitHandler, but private to Tk. 1992 * 1993 * Results: 1994 * None. 1995 * 1996 * Side effects: 1997 * If there is an exit handler corresponding to proc and clientData then 1998 * it is cancelled; if no such handler exists then nothing happens. 1999 * 2000 *---------------------------------------------------------------------- 2001 */ 2002 2003void 2004TkDeleteThreadExitHandler( 2005 Tcl_ExitProc *proc, /* Function that was previously registered. */ 2006 ClientData clientData) /* Arbitrary value to pass to proc. */ 2007{ 2008 ExitHandler *exitPtr, *prevPtr; 2009 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 2010 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 2011 2012 for (prevPtr = NULL, exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL; 2013 prevPtr = exitPtr, exitPtr = exitPtr->nextPtr) { 2014 if ((exitPtr->proc == proc) 2015 && (exitPtr->clientData == clientData)) { 2016 if (prevPtr == NULL) { 2017 tsdPtr->firstExitPtr = exitPtr->nextPtr; 2018 } else { 2019 prevPtr->nextPtr = exitPtr->nextPtr; 2020 } 2021 ckfree((char *) exitPtr); 2022 return; 2023 } 2024 } 2025} 2026 2027/* 2028 *---------------------------------------------------------------------- 2029 * 2030 * TkFinalize -- 2031 * 2032 * Runs our private exit handlers and removes itself from Tcl. This is 2033 * benificial should we want to protect from dangling pointers should the 2034 * Tk shared library be unloaded prior to Tcl which can happen on windows 2035 * should the process be forcefully exiting from an exception handler. 2036 * 2037 * Results: 2038 * None. 2039 * 2040 * Side effects. 2041 * None. 2042 * 2043 *---------------------------------------------------------------------- 2044 */ 2045 2046void 2047TkFinalize( 2048 ClientData clientData) /* Arbitrary value to pass to proc. */ 2049{ 2050 ExitHandler *exitPtr; 2051 2052 Tcl_DeleteExitHandler(TkFinalize, NULL); 2053 2054 Tcl_MutexLock(&exitMutex); 2055 for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) { 2056 /* 2057 * Be careful to remove the handler from the list before invoking its 2058 * callback. This protects us against double-freeing if the callback 2059 * should call TkDeleteExitHandler on itself. 2060 */ 2061 2062 firstExitPtr = exitPtr->nextPtr; 2063 Tcl_MutexUnlock(&exitMutex); 2064 (*exitPtr->proc)(exitPtr->clientData); 2065 ckfree((char *) exitPtr); 2066 Tcl_MutexLock(&exitMutex); 2067 } 2068 firstExitPtr = NULL; 2069 Tcl_MutexUnlock(&exitMutex); 2070} 2071 2072/* 2073 *---------------------------------------------------------------------- 2074 * 2075 * TkFinalizeThread -- 2076 * 2077 * Runs our private thread exit handlers and removes itself from Tcl. 2078 * This is benificial should we want to protect from dangling pointers 2079 * should the Tk shared library be unloaded prior to Tcl which can happen 2080 * on Windows should the process be forcefully exiting from an exception 2081 * handler. 2082 * 2083 * Results: 2084 * None. 2085 * 2086 * Side effects. 2087 * None. 2088 * 2089 *---------------------------------------------------------------------- 2090 */ 2091 2092void 2093TkFinalizeThread( 2094 ClientData clientData) /* Arbitrary value to pass to proc. */ 2095{ 2096 ExitHandler *exitPtr; 2097 ThreadSpecificData *tsdPtr = (ThreadSpecificData *) 2098 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); 2099 2100 Tcl_DeleteThreadExitHandler(TkFinalizeThread, NULL); 2101 2102 if (tsdPtr != NULL) { 2103 tsdPtr->inExit = 1; 2104 2105 for (exitPtr = tsdPtr->firstExitPtr; exitPtr != NULL; 2106 exitPtr = tsdPtr->firstExitPtr) { 2107 /* 2108 * Be careful to remove the handler from the list before invoking 2109 * its callback. This protects us against double-freeing if the 2110 * callback should call TkDeleteThreadExitHandler on itself. 2111 */ 2112 2113 tsdPtr->firstExitPtr = exitPtr->nextPtr; 2114 (*exitPtr->proc)(exitPtr->clientData); 2115 ckfree((char *) exitPtr); 2116 } 2117 } 2118} 2119 2120/* 2121 *---------------------------------------------------------------------- 2122 * 2123 * Tk_MainLoop -- 2124 * 2125 * Call Tcl_DoOneEvent over and over again in an infinite loop as long as 2126 * there exist any main windows. 2127 * 2128 * Results: 2129 * None. 2130 * 2131 * Side effects: 2132 * Arbitrary; depends on handlers for events. 2133 * 2134 *---------------------------------------------------------------------- 2135 */ 2136 2137void 2138Tk_MainLoop(void) 2139{ 2140 while (Tk_GetNumMainWindows() > 0) { 2141 Tcl_DoOneEvent(0); 2142 } 2143} 2144 2145/* 2146 * Local Variables: 2147 * mode: c 2148 * c-basic-offset: 4 2149 * fill-column: 78 2150 * End: 2151 */ 2152