1/* 2 * tkMacOSXEmbed.c -- 3 * 4 * This file contains platform-specific procedures for theMac to provide 5 * basic operations needed for application embedding (where one 6 * application can use as its main window an internal window from 7 * some other application). 8 * Currently only Toplevel embedding within the same Tk application is 9 * allowed on the Macintosh. 10 * 11 * Copyright (c) 1996-1997 Sun Microsystems, Inc. 12 * Copyright 2001, Apple Computer, Inc. 13 * Copyright (c) 2006-2008 Daniel A. Steffen <das@users.sourceforge.net> 14 * 15 * See the file "license.terms" for information on usage and redistribution 16 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 17 * 18 * RCS: @(#) $Id: tkMacOSXEmbed.c,v 1.2.2.8 2007/11/09 06:26:55 das Exp $ 19 */ 20 21#include "tkMacOSXInt.h" 22 23/* 24 * One of the following structures exists for each container in this 25 * application. It keeps track of the container window and its 26 * associated embedded window. 27 */ 28 29typedef struct Container { 30 Window parent; /* The Mac Drawable for the parent of 31 * the pair (the container). */ 32 TkWindow *parentPtr; /* Tk's information about the container, 33 * or NULL if the container isn't 34 * in this process. */ 35 Window embedded; /* The MacDrawable for the embedded 36 * window. Starts off as None, but 37 * gets filled in when the window is 38 * eventually created. */ 39 TkWindow *embeddedPtr; /* Tk's information about the embedded 40 * window, or NULL if the 41 * embedded application isn't in 42 * this process. */ 43 struct Container *nextPtr; /* Next in list of all containers in 44 * this process. */ 45} Container; 46 47static Container *firstContainerPtr = NULL; 48 /* First in list of all containers 49 * managed by this process. */ 50/* 51 * Globals defined in this file 52 */ 53 54TkMacOSXEmbedHandler *tkMacOSXEmbedHandler = NULL; 55 56/* 57 * Prototypes for static procedures defined in this file: 58 */ 59 60static void ContainerEventProc(ClientData clientData, XEvent *eventPtr); 61static void EmbeddedEventProc(ClientData clientData, XEvent *eventPtr); 62static void EmbedActivateProc(ClientData clientData, XEvent *eventPtr); 63static void EmbedFocusProc(ClientData clientData, XEvent *eventPtr); 64static void EmbedGeometryRequest(Container * containerPtr, int width, 65 int height); 66static void EmbedSendConfigure(Container *containerPtr); 67static void EmbedStructureProc(ClientData clientData, XEvent *eventPtr); 68static void EmbedWindowDeleted(TkWindow *winPtr); 69 70 71/* 72 *---------------------------------------------------------------------- 73 * 74 * Tk_MacOSXSetEmbedHandler -- 75 * 76 * Registers a handler for an in process form of embedding, like 77 * Netscape plugins, where Tk is loaded into the process, but does 78 * not control the main window 79 * 80 * Results: 81 * None 82 * 83 * Side effects: 84 * The embed handler is set. 85 * 86 *---------------------------------------------------------------------- 87 */ 88 89void 90Tk_MacOSXSetEmbedHandler( 91 Tk_MacOSXEmbedRegisterWinProc *registerWinProc, 92 Tk_MacOSXEmbedGetGrafPortProc *getPortProc, 93 Tk_MacOSXEmbedMakeContainerExistProc *containerExistProc, 94 Tk_MacOSXEmbedGetClipProc *getClipProc, 95 Tk_MacOSXEmbedGetOffsetInParentProc *getOffsetProc) 96{ 97 if (tkMacOSXEmbedHandler == NULL) { 98 tkMacOSXEmbedHandler = (TkMacOSXEmbedHandler *) ckalloc(sizeof(TkMacOSXEmbedHandler)); 99 } 100 tkMacOSXEmbedHandler->registerWinProc = registerWinProc; 101 tkMacOSXEmbedHandler->getPortProc = getPortProc; 102 tkMacOSXEmbedHandler->containerExistProc = containerExistProc; 103 tkMacOSXEmbedHandler->getClipProc = getClipProc; 104 tkMacOSXEmbedHandler->getOffsetProc = getOffsetProc; 105} 106 107/* 108 *---------------------------------------------------------------------- 109 * 110 * TkpMakeWindow -- 111 * 112 * Creates an X Window (Mac subwindow). 113 * 114 * Results: 115 * The window id is returned. 116 * 117 * Side effects: 118 * None. 119 * 120 *---------------------------------------------------------------------- 121 */ 122 123Window 124TkpMakeWindow( 125 TkWindow *winPtr, 126 Window parent) 127{ 128 MacDrawable *macWin; 129 130 /* 131 * If this window is marked as embedded then 132 * the window structure should have already been 133 * created in the TkpUseWindow function. 134 */ 135 136 if (Tk_IsEmbedded(winPtr)) { 137 macWin = winPtr->privatePtr; 138 } else { 139 /* 140 * Allocate sub window 141 */ 142 143 macWin = (MacDrawable *) ckalloc(sizeof(MacDrawable)); 144 if (macWin == NULL) { 145 winPtr->privatePtr = NULL; 146 return None; 147 } 148 macWin->winPtr = winPtr; 149 winPtr->privatePtr = macWin; 150 macWin->visRgn = NULL; 151 macWin->aboveVisRgn = NULL; 152 macWin->drawRect = CGRectNull; 153 macWin->referenceCount = 0; 154 macWin->flags = TK_CLIP_INVALID; 155 macWin->grafPtr = NULL; 156 macWin->context = NULL; 157 macWin->size = CGSizeZero; 158 if (Tk_IsTopLevel(macWin->winPtr)) { 159 /* 160 *This will be set when we are mapped. 161 */ 162 macWin->xOff = 0; 163 macWin->yOff = 0; 164 macWin->toplevel = macWin; 165 } else { 166 macWin->xOff = winPtr->parentPtr->privatePtr->xOff + 167 winPtr->parentPtr->changes.border_width + 168 winPtr->changes.x; 169 macWin->yOff = winPtr->parentPtr->privatePtr->yOff + 170 winPtr->parentPtr->changes.border_width + 171 winPtr->changes.y; 172 macWin->toplevel = winPtr->parentPtr->privatePtr->toplevel; 173 } 174 macWin->toplevel->referenceCount++; 175 } 176 return (Window) macWin; 177} 178 179/* 180 *---------------------------------------------------------------------- 181 * 182 * TkpUseWindow -- 183 * 184 * This procedure causes a Tk window to use a given X window as 185 * its parent window, rather than the root window for the screen. 186 * It is invoked by an embedded application to specify the window 187 * in which it is embedded. 188 * 189 * Results: 190 * The return value is normally TCL_OK. If an error occurs (such 191 * as string not being a valid window spec), then the return value 192 * is TCL_ERROR and an error message is left in the interp's result if 193 * interp is non-NULL. 194 * 195 * Side effects: 196 * None. 197 * 198 *---------------------------------------------------------------------- 199 */ 200 201int 202TkpUseWindow( 203 Tcl_Interp *interp, /* If not NULL, used for error reporting 204 * if string is bogus. */ 205 Tk_Window tkwin, /* Tk window that does not yet have an 206 * associated X window. */ 207 CONST char *string) /* String identifying an X window to use 208 * for tkwin; must be an integer value. */ 209{ 210 TkWindow *winPtr = (TkWindow *) tkwin; 211 TkWindow *usePtr; 212 MacDrawable *parent, *macWin; 213 Container *containerPtr; 214 215 if (winPtr->window != None) { 216 Tcl_AppendResult(interp, "can't modify container after widget is " 217 "created", NULL); 218 return TCL_ERROR; 219 } 220 221 /* 222 * Decode the container pointer, and look for it among the 223 * list of available containers. 224 * 225 * N.B. For now, we are limiting the containers to be in the same Tk 226 * application as tkwin, since otherwise they would not be in our list 227 * of containers. 228 * 229 */ 230 231 if (Tcl_GetInt(interp, string, (int*) &parent) != TCL_OK) { 232 return TCL_ERROR; 233 } 234 235 usePtr = (TkWindow *) Tk_IdToWindow(winPtr->display, (Window) parent); 236 if (usePtr != NULL) { 237 if (!(usePtr->flags & TK_CONTAINER)) { 238 Tcl_AppendResult(interp, "window \"", usePtr->pathName, 239 "\" doesn't have -container option set", NULL); 240 return TCL_ERROR; 241 } 242 } 243 244 /* 245 * The code below can probably be simplified given we have already 246 * discovered 'usePtr' above. 247 */ 248 249 /* 250 * Save information about the container and the embedded window 251 * in a Container structure. Currently, there must already be an existing 252 * Container structure, since we only allow the case where both container 253 * and embedded app. are in the same process. 254 */ 255 256 for (containerPtr = firstContainerPtr; containerPtr != NULL; 257 containerPtr = containerPtr->nextPtr) { 258 if (containerPtr->parent == (Window) parent) { 259 winPtr->flags |= TK_BOTH_HALVES; 260 containerPtr->parentPtr->flags |= TK_BOTH_HALVES; 261 break; 262 } 263 } 264 265 /* 266 * Make the embedded window. 267 */ 268 269 macWin = (MacDrawable *) ckalloc(sizeof(MacDrawable)); 270 if (macWin == NULL) { 271 winPtr->privatePtr = NULL; 272 return TCL_ERROR; 273 } 274 275 macWin->winPtr = winPtr; 276 winPtr->privatePtr = macWin; 277 278 /* 279 * The grafPtr will be NULL for a Tk in Tk embedded window. 280 * It is none of our business what it is for a Tk not in Tk embedded window, 281 * but we will initialize it to NULL, and let the registerWinProc 282 * set it. In any case, you must always use TkMacOSXGetDrawablePort 283 * to get the portPtr. It will correctly find the container's port. 284 */ 285 286 macWin->grafPtr = NULL; 287 macWin->context = NULL; 288 macWin->size = CGSizeZero; 289 macWin->visRgn = NULL; 290 macWin->aboveVisRgn = NULL; 291 macWin->drawRect = CGRectNull; 292 macWin->referenceCount = 0; 293 macWin->flags = TK_CLIP_INVALID; 294 macWin->toplevel = macWin; 295 macWin->toplevel->referenceCount++; 296 297 winPtr->flags |= TK_EMBEDDED; 298 299 300 /* 301 * Make a copy of the TK_EMBEDDED flag, since sometimes 302 * we need this to get the port after the TkWindow structure 303 * has been freed. 304 */ 305 306 macWin->flags |= TK_EMBEDDED; 307 308 /* 309 * Now check whether it is embedded in another Tk widget. If not (the first 310 * case below) we see if there is an in-process embedding handler registered, 311 * and if so, let that fill in the rest of the macWin. 312 */ 313 314 if (containerPtr == NULL) { 315 /* 316 * If someone has registered an in process embedding handler, then 317 * see if it can handle this window... 318 */ 319 320 if (tkMacOSXEmbedHandler == NULL || 321 tkMacOSXEmbedHandler->registerWinProc((int) parent, 322 (Tk_Window) winPtr) != TCL_OK) { 323 Tcl_AppendResult(interp, "The window ID ", string, 324 " does not correspond to a valid Tk Window.", NULL); 325 return TCL_ERROR; 326 } else { 327 containerPtr = (Container *) ckalloc(sizeof(Container)); 328 329 containerPtr->parentPtr = NULL; 330 containerPtr->embedded = (Window) macWin; 331 containerPtr->embeddedPtr = macWin->winPtr; 332 containerPtr->nextPtr = firstContainerPtr; 333 firstContainerPtr = containerPtr; 334 335 } 336 } else { 337 338 /* 339 * The window is embedded in another Tk window. 340 */ 341 342 macWin->xOff = parent->winPtr->privatePtr->xOff + 343 parent->winPtr->changes.border_width + 344 winPtr->changes.x; 345 macWin->yOff = parent->winPtr->privatePtr->yOff + 346 parent->winPtr->changes.border_width + 347 winPtr->changes.y; 348 349 350 /* 351 * Finish filling up the container structure with the embedded window's 352 * information. 353 */ 354 355 containerPtr->embedded = (Window) macWin; 356 containerPtr->embeddedPtr = macWin->winPtr; 357 358 /* 359 * Create an event handler to clean up the Container structure when 360 * tkwin is eventually deleted. 361 */ 362 363 Tk_CreateEventHandler(tkwin, StructureNotifyMask, EmbeddedEventProc, 364 (ClientData) winPtr); 365 366 } 367 368 return TCL_OK; 369} 370 371/* 372 *---------------------------------------------------------------------- 373 * 374 * TkpMakeContainer -- 375 * 376 * This procedure is called to indicate that a particular window 377 * will be a container for an embedded application. This changes 378 * certain aspects of the window's behavior, such as whether it 379 * will receive events anymore. 380 * 381 * Results: 382 * None. 383 * 384 * Side effects: 385 * None. 386 * 387 *---------------------------------------------------------------------- 388 */ 389 390void 391TkpMakeContainer( 392 Tk_Window tkwin) /* Token for a window that is about to 393 * become a container. */ 394{ 395 TkWindow *winPtr = (TkWindow *) tkwin; 396 Container *containerPtr; 397 398 /* 399 * Register the window as a container so that, for example, we can 400 * make sure the argument to -use is valid. 401 */ 402 403 404 Tk_MakeWindowExist(tkwin); 405 containerPtr = (Container *) ckalloc(sizeof(Container)); 406 containerPtr->parent = Tk_WindowId(tkwin); 407 containerPtr->parentPtr = winPtr; 408 containerPtr->embedded = None; 409 containerPtr->embeddedPtr = NULL; 410 containerPtr->nextPtr = firstContainerPtr; 411 firstContainerPtr = containerPtr; 412 winPtr->flags |= TK_CONTAINER; 413 414 /* 415 * Request SubstructureNotify events so that we can find out when 416 * the embedded application creates its window or attempts to 417 * resize it. Also watch Configure events on the container so that 418 * we can resize the child to match. Also, pass activate events from 419 * the container down to the embedded toplevel. 420 */ 421 422 Tk_CreateEventHandler(tkwin, 423 SubstructureNotifyMask|SubstructureRedirectMask, 424 ContainerEventProc, (ClientData) winPtr); 425 Tk_CreateEventHandler(tkwin, StructureNotifyMask, EmbedStructureProc, 426 (ClientData) containerPtr); 427 Tk_CreateEventHandler(tkwin, ActivateMask, EmbedActivateProc, 428 (ClientData) containerPtr); 429 Tk_CreateEventHandler(tkwin, FocusChangeMask, EmbedFocusProc, 430 (ClientData) containerPtr); 431 432} 433 434/* 435 *---------------------------------------------------------------------- 436 * 437 * TkMacOSXContainerId -- 438 * 439 * Given an embedded window, this procedure returns the MacDrawable 440 * identifier for the associated container window. 441 * 442 * Results: 443 * The return value is the MacDrawable for winPtr's 444 * container window. 445 * 446 * Side effects: 447 * None. 448 * 449 *---------------------------------------------------------------------- 450 */ 451 452MacDrawable * 453TkMacOSXContainerId(winPtr) 454 TkWindow *winPtr; /* Tk's structure for an embedded window. */ 455{ 456 Container *containerPtr; 457 458 for (containerPtr = firstContainerPtr; containerPtr != NULL; 459 containerPtr = containerPtr->nextPtr) { 460 if (containerPtr->embeddedPtr == winPtr) { 461 return (MacDrawable *) containerPtr->parent; 462 } 463 } 464 Tcl_Panic("TkMacOSXContainerId couldn't find window"); 465 return None; 466} 467 468/* 469 *---------------------------------------------------------------------- 470 * 471 * TkMacOSXGetHostToplevel -- 472 * 473 * Given the TkWindow, return the MacDrawable for the outermost 474 * toplevel containing it. This will be a real Macintosh window. 475 * 476 * Results: 477 * Returns a MacDrawable corresponding to a Macintosh Toplevel 478 * 479 * Side effects: 480 * None. 481 * 482 *---------------------------------------------------------------------- 483 */ 484 485MacDrawable * 486TkMacOSXGetHostToplevel( 487 TkWindow *winPtr) /* Tk's structure for a window. */ 488{ 489 TkWindow *contWinPtr, *topWinPtr; 490 491 topWinPtr = winPtr->privatePtr->toplevel->winPtr; 492 if (!Tk_IsEmbedded(topWinPtr)) { 493 return winPtr->privatePtr->toplevel; 494 } else { 495 contWinPtr = TkpGetOtherWindow(topWinPtr); 496 497 /* 498 * TODO: Here we should handle out of process embedding. 499 */ 500 501 if (contWinPtr != NULL) { 502 return TkMacOSXGetHostToplevel(contWinPtr); 503 } else { 504 return None; 505 } 506 } 507} 508 509/* 510 *---------------------------------------------------------------------- 511 * 512 * TkpClaimFocus -- 513 * 514 * This procedure is invoked when someone asks for the input focus 515 * to be put on a window in an embedded application, but the 516 * application doesn't currently have the focus. It requests the 517 * input focus from the container application. 518 * 519 * Results: 520 * None. 521 * 522 * Side effects: 523 * The input focus may change. 524 * 525 *---------------------------------------------------------------------- 526 */ 527 528void 529TkpClaimFocus( 530 TkWindow *topLevelPtr, /* Top-level window containing desired 531 * focus window; should be embedded. */ 532 int force) /* One means that the container should 533 * claim the focus if it doesn't 534 * currently have it. */ 535{ 536 XEvent event; 537 Container *containerPtr; 538 539 if (!(topLevelPtr->flags & TK_EMBEDDED)) { 540 return; 541 } 542 543 for (containerPtr = firstContainerPtr; 544 containerPtr->embeddedPtr != topLevelPtr; 545 containerPtr = containerPtr->nextPtr) { 546 /* Empty loop body. */ 547 } 548 549 550 event.xfocus.type = FocusIn; 551 event.xfocus.serial = LastKnownRequestProcessed(topLevelPtr->display); 552 event.xfocus.send_event = 1; 553 event.xfocus.display = topLevelPtr->display; 554 event.xfocus.window = containerPtr->parent; 555 event.xfocus.mode = EMBEDDED_APP_WANTS_FOCUS; 556 event.xfocus.detail = force; 557 Tk_QueueWindowEvent(&event,TCL_QUEUE_TAIL); 558} 559 560/* 561 *---------------------------------------------------------------------- 562 * 563 * TkpTestembedCmd -- 564 * 565 * This procedure implements the "testembed" command. It returns 566 * some or all of the information in the list pointed to by 567 * firstContainerPtr. 568 * 569 * Results: 570 * A standard Tcl result. 571 * 572 * Side effects: 573 * None. 574 * 575 *---------------------------------------------------------------------- 576 */ 577 578int 579TkpTestembedCmd( 580 ClientData clientData, /* Main window for application. */ 581 Tcl_Interp *interp, /* Current interpreter. */ 582 int argc, /* Number of arguments. */ 583 CONST char **argv) /* Argument strings. */ 584{ 585 int all; 586 Container *containerPtr; 587 Tcl_DString dString; 588 char buffer[50]; 589 590 if ((argc > 1) && (strcmp(argv[1], "all") == 0)) { 591 all = 1; 592 } else { 593 all = 0; 594 } 595 Tcl_DStringInit(&dString); 596 for (containerPtr = firstContainerPtr; containerPtr != NULL; 597 containerPtr = containerPtr->nextPtr) { 598 Tcl_DStringStartSublist(&dString); 599 if (containerPtr->parent == None) { 600 Tcl_DStringAppendElement(&dString, ""); 601 } else { 602 if (all) { 603 sprintf(buffer, "0x%x", (int) containerPtr->parent); 604 Tcl_DStringAppendElement(&dString, buffer); 605 } else { 606 Tcl_DStringAppendElement(&dString, "XXX"); 607 } 608 } 609 if (containerPtr->parentPtr == NULL) { 610 Tcl_DStringAppendElement(&dString, ""); 611 } else { 612 Tcl_DStringAppendElement(&dString, 613 containerPtr->parentPtr->pathName); 614 } 615 if (containerPtr->embedded == None) { 616 Tcl_DStringAppendElement(&dString, ""); 617 } else { 618 if (all) { 619 sprintf(buffer, "0x%x", (int) containerPtr->embedded); 620 Tcl_DStringAppendElement(&dString, buffer); 621 } else { 622 Tcl_DStringAppendElement(&dString, "XXX"); 623 } 624 } 625 if (containerPtr->embeddedPtr == NULL) { 626 Tcl_DStringAppendElement(&dString, ""); 627 } else { 628 Tcl_DStringAppendElement(&dString, 629 containerPtr->embeddedPtr->pathName); 630 } 631 Tcl_DStringEndSublist(&dString); 632 } 633 Tcl_DStringResult(interp, &dString); 634 return TCL_OK; 635} 636 637/* 638 *---------------------------------------------------------------------- 639 * 640 * TkpRedirectKeyEvent -- 641 * 642 * This procedure is invoked when a key press or release event 643 * arrives for an application that does not believe it owns the 644 * input focus. This can happen because of embedding; for example, 645 * X can send an event to an embedded application when the real 646 * focus window is in the container application and is an ancestor 647 * of the container. This procedure's job is to forward the event 648 * back to the application where it really belongs. 649 * 650 * Results: 651 * None. 652 * 653 * Side effects: 654 * The event may get sent to a different application. 655 * 656 *---------------------------------------------------------------------- 657 */ 658 659void 660TkpRedirectKeyEvent( 661 TkWindow *winPtr, /* Window to which the event was originally 662 * reported. */ 663 XEvent *eventPtr) /* X event to redirect (should be KeyPress 664 * or KeyRelease). */ 665{ 666} 667 668/* 669 *---------------------------------------------------------------------- 670 * 671 * TkpGetOtherWindow -- 672 * 673 * If both the container and embedded window are in the same 674 * process, this procedure will return either one, given the other. 675 * 676 * Results: 677 * If winPtr is a container, the return value is the token for the 678 * embedded window, and vice versa. If the "other" window isn't in 679 * this process, NULL is returned. 680 * 681 * Side effects: 682 * None. 683 * 684 *---------------------------------------------------------------------- 685 */ 686 687TkWindow * 688TkpGetOtherWindow( 689 TkWindow *winPtr) /* Tk's structure for a container or 690 * embedded window. */ 691{ 692 Container *containerPtr; 693 694 /* 695 * TkpGetOtherWindow returns NULL if both windows are not 696 * in the same process... 697 */ 698 699 if (!(winPtr->flags & TK_BOTH_HALVES)) { 700 return NULL; 701 } 702 703 for (containerPtr = firstContainerPtr; containerPtr != NULL; 704 containerPtr = containerPtr->nextPtr) { 705 if (containerPtr->embeddedPtr == winPtr) { 706 return containerPtr->parentPtr; 707 } else if (containerPtr->parentPtr == winPtr) { 708 return containerPtr->embeddedPtr; 709 } 710 } 711 return NULL; 712} 713 714/* 715 *---------------------------------------------------------------------- 716 * 717 * EmbeddedEventProc -- 718 * 719 * This procedure is invoked by the Tk event dispatcher when various 720 * useful events are received for a window that is embedded in 721 * another application. 722 * 723 * Results: 724 * None. 725 * 726 * Side effects: 727 * Our internal state gets cleaned up when an embedded window is 728 * destroyed. 729 * 730 *---------------------------------------------------------------------- 731 */ 732 733static void 734EmbeddedEventProc( 735 ClientData clientData, /* Token for container window. */ 736 XEvent *eventPtr) /* ResizeRequest event. */ 737{ 738 TkWindow *winPtr = (TkWindow *) clientData; 739 740 if (eventPtr->type == DestroyNotify) { 741 EmbedWindowDeleted(winPtr); 742 } 743} 744 745/* 746 *---------------------------------------------------------------------- 747 * 748 * ContainerEventProc -- 749 * 750 * This procedure is invoked by the Tk event dispatcher when various 751 * useful events are received for the children of a container 752 * window. It forwards relevant information, such as geometry 753 * requests, from the events into the container's application. 754 * 755 * NOTE: on the Mac, only the DestroyNotify branch is ever taken. 756 * We don't synthesize the other events. 757 * 758 * Results: 759 * None. 760 * 761 * Side effects: 762 * Depends on the event. For example, when ConfigureRequest events 763 * occur, geometry information gets set for the container window. 764 * 765 *---------------------------------------------------------------------- 766 */ 767 768static void 769ContainerEventProc( 770 ClientData clientData, /* Token for container window. */ 771 XEvent *eventPtr) /* ResizeRequest event. */ 772{ 773 TkWindow *winPtr = (TkWindow *) clientData; 774 Container *containerPtr; 775 Tk_ErrorHandler errHandler; 776 777 /* 778 * Ignore any X protocol errors that happen in this procedure 779 * (almost any operation could fail, for example, if the embedded 780 * application has deleted its window). 781 */ 782 783 errHandler = Tk_CreateErrorHandler(eventPtr->xfocus.display, -1, 784 -1, -1, (Tk_ErrorProc *) NULL, (ClientData) NULL); 785 786 /* 787 * Find the Container structure associated with the parent window. 788 */ 789 790 for (containerPtr = firstContainerPtr; 791 containerPtr->parent != eventPtr->xmaprequest.parent; 792 containerPtr = containerPtr->nextPtr) { 793 if (containerPtr == NULL) { 794 Tcl_Panic("ContainerEventProc couldn't find Container record"); 795 } 796 } 797 798 if (eventPtr->type == CreateNotify) { 799 /* 800 * A new child window has been created in the container. Record 801 * its id in the Container structure (if more than one child is 802 * created, just remember the last one and ignore the earlier 803 * ones). 804 */ 805 806 containerPtr->embedded = eventPtr->xcreatewindow.window; 807 } else if (eventPtr->type == ConfigureRequest) { 808 if ((eventPtr->xconfigurerequest.x != 0) 809 || (eventPtr->xconfigurerequest.y != 0)) { 810 /* 811 * The embedded application is trying to move itself, which 812 * isn't legal. At this point, the window hasn't actually 813 * moved, but we need to send it a ConfigureNotify event to 814 * let it know that its request has been denied. If the 815 * embedded application was also trying to resize itself, a 816 * ConfigureNotify will be sent by the geometry management 817 * code below, so we don't need to do anything. Otherwise, 818 * generate a synthetic event. 819 */ 820 821 if ((eventPtr->xconfigurerequest.width == winPtr->changes.width) 822 && (eventPtr->xconfigurerequest.height 823 == winPtr->changes.height)) { 824 EmbedSendConfigure(containerPtr); 825 } 826 } 827 EmbedGeometryRequest(containerPtr, 828 eventPtr->xconfigurerequest.width, 829 eventPtr->xconfigurerequest.height); 830 } else if (eventPtr->type == MapRequest) { 831 /* 832 * The embedded application's map request was ignored and simply 833 * passed on to us, so we have to map the window for it to appear 834 * on the screen. 835 */ 836 837 XMapWindow(eventPtr->xmaprequest.display, 838 eventPtr->xmaprequest.window); 839 } else if (eventPtr->type == DestroyNotify) { 840 /* 841 * The embedded application is gone. Destroy the container window. 842 */ 843 844 Tk_DestroyWindow((Tk_Window) winPtr); 845 } 846 Tk_DeleteErrorHandler(errHandler); 847} 848 849/* 850 *---------------------------------------------------------------------- 851 * 852 * EmbedStructureProc -- 853 * 854 * This procedure is invoked by the Tk event dispatcher when 855 * a container window owned by this application gets resized 856 * (and also at several other times that we don't care about). 857 * This procedure reflects the size change in the embedded 858 * window that corresponds to the container. 859 * 860 * Results: 861 * None. 862 * 863 * Side effects: 864 * The embedded window gets resized to match the container. 865 * 866 *---------------------------------------------------------------------- 867 */ 868 869static void 870EmbedStructureProc( 871 ClientData clientData, /* Token for container window. */ 872 XEvent *eventPtr) /* ResizeRequest event. */ 873{ 874 Container *containerPtr = (Container *) clientData; 875 Tk_ErrorHandler errHandler; 876 877 if (eventPtr->type == ConfigureNotify) { 878 if (containerPtr->embedded != None) { 879 /* 880 * Ignore errors, since the embedded application could have 881 * deleted its window. 882 */ 883 884 errHandler = Tk_CreateErrorHandler(eventPtr->xfocus.display, -1, 885 -1, -1, (Tk_ErrorProc *) NULL, (ClientData) NULL); 886 Tk_MoveResizeWindow((Tk_Window) containerPtr->embeddedPtr, 0, 0, 887 (unsigned int) Tk_Width( 888 (Tk_Window) containerPtr->parentPtr), 889 (unsigned int) Tk_Height( 890 (Tk_Window) containerPtr->parentPtr)); 891 Tk_DeleteErrorHandler(errHandler); 892 } 893 } else if (eventPtr->type == DestroyNotify) { 894 EmbedWindowDeleted(containerPtr->parentPtr); 895 } 896} 897 898/* 899 *---------------------------------------------------------------------- 900 * 901 * EmbedActivateProc -- 902 * 903 * This procedure is invoked by the Tk event dispatcher when 904 * Activate and Deactivate events occur for a container window owned 905 * by this application. It is responsible for forwarding an activate 906 * event down into the embedded toplevel. 907 * 908 * Results: 909 * None. 910 * 911 * Side effects: 912 * The X focus may change. 913 * 914 *---------------------------------------------------------------------- 915 */ 916 917static void 918EmbedActivateProc( 919 ClientData clientData, /* Token for container window. */ 920 XEvent *eventPtr) /* ResizeRequest event. */ 921{ 922 Container *containerPtr = (Container *) clientData; 923 924 if (containerPtr->embeddedPtr != NULL) { 925 if (eventPtr->type == ActivateNotify) { 926 TkGenerateActivateEvents(containerPtr->embeddedPtr,1); 927 } else if (eventPtr->type == DeactivateNotify) { 928 TkGenerateActivateEvents(containerPtr->embeddedPtr,0); 929 } 930 } 931} 932 933/* 934 *---------------------------------------------------------------------- 935 * 936 * EmbedFocusProc -- 937 * 938 * This procedure is invoked by the Tk event dispatcher when 939 * FocusIn and FocusOut events occur for a container window owned 940 * by this application. It is responsible for moving the focus 941 * back and forth between a container application and an embedded 942 * application. 943 * 944 * Results: 945 * None. 946 * 947 * Side effects: 948 * The X focus may change. 949 * 950 *---------------------------------------------------------------------- 951 */ 952 953static void 954EmbedFocusProc( 955 ClientData clientData, /* Token for container window. */ 956 XEvent *eventPtr) /* ResizeRequest event. */ 957{ 958 Container *containerPtr = (Container *) clientData; 959 Display *display; 960 XEvent event; 961 962 if (containerPtr->embeddedPtr != NULL) { 963 display = Tk_Display(containerPtr->parentPtr); 964 event.xfocus.serial = LastKnownRequestProcessed(display); 965 event.xfocus.send_event = false; 966 event.xfocus.display = display; 967 event.xfocus.mode = NotifyNormal; 968 event.xfocus.window = containerPtr->embedded; 969 970 if (eventPtr->type == FocusIn) { 971 /* 972 * The focus just arrived at the container. Change the X focus 973 * to move it to the embedded application, if there is one. 974 * Ignore X errors that occur during this operation (it's 975 * possible that the new focus window isn't mapped). 976 */ 977 978 event.xfocus.detail = NotifyNonlinear; 979 event.xfocus.type = FocusIn; 980 981 } else if (eventPtr->type == FocusOut) { 982 /* When the container gets a FocusOut event, it has to tell the embedded app 983 * that it has lost the focus. 984 */ 985 986 event.xfocus.type = FocusOut; 987 event.xfocus.detail = NotifyNonlinear; 988 } 989 990 Tk_QueueWindowEvent(&event, TCL_QUEUE_MARK); 991 } 992} 993 994/* 995 *---------------------------------------------------------------------- 996 * 997 * EmbedGeometryRequest -- 998 * 999 * This procedure is invoked when an embedded application requests 1000 * a particular size. It processes the request (which may or may 1001 * not actually honor the request) and reflects the results back 1002 * to the embedded application. 1003 * 1004 * NOTE: On the Mac, this is a stub, since we don't synthesize 1005 * ConfigureRequest events. 1006 * 1007 * Results: 1008 * None. 1009 * 1010 * Side effects: 1011 * If we deny the child's size change request, a Configure event 1012 * is synthesized to let the child know how big it ought to be. 1013 * Events get processed while we're waiting for the geometry 1014 * managers to do their thing. 1015 * 1016 *---------------------------------------------------------------------- 1017 */ 1018 1019static void 1020EmbedGeometryRequest( 1021 Container *containerPtr, /* Information about the embedding. */ 1022 int width, int height) /* Size that the child has requested. */ 1023{ 1024 TkWindow *winPtr = containerPtr->parentPtr; 1025 1026 /* 1027 * Forward the requested size into our geometry management hierarchy 1028 * via the container window. We need to send a Configure event back 1029 * to the embedded application if we decide not to honor its 1030 * request; to make this happen, process all idle event handlers 1031 * synchronously here (so that the geometry managers have had a 1032 * chance to do whatever they want to do), and if the window's size 1033 * didn't change then generate a configure event. 1034 */ 1035 1036 Tk_GeometryRequest((Tk_Window) winPtr, width, height); 1037 while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) { 1038 /* Empty loop body. */ 1039 } 1040 if ((winPtr->changes.width != width) 1041 || (winPtr->changes.height != height)) { 1042 EmbedSendConfigure(containerPtr); 1043 } 1044} 1045 1046/* 1047 *---------------------------------------------------------------------- 1048 * 1049 * EmbedSendConfigure -- 1050 * 1051 * This is currently a stub. It is called to notify an 1052 * embedded application of its current size and location. This 1053 * procedure is called when the embedded application made a 1054 * geometry request that we did not grant, so that the embedded 1055 * application knows that its geometry didn't change after all. 1056 * It is a response to ConfigureRequest events, which we do not 1057 * currently synthesize on the Mac 1058 * 1059 * Results: 1060 * None. 1061 * 1062 * Side effects: 1063 * None. 1064 * 1065 *---------------------------------------------------------------------- 1066 */ 1067 1068static void 1069EmbedSendConfigure( 1070 Container *containerPtr) /* Information about the embedding. */ 1071{ 1072} 1073 1074/* 1075 *---------------------------------------------------------------------- 1076 * 1077 * EmbedWindowDeleted -- 1078 * 1079 * This procedure is invoked when a window involved in embedding 1080 * (as either the container or the embedded application) is 1081 * destroyed. It cleans up the Container structure for the window. 1082 * 1083 * Results: 1084 * None. 1085 * 1086 * Side effects: 1087 * A Container structure may be freed. 1088 * 1089 *---------------------------------------------------------------------- 1090 */ 1091 1092static void 1093EmbedWindowDeleted( 1094 TkWindow *winPtr) /* Tk's information about window that 1095 * was deleted. */ 1096{ 1097 Container *containerPtr, *prevPtr; 1098 1099 /* 1100 * Find the Container structure for this window. Delete the 1101 * information about the embedded application and free the container's 1102 * record. 1103 */ 1104 1105 prevPtr = NULL; 1106 containerPtr = firstContainerPtr; 1107 while (1) { 1108 if (containerPtr->embeddedPtr == winPtr) { 1109 1110 /* 1111 * We also have to destroy our parent, to clean up the container. 1112 * Fabricate an event to do this. 1113 */ 1114 1115 if (containerPtr->parentPtr != NULL && 1116 containerPtr->parentPtr->flags & TK_BOTH_HALVES) { 1117 XEvent event; 1118 1119 event.xany.serial = 1120 Tk_Display(containerPtr->parentPtr)->request; 1121 event.xany.send_event = False; 1122 event.xany.display = Tk_Display(containerPtr->parentPtr); 1123 1124 event.xany.type = DestroyNotify; 1125 event.xany.window = containerPtr->parent; 1126 event.xdestroywindow.event = containerPtr->parent; 1127 Tk_QueueWindowEvent(&event, TCL_QUEUE_HEAD); 1128 1129 } 1130 1131 containerPtr->embedded = None; 1132 containerPtr->embeddedPtr = NULL; 1133 1134 break; 1135 } 1136 if (containerPtr->parentPtr == winPtr) { 1137 containerPtr->parentPtr = NULL; 1138 break; 1139 } 1140 prevPtr = containerPtr; 1141 containerPtr = containerPtr->nextPtr; 1142 } 1143 if ((containerPtr->embeddedPtr == NULL) 1144 && (containerPtr->parentPtr == NULL)) { 1145 if (prevPtr == NULL) { 1146 firstContainerPtr = containerPtr->nextPtr; 1147 } else { 1148 prevPtr->nextPtr = containerPtr->nextPtr; 1149 } 1150 ckfree((char *) containerPtr); 1151 } 1152} 1153