diff options
author | das <das> | 2009-06-29 14:35:01 (GMT) |
---|---|---|
committer | das <das> | 2009-06-29 14:35:01 (GMT) |
commit | 8f9b8fd63b4da5f6f27b46c438c89f2baa15f0e8 (patch) | |
tree | 81b5743d2ff5d45f2ee4992153e4f34949ea8f8a /macosx/tkMacOSXWindowEvent.c | |
parent | e4a87c21cb59539eeddcc65d359e9b06d597fed3 (diff) | |
download | tk-8f9b8fd63b4da5f6f27b46c438c89f2baa15f0e8.zip tk-8f9b8fd63b4da5f6f27b46c438c89f2baa15f0e8.tar.gz tk-8f9b8fd63b4da5f6f27b46c438c89f2baa15f0e8.tar.bz2 |
Merge of TkAqua Cocoa portmacosx_cocoa_merge
Diffstat (limited to 'macosx/tkMacOSXWindowEvent.c')
-rw-r--r-- | macosx/tkMacOSXWindowEvent.c | 1036 |
1 files changed, 532 insertions, 504 deletions
diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 9faabd0..d9d4429 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -4,51 +4,13 @@ * 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> + * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2005-2009 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.35 2008/12/10 02:51:16 das Exp $ + * RCS: @(#) $Id: tkMacOSXWindowEvent.c,v 1.36 2009/06/29 14:35:01 das Exp $ */ #include "tkMacOSXPrivate.h" @@ -58,7 +20,8 @@ /* #ifdef TK_MAC_DEBUG -#define TK_MAC_DEBUG_CLIP_REGIONS +#define TK_MAC_DEBUG_EVENTS +#define TK_MAC_DEBUG_DRAWING #endif */ @@ -66,422 +29,223 @@ * 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. - * - *---------------------------------------------------------------------- - */ +static int GenerateActivateEvents(TkWindow *winPtr, + int activeFlag); +static void DoWindowActivate(ClientData clientData); -MODULE_SCOPE int -TkMacOSXProcessApplicationEvent( - TkMacOSXEvent *eventPtr, - MacEventStatus *statusPtr) -{ - Tcl_CmdInfo dummy; +#pragma mark TKApplication(TKWindowEvent) - /* - * 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... - */ +#ifdef TK_MAC_DEBUG_NOTIFICATIONS +extern NSString *NSWindowWillOrderOnScreenNotification; +extern NSString *NSWindowDidOrderOnScreenNotification; +extern NSString *NSWindowDidOrderOffScreenNotification; - 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"); - } +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +#define NSWindowWillStartLiveResizeNotification @"NSWindowWillStartLiveResizeNotification" +#define NSWindowDidEndLiveResizeNotification @"NSWindowDidEndLiveResizeNotification" +#endif +#endif + +@implementation TKApplication(TKWindowEvent) +- (void)windowActivation:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + BOOL activate = [[notification name] isEqualToString:NSWindowDidBecomeKeyNotification]; + NSWindow *w = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); + + if (winPtr && Tk_IsMapped(winPtr)) { + GenerateActivateEvents(winPtr, activate); + } +} +- (void)windowBoundsChanged:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + BOOL movedOnly = [[notification name] isEqualToString:NSWindowDidMoveNotification]; + if (movedOnly) { + /* constraining to screen after move not needed with AppKit */ + } + NSWindow *w = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); + + if (winPtr) { + WmInfo *wmPtr = winPtr->wmInfoPtr; + NSRect bounds = [w frame]; + int x, y, width = -1, height = -1, flags = 0; + + x = bounds.origin.x; + y = tkMacOSXZeroScreenHeight - (bounds.origin.y + bounds.size.height); + if (winPtr->changes.x != x || winPtr->changes.y != y){ + flags |= TK_LOCATION_CHANGED; + } else { + x = y = -1; } - 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"); - } + if (!movedOnly && (winPtr->changes.width != bounds.size.width || + winPtr->changes.height != bounds.size.height)) { + width = bounds.size.width - wmPtr->xInParent; + height = bounds.size.height - wmPtr->yInParent; + flags |= TK_SIZE_CHANGED; } - 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); + if (Tcl_GetServiceMode() != TCL_SERVICE_NONE) { + /* propagate geometry changes immediately */ + flags |= TK_MACOSX_HANDLE_EVENT_IMMEDIATELY; } - /* - * Should we call ::tk::mac::OnDisplayChanged? - */ - break; - } - default: - break; + TkGenWMConfigureEvent((Tk_Window) winPtr, x, y, width, height, flags); } - return 0; } - -/* - *---------------------------------------------------------------------- - * - * TkMacOSXProcessAppearanceEvent -- - * - * This processes Appearance events. - * - * Results: - * 0. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ +- (void)windowExpanded:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + NSWindow *w = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); + + if (winPtr) { + winPtr->wmInfoPtr->hints.initial_state = + TkMacOSXIsWindowZoomed(winPtr) ? ZoomState : NormalState; + Tk_MapWindow((Tk_Window) winPtr); + if (Tcl_GetServiceMode() != TCL_SERVICE_NONE) { + /* Process all Tk events generated by Tk_MapWindow() */ + while (Tcl_ServiceEvent(0)) {} + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} -MODULE_SCOPE int -TkMacOSXProcessAppearanceEvent( - TkMacOSXEvent *eventPtr, - MacEventStatus *statusPtr) -{ - switch (eventPtr->eKind) { - case kEventAppearanceScrollBarVariantChanged: - TkMacOSXInitScrollbarMetrics(); - break; - default: - break; + /* + * NSWindowDidDeminiaturizeNotification is received after + * NSWindowDidBecomeKeyNotification, so activate manually + */ + + GenerateActivateEvents(winPtr, 1); + } else { + Tcl_DoWhenIdle(DoWindowActivate, winPtr); + } } - 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. - * - *---------------------------------------------------------------------- - */ +- (void)windowCollapsed:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + NSWindow *w = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); -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; + if (winPtr) { + Tk_UnmapWindow((Tk_Window) winPtr); } - err = ChkErr(GetEventParameter, eventPtr->eventRef, - kEventParamDirectObject, typeWindowRef, NULL, sizeof(whichWindow), - NULL, &whichWindow); - if (err != noErr) { - return 0; +} +- (void)windowClosed:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + NSWindow *w = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); + + if (winPtr) { + TkGenWMDestroyEvent((Tk_Window) winPtr); } +} +#ifdef TK_MAC_DEBUG_NOTIFICATIONS +- (void)windowDragStart:(NSNotification *)notification { + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +} +- (void)windowLiveResize:(NSNotification *)notification { + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); + //BOOL start = [[notification name] isEqualToString:NSWindowWillStartLiveResizeNotification]; +} +- (void)windowMapped:(NSNotification *)notification { + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); + NSWindow *w = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); - window = TkMacOSXGetXWindow(whichWindow); - dispPtr = TkGetDisplayList(); - winPtr = (TkWindow *) Tk_IdToWindow(dispPtr->display, window); + if (winPtr) { + //Tk_MapWindow((Tk_Window) winPtr); + } +} +- (void)windowBecameVisible:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif +} +- (void)windowUnmapped:(NSNotification *)notification { + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); + NSWindow *w = [notification object]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); - switch (eventPtr->eKind) { - case kEventWindowActivated: - case kEventWindowDeactivated: - if (window != None) { - int activate = (eventPtr->eKind == kEventWindowActivated); + if (winPtr) { + //Tk_UnmapWindow((Tk_Window) winPtr); + } +} +#endif - 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); +- (void)_setupWindowNotifications { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; +#define observe(n, s) [nc addObserver:self selector:@selector(s) name:(n) object:nil] + observe(NSWindowDidBecomeKeyNotification, windowActivation:); + observe(NSWindowDidResignKeyNotification, windowActivation:); + observe(NSWindowDidMoveNotification, windowBoundsChanged:); + observe(NSWindowDidResizeNotification, windowBoundsChanged:); + observe(NSWindowDidDeminiaturizeNotification, windowExpanded:); + observe(NSWindowDidMiniaturizeNotification, windowCollapsed:); + observe(NSWindowWillCloseNotification, windowClosed:); +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + observe(NSWindowWillMoveNotification, windowDragStart:); + observe(NSWindowWillStartLiveResizeNotification, windowLiveResize:); + observe(NSWindowDidEndLiveResizeNotification, windowLiveResize:); + observe(NSWindowWillOrderOnScreenNotification, windowMapped:); + observe(NSWindowDidOrderOnScreenNotification, windowBecameVisible:); + observe(NSWindowDidOrderOffScreenNotification, windowUnmapped:); +#endif +#undef observe +} +@end - /* - * 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. - */ +#pragma mark TKApplication(TKApplicationEvent) - while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {/*empty body*/}; - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {/*empty body*/}; - } - 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; +@implementation TKApplication(TKApplicationEvent) +- (void)applicationActivate:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + [NSApp tkCheckPasteboard]; +} +- (void)applicationDeactivate:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + TkSuspendClipboard(); +} +- (void)applicationShowHide:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + const char *cmd = ([[notification name] isEqualToString: + NSApplicationDidUnhideNotification] ? + "::tk::mac::OnShow" : "::tk::mac::OnHide"); + Tcl_CmdInfo dummy; - 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 (_eventInterp && Tcl_GetCommandInfo(_eventInterp, cmd, &dummy)) { + int code = Tcl_EvalEx(_eventInterp, cmd, -1, TCL_EVAL_GLOBAL); + if (code != TCL_OK) { + Tcl_BackgroundException(_eventInterp, code); } - 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; + Tcl_ResetResult(_eventInterp); } - 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; +} +- (void)displayChanged:(NSNotification *)notification { +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + TkDisplay *dispPtr = TkGetDisplayList(); - GetPort(&port); - ClearPort(port, NULL); - } - break; + if (dispPtr) { + TkMacOSXDisplayChanged(dispPtr->display); } - - return eventFound; } - -/* - *---------------------------------------------------------------------- - * - * 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. - * - *---------------------------------------------------------------------- - */ +@end -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; - } +#pragma mark - /* *---------------------------------------------------------------------- @@ -517,11 +281,9 @@ GenerateUpdates( if (!CGRectIntersectsRect(bounds, *updateBounds)) { return 0; } - TK_IF_MAC_OS_X_API (4, HIShapeIntersectsRect, - if (!HIShapeIntersectsRect(updateRgn, &bounds)) { - return 0; - } - ) TK_ENDIF + if (!HIShapeIntersectsRect(updateRgn, &bounds)) { + return 0; + } /* * Compute the bounding box of the area that the damage occured in. @@ -535,12 +297,14 @@ GenerateUpdates( return 0; } HIShapeGetBounds(damageRgn, &damageBounds); - ChkErr(TkMacOSHIShapeUnion, boundsRgn, updateRgn, updateRgn); - HIShapeGetBounds(updateRgn, updateBounds); + if (!Tk_IsTopLevel(winPtr)) { + ChkErr(TkMacOSHIShapeUnion, boundsRgn, updateRgn, updateRgn); + HIShapeGetBounds(updateRgn, updateBounds); + } CFRelease(damageRgn); CFRelease(boundsRgn); - event.xany.serial = Tk_Display(winPtr)->request; + event.xany.serial = LastKnownRequestProcessed(Tk_Display(winPtr)); event.xany.send_event = false; event.xany.window = Tk_WindowId(winPtr); event.xany.display = Tk_Display(winPtr); @@ -551,6 +315,10 @@ GenerateUpdates( event.xexpose.height = damageBounds.size.height; event.xexpose.count = 0; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); +#ifdef TK_MAC_DEBUG_DRAWING + TKLog(@"Expose %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, + event.xexpose.y, event.xexpose.width, event.xexpose.height); +#endif /* * Generate updates for the children of this window @@ -601,25 +369,41 @@ GenerateUpdates( int GenerateActivateEvents( - Window window, /* Root X window for event. */ + TkWindow *winPtr, 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); + TkGenerateActivateEvents(winPtr, activeFlag); + TkMacOSXGenerateFocusEvent(winPtr, activeFlag); + TkMacOSXEnterExitFullscreen(winPtr, activeFlag); return true; } /* *---------------------------------------------------------------------- * + * DoWindowActivate -- + * + * Idle handler that calls GenerateActivateEvents(). + * + * Results: + * None. + * + * Side effects: + * Additional events may be place on the Tk event queue. + * + *---------------------------------------------------------------------- + */ + +void +DoWindowActivate( + ClientData clientData) +{ + GenerateActivateEvents(clientData, 1); +} + +/* + *---------------------------------------------------------------------- + * * TkMacOSXGenerateFocusEvent -- * * Given a Macintosh window activate event this function generates all @@ -636,27 +420,18 @@ GenerateActivateEvents( MODULE_SCOPE int TkMacOSXGenerateFocusEvent( - Window window, /* Root X window for event. */ + TkWindow *winPtr, /* 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) { + if (winPtr->wmInfoPtr && (winPtr->wmInfoPtr->macClass == kHelpWindowClass || + winPtr->wmInfoPtr->attributes & kWindowNoActivatesAttribute)) { return false; } @@ -671,10 +446,10 @@ TkMacOSXGenerateFocusEvent( event.xany.type = FocusOut; } - event.xany.serial = dispPtr->display->request; + event.xany.serial = LastKnownRequestProcessed(Tk_Display(winPtr)); event.xany.send_event = False; - event.xfocus.display = dispPtr->display; - event.xfocus.window = window; + event.xfocus.display = Tk_Display(winPtr); + event.xfocus.window = winPtr->window; event.xfocus.mode = NotifyNormal; event.xfocus.detail = NotifyDetailNone; @@ -717,7 +492,7 @@ TkGenWMConfigureEvent( } event.type = ConfigureNotify; - event.xconfigure.serial = Tk_Display(tkwin)->request; + event.xconfigure.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); event.xconfigure.send_event = False; event.xconfigure.display = Tk_Display(tkwin); event.xconfigure.event = Tk_WindowId(tkwin); @@ -743,7 +518,11 @@ TkGenWMConfigureEvent( event.xconfigure.width = width; event.xconfigure.height = height; - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + if (flags & TK_MACOSX_HANDLE_EVENT_IMMEDIATELY) { + Tk_HandleEvent(&event); + } else { + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + } /* * Update window manager information. @@ -832,7 +611,7 @@ TkGenWMDestroyEvent( { XEvent event; - event.xany.serial = Tk_Display(tkwin)->request; + event.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); event.xany.send_event = False; event.xany.display = Tk_Display(tkwin); @@ -841,7 +620,7 @@ TkGenWMDestroyEvent( 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); + Tk_HandleEvent(&event); } /* @@ -936,52 +715,301 @@ Tk_MacOSXIsAppInFront(void) if (err == noErr) { ChkErr(SameProcess, &frontPsn, &ourPsn, &isFrontProcess); } - + return (isFrontProcess == true); } - + +#pragma mark TKContentView + +#import <ApplicationServices/ApplicationServices.h> + /* - *---------------------------------------------------------------------- - * - * ClearPort -- - * - * Clear (i.e. fill with transparent color) the given port. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- + * Custom content view for Tk NSWindows, containing standard NSView subviews. + * The goal is to emulate X11-style drawing in response to Expose events: + * during the normal AppKit drawing cycle, we supress drawing of all subviews + * (using a technique adapted from WebKit's WebHTMLView) and instead send + * Expose events about the subviews that would be redrawn. + * Tk Expose event handling and drawing handlers then draw the subviews + * manually via their -displayRectIgnoringOpacity: */ -static void -ClearPort( - CGrafPtr port, - HIShapeRef updateRgn) +@interface TKContentView(TKWindowEvent) +- (void)drawRect:(NSRect)rect; +- (void)generateExposeEvents:(HIMutableShapeRef)shape; +- (BOOL)isOpaque; +- (BOOL)wantsDefaultClipping; +- (BOOL)acceptsFirstResponder; +- (void)keyDown:(NSEvent *)theEvent; +@end + +@implementation TKContentView +@end + +static Tk_RestrictAction ExposeRestrictProc(ClientData arg, XEvent *eventPtr) { - 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); + return (eventPtr->type == Expose && eventPtr->xany.serial == PTR2UINT(arg) ? + TK_PROCESS_EVENT : TK_DEFER_EVENT); +} + +@implementation TKContentView(TKWindowEvent) + +- (void)drawRect:(NSRect)rect { + const NSRect *rectsBeingDrawn; + NSInteger rectsBeingDrawnCount; + [self getRectsBeingDrawn:&rectsBeingDrawn count:&rectsBeingDrawnCount]; +#ifdef TK_MAC_DEBUG_DRAWING + TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, NSStringFromRect(rect)); + [[NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:.1] setFill]; + NSRectFillListUsingOperation(rectsBeingDrawn, rectsBeingDrawnCount, + NSCompositeSourceOver); +#endif + NSWindow *w = [self window]; + if ([self isOpaque] && [w showsResizeIndicator]) { + NSRect bounds = [self convertRect:[w _growBoxRect] fromView:nil]; + if ([self needsToDrawRect:bounds]) { + NSEraseRect(bounds); + } + } + CGFloat height = [self bounds].size.height; + HIMutableShapeRef drawShape = HIShapeCreateMutable(); + while (rectsBeingDrawnCount--) { + CGRect r = NSRectToCGRect(*rectsBeingDrawn++); + r.origin.y = height - (r.origin.y + r.size.height); + HIShapeUnionWithRect(drawShape, &r); + } + if (CFRunLoopGetMain() == CFRunLoopGetCurrent()) { + [self generateExposeEvents:drawShape]; + } else { + [self performSelectorOnMainThread:@selector(generateExposeEvents:) + withObject:(id)drawShape waitUntilDone:NO + modes:[NSArray arrayWithObjects:NSRunLoopCommonModes, + NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, + nil]]; + } + CFRelease(drawShape); } + +- (void)generateExposeEvents:(HIMutableShapeRef)shape { + TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); + unsigned long serial; + CGRect updateBounds; + + if (!winPtr) { + return; + } + HIShapeGetBounds(shape, &updateBounds); + serial = LastKnownRequestProcessed(Tk_Display(winPtr)); + if (GenerateUpdates(shape, &updateBounds, winPtr) && + ![[NSRunLoop currentRunLoop] currentMode] && + Tcl_GetServiceMode() != TCL_SERVICE_NONE) { + /* + * Ensure there are no pending idle-time redraws that could prevent + * the just posted Expose events from generating new redraws. + */ + + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} + + /* + * For smoother drawing, process Expose events and resulting redraws + * immediately instead of at idle time. + */ + + ClientData oldArg; + Tk_RestrictProc *oldProc = Tk_RestrictEvents(ExposeRestrictProc, + UINT2PTR(serial), &oldArg); + while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {} + Tk_RestrictEvents(oldProc, oldArg, &oldArg); + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} + } +} + +- (void)tkToolbarButton:(id)sender { +#ifdef TK_MAC_DEBUG_EVENTS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd); +#endif + XVirtualEvent event; + int x, y; + TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); + Tk_Window tkwin = (Tk_Window) winPtr; + + bzero(&event, sizeof(XVirtualEvent)); + event.type = VirtualEvent; + event.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); + event.send_event = false; + event.display = Tk_Display(tkwin); + event.event = Tk_WindowId(tkwin); + event.root = XRootWindow(Tk_Display(tkwin), 0); + event.subwindow = None; + event.time = TkpGetMS(); + XQueryPointer(NULL, winPtr->window, NULL, NULL, + &event.x_root, &event.y_root, &x, &y, &event.state); + Tk_TopCoordsToWindow(tkwin, x, y, &event.x, &event.y); + event.same_screen = true; + event.name = Tk_GetUid("ToolbarButton"); + Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); +} + +#ifdef TK_MAC_DEBUG_DRAWING +- (void)setFrameSize:(NSSize)newSize { + TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, NSStringFromSize(newSize)); + [super setFrameSize:newSize]; +} +- (void)setNeedsDisplayInRect:(NSRect)invalidRect { + TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, + NSStringFromRect(invalidRect)); + [super setNeedsDisplayInRect:invalidRect]; +} +#endif +- (BOOL)isOpaque { + NSWindow *w = [self window]; + + return (w && (([w styleMask] & NSTexturedBackgroundWindowMask) || + ![w isOpaque]) ? NO : YES); +} + +- (BOOL)wantsDefaultClipping { + return NO; +} + +- (BOOL)acceptsFirstResponder { + return YES; +} + +- (void)keyDown:(NSEvent *)theEvent { +#ifdef TK_MAC_DEBUG_EVENTS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent); +#endif +} + +@end + +#pragma mark TKContentViewPrivate + +/* + * Technique adapted from WebKit/WebKit/mac/WebView/WebHTMLView.mm to supress + * normal AppKit subview drawing and make all drawing go through us. + * Overrides NSView internals. + */ + +@interface TKContentView(TKContentViewPrivate) +- (id)initWithFrame:(NSRect)frame; +- (void)_setAsideSubviews; +- (void)_restoreSubviews; +@end + +@interface NSView(TKContentViewPrivate) +- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView; +- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect; +- (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView; +- (void)_lightWeightRecursiveDisplayInRect:(NSRect)visRect; +- (BOOL)_drawRectIfEmpty; +- (void)_drawRect:(NSRect)inRect clip:(BOOL)clip; +- (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants; +@end + + +@implementation TKContentView(TKContentViewPrivate) + +- (id)initWithFrame:(NSRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _savedSubviews = nil; + _subviewsSetAside = NO; + [self _setDrawsOwnDescendants:YES]; + } + return self; +} + +- (void)_setAsideSubviews +{ +#ifdef TK_MAC_DEBUG + if (_subviewsSetAside || _savedSubviews) { + Tcl_Panic("TKContentView _setAsideSubviews called incorrectly"); + } +#endif + _savedSubviews = _subviews; + _subviews = nil; + _subviewsSetAside = YES; + } + + - (void)_restoreSubviews + { +#ifdef TK_MAC_DEBUG + if (!_subviewsSetAside || _subviews) { + Tcl_Panic("TKContentView _restoreSubviews called incorrectly"); + } +#endif + _subviews = _savedSubviews; + _savedSubviews = nil; + _subviewsSetAside = NO; +} + +- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView +{ + [self _setAsideSubviews]; + [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView]; + [self _restoreSubviews]; +} + +- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect +{ + BOOL needToSetAsideSubviews = !_subviewsSetAside; + if (needToSetAsideSubviews) { + [self _setAsideSubviews]; + } + [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect]; + if (needToSetAsideSubviews) { + [self _restoreSubviews]; + } +} + +- (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView +{ + [self _setAsideSubviews]; + [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView]; + [self _restoreSubviews]; +} + +- (void)_lightWeightRecursiveDisplayInRect:(NSRect)visRect { + BOOL needToSetAsideSubviews = !_subviewsSetAside; + if (needToSetAsideSubviews) { + [self _setAsideSubviews]; + } + [super _lightWeightRecursiveDisplayInRect:visRect]; + if (needToSetAsideSubviews) { + [self _restoreSubviews]; + } +} + +- (BOOL)_drawRectIfEmpty { + /* + * Our -drawRect manages subview drawing directly, so it needs to be called + * even if the area to be redrawn is completely obscured by subviews. + */ + return YES; +} + +- (void)_drawRect:(NSRect)inRect clip:(BOOL)clip { +#ifdef TK_MAC_DEBUG_DRAWING + TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, NSStringFromRect(inRect)); +#endif + BOOL subviewsWereSetAside = _subviewsSetAside; + if (subviewsWereSetAside) { + [self _restoreSubviews]; + } + [super _drawRect:inRect clip:clip]; + if (subviewsWereSetAside) { + [self _setAsideSubviews]; + } +} + +@end /* * Local Variables: - * fill-column: 78 + * mode: c * c-basic-offset: 4 + * fill-column: 79 + * coding: utf-8 * End: */ |