1/*
2 * tkMacOSXWindowEvent.c --
3 *
4 *	This file defines the routines for both creating and handling
5 *	Window Manager class events for Tk.
6 *
7 * Copyright 2001, Apple Computer, Inc.
8 * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net>
9 *
10 * See the file "license.terms" for information on usage and redistribution of
11 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 *	The following terms apply to all files originating from Apple
14 *	Computer, Inc. ("Apple") and associated with the software
15 *	unless explicitly disclaimed in individual files.
16 *
17 *
18 *	Apple hereby grants permission to use, copy, modify,
19 *	distribute, and license this software and its documentation
20 *	for any purpose, provided that existing copyright notices are
21 *	retained in all copies and that this notice is included
22 *	verbatim in any distributions. No written agreement, license,
23 *	or royalty fee is required for any of the authorized
24 *	uses. Modifications to this software may be copyrighted by
25 *	their authors and need not follow the licensing terms
26 *	described here, provided that the new terms are clearly
27 *	indicated on the first page of each file where they apply.
28 *
29 *
30 *	IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE
31 *	SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
32 *	INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
33 *	THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
34 *	EVEN IF APPLE OR THE AUTHORS HAVE BEEN ADVISED OF THE
35 *	POSSIBILITY OF SUCH DAMAGE.  APPLE, THE AUTHORS AND
36 *	DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
37 *	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
38 *	FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.	 THIS
39 *	SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND APPLE,THE
40 *	AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
41 *	MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
42 *
43 *	GOVERNMENT USE: If you are acquiring this software on behalf
44 *	of the U.S. government, the Government shall have only
45 *	"Restricted Rights" in the software and related documentation
46 *	as defined in the Federal Acquisition Regulations (FARs) in
47 *	Clause 52.227.19 (c) (2).  If you are acquiring the software
48 *	on behalf of the Department of Defense, the software shall be
49 *	classified as "Commercial Computer Software" and the
50 *	Government shall have only "Restricted Rights" as defined in
51 *	Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
52 *	foregoing, the authors grant the U.S. Government and others
53 *	acting in its behalf permission to use and distribute the
54 *	software in accordance with the terms specified in this
55 *	license.
56 *
57 * RCS: @(#) $Id: tkMacOSXWindowEvent.c,v 1.3.2.26 2007/11/09 06:26:56 das Exp $
58 */
59
60#include "tkMacOSXPrivate.h"
61#include "tkMacOSXWm.h"
62#include "tkMacOSXEvent.h"
63#include "tkMacOSXDebug.h"
64
65/*
66#ifdef TK_MAC_DEBUG
67#define TK_MAC_DEBUG_CLIP_REGIONS
68#endif
69*/
70
71/*
72 * Declaration of functions used only in this file
73 */
74
75static int GenerateUpdateEvent(Window window);
76static int GenerateUpdates(HIMutableShapeRef updateRgn, CGRect *updateBounds,
77	TkWindow *winPtr);
78static int GenerateActivateEvents(Window window, int activeFlag);
79static void ClearPort(CGrafPtr port, HIShapeRef updateRgn);
80
81
82/*
83 *----------------------------------------------------------------------
84 *
85 * TkMacOSXProcessApplicationEvent --
86 *
87 *	This processes Application level events, mainly activate
88 *	and deactivate.
89 *
90 * Results:
91 *	0.
92 *
93 * Side effects:
94 *	Hide or reveal floating windows.
95 *
96 *----------------------------------------------------------------------
97 */
98
99MODULE_SCOPE int
100TkMacOSXProcessApplicationEvent(
101	TkMacOSXEvent *eventPtr,
102	MacEventStatus *statusPtr)
103{
104    Tcl_CmdInfo dummy;
105
106    /*
107     * This is a bit of a hack. We get "show" events both when we come back
108     * from being hidden, and whenever we are activated. I only want to run
109     * the "show" proc when we have been hidden already, not as a substitute
110     * for <Activate>. So I use this toggle...
111     */
112    static int toggleHide = 0;
113
114    switch (eventPtr->eKind) {
115	case kEventAppActivated:
116	    ShowFloatingWindows();
117	    break;
118	case kEventAppDeactivated:
119	    TkSuspendClipboard();
120	    HideFloatingWindows();
121	    break;
122	case kEventAppQuit:
123	    statusPtr->stopProcessing = 1;
124	    break;
125	case kEventAppHidden:
126	    if (toggleHide == 0) {
127		toggleHide = 1;
128		if (eventPtr->interp && Tcl_GetCommandInfo(eventPtr->interp,
129			"::tk::mac::OnHide", &dummy)) {
130		    Tcl_GlobalEval(eventPtr->interp, "::tk::mac::OnHide");
131		}
132	    }
133	    statusPtr->stopProcessing = 1;
134	    break;
135	case kEventAppShown:
136	    if (toggleHide == 1) {
137		toggleHide = 0;
138		if (eventPtr->interp && Tcl_GetCommandInfo(eventPtr->interp,
139			"::tk::mac::OnShow", &dummy)) {
140		    Tcl_GlobalEval(eventPtr->interp, "::tk::mac::OnShow");
141		}
142	    }
143	    statusPtr->stopProcessing = 1;
144	    break;
145	case kEventAppAvailableWindowBoundsChanged: {
146	    static UInt32 prevId = 0;
147	    UInt32 id;
148	    OSStatus err;
149
150	    err = ChkErr(GetEventParameter, eventPtr->eventRef,
151		    kEventParamTransactionID, typeUInt32,
152		    NULL, sizeof(id), NULL, &id);
153	    if (err != noErr || id != prevId) {
154		TkDisplay *dispPtr = TkGetDisplayList();
155
156		prevId = id;
157		TkMacOSXDisplayChanged(dispPtr->display);
158	    }
159	    /*
160	     * Should we call ::tk::mac::OnDisplayChanged?
161	     */
162	    break;
163	}
164	default:
165	    break;
166    }
167    return 0;
168}
169
170/*
171 *----------------------------------------------------------------------
172 *
173 * TkMacOSXProcessAppearanceEvent --
174 *
175 *	This processes Appearance events.
176 *
177 * Results:
178 *	0.
179 *
180 * Side effects:
181 *	None.
182 *
183 *----------------------------------------------------------------------
184 */
185
186MODULE_SCOPE int
187TkMacOSXProcessAppearanceEvent(
188	TkMacOSXEvent *eventPtr,
189	MacEventStatus *statusPtr)
190{
191    switch (eventPtr->eKind) {
192	case kEventAppearanceScrollBarVariantChanged:
193	    TkMacOSXInitScrollbarMetrics();
194	    break;
195	default:
196	    break;
197    }
198    return 0;
199}
200
201/*
202 *----------------------------------------------------------------------
203 *
204 * TkMacOSXProcessWindowEvent --
205 *
206 *	This processes Window level events, mainly activate
207 *	and deactivate.
208 *
209 * Results:
210 *	0.
211 *
212 * Side effects:
213 *	Cause Windows to be moved forward or backward in the
214 *	window stack.
215 *
216 *----------------------------------------------------------------------
217 */
218
219MODULE_SCOPE int
220TkMacOSXProcessWindowEvent(
221	TkMacOSXEvent * eventPtr,
222	MacEventStatus * statusPtr)
223{
224    OSStatus err;
225    WindowRef whichWindow;
226    Window window;
227    int eventFound = false;
228    TkDisplay *dispPtr;
229    TkWindow *winPtr;
230
231    switch (eventPtr->eKind) {
232	case kEventWindowActivated:
233	case kEventWindowDeactivated:
234	case kEventWindowUpdate:
235	case kEventWindowExpanding:
236	case kEventWindowBoundsChanged:
237	case kEventWindowDragStarted:
238	case kEventWindowDragCompleted:
239	case kEventWindowConstrain:
240	case kEventWindowGetRegion:
241	case kEventWindowDrawContent:
242	    break;
243	default:
244	    return 0;
245	    break;
246    }
247    err = ChkErr(GetEventParameter, eventPtr->eventRef,
248	    kEventParamDirectObject, typeWindowRef, NULL, sizeof(whichWindow),
249	    NULL, &whichWindow);
250    if (err != noErr) {
251	return 0;
252    }
253
254    window = TkMacOSXGetXWindow(whichWindow);
255    dispPtr = TkGetDisplayList();
256    winPtr = (TkWindow *)Tk_IdToWindow(dispPtr->display, window);
257
258    switch (eventPtr->eKind) {
259	case kEventWindowActivated:
260	case kEventWindowDeactivated:
261	    if (window != None) {
262		int activate = (eventPtr->eKind == kEventWindowActivated);
263
264		eventFound |= GenerateActivateEvents(window, activate);
265		eventFound |= TkMacOSXGenerateFocusEvent(window, activate);
266		if (winPtr) {
267		    TkMacOSXEnterExitFullscreen(winPtr, activate);
268		}
269		statusPtr->stopProcessing = 1;
270	    }
271	    break;
272	case kEventWindowUpdate:
273	    if (window != None && GenerateUpdateEvent(window)) {
274		eventFound = true;
275		statusPtr->stopProcessing = 1;
276	    }
277	    break;
278	case kEventWindowExpanding:
279	    if (winPtr) {
280		winPtr->wmInfoPtr->hints.initial_state =
281			TkMacOSXIsWindowZoomed(winPtr) ? ZoomState :
282			NormalState;
283		Tk_MapWindow((Tk_Window) winPtr);
284		/*
285		 * Need to process all Tk events generated by Tk_MapWindow()
286		 * before returning to ensure all children are mapped, as
287		 * otherwise the Activate event that follows Expanding would
288		 * not be processed by any unmapped children.
289		 */
290		while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {};
291		while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {};
292	    }
293	    break;
294	case kEventWindowBoundsChanged:
295	    if (winPtr) {
296		WmInfo *wmPtr = winPtr->wmInfoPtr;
297		UInt32 attr;
298		Rect bounds;
299		int x = -1, y = -1, width = -1, height = -1, flags = 0;
300
301		ChkErr(GetEventParameter, eventPtr->eventRef,
302			kEventParamAttributes, typeUInt32,
303			NULL, sizeof(attr), NULL, &attr);
304		ChkErr(GetEventParameter, eventPtr->eventRef,
305			kEventParamCurrentBounds, typeQDRectangle,
306			NULL, sizeof(bounds), NULL, &bounds);
307		if (attr & kWindowBoundsChangeOriginChanged) {
308		    x = bounds.left - wmPtr->xInParent;
309		    y = bounds.top	- wmPtr->yInParent;
310		    flags |= TK_LOCATION_CHANGED;
311		}
312		if (attr & kWindowBoundsChangeSizeChanged) {
313		    width = bounds.right  - bounds.left;
314		    height = bounds.bottom - bounds.top;
315		    flags |= TK_SIZE_CHANGED;
316		}
317		TkMacOSXInvalClipRgns((Tk_Window) winPtr);
318		TkMacOSXInvalidateWindow((MacDrawable *) window,
319			TK_PARENT_WINDOW);
320		TkGenWMConfigureEvent((Tk_Window)winPtr, x, y, width,
321			height, flags);
322		if (attr & kWindowBoundsChangeUserResize ||
323			attr & kWindowBoundsChangeUserDrag) {
324		    TkMacOSXRunTclEventLoop();
325		}
326		if (wmPtr->attributes & kWindowResizableAttribute) {
327		    HIViewRef growBoxView;
328
329		    err = HIViewFindByID(HIViewGetRoot(whichWindow),
330			    kHIViewWindowGrowBoxID, &growBoxView);
331		    if (err == noErr) {
332			ChkErr(HIViewSetNeedsDisplay, growBoxView, true);
333		    }
334		}
335	    }
336	    break;
337	case kEventWindowDragStarted:
338	    if (!(TkMacOSXModifierState() & cmdKey)) {
339		TkMacOSXBringWindowForward(whichWindow);
340	    }
341	    TkMacOSXTrackingLoop(1);
342	    break;
343	case kEventWindowDragCompleted: {
344	    Rect maxBounds, bounds, strWidths;
345	    int h = 0, v = 0;
346
347	    TkMacOSXTrackingLoop(0);
348	    ChkErr(GetWindowGreatestAreaDevice, whichWindow,
349		    kWindowDragRgn, NULL, &maxBounds);
350	    ChkErr(GetWindowBounds, whichWindow, kWindowStructureRgn,
351		    &bounds);
352	    ChkErr(GetWindowStructureWidths, whichWindow, &strWidths);
353	    if (bounds.left > maxBounds.right - strWidths.left) {
354		h = maxBounds.right
355			- (strWidths.left ? strWidths.left : 40)
356			- bounds.left;
357	    } else if (bounds.right < maxBounds.left
358		    + strWidths.right) {
359		h = maxBounds.left
360			+ (strWidths.right ? strWidths.right : 40)
361			- bounds.right;
362	    }
363	    if (bounds.top > maxBounds.bottom - strWidths.top) {
364		v = maxBounds.bottom
365			- (strWidths.top ? strWidths.top : 40)
366			- bounds.top;
367	    } else if (bounds.bottom < maxBounds.top
368		    + strWidths.bottom) {
369		v = maxBounds.top
370			+ (strWidths.bottom ? strWidths.bottom : 40)
371			- bounds.bottom;
372	    } else if (strWidths.top && bounds.top < maxBounds.top) {
373		v = maxBounds.top - bounds.top;
374	    }
375	    if (h || v) {
376		OffsetRect(&bounds, h, v);
377		ChkErr(SetWindowBounds, whichWindow,
378		    kWindowStructureRgn, &bounds);
379	    }
380	    break;
381	}
382	case kEventWindowConstrain:
383	    if (winPtr && (winPtr->wmInfoPtr->flags & WM_FULLSCREEN) &&
384		    TkMacOSXMakeFullscreen(winPtr, whichWindow, 1,
385		    NULL) == TCL_OK) {
386		statusPtr->stopProcessing = 1;
387	    }
388	    break;
389	case kEventWindowGetRegion:
390	    if (winPtr && (winPtr->wmInfoPtr->flags & WM_TRANSPARENT)) {
391		WindowRegionCode code;
392
393		statusPtr->stopProcessing = (CallNextEventHandler(
394			eventPtr->callRef, eventPtr->eventRef) == noErr);
395		err = ChkErr(GetEventParameter, eventPtr->eventRef,
396			kEventParamWindowRegionCode, typeWindowRegionCode,
397			NULL, sizeof(code), NULL, &code);
398		if (err == noErr && code == kWindowOpaqueRgn) {
399		    RgnHandle rgn;
400
401		    err = ChkErr(GetEventParameter, eventPtr->eventRef,
402			    kEventParamRgnHandle, typeQDRgnHandle, NULL,
403			    sizeof(rgn), NULL, &rgn);
404		    if (err == noErr) {
405			SetEmptyRgn(rgn);
406			statusPtr->stopProcessing = 1;
407		    }
408		}
409	    }
410	    break;
411	case kEventWindowDrawContent:
412	    if (winPtr && (winPtr->wmInfoPtr->flags & WM_TRANSPARENT)) {
413		CGrafPtr port;
414
415		GetPort(&port);
416		ClearPort(port, NULL);
417	    }
418	    break;
419    }
420
421    return eventFound;
422}
423
424/*
425 *----------------------------------------------------------------------
426 *
427 * GenerateUpdateEvent --
428 *
429 *	Given a Macintosh window update event this function generates
430 *	all the Expose XEvents needed by Tk.
431 *
432 * Results:
433 *	True if event(s) are generated - false otherwise.
434 *
435 * Side effects:
436 *	Additional events may be place on the Tk event queue.
437 *
438 *----------------------------------------------------------------------
439 */
440static int
441GenerateUpdateEvent(Window window)
442{
443    WindowRef macWindow;
444    TkDisplay *dispPtr;
445    TkWindow  *winPtr;
446    int result = 0;
447    CGRect updateBounds;
448    HIShapeRef rgn;
449    HIMutableShapeRef updateRgn;
450    int dx, dy;
451
452    dispPtr = TkGetDisplayList();
453    winPtr = (TkWindow *)Tk_IdToWindow(dispPtr->display, window);
454
455    if (winPtr ==NULL ){
456	return result;
457    }
458    macWindow = TkMacOSXDrawableWindow(window);
459    TK_IF_MAC_OS_X_API (5, HIWindowCopyShape,
460	ChkErr(HIWindowCopyShape, macWindow, kWindowUpdateRgn,
461		kHICoordSpaceWindow, &rgn);
462	dx = -winPtr->wmInfoPtr->xInParent;
463	dy = -winPtr->wmInfoPtr->yInParent;
464    ) TK_ELSE_MAC_OS_X (5,
465	Rect bounds;
466
467	TkMacOSXCheckTmpQdRgnEmpty();
468	ChkErr(GetWindowRegion, macWindow, kWindowUpdateRgn, tkMacOSXtmpQdRgn);
469	rgn = HIShapeCreateWithQDRgn(tkMacOSXtmpQdRgn);
470	SetEmptyRgn(tkMacOSXtmpQdRgn);
471	ChkErr(GetWindowBounds, macWindow, kWindowContentRgn, &bounds);
472	dx = -bounds.left;
473	dy = -bounds.top;
474    ) TK_ENDIF
475    updateRgn = HIShapeCreateMutableCopy(rgn);
476    CFRelease(rgn);
477    ChkErr(HIShapeOffset, updateRgn, dx, dy);
478    HIShapeGetBounds(updateRgn, &updateBounds);
479#ifdef TK_MAC_DEBUG_CLIP_REGIONS
480    TkMacOSXDebugFlashRegion(window, updateRgn);
481#endif /* TK_MAC_DEBUG_CLIP_REGIONS */
482    BeginUpdate(macWindow);
483    if (winPtr->wmInfoPtr->flags & WM_TRANSPARENT) {
484	ClearPort(TkMacOSXGetDrawablePort(window), updateRgn);
485    }
486    result = GenerateUpdates(updateRgn, &updateBounds, winPtr);
487    EndUpdate(macWindow);
488    CFRelease(updateRgn);
489    if (result) {
490	/*
491	 * Ensure there are no pending idle-time redraws that could prevent
492	 * the just posted Expose events from generating new redraws.
493	 */
494
495	Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT);
496    }
497    return result;
498 }
499
500/*
501 *----------------------------------------------------------------------
502 *
503 * GenerateUpdates --
504 *
505 *	Given a Macintosh update region and a Tk window this function
506 *	geneates a X Expose event for the window if it is within the
507 *	update region. The function will then recursivly have each
508 *	damaged window generate Expose events for its child windows.
509 *
510 * Results:
511 *	True if event(s) are generated - false otherwise.
512 *
513 * Side effects:
514 *	Additional events may be place on the Tk event queue.
515 *
516 *----------------------------------------------------------------------
517 */
518
519static int
520GenerateUpdates(
521    HIMutableShapeRef updateRgn,
522    CGRect *updateBounds,
523    TkWindow *winPtr)
524{
525    TkWindow *childPtr;
526    XEvent event;
527    CGRect bounds, damageBounds;
528    HIShapeRef boundsRgn, damageRgn;
529
530    TkMacOSXWinCGBounds(winPtr, &bounds);
531    if (!CGRectIntersectsRect(bounds, *updateBounds)) {
532	return 0;
533    }
534    TK_IF_MAC_OS_X_API (4, HIShapeIntersectsRect,
535	if (!HIShapeIntersectsRect(updateRgn, &bounds)) {
536	    return 0;
537	}
538    ) TK_ENDIF
539
540    /*
541     * Compute the bounding box of the area that the damage occured in.
542     */
543
544    boundsRgn = HIShapeCreateWithRect(&bounds);
545    damageRgn = HIShapeCreateIntersection(updateRgn, boundsRgn);
546    if (HIShapeIsEmpty(damageRgn)) {
547	CFRelease(damageRgn);
548	CFRelease(boundsRgn);
549	return 0;
550    }
551    HIShapeGetBounds(damageRgn, &damageBounds);
552    ChkErr(TkMacOSHIShapeUnion, boundsRgn, updateRgn, updateRgn);
553    HIShapeGetBounds(updateRgn, updateBounds);
554    CFRelease(damageRgn);
555    CFRelease(boundsRgn);
556
557    event.xany.serial = Tk_Display(winPtr)->request;
558    event.xany.send_event = false;
559    event.xany.window = Tk_WindowId(winPtr);
560    event.xany.display = Tk_Display(winPtr);
561    event.type = Expose;
562    event.xexpose.x = damageBounds.origin.x - bounds.origin.x;
563    event.xexpose.y = damageBounds.origin.y - bounds.origin.y;
564    event.xexpose.width = damageBounds.size.width;
565    event.xexpose.height = damageBounds.size.height;
566    event.xexpose.count = 0;
567    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
568
569    /*
570     * Generate updates for the children of this window
571     */
572
573    for (childPtr = winPtr->childList; childPtr != NULL;
574	    childPtr = childPtr->nextPtr) {
575	if (!Tk_IsMapped(childPtr) || Tk_IsTopLevel(childPtr)) {
576	    continue;
577	}
578	GenerateUpdates(updateRgn, updateBounds, childPtr);
579    }
580
581    /*
582     * Generate updates for any contained windows
583     */
584
585    if (Tk_IsContainer(winPtr)) {
586	childPtr = TkpGetOtherWindow(winPtr);
587	if (childPtr != NULL && Tk_IsMapped(childPtr)) {
588	    GenerateUpdates(updateRgn, updateBounds, childPtr);
589	}
590
591	/*
592	 * TODO: Here we should handle out of process embedding.
593	 */
594    }
595
596    return 1;
597}
598
599/*
600 *----------------------------------------------------------------------
601 *
602 * GenerateActivateEvents --
603 *
604 *	Given a Macintosh window activate event this function generates all the
605 *	X Activate events needed by Tk.
606 *
607 * Results:
608 *	True if event(s) are generated - false otherwise.
609 *
610 * Side effects:
611 *	Additional events may be place on the Tk event queue.
612 *
613 *----------------------------------------------------------------------
614 */
615
616int
617GenerateActivateEvents(
618    Window window,		  /* Root X window for event. */
619    int activeFlag )
620{
621    TkWindow *winPtr;
622    TkDisplay *dispPtr;
623
624    dispPtr = TkGetDisplayList();
625    winPtr = (TkWindow *) Tk_IdToWindow(dispPtr->display, window);
626    if (winPtr == NULL || winPtr->window == None) {
627	return false;
628    }
629
630    TkGenerateActivateEvents(winPtr,activeFlag);
631    return true;
632}
633
634/*
635 *----------------------------------------------------------------------
636 *
637 * TkMacOSXGenerateFocusEvent --
638 *
639 *	Given a Macintosh window activate event this function generates all the
640 *	X Focus events needed by Tk.
641 *
642 * Results:
643 *	True if event(s) are generated - false otherwise.
644 *
645 * Side effects:
646 *	Additional events may be place on the Tk event queue.
647 *
648 *----------------------------------------------------------------------
649 */
650
651MODULE_SCOPE int
652TkMacOSXGenerateFocusEvent(
653    Window window,		/* Root X window for event. */
654    int activeFlag )
655{
656    XEvent event;
657    Tk_Window tkwin;
658    TkDisplay *dispPtr;
659
660    dispPtr = TkGetDisplayList();
661    tkwin = Tk_IdToWindow(dispPtr->display, window);
662    if (tkwin == NULL) {
663	return false;
664    }
665
666    /*
667     * Don't send focus events to windows of class help or to
668     * windows with the kWindowNoActivatesAttribute.
669     */
670    if (((TkWindow *)tkwin)->wmInfoPtr->macClass == kHelpWindowClass ||
671	    ((TkWindow *)tkwin)->wmInfoPtr->attributes &
672		    kWindowNoActivatesAttribute) {
673	return false;
674    }
675
676    /*
677     * Generate FocusIn and FocusOut events. This event
678     * is only sent to the toplevel window.
679     */
680
681    if (activeFlag) {
682	event.xany.type = FocusIn;
683    } else {
684	event.xany.type = FocusOut;
685    }
686
687    event.xany.serial = dispPtr->display->request;
688    event.xany.send_event = False;
689    event.xfocus.display = dispPtr->display;
690    event.xfocus.window = window;
691    event.xfocus.mode = NotifyNormal;
692    event.xfocus.detail = NotifyDetailNone;
693
694    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
695    return true;
696}
697
698/*
699 *----------------------------------------------------------------------
700 *
701 * TkGenWMConfigureEvent --
702 *
703 *	Generate a ConfigureNotify event for Tk. Depending on the
704 *	value of flag the values of width/height, x/y, or both may
705 *	be changed.
706 *
707 * Results:
708 *	None.
709 *
710 * Side effects:
711 *	A ConfigureNotify event is sent to Tk.
712 *
713 *----------------------------------------------------------------------
714 */
715
716void
717TkGenWMConfigureEvent(
718    Tk_Window tkwin,
719    int x,
720    int y,
721    int width,
722    int height,
723    int flags)
724{
725    XEvent event;
726    WmInfo *wmPtr;
727    TkWindow *winPtr = (TkWindow *) tkwin;
728
729    if (tkwin == NULL) {
730	return;
731    }
732
733    event.type = ConfigureNotify;
734    event.xconfigure.serial = Tk_Display(tkwin)->request;
735    event.xconfigure.send_event = False;
736    event.xconfigure.display = Tk_Display(tkwin);
737    event.xconfigure.event = Tk_WindowId(tkwin);
738    event.xconfigure.window = Tk_WindowId(tkwin);
739    event.xconfigure.border_width = winPtr->changes.border_width;
740    event.xconfigure.override_redirect = winPtr->atts.override_redirect;
741    if (winPtr->changes.stack_mode == Above) {
742	event.xconfigure.above = winPtr->changes.sibling;
743    } else {
744	event.xconfigure.above = None;
745    }
746
747    if (!(flags & TK_LOCATION_CHANGED)) {
748	x = Tk_X(tkwin);
749	y = Tk_Y(tkwin);
750    }
751    if (!(flags & TK_SIZE_CHANGED)) {
752	width = Tk_Width(tkwin);
753	height = Tk_Height(tkwin);
754    }
755    event.xconfigure.x = x;
756    event.xconfigure.y = y;
757    event.xconfigure.width = width;
758    event.xconfigure.height = height;
759
760    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
761
762    /*
763     * Update window manager information.
764     */
765    if (Tk_IsTopLevel(winPtr)) {
766	wmPtr = winPtr->wmInfoPtr;
767	if (flags & TK_LOCATION_CHANGED) {
768	    wmPtr->x = x;
769	    wmPtr->y = y;
770	    wmPtr->flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
771	}
772	if ((flags & TK_SIZE_CHANGED) && !(wmPtr->flags & WM_SYNC_PENDING) &&
773		((width != Tk_Width(tkwin)) || (height != Tk_Height(tkwin)))) {
774	    if ((wmPtr->width == -1) && (width == winPtr->reqWidth)) {
775		/*
776		 * Don't set external width, since the user didn't change it
777		 * from what the widgets asked for.
778		 */
779	    } else {
780		if (wmPtr->gridWin != NULL) {
781		    wmPtr->width = wmPtr->reqGridWidth
782			+ (width - winPtr->reqWidth)/wmPtr->widthInc;
783		    if (wmPtr->width < 0) {
784			wmPtr->width = 0;
785		    }
786		} else {
787		    wmPtr->width = width;
788		}
789	    }
790	    if ((wmPtr->height == -1) && (height == winPtr->reqHeight)) {
791		/*
792		 * Don't set external height, since the user didn't change it
793		 * from what the widgets asked for.
794		 */
795	    } else {
796		if (wmPtr->gridWin != NULL) {
797		    wmPtr->height = wmPtr->reqGridHeight
798			+ (height - winPtr->reqHeight)/wmPtr->heightInc;
799		    if (wmPtr->height < 0) {
800			wmPtr->height = 0;
801		    }
802		} else {
803		    wmPtr->height = height;
804		}
805	    }
806	    wmPtr->configWidth = width;
807	    wmPtr->configHeight = height;
808	}
809    }
810
811    /*
812     * Now set up the changes structure. Under X we wait for the
813     * ConfigureNotify to set these values. On the Mac we know imediatly that
814     * this is what we want - so we just set them. However, we need to
815     * make sure the windows clipping region is marked invalid so the
816     * change is visible to the subwindow.
817     */
818    winPtr->changes.x = x;
819    winPtr->changes.y = y;
820    winPtr->changes.width = width;
821    winPtr->changes.height = height;
822    TkMacOSXInvalClipRgns(tkwin);
823}
824
825/*
826 *----------------------------------------------------------------------
827 *
828 * TkGenWMDestroyEvent --
829 *
830 *	Generate a WM Destroy event for Tk.
831 *
832 * Results:
833 *	None.
834 *
835 * Side effects:
836 *	A WM_PROTOCOL/WM_DELETE_WINDOW event is sent to Tk.
837 *
838 *----------------------------------------------------------------------
839 */
840
841void
842TkGenWMDestroyEvent(
843    Tk_Window tkwin)
844{
845    XEvent event;
846
847    event.xany.serial = Tk_Display(tkwin)->request;
848    event.xany.send_event = False;
849    event.xany.display = Tk_Display(tkwin);
850
851    event.xclient.window = Tk_WindowId(tkwin);
852    event.xclient.type = ClientMessage;
853    event.xclient.message_type = Tk_InternAtom(tkwin, "WM_PROTOCOLS");
854    event.xclient.format = 32;
855    event.xclient.data.l[0] = Tk_InternAtom(tkwin, "WM_DELETE_WINDOW");
856    Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
857}
858
859/*
860 *----------------------------------------------------------------------
861 *
862 * TkWmProtocolEventProc --
863 *
864 *	This procedure is called by the Tk_HandleEvent whenever a
865 *	ClientMessage event arrives whose type is "WM_PROTOCOLS".
866 *	This procedure handles the message from the window manager
867 *	in an appropriate fashion.
868 *
869 * Results:
870 *	None.
871 *
872 * Side effects:
873 *	Depends on what sort of handler, if any, was set up for the
874 *	protocol.
875 *
876 *----------------------------------------------------------------------
877 */
878
879void
880TkWmProtocolEventProc(
881    TkWindow *winPtr,		/* Window to which the event was sent. */
882    XEvent *eventPtr)		/* X event. */
883{
884    WmInfo *wmPtr;
885    ProtocolHandler *protPtr;
886    Tcl_Interp *interp;
887    Atom protocol;
888    int result;
889
890    wmPtr = winPtr->wmInfoPtr;
891    if (wmPtr == NULL) {
892	return;
893    }
894    protocol = (Atom) eventPtr->xclient.data.l[0];
895    for (protPtr = wmPtr->protPtr; protPtr != NULL;
896		protPtr = protPtr->nextPtr) {
897	if (protocol == protPtr->protocol) {
898	    Tcl_Preserve((ClientData) protPtr);
899	    interp = protPtr->interp;
900	    Tcl_Preserve((ClientData) interp);
901	    result = Tcl_GlobalEval(interp, protPtr->command);
902	    if (result != TCL_OK) {
903		Tcl_AddErrorInfo(interp, "\n    (command for \"");
904		Tcl_AddErrorInfo(interp,
905			Tk_GetAtomName((Tk_Window) winPtr, protocol));
906		Tcl_AddErrorInfo(interp, "\" window manager protocol)");
907		Tk_BackgroundError(interp);
908	    }
909	    Tcl_Release((ClientData) interp);
910	    Tcl_Release((ClientData) protPtr);
911	    return;
912	}
913    }
914
915    /*
916     * No handler was present for this protocol. If this is a
917     * WM_DELETE_WINDOW message then just destroy the window.
918     */
919
920    if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
921	Tk_DestroyWindow((Tk_Window) winPtr);
922    }
923}
924
925/*
926 *----------------------------------------------------------------------
927 *
928 * Tk_MacOSXIsAppInFront --
929 *
930 *	Returns 1 if this app is the foreground app.
931 *
932 * Results:
933 *	1 if app is in front, 0 otherwise.
934 *
935 * Side effects:
936 *	None.
937 *
938 *----------------------------------------------------------------------
939 */
940
941int
942Tk_MacOSXIsAppInFront(void)
943{
944    OSStatus err;
945    ProcessSerialNumber frontPsn, ourPsn = {0, kCurrentProcess};
946    Boolean isFrontProcess = true;
947
948    err = ChkErr(GetFrontProcess, &frontPsn);
949    if (err == noErr) {
950	ChkErr(SameProcess, &frontPsn, &ourPsn, &isFrontProcess);
951    }
952
953    return (isFrontProcess == true);
954}
955
956/*
957 *----------------------------------------------------------------------
958 *
959 * ClearPort --
960 *
961 *	Clear (i.e. fill with transparent color) the given port.
962 *
963 * Results:
964 *	None.
965 *
966 * Side effects:
967 *	None.
968 *
969 *----------------------------------------------------------------------
970 */
971static void
972ClearPort(
973    CGrafPtr port,
974    HIShapeRef updateRgn)
975{
976    CGContextRef context;
977    Rect bounds;
978    CGRect rect;
979
980    GetPortBounds(port, &bounds);
981    QDBeginCGContext(port, &context);
982    SyncCGContextOriginWithPort(context, port);
983    CGContextConcatCTM(context, CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0,
984	    bounds.bottom - bounds.top));
985    if (updateRgn) {
986	ChkErr(HIShapeReplacePathInCGContext, updateRgn, context);
987	CGContextEOClip(context);
988    }
989    rect = CGRectMake(0, 0, bounds.right, bounds.bottom);
990    CGContextClearRect(context, rect);
991    QDEndCGContext(port, &context);
992}
993