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