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