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