1/* 2 * tkFocus.c -- 3 * 4 * This file contains functions that manage the input focus for Tk. 5 * 6 * Copyright (c) 1990-1994 The Regents of the University of California. 7 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id$ 13 */ 14 15#include "tkInt.h" 16 17/* 18 * For each top-level window that has ever received the focus, there is a 19 * record of the following type: 20 */ 21 22typedef struct TkToplevelFocusInfo { 23 TkWindow *topLevelPtr; /* Information about top-level window. */ 24 TkWindow *focusWinPtr; /* The next time the focus comes to this 25 * top-level, it will be given to this 26 * window. */ 27 struct TkToplevelFocusInfo *nextPtr; 28 /* Next in list of all toplevel focus records 29 * for a given application. */ 30} ToplevelFocusInfo; 31 32/* 33 * One of the following structures exists for each display used by each 34 * application. These are linked together from the TkMainInfo structure. 35 * These structures are needed because it isn't sufficient to store a single 36 * piece of focus information in each display or in each application: we need 37 * the cross-product. There needs to be separate information for each display, 38 * because it's possible to have multiple focus windows active simultaneously 39 * on different displays. There also needs to be separate information for each 40 * application, because of embedding: if an embedded application has the 41 * focus, its container application also has the focus. Thus we keep a list of 42 * structures for each application: the same display can appear in structures 43 * for several applications at once. 44 */ 45 46typedef struct TkDisplayFocusInfo { 47 TkDisplay *dispPtr; /* Display that this information pertains 48 * to. */ 49 struct TkWindow *focusWinPtr; 50 /* Window that currently has the focus for 51 * this application on this display, or NULL 52 * if none. */ 53 struct TkWindow *focusOnMapPtr; 54 /* This points to a toplevel window that is 55 * supposed to receive the X input focus as 56 * soon as it is mapped (needed to handle the 57 * fact that X won't allow the focus on an 58 * unmapped window). NULL means no delayed 59 * focus op in progress for this display. */ 60 int forceFocus; /* Associated with focusOnMapPtr: non-zero 61 * means claim the focus even if some other 62 * application currently has it. */ 63 unsigned long focusSerial; /* Serial number of last request this 64 * application made to change the focus on 65 * this display. Used to identify stale focus 66 * notifications coming from the X server. */ 67 struct TkDisplayFocusInfo *nextPtr; 68 /* Next in list of all display focus records 69 * for a given application. */ 70} DisplayFocusInfo; 71 72/* 73 * The following magic value is stored in the "send_event" field of FocusIn 74 * and FocusOut events that are generated in this file. This allows us to 75 * separate "real" events coming from the server from those that we generated. 76 */ 77 78#define GENERATED_EVENT_MAGIC ((Bool) 0x547321ac) 79 80/* 81 * Debugging support... 82 */ 83 84#define DEBUG(dispPtr, arguments) \ 85 if ((dispPtr)->focusDebug) { \ 86 printf arguments; \ 87 } 88 89/* 90 * Forward declarations for functions defined in this file: 91 */ 92 93static DisplayFocusInfo*FindDisplayFocusInfo(TkMainInfo *mainPtr, 94 TkDisplay *dispPtr); 95static void FocusMapProc(ClientData clientData, XEvent *eventPtr); 96static void GenerateFocusEvents(TkWindow *sourcePtr, 97 TkWindow *destPtr); 98 99/* 100 *-------------------------------------------------------------- 101 * 102 * Tk_FocusObjCmd -- 103 * 104 * This function is invoked to process the "focus" Tcl command. See the 105 * user documentation for details on what it does. 106 * 107 * Results: 108 * A standard Tcl result. 109 * 110 * Side effects: 111 * See the user documentation. 112 * 113 *-------------------------------------------------------------- 114 */ 115 116int 117Tk_FocusObjCmd( 118 ClientData clientData, /* Main window associated with interpreter. */ 119 Tcl_Interp *interp, /* Current interpreter. */ 120 int objc, /* Number of arguments. */ 121 Tcl_Obj *CONST objv[]) /* Argument objects. */ 122{ 123 static CONST char *focusOptions[] = { 124 "-displayof", "-force", "-lastfor", NULL 125 }; 126 Tk_Window tkwin = (Tk_Window) clientData; 127 TkWindow *winPtr = (TkWindow *) clientData; 128 TkWindow *newPtr, *focusWinPtr, *topLevelPtr; 129 ToplevelFocusInfo *tlFocusPtr; 130 char *windowName; 131 int index; 132 133 /* 134 * If invoked with no arguments, just return the current focus window. 135 */ 136 137 if (objc == 1) { 138 focusWinPtr = TkGetFocusWin(winPtr); 139 if (focusWinPtr != NULL) { 140 Tcl_SetResult(interp, focusWinPtr->pathName, TCL_STATIC); 141 } 142 return TCL_OK; 143 } 144 145 /* 146 * If invoked with a single argument beginning with "." then focus on that 147 * window. 148 */ 149 150 if (objc == 2) { 151 windowName = Tcl_GetString(objv[1]); 152 153 /* 154 * The empty string case exists for backwards compatibility. 155 */ 156 157 if (windowName[0] == '\0') { 158 return TCL_OK; 159 } 160 if (windowName[0] == '.') { 161 newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); 162 if (newPtr == NULL) { 163 return TCL_ERROR; 164 } 165 if (!(newPtr->flags & TK_ALREADY_DEAD)) { 166 TkSetFocusWin(newPtr, 0); 167 } 168 return TCL_OK; 169 } 170 } 171 172 /* 173 * We have a subcommand to parse and act upon. 174 */ 175 176 if (Tcl_GetIndexFromObj(interp, objv[1], focusOptions, "option", 0, 177 &index) != TCL_OK) { 178 return TCL_ERROR; 179 } 180 if (objc != 3) { 181 Tcl_WrongNumArgs(interp, 2, objv, "window"); 182 return TCL_ERROR; 183 } 184 switch (index) { 185 case 0: /* -displayof */ 186 windowName = Tcl_GetString(objv[2]); 187 newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); 188 if (newPtr == NULL) { 189 return TCL_ERROR; 190 } 191 newPtr = TkGetFocusWin(newPtr); 192 if (newPtr != NULL) { 193 Tcl_SetResult(interp, newPtr->pathName, TCL_STATIC); 194 } 195 break; 196 case 1: /* -force */ 197 windowName = Tcl_GetString(objv[2]); 198 199 /* 200 * The empty string case exists for backwards compatibility. 201 */ 202 203 if (windowName[0] == '\0') { 204 return TCL_OK; 205 } 206 newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); 207 if (newPtr == NULL) { 208 return TCL_ERROR; 209 } 210 TkSetFocusWin(newPtr, 1); 211 break; 212 case 2: /* -lastfor */ 213 windowName = Tcl_GetString(objv[2]); 214 newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); 215 if (newPtr == NULL) { 216 return TCL_ERROR; 217 } 218 for (topLevelPtr = newPtr; topLevelPtr != NULL; 219 topLevelPtr = topLevelPtr->parentPtr) { 220 if (!(topLevelPtr->flags & TK_TOP_HIERARCHY)) { 221 continue; 222 } 223 for (tlFocusPtr = newPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; 224 tlFocusPtr = tlFocusPtr->nextPtr) { 225 if (tlFocusPtr->topLevelPtr == topLevelPtr) { 226 Tcl_SetResult(interp, 227 tlFocusPtr->focusWinPtr->pathName, TCL_STATIC); 228 return TCL_OK; 229 } 230 } 231 Tcl_SetResult(interp, topLevelPtr->pathName, TCL_STATIC); 232 return TCL_OK; 233 } 234 break; 235 default: 236 Tcl_Panic("bad const entries to focusOptions in focus command"); 237 } 238 return TCL_OK; 239} 240 241/* 242 *-------------------------------------------------------------- 243 * 244 * TkFocusFilterEvent -- 245 * 246 * This function is invoked by Tk_HandleEvent when it encounters a 247 * FocusIn, FocusOut, Enter, or Leave event. 248 * 249 * Results: 250 * A return value of 1 means that Tk_HandleEvent should process the event 251 * normally (i.e. event handlers should be invoked). A return value of 0 252 * means that this event should be ignored. 253 * 254 * Side effects: 255 * Additional events may be generated, and the focus may switch. 256 * 257 *-------------------------------------------------------------- 258 */ 259 260int 261TkFocusFilterEvent( 262 TkWindow *winPtr, /* Window that focus event is directed to. */ 263 XEvent *eventPtr) /* FocusIn, FocusOut, Enter, or Leave 264 * event. */ 265{ 266 /* 267 * Design notes: the window manager and X server work together to transfer 268 * the focus among top-level windows. This function takes care of 269 * transferring the focus from a top-level or wrapper window to the actual 270 * window within that top-level that has the focus. We do this by 271 * synthesizing X events to move the focus around. None of the FocusIn and 272 * FocusOut events generated by X are ever used outside of this function; 273 * only the synthesized events get through to the rest of the application. 274 * At one point (e.g. Tk4.0b1) Tk used to call X to move the focus from a 275 * top-level to one of its descendants, then just pass through the events 276 * generated by X. This approach didn't work very well, for a variety of 277 * reasons. For example, if X generates the events they go at the back of 278 * the event queue, which could cause problems if other things have 279 * already happened, such as moving the focus to yet another window. 280 */ 281 282 ToplevelFocusInfo *tlFocusPtr; 283 DisplayFocusInfo *displayFocusPtr; 284 TkDisplay *dispPtr = winPtr->dispPtr; 285 TkWindow *newFocusPtr; 286 int retValue, delta; 287 288 /* 289 * If this was a generated event, just turn off the generated flag and 290 * pass the event through to Tk bindings. 291 */ 292 293 if (eventPtr->xfocus.send_event == GENERATED_EVENT_MAGIC) { 294 eventPtr->xfocus.send_event = 0; 295 return 1; 296 } 297 298 /* 299 * Check for special events generated by embedded applications to request 300 * the input focus. If this is one of those events, make the change in 301 * focus and return without any additional processing of the event (note: 302 * the "detail" field of the event indicates whether to claim the focus 303 * even if we don't already have it). 304 */ 305 306 if ((eventPtr->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS) 307 && (eventPtr->type == FocusIn)) { 308 TkSetFocusWin(winPtr, eventPtr->xfocus.detail); 309 return 0; 310 } 311 312 /* 313 * This was not a generated event. We'll return 1 (so that the event will 314 * be processed) if it's an Enter or Leave event, and 0 (so that the event 315 * won't be processed) if it's a FocusIn or FocusOut event. 316 */ 317 318 retValue = 0; 319 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); 320 if (eventPtr->type == FocusIn) { 321 /* 322 * Skip FocusIn events that cause confusion 323 * NotifyVirtual and NotifyNonlinearVirtual - Virtual events occur on 324 * windows in between the origin and destination of the focus 325 * change. For FocusIn we may see this when focus goes into an 326 * embedded child. We don't care about this, although we may end 327 * up getting a NotifyPointer later. 328 * NotifyInferior - focus is coming to us from an embedded child. When 329 * focus is on an embeded focus, we still think we have the 330 * focus, too, so this message doesn't change our state. 331 * NotifyPointerRoot - should never happen because this is sent to the 332 * root window. 333 * 334 * Interesting FocusIn events are 335 * NotifyAncestor - focus is coming from our parent, probably the root. 336 * NotifyNonlinear - focus is coming from a different branch, probably 337 * another toplevel. 338 * NotifyPointer - implicit focus because of the mouse position. This 339 * is only interesting on toplevels, when it means that the focus 340 * has been set to the root window but the mouse is over this 341 * toplevel. We take the focus implicitly (probably no window 342 * manager) 343 */ 344 345 if ((eventPtr->xfocus.detail == NotifyVirtual) 346 || (eventPtr->xfocus.detail == NotifyNonlinearVirtual) 347 || (eventPtr->xfocus.detail == NotifyPointerRoot) 348 || (eventPtr->xfocus.detail == NotifyInferior)) { 349 return retValue; 350 } 351 } else if (eventPtr->type == FocusOut) { 352 /* 353 * Skip FocusOut events that cause confusion. 354 * NotifyPointer - the pointer is in us or a child, and we are losing 355 * focus because of an XSetInputFocus. Other focus events will 356 * set our state properly. 357 * NotifyPointerRoot - should never happen because this is sent to the 358 * root window. 359 * NotifyInferior - focus leaving us for an embedded child. We retain 360 * a notion of focus when an embedded child has focus. 361 * 362 * Interesting events are: 363 * NotifyAncestor - focus is going to root. 364 * NotifyNonlinear - focus is going to another branch, probably 365 * another toplevel. 366 * NotifyVirtual, NotifyNonlinearVirtual - focus is passing through, 367 * and we need to make sure we track this. 368 */ 369 370 if ((eventPtr->xfocus.detail == NotifyPointer) 371 || (eventPtr->xfocus.detail == NotifyPointerRoot) 372 || (eventPtr->xfocus.detail == NotifyInferior)) { 373 return retValue; 374 } 375 } else { 376 retValue = 1; 377 if (eventPtr->xcrossing.detail == NotifyInferior) { 378 return retValue; 379 } 380 } 381 382 /* 383 * If winPtr isn't a top-level window than just ignore the event. 384 */ 385 386 winPtr = TkWmFocusToplevel(winPtr); 387 if (winPtr == NULL) { 388 return retValue; 389 } 390 391 /* 392 * If there is a grab in effect and this window is outside the grabbed 393 * tree, then ignore the event. 394 */ 395 396 if (TkGrabState(winPtr) == TK_GRAB_EXCLUDED) { 397 return retValue; 398 } 399 400 /* 401 * It is possible that there were outstanding FocusIn and FocusOut events 402 * on their way to us at the time the focus was changed internally with 403 * the "focus" command. If so, these events could potentially cause us to 404 * lose the focus (switch it to the window of the last FocusIn event) even 405 * though the focus change occurred after those events. The following code 406 * detects this and ignores the stale events. 407 * 408 * Note: the focusSerial is only generated by TkpChangeFocus, whereas in 409 * Tk 4.2 there was always a nop marker generated. 410 */ 411 412 delta = eventPtr->xfocus.serial - displayFocusPtr->focusSerial; 413 if (delta < 0) { 414 return retValue; 415 } 416 417 /* 418 * Find the ToplevelFocusInfo structure for the window, and make a new one 419 * if there isn't one already. 420 */ 421 422 for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; 423 tlFocusPtr = tlFocusPtr->nextPtr) { 424 if (tlFocusPtr->topLevelPtr == winPtr) { 425 break; 426 } 427 } 428 if (tlFocusPtr == NULL) { 429 tlFocusPtr = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo)); 430 tlFocusPtr->topLevelPtr = tlFocusPtr->focusWinPtr = winPtr; 431 tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; 432 winPtr->mainPtr->tlFocusPtr = tlFocusPtr; 433 } 434 newFocusPtr = tlFocusPtr->focusWinPtr; 435 436 /* 437 * Ignore event if newFocus window is already dead! 438 */ 439 440 if (newFocusPtr->flags & TK_ALREADY_DEAD) { 441 return retValue; 442 } 443 444 if (eventPtr->type == FocusIn) { 445 GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr); 446 displayFocusPtr->focusWinPtr = newFocusPtr; 447 dispPtr->focusPtr = newFocusPtr; 448 449 /* 450 * NotifyPointer gets set when the focus has been set to the root 451 * window but we have the pointer. We'll treat this like an implicit 452 * focus in event so that upon Leave events we release focus. 453 */ 454 455 if (!(winPtr->flags & TK_EMBEDDED)) { 456 if (eventPtr->xfocus.detail == NotifyPointer) { 457 dispPtr->implicitWinPtr = winPtr; 458 } else { 459 dispPtr->implicitWinPtr = NULL; 460 } 461 } 462 } else if (eventPtr->type == FocusOut) { 463 GenerateFocusEvents(displayFocusPtr->focusWinPtr, NULL); 464 465 /* 466 * Reset dispPtr->focusPtr, but only if it currently is the same as 467 * this application's focusWinPtr: this check is needed to handle 468 * embedded applications in the same process. 469 */ 470 471 if (dispPtr->focusPtr == displayFocusPtr->focusWinPtr) { 472 dispPtr->focusPtr = NULL; 473 } 474 displayFocusPtr->focusWinPtr = NULL; 475 } else if (eventPtr->type == EnterNotify) { 476 /* 477 * If there is no window manager, or if the window manager isn't 478 * moving the focus around (e.g. the disgusting "NoTitleFocus" option 479 * has been selected in twm), then we won't get FocusIn or FocusOut 480 * events. Instead, the "focus" field will be set in an Enter event to 481 * indicate that we've already got the focus when the mouse enters the 482 * window (even though we didn't get a FocusIn event). Watch for this 483 * and grab the focus when it happens. Note: if this is an embedded 484 * application then don't accept the focus implicitly like this; the 485 * container application will give us the focus explicitly if it wants 486 * us to have it. 487 */ 488 489 if (eventPtr->xcrossing.focus && 490 (displayFocusPtr->focusWinPtr == NULL) 491 && !(winPtr->flags & TK_EMBEDDED)) { 492 DEBUG(dispPtr, 493 ("Focussed implicitly on %s\n", newFocusPtr->pathName)); 494 495 GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr); 496 displayFocusPtr->focusWinPtr = newFocusPtr; 497 dispPtr->implicitWinPtr = winPtr; 498 dispPtr->focusPtr = newFocusPtr; 499 } 500 } else if (eventPtr->type == LeaveNotify) { 501 /* 502 * If the pointer just left a window for which we automatically 503 * claimed the focus on enter, move the focus back to the root 504 * window, where it was before we claimed it above. Note: 505 * dispPtr->implicitWinPtr may not be the same as 506 * displayFocusPtr->focusWinPtr (e.g. because the "focus" command 507 * was used to redirect the focus after it arrived at 508 * dispPtr->implicitWinPtr)!! In addition, we generate events 509 * because the window manager won't give us a FocusOut event when 510 * we focus on the root. 511 */ 512 513 if ((dispPtr->implicitWinPtr != NULL) 514 && !(winPtr->flags & TK_EMBEDDED)) { 515 DEBUG(dispPtr, ("Defocussed implicit Async\n")); 516 GenerateFocusEvents(displayFocusPtr->focusWinPtr, NULL); 517 XSetInputFocus(dispPtr->display, PointerRoot, RevertToPointerRoot, 518 CurrentTime); 519 displayFocusPtr->focusWinPtr = NULL; 520 dispPtr->implicitWinPtr = NULL; 521 } 522 } 523 return retValue; 524} 525 526/* 527 *---------------------------------------------------------------------- 528 * 529 * TkSetFocusWin -- 530 * 531 * This function is invoked to change the focus window for a given 532 * display in a given application. 533 * 534 * Results: 535 * None. 536 * 537 * Side effects: 538 * Event handlers may be invoked to process the change of 539 * focus. 540 * 541 *---------------------------------------------------------------------- 542 */ 543 544void 545TkSetFocusWin( 546 TkWindow *winPtr, /* Window that is to be the new focus for its 547 * display and application. */ 548 int force) /* If non-zero, set the X focus to this window 549 * even if the application doesn't currently 550 * have the X focus. */ 551{ 552 ToplevelFocusInfo *tlFocusPtr; 553 DisplayFocusInfo *displayFocusPtr; 554 TkWindow *topLevelPtr; 555 int allMapped, serial; 556 557 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); 558 559 /* 560 * If force is set, we should make sure we grab the focus regardless of 561 * the current focus window since under Windows, we may need to take 562 * control away from another application. 563 */ 564 565 if (winPtr == displayFocusPtr->focusWinPtr && !force) { 566 return; 567 } 568 569 /* 570 * Find the top-level window for winPtr, then find (or create) a record 571 * for the top-level. Also see whether winPtr and all its ancestors are 572 * mapped. 573 */ 574 575 allMapped = 1; 576 for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr) { 577 if (topLevelPtr == NULL) { 578 /* 579 * The window is being deleted. No point in worrying about giving 580 * it the focus. 581 */ 582 583 return; 584 } 585 if (!(topLevelPtr->flags & TK_MAPPED)) { 586 allMapped = 0; 587 } 588 if (topLevelPtr->flags & TK_TOP_HIERARCHY) { 589 break; 590 } 591 } 592 593 /* 594 * If the new focus window isn't mapped, then we can't focus on it (X will 595 * generate an error, for example). Instead, create an event handler that 596 * will set the focus to this window once it gets mapped. At the same 597 * time, delete any old handler that might be around; it's no longer 598 * relevant. 599 */ 600 601 if (displayFocusPtr->focusOnMapPtr != NULL) { 602 Tk_DeleteEventHandler( 603 (Tk_Window) displayFocusPtr->focusOnMapPtr, 604 StructureNotifyMask, FocusMapProc, 605 (ClientData) displayFocusPtr->focusOnMapPtr); 606 displayFocusPtr->focusOnMapPtr = NULL; 607 } 608 if (!allMapped) { 609 Tk_CreateEventHandler((Tk_Window) winPtr, 610 VisibilityChangeMask, FocusMapProc, 611 (ClientData) winPtr); 612 displayFocusPtr->focusOnMapPtr = winPtr; 613 displayFocusPtr->forceFocus = force; 614 return; 615 } 616 617 for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; 618 tlFocusPtr = tlFocusPtr->nextPtr) { 619 if (tlFocusPtr->topLevelPtr == topLevelPtr) { 620 break; 621 } 622 } 623 if (tlFocusPtr == NULL) { 624 tlFocusPtr = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo)); 625 tlFocusPtr->topLevelPtr = topLevelPtr; 626 tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; 627 winPtr->mainPtr->tlFocusPtr = tlFocusPtr; 628 } 629 tlFocusPtr->focusWinPtr = winPtr; 630 631 /* 632 * Reset the window system's focus window and generate focus events, with 633 * two special cases: 634 * 635 * 1. If the application is embedded and doesn't currently have the focus, 636 * don't set the focus directly. Instead, see if the embedding code can 637 * claim the focus from the enclosing container. 638 * 2. Otherwise, if the application doesn't currently have the focus, 639 * don't change the window system's focus unless it was already in this 640 * application or "force" was specified. 641 */ 642 643 if ((topLevelPtr->flags & TK_EMBEDDED) 644 && (displayFocusPtr->focusWinPtr == NULL)) { 645 TkpClaimFocus(topLevelPtr, force); 646 } else if ((displayFocusPtr->focusWinPtr != NULL) || force) { 647 /* 648 * Generate events to shift focus between Tk windows. We do this 649 * regardless of what TkpChangeFocus does with the real X focus so 650 * that Tk widgets track focus commands when there is no window 651 * manager. GenerateFocusEvents will set up a serial number marker so 652 * we discard focus events that are triggered by the ChangeFocus. 653 */ 654 655 serial = TkpChangeFocus(TkpGetWrapperWindow(topLevelPtr), force); 656 if (serial != 0) { 657 displayFocusPtr->focusSerial = serial; 658 } 659 GenerateFocusEvents(displayFocusPtr->focusWinPtr, winPtr); 660 displayFocusPtr->focusWinPtr = winPtr; 661 winPtr->dispPtr->focusPtr = winPtr; 662 } 663} 664 665/* 666 *---------------------------------------------------------------------- 667 * 668 * TkGetFocusWin -- 669 * 670 * Given a window, this function returns the current focus window for its 671 * application and display. 672 * 673 * Results: 674 * The return value is a pointer to the window that currently has the 675 * input focus for the specified application and display, or NULL if 676 * none. 677 * 678 * Side effects: 679 * None. 680 * 681 *---------------------------------------------------------------------- 682 */ 683 684TkWindow * 685TkGetFocusWin( 686 TkWindow *winPtr) /* Window that selects an application and a 687 * display. */ 688{ 689 DisplayFocusInfo *displayFocusPtr; 690 691 if (winPtr == NULL) { 692 return NULL; 693 } 694 695 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); 696 return displayFocusPtr->focusWinPtr; 697} 698 699/* 700 *---------------------------------------------------------------------- 701 * 702 * TkFocusKeyEvent -- 703 * 704 * Given a window and a key press or release event that arrived for the 705 * window, use information about the keyboard focus to compute which 706 * window should really get the event. In addition, update the event to 707 * refer to its new window. 708 * 709 * Results: 710 * The return value is a pointer to the window that has the input focus 711 * in winPtr's application, or NULL if winPtr's application doesn't have 712 * the input focus. If a non-NULL value is returned, eventPtr will be 713 * updated to refer properly to the focus window. 714 * 715 * Side effects: 716 * None. 717 * 718 *---------------------------------------------------------------------- 719 */ 720 721TkWindow * 722TkFocusKeyEvent( 723 TkWindow *winPtr, /* Window that selects an application and a 724 * display. */ 725 XEvent *eventPtr) /* X event to redirect (should be KeyPress or 726 * KeyRelease). */ 727{ 728 DisplayFocusInfo *displayFocusPtr; 729 TkWindow *focusWinPtr; 730 int focusX, focusY, vRootX, vRootY, vRootWidth, vRootHeight; 731 732 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); 733 focusWinPtr = displayFocusPtr->focusWinPtr; 734 735 /* 736 * The code below is a debugging aid to make sure that dispPtr->focusPtr 737 * is kept properly in sync with the "truth", which is the value in 738 * displayFocusPtr->focusWinPtr. 739 */ 740 741#ifdef TCL_MEM_DEBUG 742 if (focusWinPtr != winPtr->dispPtr->focusPtr) { 743 printf("TkFocusKeyEvent found dispPtr->focusPtr out of sync:\n"); 744 printf("expected %s, got %s\n", 745 (focusWinPtr != NULL) ? focusWinPtr->pathName : "??", 746 (winPtr->dispPtr->focusPtr != NULL) ? 747 winPtr->dispPtr->focusPtr->pathName : "??"); 748 } 749#endif 750 751 if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) { 752 /* 753 * Map the x and y coordinates to make sense in the context of the 754 * focus window, if possible (make both -1 if the map-from and map-to 755 * windows don't share the same screen). 756 */ 757 758 if ((focusWinPtr->display != winPtr->display) 759 || (focusWinPtr->screenNum != winPtr->screenNum)) { 760 eventPtr->xkey.x = -1; 761 eventPtr->xkey.y = -1; 762 } else { 763 Tk_GetVRootGeometry((Tk_Window) focusWinPtr, &vRootX, &vRootY, 764 &vRootWidth, &vRootHeight); 765 Tk_GetRootCoords((Tk_Window) focusWinPtr, &focusX, &focusY); 766 eventPtr->xkey.x = eventPtr->xkey.x_root - vRootX - focusX; 767 eventPtr->xkey.y = eventPtr->xkey.y_root - vRootY - focusY; 768 } 769 eventPtr->xkey.window = focusWinPtr->window; 770 return focusWinPtr; 771 } 772 773 /* 774 * The event doesn't belong to us. Perhaps, due to embedding, it really 775 * belongs to someone else. Give the embedding code a chance to redirect 776 * the event. 777 */ 778 779 TkpRedirectKeyEvent(winPtr, eventPtr); 780 return NULL; 781} 782 783/* 784 *---------------------------------------------------------------------- 785 * 786 * TkFocusDeadWindow -- 787 * 788 * This function is invoked when it is determined that a window is dead. 789 * It cleans up focus-related information about the window. 790 * 791 * Results: 792 * None. 793 * 794 * Side effects: 795 * Various things get cleaned up and recycled. 796 * 797 *---------------------------------------------------------------------- 798 */ 799 800void 801TkFocusDeadWindow( 802 register TkWindow *winPtr) /* Information about the window that is being 803 * deleted. */ 804{ 805 ToplevelFocusInfo *tlFocusPtr, *prevPtr; 806 DisplayFocusInfo *displayFocusPtr; 807 TkDisplay *dispPtr = winPtr->dispPtr; 808 809 /* 810 * Certain special windows like those used for send and clipboard have no 811 * mainPtr. 812 */ 813 814 if (winPtr->mainPtr == NULL) { 815 return; 816 } 817 818 /* 819 * Search for focus records that refer to this window either as the 820 * top-level window or the current focus window. 821 */ 822 823 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); 824 for (prevPtr = NULL, tlFocusPtr = winPtr->mainPtr->tlFocusPtr; 825 tlFocusPtr != NULL; 826 prevPtr = tlFocusPtr, tlFocusPtr = tlFocusPtr->nextPtr) { 827 if (winPtr == tlFocusPtr->topLevelPtr) { 828 /* 829 * The top-level window is the one being deleted: free the focus 830 * record and release the focus back to PointerRoot if we acquired 831 * it implicitly. 832 */ 833 834 if (dispPtr->implicitWinPtr == winPtr) { 835 DEBUG(dispPtr, ("releasing focus to root after %s died\n", 836 tlFocusPtr->topLevelPtr->pathName)); 837 dispPtr->implicitWinPtr = NULL; 838 displayFocusPtr->focusWinPtr = NULL; 839 dispPtr->focusPtr = NULL; 840 } 841 if (displayFocusPtr->focusWinPtr == tlFocusPtr->focusWinPtr) { 842 displayFocusPtr->focusWinPtr = NULL; 843 dispPtr->focusPtr = NULL; 844 } 845 if (prevPtr == NULL) { 846 winPtr->mainPtr->tlFocusPtr = tlFocusPtr->nextPtr; 847 } else { 848 prevPtr->nextPtr = tlFocusPtr->nextPtr; 849 } 850 ckfree((char *) tlFocusPtr); 851 break; 852 } else if (winPtr == tlFocusPtr->focusWinPtr) { 853 /* 854 * The deleted window had the focus for its top-level: move the 855 * focus to the top-level itself. 856 */ 857 858 tlFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr; 859 if ((displayFocusPtr->focusWinPtr == winPtr) 860 && !(tlFocusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) { 861 DEBUG(dispPtr, ("forwarding focus to %s after %s died\n", 862 tlFocusPtr->topLevelPtr->pathName, winPtr->pathName)); 863 GenerateFocusEvents(displayFocusPtr->focusWinPtr, 864 tlFocusPtr->topLevelPtr); 865 displayFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr; 866 dispPtr->focusPtr = tlFocusPtr->topLevelPtr; 867 } 868 break; 869 } 870 } 871 872 /* 873 * Occasionally, things can become unsynchronized. Move them back into 874 * synch now. [Bug 2496114] 875 */ 876 877 if (displayFocusPtr->focusWinPtr == winPtr) { 878 DEBUG(dispPtr, ("focus cleared after %s died\n", winPtr->pathName)); 879 displayFocusPtr->focusWinPtr = NULL; 880 } 881 882 if (displayFocusPtr->focusOnMapPtr == winPtr) { 883 displayFocusPtr->focusOnMapPtr = NULL; 884 } 885} 886 887/* 888 *---------------------------------------------------------------------- 889 * 890 * GenerateFocusEvents -- 891 * 892 * This function is called to create FocusIn and FocusOut events to move 893 * the input focus from one window to another. 894 * 895 * Results: 896 * None. 897 * 898 * Side effects: 899 * FocusIn and FocusOut events are generated. 900 * 901 *---------------------------------------------------------------------- 902 */ 903 904static void 905GenerateFocusEvents( 906 TkWindow *sourcePtr, /* Window that used to have the focus (may be 907 * NULL). */ 908 TkWindow *destPtr) /* New window to have the focus (may be 909 * NULL). */ 910{ 911 XEvent event; 912 TkWindow *winPtr; 913 914 winPtr = sourcePtr; 915 if (winPtr == NULL) { 916 winPtr = destPtr; 917 if (winPtr == NULL) { 918 return; 919 } 920 } 921 922 event.xfocus.serial = LastKnownRequestProcessed(winPtr->display); 923 event.xfocus.send_event = GENERATED_EVENT_MAGIC; 924 event.xfocus.display = winPtr->display; 925 event.xfocus.mode = NotifyNormal; 926 TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn, 927 TCL_QUEUE_MARK); 928} 929 930/* 931 *---------------------------------------------------------------------- 932 * 933 * FocusMapProc -- 934 * 935 * This function is called as an event handler for VisibilityNotify 936 * events, if a window receives the focus at a time when its toplevel 937 * isn't mapped. The function is needed because X won't allow the focus 938 * to be set to an unmapped window; we detect when the toplevel is mapped 939 * and set the focus to it then. 940 * 941 * Results: 942 * None. 943 * 944 * Side effects: 945 * If this is a map event, the focus gets set to the toplevel given by 946 * clientData. 947 * 948 *---------------------------------------------------------------------- 949 */ 950 951static void 952FocusMapProc( 953 ClientData clientData, /* Toplevel window. */ 954 XEvent *eventPtr) /* Information about event. */ 955{ 956 TkWindow *winPtr = (TkWindow *) clientData; 957 DisplayFocusInfo *displayFocusPtr; 958 959 if (eventPtr->type == VisibilityNotify) { 960 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, 961 winPtr->dispPtr); 962 DEBUG(winPtr->dispPtr, ("auto-focussing on %s, force %d\n", 963 winPtr->pathName, displayFocusPtr->forceFocus)); 964 Tk_DeleteEventHandler((Tk_Window) winPtr, VisibilityChangeMask, 965 FocusMapProc, clientData); 966 displayFocusPtr->focusOnMapPtr = NULL; 967 TkSetFocusWin(winPtr, displayFocusPtr->forceFocus); 968 } 969} 970 971/* 972 *---------------------------------------------------------------------- 973 * 974 * FindDisplayFocusInfo -- 975 * 976 * Given an application and a display, this function locate the focus 977 * record for that combination. If no such record exists, it creates a 978 * new record and initializes it. 979 * 980 * Results: 981 * The return value is a pointer to the record. 982 * 983 * Side effects: 984 * A new record will be allocated if there wasn't one already. 985 * 986 *---------------------------------------------------------------------- 987 */ 988 989static DisplayFocusInfo * 990FindDisplayFocusInfo( 991 TkMainInfo *mainPtr, /* Record that identifies a particular 992 * application. */ 993 TkDisplay *dispPtr) /* Display whose focus information is 994 * needed. */ 995{ 996 DisplayFocusInfo *displayFocusPtr; 997 998 for (displayFocusPtr = mainPtr->displayFocusPtr; 999 displayFocusPtr != NULL; 1000 displayFocusPtr = displayFocusPtr->nextPtr) { 1001 if (displayFocusPtr->dispPtr == dispPtr) { 1002 return displayFocusPtr; 1003 } 1004 } 1005 1006 /* 1007 * The record doesn't exist yet. Make a new one. 1008 */ 1009 1010 displayFocusPtr = (DisplayFocusInfo *) ckalloc(sizeof(DisplayFocusInfo)); 1011 displayFocusPtr->dispPtr = dispPtr; 1012 displayFocusPtr->focusWinPtr = NULL; 1013 displayFocusPtr->focusOnMapPtr = NULL; 1014 displayFocusPtr->forceFocus = 0; 1015 displayFocusPtr->focusSerial = 0; 1016 displayFocusPtr->nextPtr = mainPtr->displayFocusPtr; 1017 mainPtr->displayFocusPtr = displayFocusPtr; 1018 return displayFocusPtr; 1019} 1020 1021/* 1022 *---------------------------------------------------------------------- 1023 * 1024 * TkFocusFree -- 1025 * 1026 * Free resources associated with maintaining the focus. 1027 * 1028 * Results: 1029 * None. 1030 * 1031 * Side effects: 1032 * This mainPtr should no long access focus information. 1033 * 1034 *---------------------------------------------------------------------- 1035 */ 1036 1037void 1038TkFocusFree( 1039 TkMainInfo *mainPtr) /* Record that identifies a particular 1040 * application. */ 1041{ 1042 while (mainPtr->displayFocusPtr != NULL) { 1043 DisplayFocusInfo *displayFocusPtr = mainPtr->displayFocusPtr; 1044 1045 mainPtr->displayFocusPtr = mainPtr->displayFocusPtr->nextPtr; 1046 ckfree((char *) displayFocusPtr); 1047 } 1048 while (mainPtr->tlFocusPtr != NULL) { 1049 ToplevelFocusInfo *tlFocusPtr = mainPtr->tlFocusPtr; 1050 1051 mainPtr->tlFocusPtr = mainPtr->tlFocusPtr->nextPtr; 1052 ckfree((char *) tlFocusPtr); 1053 } 1054} 1055 1056/* 1057 *---------------------------------------------------------------------- 1058 * 1059 * TkFocusSplit -- 1060 * 1061 * Adjust focus window for a newly managed toplevel, thus splitting 1062 * the toplevel into two toplevels. 1063 * 1064 * Results: 1065 * None. 1066 * 1067 * Side effects: 1068 * A new record is allocated for the new toplevel window. 1069 * 1070 *---------------------------------------------------------------------- 1071 */ 1072 1073void 1074TkFocusSplit(winPtr) 1075 TkWindow *winPtr; /* Window is the new toplevel 1076 * Any focus point at or below window 1077 * must be moved to this new toplevel */ 1078{ 1079 ToplevelFocusInfo *tlFocusPtr; 1080 DisplayFocusInfo *displayFocusPtr; 1081 TkWindow *topLevelPtr; 1082 TkWindow *subWinPtr; 1083 1084 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); 1085 1086 /* 1087 * Find the top-level window for winPtr, then find (or create) 1088 * a record for the top-level. Also see whether winPtr and all its 1089 * ancestors are mapped. 1090 */ 1091 1092 for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr) { 1093 if (topLevelPtr == NULL) { 1094 /* 1095 * The window is being deleted. No point in worrying about 1096 * giving it the focus. 1097 */ 1098 return; 1099 } 1100 if (topLevelPtr->flags & TK_TOP_HIERARCHY) { 1101 break; 1102 } 1103 } 1104 1105 /* Search all focus records to find child windows of winPtr */ 1106 for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; 1107 tlFocusPtr = tlFocusPtr->nextPtr) { 1108 if (tlFocusPtr->topLevelPtr == topLevelPtr) { 1109 break; 1110 } 1111 } 1112 1113 if (tlFocusPtr == NULL) { 1114 /* No focus record for this toplevel, nothing to do. */ 1115 return; 1116 } 1117 1118 /* See if current focusWin is child of the new toplevel */ 1119 for (subWinPtr = tlFocusPtr->focusWinPtr; 1120 subWinPtr && subWinPtr != winPtr && subWinPtr != topLevelPtr; 1121 subWinPtr = subWinPtr->parentPtr) {} 1122 1123 if (subWinPtr == winPtr) { 1124 /* Move focus to new toplevel */ 1125 ToplevelFocusInfo *newTlFocusPtr; 1126 1127 newTlFocusPtr = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo)); 1128 newTlFocusPtr->topLevelPtr = winPtr; 1129 newTlFocusPtr->focusWinPtr = tlFocusPtr->focusWinPtr; 1130 newTlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; 1131 winPtr->mainPtr->tlFocusPtr = newTlFocusPtr; 1132 /* Move old toplevel's focus to the toplevel itself */ 1133 tlFocusPtr->focusWinPtr = topLevelPtr; 1134 } 1135 /* If it's not, then let focus progress naturally */ 1136} 1137 1138/* 1139 *---------------------------------------------------------------------- 1140 * 1141 * TkFocusJoin -- 1142 * 1143 * Remove the focus record for this window that is nolonger managed 1144 * 1145 * Results: 1146 * None. 1147 * 1148 * Side effects: 1149 * A tlFocusPtr record is removed 1150 * 1151 *---------------------------------------------------------------------- 1152 */ 1153 1154void 1155TkFocusJoin(winPtr) 1156 TkWindow *winPtr; /* Window is no longer a toplevel */ 1157{ 1158 ToplevelFocusInfo *tlFocusPtr; 1159 ToplevelFocusInfo *tmpPtr; 1160 1161 /* 1162 * Remove old toplevel record 1163 */ 1164 if (winPtr && winPtr->mainPtr && winPtr->mainPtr->tlFocusPtr 1165 && winPtr->mainPtr->tlFocusPtr->topLevelPtr == winPtr) { 1166 tmpPtr = winPtr->mainPtr->tlFocusPtr; 1167 winPtr->mainPtr->tlFocusPtr = tmpPtr->nextPtr; 1168 ckfree((char *)tmpPtr); 1169 } else { 1170 for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; 1171 tlFocusPtr = tlFocusPtr->nextPtr) { 1172 if (tlFocusPtr->nextPtr && 1173 tlFocusPtr->nextPtr->topLevelPtr == winPtr) { 1174 tmpPtr = tlFocusPtr->nextPtr; 1175 tlFocusPtr->nextPtr = tmpPtr->nextPtr; 1176 ckfree((char *)tmpPtr); 1177 break; 1178 } 1179 } 1180 } 1181} 1182 1183/* 1184 * Local Variables: 1185 * mode: c 1186 * c-basic-offset: 4 1187 * fill-column: 78 1188 * End: 1189 */ 1190