/*
 * tkMacOSXWindowEvent.c --
 *
 *	This file defines the routines for both creating and handling
 *	Window Manager class events for Tk.
 *
 * Copyright 2001, Apple Computer, Inc.
 * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net>
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 *	The following terms apply to all files originating from Apple
 *	Computer, Inc. ("Apple") and associated with the software
 *	unless explicitly disclaimed in individual files.
 *
 *
 *	Apple hereby grants permission to use, copy, modify,
 *	distribute, and license this software and its documentation
 *	for any purpose, provided that existing copyright notices are
 *	retained in all copies and that this notice is included
 *	verbatim in any distributions. No written agreement, license,
 *	or royalty fee is required for any of the authorized
 *	uses. Modifications to this software may be copyrighted by
 *	their authors and need not follow the licensing terms
 *	described here, provided that the new terms are clearly
 *	indicated on the first page of each file where they apply.
 *
 *
 *	IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE
 *	SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
 *	INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
 *	THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
 *	EVEN IF APPLE OR THE AUTHORS HAVE BEEN ADVISED OF THE
 *	POSSIBILITY OF SUCH DAMAGE.  APPLE, THE AUTHORS AND
 *	DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
 *	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.	 THIS
 *	SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND APPLE,THE
 *	AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
 *	MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 *	GOVERNMENT USE: If you are acquiring this software on behalf
 *	of the U.S. government, the Government shall have only
 *	"Restricted Rights" in the software and related documentation
 *	as defined in the Federal Acquisition Regulations (FARs) in
 *	Clause 52.227.19 (c) (2).  If you are acquiring the software
 *	on behalf of the Department of Defense, the software shall be
 *	classified as "Commercial Computer Software" and the
 *	Government shall have only "Restricted Rights" as defined in
 *	Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
 *	foregoing, the authors grant the U.S. Government and others
 *	acting in its behalf permission to use and distribute the
 *	software in accordance with the terms specified in this
 *	license.
 *
 * RCS: @(#) $Id: tkMacOSXWindowEvent.c,v 1.31 2007/12/13 15:27:10 dgp Exp $
 */

#include "tkMacOSXPrivate.h"
#include "tkMacOSXWm.h"
#include "tkMacOSXEvent.h"
#include "tkMacOSXDebug.h"

/*
#ifdef TK_MAC_DEBUG
#define TK_MAC_DEBUG_CLIP_REGIONS
#endif
*/

/*
 * Declaration of functions used only in this file
 */

static int GenerateUpdateEvent(Window window);
static int GenerateUpdates(HIMutableShapeRef updateRgn, CGRect *updateBounds,
	TkWindow *winPtr);
static int GenerateActivateEvents(Window window, int activeFlag);
static void ClearPort(CGrafPtr port, HIShapeRef updateRgn);


/*
 *----------------------------------------------------------------------
 *
 * TkMacOSXProcessApplicationEvent --
 *
 *	This processes Application level events, mainly activate
 *	and deactivate.
 *
 * Results:
 *	0.
 *
 * Side effects:
 *	Hide or reveal floating windows.
 *
 *----------------------------------------------------------------------
 */

