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