1/*
2 * tkMacOSXSubwindows.c --
3 *
4 *	Implements subwindows for the macintosh version of Tk.
5 *
6 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
7 * Copyright 2001, Apple Computer, Inc.
8 * Copyright (c) 2006-2008 Daniel A. Steffen <das@users.sourceforge.net>
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tkMacOSXSubwindows.c,v 1.2.2.22 2007/12/18 18:21:31 das Exp $
14 */
15
16#include "tkMacOSXPrivate.h"
17#include "tkMacOSXDebug.h"
18#include "tkMacOSXWm.h"
19
20/*
21#ifdef TK_MAC_DEBUG
22#define TK_MAC_DEBUG_CLIP_REGIONS
23#endif
24*/
25
26/*
27 * Prototypes for functions used only in this file.
28 */
29
30static void MoveResizeWindow(MacDrawable *macWin);
31static void GenerateConfigureNotify(TkWindow *winPtr, int includeWin);
32static void UpdateOffsets(TkWindow *winPtr, int deltaX, int deltaY);
33static void NotifyVisibility(TkWindow *winPtr, XEvent *eventPtr);
34
35
36/*
37 *----------------------------------------------------------------------
38 *
39 * XDestroyWindow --
40 *
41 *	Dealocates the given X Window.
42 *
43 * Results:
44 *	The window id is returned.
45 *
46 * Side effects:
47 *	None.
48 *
49 *----------------------------------------------------------------------
50 */
51
52void
53XDestroyWindow(
54    Display* display,		/* Display. */
55    Window window)		/* Window. */
56{
57    MacDrawable *macWin = (MacDrawable *) window;
58
59    /*
60     * Remove any dangling pointers that may exist if
61     * the window we are deleting is being tracked by
62     * the grab code.
63     */
64
65    TkPointerDeadWindow(macWin->winPtr);
66    macWin->toplevel->referenceCount--;
67
68    if (Tk_IsTopLevel(macWin->winPtr)) {
69	WindowRef winRef;
70	/*
71	 * We are relying on the Activate Mac OS event to pass the
72	 * focus away from a window that is getting Destroyed to the
73	 * Front non-floating window. BUT we don't get activate events
74	 * when a floating window is destroyed - since the front non-floating
75	 * window doesn't in fact get activated... So maybe we can check here
76	 * and if we are destroying a floating window, we can pass the focus
77	 * back to the front non-floating window...
78	 */
79
80	if (macWin->grafPtr != NULL) {
81	    TkWindow *focusPtr = TkGetFocusWin(macWin->winPtr);
82	    if (focusPtr == NULL || (focusPtr->mainPtr->winPtr == macWin->winPtr)) {
83		winRef = TkMacOSXDrawableWindow(window);
84		if (TkpIsWindowFloating (winRef)) {
85		    Window window;
86
87		    window = TkMacOSXGetXWindow(ActiveNonFloatingWindow());
88		    if (window != None) {
89			TkMacOSXGenerateFocusEvent(window, 1);
90		    }
91		}
92	    }
93	}
94	if (macWin->visRgn) {
95	    CFRelease(macWin->visRgn);
96	}
97	if (macWin->aboveVisRgn) {
98	    CFRelease(macWin->aboveVisRgn);
99	}
100
101	/*
102	 * Delete the Mac window and remove it from the windowTable.
103	 * The window could be NULL if the window was never mapped.
104	 * However, we don't do this for embedded windows, they don't
105	 * go in the window list, and they do not own their portPtr's.
106	 */
107
108	if (!(Tk_IsEmbedded(macWin->winPtr))) {
109	    WindowRef winRef = TkMacOSXDrawableWindow(window);
110
111	    if (winRef) {
112		TkMacOSXWindowList *listPtr, *prevPtr;
113		WindowGroupRef group;
114
115		if (GetWindowProperty(winRef, 'Tk  ', 'TsGp', sizeof(group),
116			NULL, &group) == noErr) {
117		    TkDisplay *dispPtr = TkGetDisplayList();
118		    ItemCount i = CountWindowGroupContents(group,
119			    kWindowGroupContentsReturnWindows);
120
121		    while (i > 0) {
122			WindowRef macWin;
123
124			ChkErr(GetIndexedWindow, group, i--, 0, &macWin);
125			if (macWin) {
126			    WindowGroupRef newGroup = NULL;
127			    Window window = TkMacOSXGetXWindow(macWin);
128
129			    if (window != None) {
130				TkWindow * winPtr = (TkWindow *)Tk_IdToWindow(
131					dispPtr->display, window);
132
133				if (winPtr && winPtr->wmInfoPtr) {
134				    newGroup = GetWindowGroupOfClass(
135					    winPtr->wmInfoPtr->macClass);
136				}
137			    }
138			    if (!newGroup) {
139				newGroup = GetWindowGroupOfClass(
140					kDocumentWindowClass);
141			    }
142			    ChkErr(SetWindowGroup, macWin, newGroup);
143			}
144
145		    }
146		    ChkErr(SetWindowGroupOwner, group, NULL);
147		    ChkErr(ReleaseWindowGroup, group);
148		}
149		TkMacOSXUnregisterMacWindow(winRef);
150		DisposeWindow(winRef);
151
152		for (listPtr = tkMacOSXWindowListPtr, prevPtr = NULL;
153			tkMacOSXWindowListPtr != NULL;
154			prevPtr = listPtr, listPtr = listPtr->nextPtr) {
155		    if (listPtr->winPtr == macWin->winPtr) {
156			if (prevPtr == NULL) {
157			    tkMacOSXWindowListPtr = listPtr->nextPtr;
158			} else {
159			    prevPtr->nextPtr = listPtr->nextPtr;
160			}
161			ckfree((char *) listPtr);
162			break;
163		    }
164		}
165	    }
166	}
167
168	macWin->grafPtr = NULL;
169
170	/*
171	 * Delay deletion of a toplevel data structure untill all
172	 * children have been deleted.
173	 */
174	if (macWin->toplevel->referenceCount == 0) {
175	    ckfree((char *) macWin->toplevel);
176	}
177    } else {
178	TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW);
179	if (macWin->winPtr->parentPtr != NULL) {
180	    TkMacOSXInvalClipRgns((Tk_Window) macWin->winPtr->parentPtr);
181	}
182	if (macWin->visRgn) {
183	    CFRelease(macWin->visRgn);
184	}
185	if (macWin->aboveVisRgn) {
186	    CFRelease(macWin->aboveVisRgn);
187	}
188
189	if (macWin->toplevel->referenceCount == 0) {
190	    ckfree((char *) macWin->toplevel);
191	}
192	ckfree((char *) macWin);
193    }
194}
195
196/*
197 *----------------------------------------------------------------------
198 *
199 * XMapWindow --
200 *
201 *	Map the given X Window to the screen. See X window documentation
202 *  for more details.
203 *
204 * Results:
205 *	None.
206 *
207 * Side effects:
208 *	The subwindow or toplevel may appear on the screen.
209 *
210 *----------------------------------------------------------------------
211 */
212
213void
214XMapWindow(
215    Display* display,		/* Display. */
216    Window window)		/* Window. */
217{
218    MacDrawable *macWin = (MacDrawable *) window;
219    XEvent event;
220
221    /*
222     * Under certain situations it's possible for this function to be
223     * called before the toplevel window it's associated with has actually
224     * been mapped. In that case we need to create the real Macintosh
225     * window now as this function as well as other X functions assume that
226     * the portPtr is valid.
227     */
228    if (!TkMacOSXHostToplevelExists(macWin->toplevel->winPtr)) {
229	TkMacOSXMakeRealWindowExist(macWin->toplevel->winPtr);
230    }
231
232    display->request++;
233    macWin->winPtr->flags |= TK_MAPPED;
234    if (Tk_IsTopLevel(macWin->winPtr)) {
235	if (!Tk_IsEmbedded(macWin->winPtr)) {
236	    /*
237	     * XXX This should be ShowSheetWindow for kSheetWindowClass
238	     * XXX windows that have a wmPtr->master parent set.
239	     */
240	    WindowRef wRef = TkMacOSXDrawableWindow(window);
241
242	    if ((macWin->winPtr->wmInfoPtr->macClass == kSheetWindowClass)
243		    && (macWin->winPtr->wmInfoPtr->master != None)) {
244		ShowSheetWindow(wRef, TkMacOSXDrawableWindow(
245			macWin->winPtr->wmInfoPtr->master));
246	    } else {
247		ShowWindow(wRef);
248	    }
249	}
250	TkMacOSXInvalClipRgns((Tk_Window) macWin->winPtr);
251
252	/*
253	 * We only need to send the MapNotify event
254	 * for toplevel windows.
255	 */
256
257	event.xany.serial = display->request;
258	event.xany.send_event = False;
259	event.xany.display = display;
260
261	event.xmap.window = window;
262	event.xmap.type = MapNotify;
263	event.xmap.event = window;
264	event.xmap.override_redirect = macWin->winPtr->atts.override_redirect;
265	Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
266    } else {
267	/*
268	 * Generate damage for that area of the window
269	 */
270
271	TkMacOSXInvalClipRgns((Tk_Window) macWin->winPtr->parentPtr);
272	TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW);
273    }
274
275    /*
276     * Generate VisibilityNotify events for window and all mapped children.
277     */
278
279    event.xany.send_event = False;
280    event.xany.display = display;
281    event.xvisibility.type = VisibilityNotify;
282    event.xvisibility.state = VisibilityUnobscured;
283    NotifyVisibility(macWin->winPtr, &event);
284}
285
286/*
287 *----------------------------------------------------------------------
288 *
289 * NotifyVisibility --
290 *
291 *	Recursively called helper proc for XMapWindow().
292
293 * Results:
294 *	None.
295 *
296 * Side effects:
297 *	VisibilityNotify events are queued.
298 *
299 *----------------------------------------------------------------------
300 */
301
302static void
303NotifyVisibility(
304    TkWindow *winPtr,
305    XEvent *eventPtr)
306{
307    if (winPtr->atts.event_mask & VisibilityChangeMask) {
308	eventPtr->xany.serial = LastKnownRequestProcessed(winPtr->display);
309	eventPtr->xvisibility.window = winPtr->window;
310	Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL);
311    }
312    for (winPtr = winPtr->childList; winPtr != NULL;
313	    winPtr = winPtr->nextPtr) {
314	if (winPtr->flags & TK_MAPPED) {
315	    NotifyVisibility(winPtr, eventPtr);
316	}
317    }
318}
319
320/*
321 *----------------------------------------------------------------------
322 *
323 * XUnmapWindow --
324 *
325 *	Unmap the given X Window to the screen. See X window
326 *	documentation for more details.
327 *
328 * Results:
329 *	None.
330 *
331 * Side effects:
332 *	The subwindow or toplevel may be removed from the screen.
333 *
334 *----------------------------------------------------------------------
335 */
336
337void
338XUnmapWindow(
339    Display* display,		/* Display. */
340    Window window)		/* Window. */
341{
342    MacDrawable *macWin = (MacDrawable *) window;
343    XEvent event;
344
345    display->request++;
346    macWin->winPtr->flags &= ~TK_MAPPED;
347    if (Tk_IsTopLevel(macWin->winPtr)) {
348	if (!Tk_IsEmbedded(macWin->winPtr)
349		&& macWin->winPtr->wmInfoPtr->hints.initial_state != IconicState) {
350	    /*
351	     * XXX This should be HideSheetWindow for kSheetWindowClass
352	     * XXX windows that have a wmPtr->master parent set.
353	     */
354	    WindowRef wref = TkMacOSXDrawableWindow(window);
355
356	    if ((macWin->winPtr->wmInfoPtr->macClass == kSheetWindowClass)
357		    && (macWin->winPtr->wmInfoPtr->master != None)) {
358		HideSheetWindow(wref);
359	    } else {
360		HideWindow(wref);
361	    }
362	}
363	TkMacOSXInvalClipRgns((Tk_Window) macWin->winPtr);
364
365	/*
366	 * We only need to send the UnmapNotify event
367	 * for toplevel windows.
368	 */
369	event.xany.serial = display->request;
370	event.xany.send_event = False;
371	event.xany.display = display;
372
373	event.xunmap.type = UnmapNotify;
374	event.xunmap.window = window;
375	event.xunmap.event = window;
376	event.xunmap.from_configure = false;
377	Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
378    } else {
379	/*
380	 * Generate damage for that area of the window.
381	 */
382
383	TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW);
384	TkMacOSXInvalClipRgns((Tk_Window) macWin->winPtr->parentPtr);
385    }
386}
387
388/*
389 *----------------------------------------------------------------------
390 *
391 * XResizeWindow --
392 *
393 *	Resize a given X window. See X windows documentation for
394 *	further details.
395 *
396 * Results:
397 *	None.
398 *
399 * Side effects:
400 *	None.
401 *
402 *----------------------------------------------------------------------
403 */
404
405void
406XResizeWindow(
407    Display* display,		/* Display. */
408    Window window,		/* Window. */
409    unsigned int width,
410    unsigned int height)
411{
412    MacDrawable *macWin = (MacDrawable *) window;
413
414    display->request++;
415    if (Tk_IsTopLevel(macWin->winPtr) && !Tk_IsEmbedded(macWin->winPtr)) {
416	WindowRef w = TkMacOSXDrawableWindow(window);
417
418	if (w) {
419	    Rect bounds;
420
421	    ChkErr(GetWindowBounds, w, kWindowContentRgn, &bounds);
422	    bounds.right = bounds.left + width;
423	    bounds.bottom = bounds.top + height;
424	    ChkErr(SetWindowBounds, w, kWindowContentRgn, &bounds);
425	}
426    } else {
427	MoveResizeWindow(macWin);
428    }
429}
430
431/*
432 *----------------------------------------------------------------------
433 *
434 * XMoveResizeWindow --
435 *
436 *	Move or resize a given X window. See X windows documentation
437 *	for further details.
438 *
439 * Results:
440 *	None.
441 *
442 * Side effects:
443 *	None.
444 *
445 *----------------------------------------------------------------------
446 */
447
448void
449XMoveResizeWindow(
450    Display* display,		/* Display. */
451    Window window,		/* Window. */
452    int x, int y,
453    unsigned int width,
454    unsigned int height)
455{
456    MacDrawable * macWin = (MacDrawable *) window;
457
458    display->request++;
459    if (Tk_IsTopLevel(macWin->winPtr) && !Tk_IsEmbedded(macWin->winPtr)) {
460	WindowRef w = TkMacOSXDrawableWindow(window);
461
462	if (w) {
463	    Rect bounds;
464
465	    bounds.left = x + macWin->winPtr->wmInfoPtr->xInParent;
466	    bounds.right = bounds.left + width;
467	    bounds.top = y + macWin->winPtr->wmInfoPtr->yInParent;
468	    bounds.bottom = bounds.top + height;
469	    ChkErr(SetWindowBounds, w, kWindowContentRgn, &bounds);
470	}
471    } else {
472	MoveResizeWindow(macWin);
473    }
474}
475
476/*
477 *----------------------------------------------------------------------
478 *
479 * XMoveWindow --
480 *
481 *	Move a given X window. See X windows documentation for further
482 *	details.
483 *
484 * Results:
485 *	None.
486 *
487 * Side effects:
488 *	None.
489 *
490 *----------------------------------------------------------------------
491 */
492
493void
494XMoveWindow(
495    Display* display,		/* Display. */
496    Window window,		/* Window. */
497    int x,
498    int y)
499{
500    MacDrawable *macWin = (MacDrawable *) window;
501
502    display->request++;
503    if (Tk_IsTopLevel(macWin->winPtr) && !Tk_IsEmbedded(macWin->winPtr)) {
504	WindowRef w = TkMacOSXDrawableWindow(window);
505
506	if (w) {
507	    ChkErr(MoveWindowStructure, w, x, y);
508	}
509    } else {
510	MoveResizeWindow(macWin);
511    }
512}
513
514/*
515 *----------------------------------------------------------------------
516 *
517 * MoveResizeWindow --
518 *
519 *	Helper proc for XResizeWindow, XMoveResizeWindow and XMoveWindow.
520 *
521 * Results:
522 *	None.
523 *
524 * Side effects:
525 *	None.
526 *
527 *----------------------------------------------------------------------
528 */
529
530static void
531MoveResizeWindow(
532    MacDrawable *macWin)
533{
534    int deltaX = 0, deltaY = 0, parentBorderwidth = 0;
535    MacDrawable *macParent = NULL;
536    CGrafPtr destPort = TkMacOSXGetDrawablePort((Drawable) macWin);
537
538    /*
539     * Find the Parent window, for an embedded window it will be its container.
540     */
541    if (Tk_IsEmbedded(macWin->winPtr)) {
542	TkWindow *contWinPtr = TkpGetOtherWindow(macWin->winPtr);
543
544	if (contWinPtr) {
545	    macParent = contWinPtr->privatePtr;
546	} else {
547	    /*
548	     * Here we should handle out of process embedding.
549	     * At this point, we are assuming that the changes.x,y is not
550	     * maintained, if you need the info get it from Tk_GetRootCoords,
551	     * and that the toplevel sits at 0,0 when it is drawn.
552	     */
553	}
554    } else {
555	/*
556	 * TODO: update all xOff & yOffs
557	 */
558
559	macParent = macWin->winPtr->parentPtr->privatePtr;
560	parentBorderwidth = macWin->winPtr->parentPtr->changes.border_width;
561    }
562    if (macParent) {
563	deltaX = macParent->xOff + parentBorderwidth +
564		macWin->winPtr->changes.x - macWin->xOff;
565	deltaY = macParent->yOff + parentBorderwidth +
566		macWin->winPtr->changes.y - macWin->yOff;
567    }
568    if (destPort) {
569	TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW);
570	if (macParent) {
571	    TkMacOSXInvalClipRgns((Tk_Window) macParent->winPtr);
572	}
573    }
574    UpdateOffsets(macWin->winPtr, deltaX, deltaY);
575    if (destPort) {
576	TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW);
577    }
578    GenerateConfigureNotify(macWin->winPtr, 0);
579}
580
581/*
582 *----------------------------------------------------------------------
583 *
584 * GenerateConfigureNotify --
585 *
586 *	Generates ConfigureNotify events for all the child widgets
587 *	of the widget passed in the winPtr parameter. If includeWin
588 *	is true, also generates ConfigureNotify event for the
589 *	widget itself.
590 *
591 * Results:
592 *	None.
593 *
594 * Side effects:
595 *	ConfigureNotify events will be posted.
596 *
597 *----------------------------------------------------------------------
598 */
599
600static void
601GenerateConfigureNotify (TkWindow *winPtr, int includeWin)
602{
603    TkWindow *childPtr;
604
605    for (childPtr = winPtr->childList; childPtr != NULL;
606			       childPtr = childPtr->nextPtr) {
607	if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) {
608	    continue;
609	}
610	GenerateConfigureNotify(childPtr, 1);
611    }
612    if (includeWin) {
613	TkDoConfigureNotify(winPtr);
614    }
615}
616
617/*
618 *----------------------------------------------------------------------
619 *
620 * XRaiseWindow --
621 *
622 *	Change the stacking order of a window.
623 *
624 * Results:
625 *	None.
626 *
627 * Side effects:
628 *	Changes the stacking order of the specified window.
629 *
630 *----------------------------------------------------------------------
631 */
632
633void
634XRaiseWindow(
635    Display* display,		/* Display. */
636    Window window)		/* Window. */
637{
638    MacDrawable *macWin = (MacDrawable *) window;
639
640    display->request++;
641    if (Tk_IsTopLevel(macWin->winPtr) && !Tk_IsEmbedded(macWin->winPtr)) {
642	TkWmRestackToplevel(macWin->winPtr, Above, NULL);
643    } else {
644	/*
645	 * TODO: this should generate damage
646	 */
647    }
648}
649
650#if 0
651/*
652 *----------------------------------------------------------------------
653 *
654 * XLowerWindow --
655 *
656 *	Change the stacking order of a window.
657 *
658 * Results:
659 *	None.
660 *
661 * Side effects:
662 *	Changes the stacking order of the specified window.
663 *
664 *----------------------------------------------------------------------
665 */
666
667void
668XLowerWindow(
669    Display* display,		/* Display. */
670    Window window)		/* Window. */
671{
672    MacDrawable *macWin = (MacDrawable *) window;
673
674    display->request++;
675    if (Tk_IsTopLevel(macWin->winPtr) && !Tk_IsEmbedded(macWin->winPtr)) {
676	TkWmRestackToplevel(macWin->winPtr, Below, NULL);
677    } else {
678	/*
679	 * TODO: this should generate damage
680	 */
681    }
682}
683#endif
684
685/*
686 *----------------------------------------------------------------------
687 *
688 * XConfigureWindow --
689 *
690 *	Change the size, position, stacking, or border of the specified
691 *	window.
692 *
693 * Results:
694 *	None.
695 *
696 * Side effects:
697 *	Changes the attributes of the specified window. Note that we
698 *	ignore the passed in values and use the values stored in the
699 *	TkWindow data structure.
700 *
701 *----------------------------------------------------------------------
702 */
703
704void
705XConfigureWindow(
706    Display* display,		/* Display. */
707    Window w,			/* Window. */
708    unsigned int value_mask,
709    XWindowChanges* values)
710{
711    MacDrawable *macWin = (MacDrawable *) w;
712    TkWindow *winPtr = macWin->winPtr;
713
714    display->request++;
715
716    /*
717     * Change the shape and/or position of the window.
718     */
719
720    if (value_mask & (CWX|CWY|CWWidth|CWHeight)) {
721	XMoveResizeWindow(display, w, winPtr->changes.x, winPtr->changes.y,
722		winPtr->changes.width, winPtr->changes.height);
723    }
724
725    /*
726     * Change the stacking order of the window. Tk actuall keeps all
727     * the information we need for stacking order. All we need to do
728     * is make sure the clipping regions get updated and generate damage
729     * that will ensure things get drawn correctly.
730     */
731
732    if (value_mask & CWStackMode) {
733	Rect bounds;
734	WindowRef wRef = TkMacOSXDrawableWindow(w);
735
736	if (wRef) {
737	    TkMacOSXInvalClipRgns((Tk_Window) winPtr->parentPtr);
738	    TkMacOSXWinBounds(winPtr, &bounds);
739	    InvalWindowRect(wRef, &bounds);
740	}
741    }
742
743    /* TkGenWMMoveRequestEvent(macWin->winPtr,
744	    macWin->winPtr->changes.x, macWin->winPtr->changes.y); */
745}
746
747/*
748 *----------------------------------------------------------------------
749 *
750 * TkMacOSXUpdateClipRgn --
751 *
752 *	This function updates the cliping regions for a given window
753 *	and all of its children. Once updated the TK_CLIP_INVALID flag
754 *	in the subwindow data structure is unset. The TK_CLIP_INVALID
755 *	flag should always be unset before any drawing is attempted.
756 *
757 * Results:
758 *	None.
759 *
760 * Side effects:
761 *	The clip regions for the window and its children are updated.
762 *
763 *----------------------------------------------------------------------
764 */
765
766void
767TkMacOSXUpdateClipRgn(
768    TkWindow *winPtr)
769{
770    MacDrawable *macWin;
771
772    if (winPtr == NULL) {
773	return;
774    }
775    macWin = winPtr->privatePtr;
776    if (macWin && macWin->flags & TK_CLIP_INVALID) {
777	TkWindow *win2Ptr;
778
779	if (Tk_IsMapped(winPtr)) {
780	    int rgnChanged = 0;
781	    CGRect bounds;
782	    HIMutableShapeRef rgn;
783
784	    /*
785	     * Start with a region defined by the window bounds.
786	     */
787
788	    TkMacOSXWinCGBounds(winPtr, &bounds);
789	    rgn = TkMacOSXHIShapeCreateMutableWithRect(&bounds);
790
791	    /*
792	     * Clip away the area of any windows that may obscure this
793	     * window.
794	     * For a non-toplevel window, first, clip to the parents visible
795	     * clip region.
796	     * Second, clip away any siblings that are higher in the
797	     * stacking order.
798	     * For an embedded toplevel, just clip to the container's visible
799	     * clip region. Remember, we only allow one contained window
800	     * in a frame, and don't support any other widgets in the frame
801	     * either. This is not currently enforced, however.
802	     */
803
804	    if (!Tk_IsTopLevel(winPtr)) {
805		TkMacOSXUpdateClipRgn(winPtr->parentPtr);
806		if (winPtr->parentPtr) {
807		    ChkErr(HIShapeIntersect,
808			    winPtr->parentPtr->privatePtr->aboveVisRgn, rgn,
809			    rgn);
810		}
811		win2Ptr = winPtr;
812		while ((win2Ptr = win2Ptr->nextPtr)) {
813		    if (Tk_IsTopLevel(win2Ptr) || !Tk_IsMapped(win2Ptr)) {
814			continue;
815		    }
816		    TkMacOSXWinCGBounds(win2Ptr, &bounds);
817		    ChkErr(TkMacOSHIShapeDifferenceWithRect, rgn, &bounds);
818		}
819	    } else if (Tk_IsEmbedded(winPtr)) {
820		win2Ptr = TkpGetOtherWindow(winPtr);
821		if (win2Ptr) {
822		    TkMacOSXUpdateClipRgn(win2Ptr);
823		    ChkErr(HIShapeIntersect,
824			    win2Ptr->privatePtr->aboveVisRgn, rgn, rgn);
825		} else if (tkMacOSXEmbedHandler != NULL) {
826		    HIShapeRef visRgn;
827
828		    TkMacOSXCheckTmpQdRgnEmpty();
829		    tkMacOSXEmbedHandler->getClipProc((Tk_Window) winPtr,
830			    tkMacOSXtmpQdRgn);
831		    visRgn = HIShapeCreateWithQDRgn(tkMacOSXtmpQdRgn);
832		    SetEmptyRgn(tkMacOSXtmpQdRgn);
833		    ChkErr(HIShapeIntersect, visRgn, rgn, rgn);
834		}
835
836		/*
837		 * TODO: Here we should handle out of process embedding.
838		 */
839	    } else if (winPtr->wmInfoPtr->attributes &
840		    kWindowResizableAttribute) {
841		HIViewRef growBoxView;
842		OSErr err = HIViewFindByID(HIViewGetRoot(
843			TkMacOSXDrawableWindow(winPtr->window)),
844			kHIViewWindowGrowBoxID, &growBoxView);
845
846		if (err == noErr) {
847		    ChkErr(HIViewGetFrame, growBoxView, &bounds);
848		    bounds = CGRectOffset(bounds,
849			    -winPtr->wmInfoPtr->xInParent,
850			    -winPtr->wmInfoPtr->yInParent);
851		    ChkErr(TkMacOSHIShapeDifferenceWithRect, rgn, &bounds);
852		}
853	    }
854	    macWin->aboveVisRgn = HIShapeCreateCopy(rgn);
855
856	    /*
857	     * The final clip region is the aboveVis region (or visible
858	     * region) minus all the children of this window.
859	     * If the window is a container, we must also subtract the region
860	     * of the embedded window.
861	     */
862
863	    win2Ptr = winPtr->childList;
864	    while (win2Ptr) {
865		if (Tk_IsTopLevel(win2Ptr) || !Tk_IsMapped(win2Ptr)) {
866		    win2Ptr = win2Ptr->nextPtr;
867		    continue;
868		}
869		TkMacOSXWinCGBounds(win2Ptr, &bounds);
870		ChkErr(TkMacOSHIShapeDifferenceWithRect, rgn, &bounds);
871		rgnChanged = 1;
872		win2Ptr = win2Ptr->nextPtr;
873	    }
874
875	    if (Tk_IsContainer(winPtr)) {
876		win2Ptr = TkpGetOtherWindow(winPtr);
877		if (win2Ptr) {
878		    if (Tk_IsMapped(win2Ptr)) {
879			TkMacOSXWinCGBounds(win2Ptr, &bounds);
880			ChkErr(TkMacOSHIShapeDifferenceWithRect, rgn, &bounds);
881			rgnChanged = 1;
882		    }
883		}
884
885		/*
886		 * TODO: Here we should handle out of process embedding.
887		 */
888	    }
889	    if (rgnChanged) {
890		HIShapeRef diffRgn = HIShapeCreateDifference(
891			macWin->aboveVisRgn, rgn);
892
893		if (!HIShapeIsEmpty(diffRgn)) {
894		    macWin->visRgn = HIShapeCreateCopy(rgn);
895		}
896		CFRelease(diffRgn);
897	    }
898	    CFRelease(rgn);
899	} else {
900	    /*
901	     * An unmapped window has empty clip regions to prevent any
902	     * (erroneous) drawing into it or its children from becoming
903	     * visible. [Bug 940117]
904	     */
905
906	    if (!Tk_IsTopLevel(winPtr)) {
907		TkMacOSXUpdateClipRgn(winPtr->parentPtr);
908	    } else if (Tk_IsEmbedded(winPtr)) {
909		win2Ptr = TkpGetOtherWindow(winPtr);
910		if (win2Ptr) {
911		    TkMacOSXUpdateClipRgn(win2Ptr);
912		}
913	    }
914	    macWin->aboveVisRgn = TkMacOSXHIShapeCreateEmpty();
915	}
916	if (!macWin->visRgn) {
917	    macWin->visRgn = HIShapeCreateCopy(macWin->aboveVisRgn);
918	}
919	macWin->flags &= ~TK_CLIP_INVALID;
920
921#ifdef TK_MAC_DEBUG_CLIP_REGIONS
922	TkMacOSXDebugFlashRegion((Drawable) macWin, macWin->visRgn);
923#endif /* TK_MAC_DEBUG_CLIP_REGIONS */
924    }
925}
926
927/*
928 *----------------------------------------------------------------------
929 *
930 * TkMacOSXVisableClipRgn --
931 *
932 *	This function returnd the Macintosh cliping region for the
933 *	given window. A NULL Rgn means the window is not visible.
934 *
935 * Results:
936 *	The region.
937 *
938 * Side effects:
939 *	None.
940 *
941 *----------------------------------------------------------------------
942 */
943
944RgnHandle
945TkMacOSXVisableClipRgn(
946    TkWindow *winPtr)
947{
948    static RgnHandle visQdRgn = NULL;
949
950    if (visQdRgn == NULL) {
951	visQdRgn = NewRgn();
952    }
953    if (winPtr->privatePtr->flags & TK_CLIP_INVALID) {
954	TkMacOSXUpdateClipRgn(winPtr);
955    }
956    ChkErr(HIShapeGetAsQDRgn, winPtr->privatePtr->visRgn, visQdRgn);
957    return visQdRgn;
958}
959
960/*
961 *----------------------------------------------------------------------
962 *
963 * TkMacOSXInvalidateWindow --
964 *
965 *	This function makes the window as invalid will generate damage
966 *	for the window.
967 *
968 * Results:
969 *	None.
970 *
971 * Side effects:
972 *	Damage is created.
973 *
974 *----------------------------------------------------------------------
975 */
976
977void
978TkMacOSXInvalidateWindow(
979    MacDrawable *macWin,	/* Make window that's causing damage. */
980    int flag)			/* Should be TK_WINDOW_ONLY or
981				 * TK_PARENT_WINDOW */
982{
983    WindowRef windowRef;
984    HIShapeRef rgn;
985
986    windowRef = TkMacOSXDrawableWindow((Drawable)macWin);
987    if (macWin->flags & TK_CLIP_INVALID) {
988	TkMacOSXUpdateClipRgn(macWin->winPtr);
989    }
990    rgn = (flag == TK_WINDOW_ONLY) ? macWin->visRgn : macWin->aboveVisRgn;
991    if (!HIShapeIsEmpty(rgn)) {
992	TkMacOSXCheckTmpQdRgnEmpty();
993	ChkErr(HIShapeGetAsQDRgn, rgn, tkMacOSXtmpQdRgn);
994	InvalWindowRgn(windowRef, tkMacOSXtmpQdRgn);
995	SetEmptyRgn(tkMacOSXtmpQdRgn);
996    }
997#ifdef TK_MAC_DEBUG_CLIP_REGIONS
998    TkMacOSXDebugFlashRegion((Drawable) macWin, rgn);
999#endif /* TK_MAC_DEBUG_CLIP_REGIONS */
1000}
1001
1002/*
1003 *----------------------------------------------------------------------
1004 *
1005 * TkMacOSXGetDrawableWindow --
1006 *
1007 *	This function returns the WindowRef for a given X drawable.
1008 *
1009 * Results:
1010 *	A WindowRef, or NULL for off screen pixmaps.
1011 *
1012 * Side effects:
1013 *	None.
1014 *
1015 *----------------------------------------------------------------------
1016 */
1017
1018WindowRef
1019TkMacOSXDrawableWindow(
1020    Drawable drawable)
1021{
1022    MacDrawable *macWin = (MacDrawable *) drawable;
1023    WindowRef result = NULL;
1024
1025    if (!macWin || macWin->flags & TK_IS_PIXMAP) {
1026	result = NULL;
1027    } else {
1028	result = GetWindowFromPort(TkMacOSXGetDrawablePort(drawable));
1029    }
1030    return result;
1031}
1032
1033/*
1034 *----------------------------------------------------------------------
1035 *
1036 * TkMacOSXGetDrawablePort --
1037 *
1038 *	This function returns the Graphics Port for a given X drawable.
1039 *
1040 * Results:
1041 *	A CGrafPort . Either an off screen pixmap or a Window.
1042 *
1043 * Side effects:
1044 *	None.
1045 *
1046 *----------------------------------------------------------------------
1047 */
1048
1049CGrafPtr
1050TkMacOSXGetDrawablePort(
1051    Drawable drawable)
1052{
1053    MacDrawable *macWin = (MacDrawable *) drawable;
1054    CGrafPtr resultPort = NULL;
1055
1056    if (macWin) {
1057	if (macWin->toplevel) {
1058	    /*
1059	     * If the Drawable is in an embedded window, use the Port of its
1060	     * container.
1061	     *
1062	     * TRICKY POINT: we can have cases when a toplevel is being
1063	     * destroyed where the winPtr for the toplevel has been freed, but
1064	     * the children are not all the way destroyed. The children will
1065	     * call this function as they are being destroyed, but
1066	     * Tk_IsEmbedded will return garbage. So we check the copy of the
1067	     * TK_EMBEDDED flag we put into the toplevel's macWin flags.
1068	     */
1069
1070	    if (macWin->toplevel->flags & TK_EMBEDDED) {
1071		TkWindow *contWinPtr;
1072
1073		contWinPtr = TkpGetOtherWindow(macWin->toplevel->winPtr);
1074
1075		if (contWinPtr != NULL) {
1076		    resultPort = TkMacOSXGetDrawablePort(
1077			(Drawable) contWinPtr->privatePtr);
1078		} else if (tkMacOSXEmbedHandler != NULL) {
1079		    resultPort = tkMacOSXEmbedHandler->getPortProc(
1080			    (Tk_Window) macWin->winPtr);
1081		}
1082
1083		if (!resultPort) {
1084		    /*
1085		     * FIXME: So far as I can tell, the only time that this
1086		     * happens is when we are tearing down an embedded child
1087		     * interpreter, and most of the time, this is harmless...
1088		     * However, we really need to find why the embedding loses.
1089		     */
1090		    TkMacOSXDbgMsg("Couldn't find container");
1091		}
1092
1093		/*
1094		 * TODO: Here we should handle out of process embedding.
1095		 */
1096	    } else {
1097		resultPort = macWin->toplevel->grafPtr;
1098	    }
1099	} else {
1100	    if ((macWin->flags & TK_IS_PIXMAP) && !macWin->grafPtr) {
1101		Rect bounds = {0, 0, macWin->size.height, macWin->size.width};
1102
1103		ChkErr(NewGWorld, &macWin->grafPtr,
1104			(macWin->flags & TK_IS_BW_PIXMAP) ? 1 : 0,
1105			&bounds, NULL, NULL, 0
1106#ifdef __LITTLE_ENDIAN__
1107			| kNativeEndianPixMap
1108#endif
1109			);
1110	    }
1111	    resultPort = macWin->grafPtr;
1112	}
1113    }
1114
1115    return resultPort;
1116}
1117
1118/*
1119 *----------------------------------------------------------------------
1120 *
1121 * TkMacOSXGetRootControl --
1122 *
1123 *	This function returns the Root Control for a given X drawable.
1124 *
1125 * Results:
1126 *	A ControlRef .
1127 *
1128 * Side effects:
1129 *	None.
1130 *
1131 *----------------------------------------------------------------------
1132 */
1133
1134ControlRef
1135TkMacOSXGetRootControl(
1136    Drawable drawable)
1137{
1138    /*
1139     * will probably need to fix this up for embedding
1140     */
1141    MacDrawable *macWin = (MacDrawable *) drawable;
1142    ControlRef result = NULL;
1143
1144    if (macWin == NULL) {
1145	return NULL;
1146    }
1147    if (!(macWin->toplevel->flags & TK_EMBEDDED)) {
1148	return macWin->toplevel->rootControl;
1149    } else {
1150	TkWindow *contWinPtr;
1151
1152	contWinPtr = TkpGetOtherWindow(macWin->toplevel->winPtr);
1153
1154	if (contWinPtr != NULL) {
1155	    result = TkMacOSXGetRootControl(
1156		(Drawable) contWinPtr->privatePtr);
1157	} else if (tkMacOSXEmbedHandler != NULL) {
1158	    result = NULL;
1159	}
1160   }
1161    return result;
1162}
1163
1164/*
1165 *----------------------------------------------------------------------
1166 *
1167 * TkMacOSXInvalClipRgns --
1168 *
1169 *	This function invalidates the clipping regions for a given
1170 *	window and all of its children. This function should be
1171 *	called whenever changes are made to subwindows that would
1172 *	affect the size or position of windows.
1173 *
1174 * Results:
1175 *	None.
1176 *
1177 * Side effects:
1178 *	The cliping regions for the window and its children are
1179 *	mark invalid. (Make sure they are valid before drawing.)
1180 *
1181 *----------------------------------------------------------------------
1182 */
1183
1184void
1185TkMacOSXInvalClipRgns(
1186    Tk_Window tkwin)
1187{
1188    TkWindow *winPtr = (TkWindow *) tkwin;
1189    TkWindow *childPtr;
1190    MacDrawable *macWin = winPtr->privatePtr;
1191
1192    /*
1193     * If already marked we can stop because all
1194     * decendants will also already be marked.
1195     */
1196    if (!macWin || macWin->flags & TK_CLIP_INVALID) {
1197	return;
1198    }
1199
1200    macWin->flags |= TK_CLIP_INVALID;
1201    if (macWin->visRgn) {
1202	CFRelease(macWin->visRgn);
1203	macWin->visRgn = NULL;
1204    }
1205    if (macWin->aboveVisRgn) {
1206	CFRelease(macWin->aboveVisRgn);
1207	macWin->aboveVisRgn = NULL;
1208    }
1209
1210    /*
1211     * Invalidate clip regions for all children &
1212     * their decendants - unless the child is a toplevel.
1213     */
1214    childPtr = winPtr->childList;
1215    while (childPtr) {
1216	if (!Tk_IsTopLevel(childPtr)) {
1217	    TkMacOSXInvalClipRgns((Tk_Window) childPtr);
1218	}
1219	childPtr = childPtr->nextPtr;
1220    }
1221
1222    /*
1223     * Also, if the window is a container, mark its embedded window
1224     */
1225
1226    if (Tk_IsContainer(winPtr)) {
1227	childPtr = TkpGetOtherWindow(winPtr);
1228
1229	if (childPtr) {
1230	    TkMacOSXInvalClipRgns((Tk_Window) childPtr);
1231	}
1232
1233	/*
1234	 * TODO: Here we should handle out of process embedding.
1235	 */
1236    }
1237}
1238
1239/*
1240 *----------------------------------------------------------------------
1241 *
1242 * TkMacOSXWinBounds --
1243 *
1244 *	Given a Tk window this function determines the windows
1245 *	bounds in relation to the Macintosh window's coordinate
1246 *	system. This is also the same coordinate system as the
1247 *	Tk toplevel window in which this window is contained.
1248 *
1249 * Results:
1250 *	None.
1251 *
1252 * Side effects:
1253 *	None.
1254 *
1255 *----------------------------------------------------------------------
1256 */
1257
1258void
1259TkMacOSXWinBounds(
1260    TkWindow *winPtr,
1261    Rect *bounds)
1262{
1263    bounds->left = winPtr->privatePtr->xOff;
1264    bounds->top = winPtr->privatePtr->yOff;
1265    bounds->right = bounds->left + winPtr->changes.width;
1266    bounds->bottom = bounds->top + winPtr->changes.height;
1267}
1268
1269/*
1270 *----------------------------------------------------------------------
1271 *
1272 * TkMacOSXWinCGBounds --
1273 *
1274 *	Given a Tk window this function determines the windows
1275 *	bounds in relation to the Macintosh window's coordinate
1276 *	system. This is also the same coordinate system as the
1277 *	Tk toplevel window in which this window is contained.
1278 *
1279 * Results:
1280 *	None.
1281 *
1282 * Side effects:
1283 *	None.
1284 *
1285 *----------------------------------------------------------------------
1286 */
1287
1288void
1289TkMacOSXWinCGBounds(
1290    TkWindow *winPtr,
1291    CGRect *bounds)
1292{
1293    bounds->origin.x = winPtr->privatePtr->xOff;
1294    bounds->origin.y = winPtr->privatePtr->yOff;
1295    bounds->size.width = winPtr->changes.width;
1296    bounds->size.height = winPtr->changes.height;
1297}
1298
1299/*
1300 *----------------------------------------------------------------------
1301 *
1302 * UpdateOffsets --
1303 *
1304 *	Updates the X & Y offsets of the given TkWindow from the
1305 *	TopLevel it is a decendant of.
1306 *
1307 * Results:
1308 *	None.
1309 *
1310 * Side effects:
1311 *	The xOff & yOff fields for the Mac window datastructure
1312 *	is updated to the proper offset.
1313 *
1314 *----------------------------------------------------------------------
1315 */
1316
1317static void
1318UpdateOffsets(
1319    TkWindow *winPtr,
1320    int deltaX,
1321    int deltaY)
1322{
1323    TkWindow *childPtr;
1324
1325    if (winPtr->privatePtr == NULL) {
1326	/*
1327	 * We haven't called Tk_MakeWindowExist for this window yet. The
1328	 * offset information will be postponed and calulated at that
1329	 * time. (This will usually only happen when a mapped parent is
1330	 * being moved but has child windows that have yet to be mapped.)
1331	 */
1332	return;
1333    }
1334
1335    winPtr->privatePtr->xOff += deltaX;
1336    winPtr->privatePtr->yOff += deltaY;
1337
1338    childPtr = winPtr->childList;
1339    while (childPtr != NULL) {
1340	if (!Tk_IsTopLevel(childPtr)) {
1341	    UpdateOffsets(childPtr, deltaX, deltaY);
1342	}
1343	childPtr = childPtr->nextPtr;
1344    }
1345
1346    if (Tk_IsContainer(winPtr)) {
1347	childPtr = TkpGetOtherWindow(winPtr);
1348	if (childPtr != NULL) {
1349	    UpdateOffsets(childPtr,deltaX,deltaY);
1350	}
1351
1352	/*
1353	 * TODO: Here we should handle out of process embedding.
1354	 */
1355    }
1356}
1357
1358/*
1359 *----------------------------------------------------------------------
1360 *
1361 * Tk_GetPixmap --
1362 *
1363 *	Creates an in memory drawing surface.
1364 *
1365 * Results:
1366 *	Returns a handle to a new pixmap.
1367 *
1368 * Side effects:
1369 *	Allocates a new Macintosh GWorld.
1370 *
1371 *----------------------------------------------------------------------
1372 */
1373
1374Pixmap
1375Tk_GetPixmap(
1376    Display *display,	/* Display for new pixmap (can be null). */
1377    Drawable d,		/* Drawable where pixmap will be used (ignored). */
1378    int width,		/* Dimensions of pixmap. */
1379    int height,
1380    int depth)		/* Bits per pixel for pixmap. */
1381{
1382    MacDrawable *macPix;
1383
1384    if (display != NULL) {
1385	display->request++;
1386    }
1387    macPix = (MacDrawable *) ckalloc(sizeof(MacDrawable));
1388    macPix->winPtr = NULL;
1389    macPix->xOff = 0;
1390    macPix->yOff = 0;
1391    macPix->visRgn = NULL;
1392    macPix->aboveVisRgn = NULL;
1393    macPix->drawRect = CGRectNull;
1394    macPix->referenceCount = 0;
1395    macPix->toplevel = NULL;
1396    macPix->flags = TK_IS_PIXMAP | (depth == 1 ? TK_IS_BW_PIXMAP : 0);
1397    macPix->grafPtr = NULL;
1398    macPix->context = NULL;
1399    macPix->size = CGSizeMake(width, height);
1400    {
1401	Rect bounds = {0, 0, height, width};
1402
1403	ChkErr(NewGWorld, &macPix->grafPtr, depth == 1 ? 1 : 0, &bounds, NULL,
1404		NULL, 0
1405#ifdef __LITTLE_ENDIAN__
1406		| kNativeEndianPixMap
1407#endif
1408		);
1409    }
1410
1411    return (Pixmap) macPix;
1412}
1413
1414/*
1415 *----------------------------------------------------------------------
1416 *
1417 * Tk_FreePixmap --
1418 *
1419 *	Release the resources associated with a pixmap.
1420 *
1421 * Results:
1422 *	None.
1423 *
1424 * Side effects:
1425 *	Deletes the Macintosh GWorld created by Tk_GetPixmap.
1426 *
1427 *----------------------------------------------------------------------
1428 */
1429
1430void
1431Tk_FreePixmap(
1432    Display *display,		/* Display. */
1433    Pixmap pixmap)		/* Pixmap to destroy */
1434{
1435    MacDrawable *macPix = (MacDrawable *) pixmap;
1436
1437    display->request++;
1438    if (macPix->grafPtr) {
1439	DisposeGWorld(macPix->grafPtr);
1440    }
1441    if (macPix->context) {
1442	TkMacOSXDbgMsg("Cannot free CG backed Pixmap");
1443    }
1444    ckfree((char *) macPix);
1445}
1446