MODULE_SCOPE int
TkMacOSXProcessApplicationEvent(
	TkMacOSXEvent *eventPtr,
	MacEventStatus *statusPtr)
{
    Tcl_CmdInfo dummy;

    /*
     * This is a bit of a hack. We get "show" events both when we come back
     * from being hidden, and whenever we are activated. I only want to run
     * the "show" proc when we have been hidden already, not as a substitute
     * for <Activate>. So I use this toggle...
     */
    static int toggleHide = 0;

    switch (eventPtr->eKind) {
	case kEventAppActivated:
	    ShowFloatingWindows();
	    break;
	case kEventAppDeactivated:
	    TkSuspendClipboard();
	    HideFloatingWindows();
	    break;
	case kEventAppQuit:
	    statusPtr->stopProcessing = 1;
	    break;
	case kEventAppHidden:
	    if (toggleHide == 0) {
		toggleHide = 1;
		if (eventPtr->interp && Tcl_GetCommandInfo(eventPtr->interp,
			"::tk::mac::OnHide", &dummy)) {
		    Tcl_GlobalEval(eventPtr->interp, "::tk::mac::OnHide");
		}
	    }
	    statusPtr->stopProcessing = 1;
	    break;
	case kEventAppShown:
	    if (toggleHide == 1) {
		toggleHide = 0;
		if (eventPtr->interp && Tcl_GetCommandInfo(eventPtr->interp,
			"::tk::mac::OnShow", &dummy)) {
		    Tcl_GlobalEval(eventPtr->interp, "::tk::mac::OnShow");
		}
	    }
	    statusPtr->stopProcessing = 1;
	    break;
	case kEventAppAvailableWindowBoundsChanged: {
	    static UInt32 prevId = 0;
	    UInt32 id;
	    OSStatus err;

	    err = ChkErr(GetEventParameter, eventPtr->eventRef,
		    kEventParamTransactionID, typeUInt32,
		    NULL, sizeof(id), NULL, &id);
	    if (err != noErr || id != prevId) {
		TkDisplay *dispPtr = TkGetDisplayList();

		prevId = id;
		TkMacOSXDisplayChanged(dispPtr->display);
	    }
	    /*
	     * Should we call ::tk::mac::OnDisplayChanged?
	     */
	    break;
	}
	default:
	    break;
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TkMacOSXProcessAppearanceEvent --
 *
 *	This processes Appearance events.
 *
 * Results:
 *	0.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

MODULE_SCOPE int
TkMacOSXProcessAppearanceEvent(
	TkMacOSXEvent *eventPtr,
	MacEventStatus *statusPtr)
{
    switch (eventPtr->eKind) {
	case kEventAppearanceScrollBarVariantChanged:
	    TkMacOSXInitScrollbarMetrics();
	    break;
	default:
	    break;
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * TkMacOSXProcessWindowEvent --
 *
 *	This processes Window level events, mainly activate
 *	and deactivate.
 *
 * Results:
 *	0.
 *
 * Side effects:
 *	Cause Windows to be moved forward or backward in the
 *	window stack.
 *
 *----------------------------------------------------------------------
 */

MODULE_SCOPE int
TkMacOSXProcessWindowEvent(
	TkMacOSXEvent * eventPtr,
	MacEventStatus * statusPtr)
{
    OSStatus err;
    WindowRef whichWindow;
    Window window;
    int eventFound = false;
    TkDisplay *dispPtr;
    TkWindow *winPtr;

    switch (eventPtr->eKind) {
	case kEventWindowActivated:
	case kEventWindowDeactivated:
	case kEventWindowUpdate:
	case kEventWindowExpanding:
	case kEventWindowBoundsChanged:
	case kEventWindowDragStarted:
	case kEventWindowDragCompleted:
	case kEventWindowConstrain:
	case kEventWindowGetRegion:
	case kEventWindowDrawContent:
	    break;
	default:
	    return 0;
	    break;
    }
    err = ChkErr(GetEventParameter, eventPtr->eventRef,
	    kEventParamDirectObject, typeWindowRef, NULL, sizeof(whichWindow),
	    NULL, &whichWindow);
    if (err != noErr) {
	return 0;
    }

    window = TkMacOSXGetXWindow(whichWindow);
    dispPtr = TkGetDisplayList();
    winPtr = (TkWindow *)Tk_IdToWindow(dispPtr->display, window);

    switch (eventPtr->eKind) {
	case kEventWindowActivated:
	case kEventWindowDeactivated:
	    if (window != None) {
		int activate = (eventPtr->eKind == kEventWindowActivated);

		eventFound |= GenerateActivateEvents(window, activate);
		eventFound |= TkMacOSXGenerateFocusEvent(window, activate);
		if (winPtr) {
		    TkMacOSXEnterExitFullscreen(winPtr, activate);
		}
		statusPtr->stopProcessing = 1;
	    }
	    break;
	case kEventWindowUpdate:
	    if (window != None && GenerateUpdateEvent(window)) {
		eventFound = true;
		statusPtr->stopProcessing = 1;
	    }
	    break;
	case kEventWindowExpanding:
	    if (winPtr) {
		winPtr->wmInfoPtr->hints.initial_state =
			TkMacOSXIsWindowZoomed(winPtr) ? ZoomState : 
			NormalState;
		Tk_MapWindow((Tk_Window) winPtr);
		/*
		 * Need to process all Tk events generated by Tk_MapWindow()
		 * before returning to ensure all children are mapped, as
		 * otherwise the Activate event that follows Expanding would
		 * not be processed by any unmapped children.
		 */
		while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {};
		while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {};
	    }
	    break;
	case kEventWindowBoundsChanged:
	    if (winPtr) {
		WmInfo *wmPtr = winPtr->wmInfoPtr;
		UInt32 attr;
		Rect bounds;
		int x = -1, y = -1, width = -1, height = -1, flags = 0;

		ChkErr(GetEventParameter, eventPtr->eventRef,
			kEventParamAttributes, typeUInt32,
			NULL, sizeof(attr), NULL, &attr);
		ChkErr(GetEventParameter, eventPtr->eventRef,
			kEventParamCurrentBounds, typeQDRectangle,
			NULL, sizeof(bounds), NULL, &bounds);
		if (attr & kWindowBoundsChangeOriginChanged) {
		    x = bounds.left - wmPtr->xInParent;
		    y = bounds.top	- wmPtr->yInParent;
		    flags |= TK_LOCATION_CHANGED;
		}
		if (attr & kWindowBoundsChangeSizeChanged) {
		    width = bounds.right  - bounds.left;
		    height = bounds.bottom - bounds.top;
		    flags |= TK_SIZE_CHANGED;
		}
		TkMacOSXInvalClipRgns((Tk_Window) winPtr);
		TkMacOSXInvalidateWindow((MacDrawable *) window,
			TK_PARENT_WINDOW);
		TkGenWMConfigureEvent((Tk_Window)winPtr, x, y, width,
			height, flags);
		if (attr & kWindowBoundsChangeUserResize ||
			attr & kWindowBoundsChangeUserDrag) {
		    TkMacOSXRunTclEventLoop();
		}
		if (wmPtr->attributes & kWindowResizableAttribute) {
		    HIViewRef growBoxView;

		    err = HIViewFindByID(HIViewGetRoot(whichWindow),
			    kHIViewWindowGrowBoxID, &growBoxView);
		    if (err == noErr) {
			ChkErr(HIViewSetNeedsDisplay, growBoxView, true);
		    }
		}
	    }
	    break;
	case kEventWindowDragStarted:
	    if (!(TkMacOSXModifierState() & cmdKey)) { 
		TkMacOSXBringWindowForward(whichWindow);
	    }
	    TkMacOSXTrackingLoop(1);
	    break;
	case kEventWindowDragCompleted: {
	    Rect maxBounds, bounds, strWidths;
	    int h = 0, v = 0;

	    TkMacOSXTrackingLoop(0);
	    ChkErr(GetWindowGreatestAreaDevice, whichWindow,
		    kWindowDragRgn, NULL, &maxBounds);
	    ChkErr(GetWindowBounds, whichWindow, kWindowStructureRgn,
		    &bounds);
	    ChkErr(GetWindowStructureWidths, whichWindow, &strWidths);
	    if (bounds.left > maxBounds.right - strWidths.left) {
		h = maxBounds.right
			- (strWidths.left ? strWidths.left : 40)
			- bounds.left;
	    } else if (bounds.right < maxBounds.left
		    + strWidths.right) {
		h = maxBounds.left
			+ (strWidths.right ? strWidths.right : 40)
			- bounds.right;
	    }
	    if (bounds.top > maxBounds.bottom - strWidths.top) {
		v = maxBounds.bottom
			- (strWidths.top ? strWidths.top : 40)
			- bounds.top;
	    } else if (bounds.bottom < maxBounds.top
		    + strWidths.bottom) {
		v = maxBounds.top
			+ (strWidths.bottom ? strWidths.bottom : 40)
			- bounds.bottom;
	    } else if (strWidths.top && bounds.top < maxBounds.top) {
		v = maxBounds.top - bounds.top;
	    }
	    if (h || v) {
		OffsetRect(&bounds, h, v);
		ChkErr(SetWindowBounds, whichWindow,
		    kWindowStructureRgn, &bounds);
	    }
	    break;
	}
	case kEventWindowConstrain:
	    if (winPtr && (winPtr->wmInfoPtr->flags & WM_FULLSCREEN) &&
		    TkMacOSXMakeFullscreen(winPtr, whichWindow, 1,
		    NULL) == TCL_OK) {
		statusPtr->stopProcessing = 1;
	    }
	    break;
	case kEventWindowGetRegion:
	    if (winPtr && (winPtr->wmInfoPtr->flags & WM_TRANSPARENT)) {
		WindowRegionCode code;

		statusPtr->stopProcessing = (CallNextEventHandler(
			eventPtr->callRef, eventPtr->eventRef) == noErr);
		err = ChkErr(GetEventParameter, eventPtr->eventRef,
			kEventParamWindowRegionCode, typeWindowRegionCode,
			NULL, sizeof(code), NULL, &code);
		if (err == noErr && code == kWindowOpaqueRgn) {
		    RgnHandle rgn;

		    err = ChkErr(GetEventParameter, eventPtr->eventRef,
			    kEventParamRgnHandle, typeQDRgnHandle, NULL,
			    sizeof(rgn), NULL, &rgn);
		    if (err == noErr) {
			SetEmptyRgn(rgn);
			statusPtr->stopProcessing = 1;
		    }
		}
	    }
	    break;
	case kEventWindowDrawContent:
	    if (winPtr && (winPtr->wmInfoPtr->flags & WM_TRANSPARENT)) {
		CGrafPtr port;

		GetPort(&port);
		ClearPort(port, NULL);
	    }
	    break;
    }

    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * GenerateUpdateEvent --
 *
 *	Given a Macintosh window update event this function generates
 *	all the Expose XEvents needed by Tk.
 *
 * Results:
 *	True if event(s) are generated - false otherwise.
 *
 * Side effects:
 *	Additional events may be place on the Tk event queue.
 *
 *----------------------------------------------------------------------
 */
static int
GenerateUpdateEvent(Window window)
{
    WindowRef macWindow;
    TkDisplay *dispPtr;
    TkWindow  *winPtr;
    int result = 0;
    CGRect updateBounds;
    HIShapeRef rgn;
    HIMutableShapeRef updateRgn;
    int dx, dy;

    dispPtr = TkGetDisplayList();
    winPtr = (TkWindow *)Tk_IdToWindow(dispPtr->display, window);

    if (winPtr ==NULL ){
	return result;
    }
    macWindow = TkMacOSXDrawableWindow(window);
    TK_IF_MAC_OS_X_API (5, HIWindowCopyShape,
	ChkErr(HIWindowCopyShape, macWindow, kWindowUpdateRgn,
		kHICoordSpaceWindow, &rgn);
	dx = -winPtr->wmInfoPtr->xInParent;
	dy = -winPtr->wmInfoPtr->yInParent;
    ) TK_ELSE_MAC_OS_X (5,
	Rect bounds;

	TkMacOSXCheckTmpQdRgnEmpty();
	ChkErr(GetWindowRegion, macWindow, kWindowUpdateRgn, tkMacOSXtmpQdRgn);
	rgn = HIShapeCreateWithQDRgn(tkMacOSXtmpQdRgn);
	SetEmptyRgn(tkMacOSXtmpQdRgn);
	ChkErr(GetWindowBounds, macWindow, kWindowContentRgn, &bounds);
	dx = -bounds.left;
	dy = -bounds.top;
    ) TK_ENDIF
    updateRgn = HIShapeCreateMutableCopy(rgn);
    CFRelease(rgn);
    ChkErr(HIShapeOffset, updateRgn, dx, dy);
    HIShapeGetBounds(updateRgn, &updateBounds);
#ifdef TK_MAC_DEBUG_CLIP_REGIONS
    TkMacOSXDebugFlashRegion(window, updateRgn);
#endif /* TK_MAC_DEBUG_CLIP_REGIONS */
    BeginUpdate(macWindow);
    if (winPtr->wmInfoPtr->flags & WM_TRANSPARENT) {
	ClearPort(TkMacOSXGetDrawablePort(window), updateRgn);
    }
    result = GenerateUpdates(updateRgn, &updateBounds, winPtr);
    EndUpdate(macWindow);
    CFRelease(updateRgn);
    if (result) {
	/*
	 * Ensure there are no pending idle-time redraws that could prevent
	 * the just posted Expose events from generating new redraws.
	 */

	Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT);
    }
    return result;
 }

/*
 *----------------------------------------------------------------------
 *
 * GenerateUpdates --
 *
 *	Given a Macintosh update region and a Tk window this function
 *	geneates a X Expose event for the window if it is within the
 *	update region. The function will then recursivly have each
 *	damaged window generate Expose events for its child windows.
 *
 * Results:
 *	True if event(s) are generated - false otherwise.
 *
 * Side effects:
 *	Additional events may be place on the Tk event queue.
 *
 *----------------------------------------------------------------------
 */

static int
GenerateUpdates(
    HIMutableShapeRef updateRgn,
    CGRect *updateBounds,
    TkWindow *winPtr)
{
    TkWindow *childPtr;
    XEvent event;
    CGRect bounds, damageBounds;
    HIShapeRef boundsRgn, damageRgn;

    TkMacOSXWinCGBounds(winPtr, &bounds);
    if (!CGRectIntersectsRect(bounds, *updateBounds)) {
	return 0;
    }
    TK_IF_MAC_OS_X_API (4, HIShapeIntersectsRect,
	if (!HIShapeIntersectsRect(updateRgn, &bounds)) {
	    return 0;
	}
    ) TK_ENDIF

    /*
     * Compute the bounding box of the area that the damage occured in.
     */

    boundsRgn = HIShapeCreateWithRect(&bounds);
    damageRgn = HIShapeCreateIntersection(updateRgn, boundsRgn);
    if (HIShapeIsEmpty(damageRgn)) {
	CFRelease(damageRgn);
	CFRelease(boundsRgn);
	return 0;
    }
    HIShapeGetBounds(damageRgn, &damageBounds);
    ChkErr(TkMacOSHIShapeUnion, boundsRgn, updateRgn, updateRgn);
    HIShapeGetBounds(updateRgn, updateBounds);
    CFRelease(damageRgn);
    CFRelease(boundsRgn);

    event.xany.serial = Tk_Display(winPtr)->request;
    event.xany.send_event = false;
    event.xany.window = Tk_WindowId(winPtr);
    event.xany.display = Tk_Display(winPtr);
    event.type = Expose;
    event.xexpose.x = damageBounds.origin.x - bounds.origin.x;
    event.xexpose.y = damageBounds.origin.y - bounds.origin.y;
    event.xexpose.width = damageBounds.size.width;
    event.xexpose.height = damageBounds.size.height;
    event.xexpose.count = 0;
    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);

    /*
     * Generate updates for the children of this window
     */

    for (childPtr = winPtr->childList; childPtr != NULL;
	    childPtr = childPtr->nextPtr) {
	if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) {
	    continue;
	}
	GenerateUpdates(updateRgn, updateBounds, childPtr);
    }

    /*
     * Generate updates for any contained windows
     */

    if (Tk_IsContainer(winPtr)) {
	childPtr = TkpGetOtherWindow(winPtr);
	if (childPtr != NULL && Tk_IsMapped(childPtr)) {
	    GenerateUpdates(updateRgn, updateBounds, childPtr);
	}

	/*
	 * TODO: Here we should handle out of process embedding.
	 */
    }

    return 1;
}

/*
 *----------------------------------------------------------------------
 *
 * GenerateActivateEvents --
 *
 *	Given a Macintosh window activate event this function generates all the
 *	X Activate events needed by Tk.
 *
 * Results:
 *	True if event(s) are generated - false otherwise.
 *
 * Side effects:
 *	Additional events may be place on the Tk event queue.
 *
 *----------------------------------------------------------------------
 */

int
GenerateActivateEvents(
    Window window,		  /* Root X window for event. */
    int activeFlag )
{
    TkWindow *winPtr;
    TkDisplay *dispPtr;

    dispPtr = TkGetDisplayList();
    winPtr = (TkWindow *) Tk_IdToWindow(dispPtr->display, window);
    if (winPtr == NULL || winPtr->window == None) {
	return false;
    }

    TkGenerateActivateEvents(winPtr,activeFlag);
    return true;
}

/*
 *----------------------------------------------------------------------
 *
 * TkMacOSXGenerateFocusEvent --
 *
 *	Given a Macintosh window activate event this function generates all the
 *	X Focus events needed by Tk.
 *
 * Results:
 *	True if event(s) are generated - false otherwise.
 *
 * Side effects:
 *	Additional events may be place on the Tk event queue.
 *
 *----------------------------------------------------------------------
 */

MODULE_SCOPE int
TkMacOSXGenerateFocusEvent(
    Window window,		/* Root X window for event. */
    int activeFlag )
{
    XEvent event;
    Tk_Window tkwin;
    TkDisplay *dispPtr;

    dispPtr = TkGetDisplayList();
    tkwin = Tk_IdToWindow(dispPtr->display, window);
    if (tkwin == NULL) {
	return false;
    }

    /*
     * Don't send focus events to windows of class help or to
     * windows with the kWindowNoActivatesAttribute.
     */
    if (((TkWindow *)tkwin)->wmInfoPtr->macClass == kHelpWindowClass ||
	    ((TkWindow *)tkwin)->wmInfoPtr->attributes &
		    kWindowNoActivatesAttribute) {
	return false;
    }

    /*
     * Generate FocusIn and FocusOut events. This event
     * is only sent to the toplevel window.
     */

    if (activeFlag) {
	event.xany.type = FocusIn;
    } else {
	event.xany.type = FocusOut;
    }

    event.xany.serial = dispPtr->display->request;
    event.xany.send_event = False;
    event.xfocus.display = dispPtr->display;
    event.xfocus.window = window;
    event.xfocus.mode = NotifyNormal;
    event.xfocus.detail = NotifyDetailNone;

    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
    return true;
}

/*
 *----------------------------------------------------------------------
 *
 * TkGenWMConfigureEvent --
 *
 *	Generate a ConfigureNotify event for Tk. Depending on the
 *	value of flag the values of width/height, x/y, or both may
 *	be changed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A ConfigureNotify event is sent to Tk.
 *
 *----------------------------------------------------------------------
 */

void
TkGenWMConfigureEvent(
    Tk_Window tkwin,
    int x,
    int y,
    int width,
    int height,
    int flags)
{
    XEvent event;
    WmInfo *wmPtr;
    TkWindow *winPtr = (TkWindow *) tkwin;

    if (tkwin == NULL) {
	return;
    }

    event.type = ConfigureNotify;
    event.xconfigure.serial = Tk_Display(tkwin)->request;
    event.xconfigure.send_event = False;
    event.xconfigure.display = Tk_Display(tkwin);
    event.xconfigure.event = Tk_WindowId(tkwin);
    event.xconfigure.window = Tk_WindowId(tkwin);
    event.xconfigure.border_width = winPtr->changes.border_width;
    event.xconfigure.override_redirect = winPtr->atts.override_redirect;
    if (winPtr->changes.stack_mode == Above) {
	event.xconfigure.above = winPtr->changes.sibling;
    } else {
	event.xconfigure.above = None;
    }

    if (!(flags & TK_LOCATION_CHANGED)) {
	x = Tk_X(tkwin);
	y = Tk_Y(tkwin);
    }
    if (!(flags & TK_SIZE_CHANGED)) {
	width = Tk_Width(tkwin);
	height = Tk_Height(tkwin);
    }
    event.xconfigure.x = x;
    event.xconfigure.y = y;
    event.xconfigure.width = width;
    event.xconfigure.height = height;

    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);

    /*
     * Update window manager information.
     */
    if (Tk_IsTopLevel(winPtr)) {
	wmPtr = winPtr->wmInfoPtr;
	if (flags & TK_LOCATION_CHANGED) {
	    wmPtr->x = x;
	    wmPtr->y = y;
	    wmPtr->flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
	}
	if ((flags & TK_SIZE_CHANGED) && !(wmPtr->flags & WM_SYNC_PENDING) &&
		((width != Tk_Width(tkwin)) || (height != Tk_Height(tkwin)))) {
	    if ((wmPtr->width == -1) && (width == winPtr->reqWidth)) {
		/*
		 * Don't set external width, since the user didn't change it
		 * from what the widgets asked for.
		 */
	    } else {
		if (wmPtr->gridWin != NULL) {
		    wmPtr->width = wmPtr->reqGridWidth
			+ (width - winPtr->reqWidth)/wmPtr->widthInc;
		    if (wmPtr->width < 0) {
			wmPtr->width = 0;
		    }
		} else {
		    wmPtr->width = width;
		}
	    }
	    if ((wmPtr->height == -1) && (height == winPtr->reqHeight)) {
		/*
		 * Don't set external height, since the user didn't change it
		 * from what the widgets asked for.
		 */
	    } else {
		if (wmPtr->gridWin != NULL) {
		    wmPtr->height = wmPtr->reqGridHeight
			+ (height - winPtr->reqHeight)/wmPtr->heightInc;
		    if (wmPtr->height < 0) {
			wmPtr->height = 0;
		    }
		} else {
		    wmPtr->height = height;
		}
	    }
	    wmPtr->configWidth = width;
	    wmPtr->configHeight = height;
	}
    }

    /*
     * Now set up the changes structure. Under X we wait for the
     * ConfigureNotify to set these values. On the Mac we know imediatly that
     * this is what we want - so we just set them. However, we need to
     * make sure the windows clipping region is marked invalid so the
     * change is visible to the subwindow.
     */
    winPtr->changes.x = x;
    winPtr->changes.y = y;
    winPtr->changes.width = width;
    winPtr->changes.height = height;
    TkMacOSXInvalClipRgns(tkwin);
}

/*
 *----------------------------------------------------------------------
 *
 * TkGenWMDestroyEvent --
 *
 *	Generate a WM Destroy event for Tk.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A WM_PROTOCOL/WM_DELETE_WINDOW event is sent to Tk.
 *
 *----------------------------------------------------------------------
 */

void
TkGenWMDestroyEvent(
    Tk_Window tkwin)
{
    XEvent event;

    event.xany.serial = Tk_Display(tkwin)->request;
    event.xany.send_event = False;
    event.xany.display = Tk_Display(tkwin);

    event.xclient.window = Tk_WindowId(tkwin);
    event.xclient.type = ClientMessage;
    event.xclient.message_type = Tk_InternAtom(tkwin, "WM_PROTOCOLS");
    event.xclient.format = 32;
    event.xclient.data.l[0] = Tk_InternAtom(tkwin, "WM_DELETE_WINDOW");
    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
}

/*
 *----------------------------------------------------------------------
 *
 * TkWmProtocolEventProc --
 *
 *	This procedure is called by the Tk_HandleEvent whenever a
 *	ClientMessage event arrives whose type is "WM_PROTOCOLS".
 *	This procedure handles the message from the window manager
 *	in an appropriate fashion.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Depends on what sort of handler, if any, was set up for the
 *	protocol.
 *
 *----------------------------------------------------------------------
 */

void
TkWmProtocolEventProc(
    TkWindow *winPtr,		/* Window to which the event was sent. */
    XEvent *eventPtr)		/* X event. */
{
    WmInfo *wmPtr;
    ProtocolHandler *protPtr;
    Tcl_Interp *interp;
    Atom protocol;
    int result;

    wmPtr = winPtr->wmInfoPtr;
    if (wmPtr == NULL) {
	return;
    }
    protocol = (Atom) eventPtr->xclient.data.l[0];
    for (protPtr = wmPtr->protPtr; protPtr != NULL;
		protPtr = protPtr->nextPtr) {
	if (protocol == protPtr->protocol) {
	    Tcl_Preserve((ClientData) protPtr);
	    interp = protPtr->interp;
	    Tcl_Preserve((ClientData) interp);
	    result = Tcl_GlobalEval(interp, protPtr->command);
	    if (result != TCL_OK) {
		Tcl_AddErrorInfo(interp, "\n    (command for \"");
		Tcl_AddErrorInfo(interp,
			Tk_GetAtomName((Tk_Window) winPtr, protocol));
		Tcl_AddErrorInfo(interp, "\" window manager protocol)");
		Tk_BackgroundError(interp);
	    }
	    Tcl_Release((ClientData) interp);
	    Tcl_Release((ClientData) protPtr);
	    return;
	}
    }

    /*
     * No handler was present for this protocol. If this is a
     * WM_DELETE_WINDOW message then just destroy the window.
     */

    if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
	Tk_DestroyWindow((Tk_Window) winPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_MacOSXIsAppInFront --
 *
 *	Returns 1 if this app is the foreground app.
 *
 * Results:
 *	1 if app is in front, 0 otherwise.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
Tk_MacOSXIsAppInFront(void)
{
    OSStatus err;
    ProcessSerialNumber frontPsn, ourPsn = {0, kCurrentProcess};
    Boolean isFrontProcess = true;

    err = ChkErr(GetFrontProcess, &frontPsn);
    if (err == noErr) {
	ChkErr(SameProcess, &frontPsn, &ourPsn, &isFrontProcess);
    }
    
    return (isFrontProcess == true);
}

/*
 *----------------------------------------------------------------------
 *
 * ClearPort --
 *
 *	Clear (i.e. fill with transparent color) the given port.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
static void
ClearPort(
    CGrafPtr port,
    HIShapeRef updateRgn)
{
    CGContextRef context;
    Rect bounds;
    CGRect rect;

    GetPortBounds(port, &bounds);
    QDBeginCGContext(port, &context);
    SyncCGContextOriginWithPort(context, port);
    CGContextConcatCTM(context, CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0,
	    bounds.bottom - bounds.top));
    if (updateRgn) {
	ChkErr(HIShapeReplacePathInCGContext, updateRgn, context);
	CGContextEOClip(context);
    }
    rect = CGRectMake(0, 0, bounds.right, bounds.bottom);
    CGContextClearRect(context, rect);
    QDEndCGContext(port, &context);
}
