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