From c23b714ab089bf546c9cc37188f29274b4e4991a Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Tue, 15 Dec 2015 02:50:56 +0000 Subject: Fix for some redraw issues on Tk-Cocoa on OS X 10.11; further refinement of memory management; thanks to Marc Culler for patches --- macosx/README | 61 +++++++++++++++++++++++++++++++++++++++ macosx/tkMacOSXDialog.c | 2 -- macosx/tkMacOSXDraw.c | 10 ------- macosx/tkMacOSXEvent.c | 5 ---- macosx/tkMacOSXFont.c | 7 +++-- macosx/tkMacOSXInit.c | 68 +++++++++++++++++++++++++++++-------------- macosx/tkMacOSXKeyEvent.c | 2 +- macosx/tkMacOSXMenu.c | 27 +++++------------ macosx/tkMacOSXNotify.c | 63 ++++++++++++++++++++-------------------- macosx/tkMacOSXPrivate.h | 3 ++ macosx/tkMacOSXSubwindows.c | 11 +------ macosx/tkMacOSXWindowEvent.c | 44 ++++++++++++++++++---------- macosx/tkMacOSXWm.c | 69 ++++++++++++++++++++++++++------------------ macosx/tkMacOSXXStubs.c | 6 ++-- 14 files changed, 227 insertions(+), 151 deletions(-) diff --git a/macosx/README b/macosx/README index 69be037..202dbbd 100644 --- a/macosx/README +++ b/macosx/README @@ -388,3 +388,64 @@ make overrides to the tk/macosx GNUmakefile, e.g. sudo make -C tk${ver}/macosx install \ TCL_FRAMEWORK_DIR=$HOME/Library/Frameworks TCLSH_DIR=$HOME/usr/bin The Makefile variables TCL_FRAMEWORK_DIR and TCLSH_DIR were added with Tk 8.4.3. + +4. About the event loop in Tk for Mac OSX +----------------------------------------- + +The main program in a typical OSX application looks like this (see *) + + void NSApplicationMain(int argc, char *argv[]) { + [NSApplication sharedApplication]; + [NSBundle loadNibNamed:@"myMain" owner:NSApp]; + [NSApp run]; + } + +The run method implements the event loop for the application. There +are three key steps in the run method. First it calls +[NSApp finishLaunching], which creates the bouncing application icon +and does other mysterious things. Second it creates an +NSAutoreleasePool. Third, it starts an event loop which drains the +NSAutoreleasePool every time the queue is empty, and replaces the +drained pool with a new one. This third step is essential to +preventing memory leaks, since the internal methods of Appkit objects +all assume that an autorelease pool is in scope and will be drained +when the event processing cycle ends. + +Mac OSX Tk does not call the [NSApp run] method at all. Instead it +uses the event loop built in to Tk. So we must take care to replicate +the important features of the method ourselves. Here is how this +works in outline. + +We add a private NSAUtoreleasePool* property to our subclass of +NSApplication. (The subclass is called TKApplication but can be +referenced with the global variable NSApp). The TkpInit +function calls [NSApp _setup] which initializes this property by +creating an NSAutoreleasePool. A bit later on, TkpInit calls +[NSAPP _setupEventLoop] which in turn calls the +[NSApp finishLaunching] method. + +Each time that Tcl processes an event in its queue, it calls a +platform specific function which, in the case of Mac OSX, is named +TkMacOSXEventsCheckProc. In the unix implementations of Tk, including +the Mac OSX version, this function collects events from an "event +source", and transfers them to the Tcl event queue. In Mac OSX the +event source is the NSApplication event queue. Each NSEvent is +converted to a Tcl event which is added to the Tcl event queue. The +NSEvent is also passed to [NSApp sendevent], which sends the event on +to the application's NSWindows, which send it to their NSViews, etc. +Since the CheckProc function gets called for every Tk event, it is an +appropriate place to drain the main NSAutoreleasePool and replace it +with a new pool. This is done by calling the method +[NSApp _resetAutoreleasePool], where _resetAutoreleasePool is a method +which we define for the subclass TKApplication. + +One minor caveat is that there are several steps of the Tk +initialization which precede the call to TkpInit. Notably, the font +package is initialized first. Since there is no NSAUtoreleasePool in +scope prior to calling TkpInit, the functions called in these +preliminary stages need to create and drain their own +NSAutoreleasePools whenever they call methods of Appkit objects +(e.g. NSFont). + +* https://developer.apple.com/library/mac/documentation/Cocoa/\ +Reference/ApplicationKit/Classes/NSApplication_Class diff --git a/macosx/tkMacOSXDialog.c b/macosx/tkMacOSXDialog.c index fb6e490..a3510f8 100644 --- a/macosx/tkMacOSXDialog.c +++ b/macosx/tkMacOSXDialog.c @@ -1733,9 +1733,7 @@ TkInitFontchooser( Tcl_SetAssocData(interp, "::tk::fontchooser", DeleteFontchooserData, fcdPtr); if (!fontPanelFontAttributes) { - NSAutoreleasePool *pool = [NSAutoreleasePool new]; fontPanelFontAttributes = [NSMutableDictionary new]; - [pool drain]; } return TCL_OK; } diff --git a/macosx/tkMacOSXDraw.c b/macosx/tkMacOSXDraw.c index 10f7b9e..6a0b409 100644 --- a/macosx/tkMacOSXDraw.c +++ b/macosx/tkMacOSXDraw.c @@ -138,7 +138,6 @@ BitmapRepFromDrawableRect( CGImageRef cg_image=NULL, sub_cg_image=NULL; NSBitmapImageRep *bitmap_rep=NULL; NSView *view=NULL; - NSAutoreleasePool *pool = [NSAutoreleasePool new]; if ( mac_drawable->flags & TK_IS_PIXMAP ) { /* This means that the MacDrawable is functioning as a Tk Pixmap, so its view @@ -175,7 +174,6 @@ BitmapRepFromDrawableRect( } else { TkMacOSXDbgMsg("Invalid source drawable"); } - [pool drain]; return bitmap_rep; } @@ -1636,7 +1634,6 @@ TkMacOSXSetupDrawingContext( int dontDraw = 0, isWin = 0; TkMacOSXDrawingContext dc = {}; CGRect clipBounds; - NSAutoreleasePool *pool = [NSAutoreleasePool new]; dc.clipRgn = TkMacOSXGetClipRgn(d); if (!dontDraw) { @@ -1767,7 +1764,6 @@ end: dc.clipRgn = NULL; } *dcPtr = dc; - [pool drain]; return !dontDraw; } @@ -1791,7 +1787,6 @@ void TkMacOSXRestoreDrawingContext( TkMacOSXDrawingContext *dcPtr) { - NSAutoreleasePool *pool = [NSAutoreleasePool new]; if (dcPtr->context) { CGContextSynchronize(dcPtr->context); [[dcPtr->view window] setViewsNeedDisplay:YES]; @@ -1808,7 +1803,6 @@ TkMacOSXRestoreDrawingContext( #ifdef TK_MAC_DEBUG bzero(dcPtr, sizeof(TkMacOSXDrawingContext)); #endif /* TK_MAC_DEBUG */ - [pool drain]; } /* @@ -1834,7 +1828,6 @@ TkMacOSXGetClipRgn( { MacDrawable *macDraw = (MacDrawable *) drawable; HIShapeRef clipRgn = NULL; - NSAutoreleasePool *pool = [NSAutoreleasePool new]; if (macDraw->winPtr && macDraw->flags & TK_CLIP_INVALID) { TkMacOSXUpdateClipRgn(macDraw->winPtr); @@ -1860,7 +1853,6 @@ TkMacOSXGetClipRgn( } else if (macDraw->visRgn) { clipRgn = HIShapeCreateCopy(macDraw->visRgn); } - [pool drain]; return clipRgn; } @@ -1913,7 +1905,6 @@ TkpClipDrawableToRect( { MacDrawable *macDraw = (MacDrawable *) d; NSView *view = TkMacOSXDrawableView(macDraw); - NSAutoreleasePool *pool = [NSAutoreleasePool new]; if (macDraw->drawRgn) { CFRelease(macDraw->drawRgn); @@ -1947,7 +1938,6 @@ TkpClipDrawableToRect( macDraw->flags &= ~TK_FOCUSED_VIEW; } } - [pool drain]; } /* diff --git a/macosx/tkMacOSXEvent.c b/macosx/tkMacOSXEvent.c index db13249..7f3357f 100644 --- a/macosx/tkMacOSXEvent.c +++ b/macosx/tkMacOSXEvent.c @@ -128,10 +128,6 @@ enum { MODULE_SCOPE void TkMacOSXFlushWindows(void) { - /* This can be called from outside the Appkit event loop, - * so it needs its own AutoreleasePool. - */ - NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSArray *macWindows = [NSApp orderedWindows]; for (NSWindow *w in macWindows) { @@ -139,7 +135,6 @@ TkMacOSXFlushWindows(void) [w flushWindow]; } } - [pool drain]; } diff --git a/macosx/tkMacOSXFont.c b/macosx/tkMacOSXFont.c index 54b0fb8..c48e56e 100644 --- a/macosx/tkMacOSXFont.c +++ b/macosx/tkMacOSXFont.c @@ -210,6 +210,7 @@ FindNSFont( nsFont = [fm convertFont:nsFont toSize:size]; nsFont = [fm convertFont:nsFont toHaveTrait:traits]; } + [nsFont retain]; #undef defaultFont return nsFont; } @@ -306,7 +307,7 @@ InitFont( [NSNumber numberWithInt:fmPtr->fixed ? 0 : 1], NSLigatureAttributeName, [NSNumber numberWithDouble:kern], NSKernAttributeName, nil]; - fontPtr->nsAttributes = [nsAttributes retain]; + fontPtr->nsAttributes = [nsAttributes retain]; #undef nCh } @@ -371,6 +372,7 @@ TkpFontPkgInit( NSFont *nsFont; TkFontAttributes fa; NSMutableCharacterSet *cs; + /* Since we called before TkpInit, we need our own autorelease pool. */ NSAutoreleasePool *pool = [NSAutoreleasePool new]; /* force this for now */ @@ -530,7 +532,7 @@ TkpGetFontFromAttributes( nsFont = FindNSFont(faPtr->family, traits, weight, points, 1); } if (!nsFont) { - Tcl_Panic("Could not deternmine NSFont from TkFontAttributes"); + Tcl_Panic("Could not determine NSFont from TkFontAttributes"); } if (tkFontPtr == NULL) { fontPtr = ckalloc(sizeof(MacFont)); @@ -675,7 +677,6 @@ TkpGetFontAttrsForChar( { MacFont *fontPtr = (MacFont *) tkfont; NSFont *nsFont = fontPtr->nsFont; - *faPtr = fontPtr->font.fa; if (nsFont && ![[nsFont coveredCharacterSet] characterIsMember:c]) { UTF16Char ch = c; diff --git a/macosx/tkMacOSXInit.c b/macosx/tkMacOSXInit.c index 98e6824..c658149 100644 --- a/macosx/tkMacOSXInit.c +++ b/macosx/tkMacOSXInit.c @@ -57,9 +57,19 @@ static void keyboardChanged(CFNotificationCenterRef center, void *observer, CFSt @end @implementation TKApplication +#ifndef __clang__ +@synthesize poolProtected = _poolProtected; +#endif @end @implementation TKApplication(TKInit) +- (void) _resetAutoreleasePool +{ + if(![self poolProtected]) { + [_mainPool drain]; + _mainPool = [NSAutoreleasePool new]; + } +} #ifdef TK_MAC_DEBUG_NOTIFICATIONS - (void) _postedNotification: (NSNotification *) notification { @@ -86,16 +96,19 @@ static void keyboardChanged(CFNotificationCenterRef center, void *observer, CFSt - (void) _setupEventLoop { - - /*Remove private API flags here.*/ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; [self finishLaunching]; [self setWindowsNeedUpdate:YES]; + [pool drain]; } - (void) _setup: (Tcl_Interp *) interp { _eventInterp = interp; + _mainPool = nil; + [NSApp setPoolProtected:NO]; _defaultMainMenu = nil; + NSAutoreleasePool *pool = [NSAutoreleasePool new]; [self _setupMenus]; [self setDelegate:self]; #ifdef TK_MAC_DEBUG_NOTIFICATIONS @@ -104,12 +117,13 @@ static void keyboardChanged(CFNotificationCenterRef center, void *observer, CFSt #endif [self _setupWindowNotifications]; [self _setupApplicationNotifications]; + [pool drain]; } - (NSString *) tkFrameworkImagePath: (NSString *) image { NSString *path = nil; - + NSAutoreleasePool *pool = [NSAutoreleasePool new]; if (tkLibPath[0] != '\0') { path = [[NSBundle bundleWithPath:[[NSString stringWithUTF8String: tkLibPath] stringByAppendingString:@"/../.."]] @@ -142,6 +156,8 @@ static void keyboardChanged(CFNotificationCenterRef center, void *observer, CFSt } } #endif + [path retain]; + [pool drain]; return path; } @end @@ -168,6 +184,7 @@ static void SetApplicationIcon( ClientData clientData) { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSString *path = [NSApp tkFrameworkImagePath:@"Tk.icns"]; if (path) { NSImage *image = [[NSImage alloc] initWithContentsOfFile:path]; @@ -176,6 +193,7 @@ SetApplicationIcon( [image release]; } } + [pool drain]; } /* @@ -253,16 +271,19 @@ TkpInit( } #endif - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - [[NSUserDefaults standardUserDefaults] registerDefaults: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:YES], - @"_NSCanWrapButtonTitles", - [NSNumber numberWithInt:-1], - @"NSStringDrawingTypesetterBehavior", - nil]]; - [TKApplication sharedApplication]; - [NSApp _setup:interp]; + { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + [[NSUserDefaults standardUserDefaults] registerDefaults: + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:YES], + @"_NSCanWrapButtonTitles", + [NSNumber numberWithInt:-1], + @"NSStringDrawingTypesetterBehavior", + nil]]; + [TKApplication sharedApplication]; + [pool drain]; + [NSApp _setup:interp]; + } /* Check whether we are a bundled executable: */ bundleRef = CFBundleGetMainBundle(); @@ -319,12 +340,15 @@ TkpInit( Tcl_DoWhenIdle(SetApplicationIcon, NULL); } - [NSApp _setupEventLoop]; - TkMacOSXInitAppleEvents(interp); - TkMacOSXUseAntialiasedText(interp, -1); - TkMacOSXInitCGDrawing(interp, TRUE, 0); - [pool drain]; - + { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + [NSApp _setupEventLoop]; + TkMacOSXInitAppleEvents(interp); + TkMacOSXUseAntialiasedText(interp, -1); + TkMacOSXInitCGDrawing(interp, TRUE, 0); + [pool drain]; + } + /* * FIXME: Close stdin & stdout for remote debugging otherwise we will * fight with gdb for stdin & stdout @@ -480,9 +504,8 @@ TkpDisplayWarning( MODULE_SCOPE void TkMacOSXDefaultStartupScript(void) { - CFBundleRef bundleRef; - - bundleRef = CFBundleGetMainBundle(); + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + CFBundleRef bundleRef = CFBundleGetMainBundle(); if (bundleRef != NULL) { CFURLRef appMainURL = CFBundleCopyResourceURL(bundleRef, @@ -506,6 +529,7 @@ TkMacOSXDefaultStartupScript(void) CFRelease(appMainURL); } } + [pool drain]; } /* diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c index 8521beb..151b4f2 100644 --- a/macosx/tkMacOSXKeyEvent.c +++ b/macosx/tkMacOSXKeyEvent.c @@ -328,7 +328,7 @@ static unsigned isFunctionKey(unsigned int code); pt.y = caret_y; pt = [self convertPoint: pt toView: nil]; - pt = [[self window] convertBaseToScreen: pt]; + pt = [[self window] convertPointToScreen: pt]; pt.y -= caret_height; rect.origin = pt; diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c index 0c078ce..c7e3a78 100644 --- a/macosx/tkMacOSXMenu.c +++ b/macosx/tkMacOSXMenu.c @@ -258,10 +258,10 @@ static int ModifierCharWidth(Tk_Font tkfont); if (menuPtr && mePtr) { Tcl_Interp *interp = menuPtr->interp; - /*Add time for errors to fire if necessary. This is sub-optimal but avoids issues with Tcl/Cocoa event loop integration.*/ + /*Add time for errors to fire if necessary. This is sub-optimal + *but avoids issues with Tcl/Cocoa event loop integration. + */ Tcl_Sleep(100); - - NSAutoreleasePool *pool = [NSAutoreleasePool new]; Tcl_Preserve(interp); Tcl_Preserve(menuPtr); @@ -274,7 +274,6 @@ static int ModifierCharWidth(Tk_Font tkfont); } Tcl_Release(menuPtr); Tcl_Release(interp); - [pool drain]; } } } @@ -377,13 +376,6 @@ static int ModifierCharWidth(Tk_Font tkfont); @implementation TKApplication(TKMenu) -- (void) safeSetMainMenu: (NSMenu *) menu -{ - NSAutoreleasePool* pool = [NSAutoreleasePool new]; - [self setMainMenu: menu]; - [pool drain]; -} - - (void) menuBeginTracking: (NSNotification *) notification { #ifdef TK_MAC_DEBUG_NOTIFICATIONS @@ -420,7 +412,7 @@ static int ModifierCharWidth(Tk_Font tkfont); if (!mePtr || !(mePtr->entryFlags & ENTRY_APPLE_MENU)) { applicationMenuItem = [NSMenuItem itemWithSubmenu: - [[_defaultApplicationMenu copy] autorelease]]; + [_defaultApplicationMenu copy]]; [menu insertItem:applicationMenuItem atIndex:0]; } [menu setSpecial:tkMainMenu]; @@ -428,7 +420,7 @@ static int ModifierCharWidth(Tk_Font tkfont); applicationMenu = (TKMenu *)[applicationMenuItem submenu]; if (![applicationMenu isSpecial:tkApplicationMenu]) { for (NSMenuItem *item in _defaultApplicationMenuItems) { - [applicationMenu addItem:[[item copy] autorelease]]; + [applicationMenu addItem:[item copy]]; } [applicationMenu setSpecial:tkApplicationMenu]; } @@ -438,15 +430,13 @@ static int ModifierCharWidth(Tk_Font tkfont); for (NSMenuItem *item in itemArray) { TkMenuEntry *mePtr = (TkMenuEntry *)[item tag]; TKMenu *submenu = (TKMenu *)[item submenu]; - if (mePtr && submenu) { if ((mePtr->entryFlags & ENTRY_WINDOWS_MENU) && ![submenu isSpecial:tkWindowsMenu]) { NSInteger index = 0; for (NSMenuItem *i in _defaultWindowsMenuItems) { - [submenu insertItem:[[i copy] autorelease] atIndex: - index++]; + [submenu insertItem:[i copy] atIndex:index++]; } [self setWindowsMenu:submenu]; [submenu setSpecial:tkWindowsMenu]; @@ -455,8 +445,7 @@ static int ModifierCharWidth(Tk_Font tkfont); NSInteger index = 0; for (NSMenuItem *i in _defaultHelpMenuItems) { - [submenu insertItem:[[i copy] autorelease] atIndex: - index++]; + [submenu insertItem:[i copy] atIndex:index++]; } [submenu setSpecial:tkHelpMenu]; } @@ -475,7 +464,7 @@ static int ModifierCharWidth(Tk_Font tkfont); [servicesMenuItem setSubmenu:_servicesMenu]; } [self setAppleMenu:applicationMenu]; - [self safeSetMainMenu:menu]; + [self setMainMenu:menu]; } @end diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index 3fe59bd..06207e2 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -50,7 +50,7 @@ static void TkMacOSXEventsCheckProc(ClientData clientData, int flags); @end @implementation TKApplication(TKNotify) -/* Call super then redisplay all of our windows. */ +/* Display all windows each time an event is removed from the queue.*/ - (NSEvent *) nextEventMatchingMask: (NSUInteger) mask untilDate: (NSDate *) expiration inMode: (NSString *) mode dequeue: (BOOL) deqFlag @@ -59,9 +59,9 @@ static void TkMacOSXEventsCheckProc(ClientData clientData, int flags); untilDate:expiration inMode:mode dequeue:deqFlag]; - NSAutoreleasePool *pool = [NSAutoreleasePool new]; + /* Retain this event for later use. Must be released.*/ + [event retain]; [NSApp makeWindowsPerform:@selector(tkDisplayIfNeeded) inOrder:NO]; - [pool drain]; return event; } @@ -70,10 +70,8 @@ static void TkMacOSXEventsCheckProc(ClientData clientData, int flags); */ - (void) sendEvent: (NSEvent *) theEvent { - NSAutoreleasePool *pool = [NSAutoreleasePool new]; [super sendEvent:theEvent]; [NSApp tkCheckPasteboard]; - [pool drain]; } @end @@ -217,19 +215,21 @@ TkMacOSXEventsSetupProc( ClientData clientData, int flags) { - if (flags & TCL_WINDOW_EVENTS && - ![[NSRunLoop currentRunLoop] currentMode]) { + NSString *runloopMode = [[NSRunLoop currentRunLoop] currentMode]; + /* runloopMode will be nil if we are in the Tcl event loop. */ + if (flags & TCL_WINDOW_EVENTS && !runloopMode) { static const Tcl_Time zeroBlockTime = { 0, 0 }; - NSAutoreleasePool *pool = [NSAutoreleasePool new]; /* Call this with dequeue=NO -- just checking if the queue is empty. */ NSEvent *currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate distantPast] - inMode:GetRunLoopMode(TkMacOSXGetModalSession()) - dequeue:NO]; - if (currentEvent && currentEvent.type > 0) { - Tcl_SetMaxBlockTime(&zeroBlockTime); + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(TkMacOSXGetModalSession()) + dequeue:NO]; + if (currentEvent) { + if (currentEvent.type > 0) { + Tcl_SetMaxBlockTime(&zeroBlockTime); + } + [currentEvent release]; } - [pool drain]; } } @@ -256,13 +256,14 @@ TkMacOSXEventsCheckProc( int flags) { NSString *runloopMode = [[NSRunLoop currentRunLoop] currentMode]; + /* runloopMode will be nil if we are in the Tcl event loop. */ if (flags & TCL_WINDOW_EVENTS && !runloopMode) { - NSEvent *currentEvent = nil; NSEvent *testEvent = nil; NSModalSession modalSession; - + do { + [NSApp _resetAutoreleasePool]; modalSession = TkMacOSXGetModalSession(); testEvent = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] @@ -277,25 +278,25 @@ TkMacOSXEventsCheckProc( untilDate:[NSDate distantPast] inMode:GetRunLoopMode(modalSession) dequeue:YES]; - if (!currentEvent) { - break; /* No events are available. */ - } - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - /* Generate Xevents. */ - int oldServiceMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); - NSEvent *processedEvent = [NSApp tkProcessEvent:currentEvent]; - Tcl_SetServiceMode(oldServiceMode); - if (processedEvent) { /* Should always be non-NULL. */ + if (currentEvent) { + /* Generate Xevents. */ + int oldServiceMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + NSEvent *processedEvent = [NSApp tkProcessEvent:currentEvent]; + Tcl_SetServiceMode(oldServiceMode); + if (processedEvent) { /* Should always be non-NULL. */ #ifdef TK_MAC_DEBUG_EVENTS - TKLog(@" event: %@", currentEvent); + TKLog(@" event: %@", currentEvent); #endif - if (modalSession) { - [NSApp _modalSession:modalSession sendEvent:currentEvent]; - } else { - [NSApp sendEvent:currentEvent]; + if (modalSession) { + [NSApp _modalSession:modalSession sendEvent:currentEvent]; + } else { + [NSApp sendEvent:currentEvent]; + } } + [currentEvent release]; + } else { + break; } - [pool drain]; } while (1); } } diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 90d5a9b..2a411f6 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -275,10 +275,13 @@ VISIBILITY_HIDDEN NSArray *_defaultApplicationMenuItems, *_defaultWindowsMenuItems; NSArray *_defaultHelpMenuItems; NSWindow *_windowWithMouse; + NSAutoreleasePool *_mainPool; } +@property BOOL poolProtected; @end @interface TKApplication(TKInit) - (NSString *)tkFrameworkImagePath:(NSString*)image; +- (void)_resetAutoreleasePool; @end @interface TKApplication(TKEvent) - (NSEvent *)tkProcessEvent:(NSEvent *)theEvent; diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index d23ba85..f026318 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -131,11 +131,6 @@ XMapWindow( { MacDrawable *macWin = (MacDrawable *) window; XEvent event; - /* - * This function can be called from outside the AppKit event - * loop, so it needs its own AutoreleasePool. - */ - NSAutoreleasePool* pool = [NSAutoreleasePool new]; /* * Under certain situations it's possible for this function to be called @@ -158,6 +153,7 @@ XMapWindow( if ( [win canBecomeKeyWindow] ) { [win makeKeyAndOrderFront:NSApp]; } + /* Why do we need this? (It is used by Carbon)*/ [win windowRef]; TkMacOSXApplyWindowAttributes(macWin->winPtr, win); } @@ -194,7 +190,6 @@ XMapWindow( event.xvisibility.type = VisibilityNotify; event.xvisibility.state = VisibilityUnobscured; NotifyVisibility(macWin->winPtr, &event); - [pool drain]; } /* @@ -318,7 +313,6 @@ XResizeWindow( unsigned int height) { MacDrawable *macWin = (MacDrawable *) window; - NSAutoreleasePool *pool= [NSAutoreleasePool new]; display->request++; if (Tk_IsTopLevel(macWin->winPtr) && !Tk_IsEmbedded(macWin->winPtr)) { NSWindow *w = macWin->winPtr->wmInfoPtr->window; @@ -333,7 +327,6 @@ XResizeWindow( } else { MoveResizeWindow(macWin); } - [pool drain]; } /* @@ -366,7 +359,6 @@ XMoveResizeWindow( display->request++; if (Tk_IsTopLevel(macWin->winPtr) && !Tk_IsEmbedded(macWin->winPtr)) { NSWindow *w = macWin->winPtr->wmInfoPtr->window; - if (w) { NSRect r = NSMakeRect(x + macWin->winPtr->wmInfoPtr->xInParent, tkMacOSXZeroScreenHeight - (y + @@ -407,7 +399,6 @@ XMoveWindow( display->request++; if (Tk_IsTopLevel(macWin->winPtr) && !Tk_IsEmbedded(macWin->winPtr)) { NSWindow *w = macWin->winPtr->wmInfoPtr->window; - if (w) { [w setFrameTopLeftPoint:NSMakePoint(x, tkMacOSXZeroScreenHeight - y)]; } diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 851358d..1150f2e 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -806,7 +806,7 @@ ConfigureRestrictProc( { const NSRect *rectsBeingDrawn; NSInteger rectsBeingDrawnCount; - + [self getRectsBeingDrawn:&rectsBeingDrawn count:&rectsBeingDrawnCount]; #ifdef TK_MAC_DEBUG_DRAWING @@ -840,7 +840,8 @@ ConfigureRestrictProc( -(void) setFrameSize: (NSSize)newsize { - if ( [self inLiveResize] ) { + [super setFrameSize: newsize]; + if ([self inLiveResize]) { NSWindow *w = [self window]; TkWindow *winPtr = TkMacOSXGetTkWindow(w); Tk_Window tkwin = (Tk_Window) winPtr; @@ -849,17 +850,29 @@ ConfigureRestrictProc( ClientData oldArg; Tk_RestrictProc *oldProc; - /* Resize the NSView */ - [super setFrameSize: newsize]; - - /* Disable drawing until the window has been completely configured.*/ + /* This can be called from outside the Tk event loop. + * Since it calls Tcl_DoOneEvent, we need to make sure we + * don't clobber the AutoreleasePool set up by the caller. + */ + [NSApp setPoolProtected:YES]; + + /* + * Try to prevent flickers and flashes. + * + * This stops the flickers on OSX 10.11. But flashes still occur when + * the width of the window is 16, 32, 48, 64, 80, 96, 112, 256, 512, + * 768, ... :^( + */ + [w disableFlushWindow]; + + /* Disable Tk drawing until the window has been completely configured.*/ TkMacOSXSetDrawingEnabled(winPtr, 0); /* Generate and handle a ConfigureNotify event for the new size.*/ TkGenWMConfigureEvent(tkwin, Tk_X(tkwin), Tk_Y(tkwin), width, height, TK_SIZE_CHANGED | TK_MACOSX_HANDLE_EVENT_IMMEDIATELY); oldProc = Tk_RestrictEvents(ConfigureRestrictProc, NULL, &oldArg); - while ( Tk_DoOneEvent(TK_X_EVENTS|TK_DONT_WAIT) ) {} + while (Tk_DoOneEvent(TK_X_EVENTS|TK_DONT_WAIT)) {} Tk_RestrictEvents(oldProc, oldArg, &oldArg); /* Now that Tk has configured all subwindows we can create the clip regions. */ @@ -871,9 +884,10 @@ ConfigureRestrictProc( HIRect bounds = NSRectToCGRect([self bounds]); HIShapeRef shape = HIShapeCreateWithRect(&bounds); [self generateExposeEvents: shape]; - while ( Tk_DoOneEvent(TK_ALL_EVENTS|TK_DONT_WAIT) ) {} - } else { - [super setFrameSize: newsize]; + while (Tk_DoOneEvent(TK_ALL_EVENTS|TK_DONT_WAIT)) {} + [w enableFlushWindow]; + [w flushWindowIfNeeded]; + [NSApp setPoolProtected:NO]; } } @@ -891,12 +905,10 @@ ConfigureRestrictProc( [self generateExposeEvents: shape]; } -/* Core method of this class: generates expose events for redrawing. - * Whereas drawRect is intended to be called only from the Appkit event - * loop, this can be called from Tk. If the Tcl_ServiceMode is set to - * TCL_SERVICE_ALL then the expose events will be immediately removed - * from the Tcl event loop and processed. Typically, they should be queued, - * however. +/* Core method of this class: generates expose events for redrawing. If the + * Tcl_ServiceMode is set to TCL_SERVICE_ALL then the expose events will be + * immediately removed from the Tcl event loop and processed. Typically, they + * should be queued, however. */ - (void) generateExposeEvents: (HIShapeRef) shape { diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 5ec38ca..308ee11 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -404,18 +404,23 @@ static void RemapWindows(TkWindow *winPtr, if (title == nil) { title = "unnamed window"; } - printf("Retained <%s>. Count is: %lu\n", title, [self retainCount]); + if (DEBUG_ZOMBIES > 1){ + printf("Retained <%s>. Count is: %lu\n", title, [self retainCount]); + } return result; } - (id) autorelease { + static int xcount = 0; id result = [super autorelease]; const char *title = [[self title] UTF8String]; if (title == nil) { title = "unnamed window"; } - printf("Autoreleased <%s>. Count is %lu\n", title, [self retainCount]); + if (DEBUG_ZOMBIES > 1){ + printf("Autoreleased <%s>. Count is %lu\n", title, [self retainCount]); + } return result; } @@ -424,9 +429,24 @@ static void RemapWindows(TkWindow *winPtr, if (title == nil) { title = "unnamed window"; } - printf("Releasing <%s>. Count is %lu\n", title, [self retainCount]); + if (DEBUG_ZOMBIES > 1){ + printf("Releasing <%s>. Count is %lu\n", title, [self retainCount]); + } [super release]; } + +- (void) dealloc { + const char *title = [[self title] UTF8String]; + if (title == nil) { + title = "unnamed window"; + } + if (DEBUG_ZOMBIES > 0){ + printf(">>>> Freeing <%s>. Count is %lu\n", title, [self retainCount]); + } + [super dealloc]; +} + + #endif @end @@ -541,7 +561,6 @@ FrontWindowAtPoint( int x, int y) { NSPoint p = NSMakePoint(x, tkMacOSXZeroScreenHeight - y); - NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSArray *windows = [NSApp orderedWindows]; TkWindow *front = NULL; @@ -551,7 +570,6 @@ FrontWindowAtPoint( break; } } - [pool drain]; return front; } @@ -855,7 +873,6 @@ TkWmDeadWindow( NSWindow *window = wmPtr->window; if (window && !Tk_IsEmbedded(winPtr) ) { - NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSWindow *parent = [window parentWindow]; if (parent) { [parent removeChildWindow:window]; @@ -865,26 +882,30 @@ TkWmDeadWindow( if (winPtr->window) { ((MacDrawable *) winPtr->window)->view = nil; } -#if DEBUG_ZOMBIES +#if DEBUG_ZOMBIES > 0 { const char *title = [[window title] UTF8String]; if (title == nil) { title = "unnamed window"; } - printf("Closing <%s>. Count is: %lu\n", title, [window retainCount]); + printf(">>>> Closing <%s>. Count is: %lu\n", title, [window retainCount]); } #endif [window release]; wmPtr->window = NULL; - /* Activate the highest window left on the screen. */ - NSArray *windows = [NSApp orderedWindows]; - if ( [windows count] > 0 ) { - NSWindow *front = [windows objectAtIndex:0]; - if ( front && [front canBecomeKeyWindow] ) { - [front makeKeyAndOrderFront:NSApp]; - } - } - [pool drain]; + + /* Activate the highest window left on the screen. */ + NSArray *windows = [NSApp orderedWindows]; + if ( [windows count] > 0 ) { + NSWindow *front = [windows objectAtIndex:0]; + if ( front && [front canBecomeKeyWindow] ) { + [front makeKeyAndOrderFront:NSApp]; + } + } +#if DEBUG_ZOMBIES > 0 + fprintf(stderr, "================= Pool dump ===================\n"); + [NSAutoreleasePool showPools]; +#endif } ckfree(wmPtr); winPtr->wmInfoPtr = NULL; @@ -5538,9 +5559,6 @@ TkMacOSXMakeRealWindowExist( * TODO: Here we should handle out of process embedding. */ } - - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - WindowClass macClass = wmPtr->macClass; wmPtr->attributes &= (tkAlwaysValidAttributes | macClassAttrs[macClass].validAttrs); @@ -5574,10 +5592,10 @@ TkMacOSXMakeRealWindowExist( NSWindow *window = [[winClass alloc] initWithContentRect:contentRect styleMask:styleMask backing:NSBackingStoreBuffered defer:YES]; if (!window) { - Tcl_Panic("couldn't allocate new Mac window"); + Tcl_Panic("couldn't allocate new Mac window"); } TKContentView *contentView = [[TKContentView alloc] - initWithFrame:NSZeroRect]; + initWithFrame:NSZeroRect]; [window setContentView:contentView]; [contentView release]; [window setDelegate:NSApp]; @@ -5612,7 +5630,7 @@ TkMacOSXMakeRealWindowExist( [window setDocumentEdited:NO]; wmPtr->window = window; - macWin->view = contentView; + macWin->view = window.contentView; TkMacOSXApplyWindowAttributes(winPtr, window); NSRect geometry = InitialWindowBounds(winPtr, window); @@ -5621,11 +5639,8 @@ TkMacOSXMakeRealWindowExist( geometry.origin.y = tkMacOSXZeroScreenHeight - (geometry.origin.y + geometry.size.height); [window setFrame:geometry display:NO]; - TkMacOSXRegisterOffScreenWindow((Window) macWin, window); macWin->flags |= TK_HOST_EXISTS; - - [pool drain]; } /* @@ -6018,7 +6033,6 @@ TkpChangeFocus( if (Tk_IsTopLevel(winPtr) && !Tk_IsEmbedded(winPtr) ){ NSWindow *win = TkMacOSXDrawableWindow(winPtr->window); - NSAutoreleasePool *pool = [NSAutoreleasePool new]; TkWmRestackToplevel(winPtr, Above, NULL); if (force ) { [NSApp activateIgnoringOtherApps:YES]; @@ -6026,7 +6040,6 @@ TkpChangeFocus( if ( win && [win canBecomeKeyWindow] ) { [win makeKeyAndOrderFront:NSApp]; } - [pool drain]; } /* diff --git a/macosx/tkMacOSXXStubs.c b/macosx/tkMacOSXXStubs.c index c39d42d..53d1eb8 100644 --- a/macosx/tkMacOSXXStubs.c +++ b/macosx/tkMacOSXXStubs.c @@ -142,8 +142,8 @@ TkpOpenDisplay( static NSRect maxBounds = {{0, 0}, {0, 0}}; static char vendor[25] = ""; NSArray *cgVers; - - + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + if (gMacDisplay != NULL) { if (strcmp(gMacDisplay->display->display_name, display_name) == 0) { return gMacDisplay; @@ -152,8 +152,6 @@ TkpOpenDisplay( } } - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - display = ckalloc(sizeof(Display)); screen = ckalloc(sizeof(Screen)); bzero(display, sizeof(Display)); -- cgit v0.12