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