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