summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Walzer <kw@codebykevin.com>2015-12-15 02:50:56 (GMT)
committerKevin Walzer <kw@codebykevin.com>2015-12-15 02:50:56 (GMT)
commitc23b714ab089bf546c9cc37188f29274b4e4991a (patch)
treec0d8acf525b0ffd8ac7769cfc2eabccc1470bb8c
parent66ba0bfddcbc40934af2383e187fc55d981902f4 (diff)
downloadtk-c23b714ab089bf546c9cc37188f29274b4e4991a.zip
tk-c23b714ab089bf546c9cc37188f29274b4e4991a.tar.gz
tk-c23b714ab089bf546c9cc37188f29274b4e4991a.tar.bz2
Fix for some redraw issues on Tk-Cocoa on OS X 10.11; further refinement of memory management; thanks to Marc Culler for patches
-rw-r--r--macosx/README61
-rw-r--r--macosx/tkMacOSXDialog.c2
-rw-r--r--macosx/tkMacOSXDraw.c10
-rw-r--r--macosx/tkMacOSXEvent.c5
-rw-r--r--macosx/tkMacOSXFont.c7
-rw-r--r--macosx/tkMacOSXInit.c68
-rw-r--r--macosx/tkMacOSXKeyEvent.c2
-rw-r--r--macosx/tkMacOSXMenu.c27
-rw-r--r--macosx/tkMacOSXNotify.c63
-rw-r--r--macosx/tkMacOSXPrivate.h3
-rw-r--r--macosx/tkMacOSXSubwindows.c11
-rw-r--r--macosx/tkMacOSXWindowEvent.c44
-rw-r--r--macosx/tkMacOSXWm.c69
-rw-r--r--macosx/tkMacOSXXStubs.c6
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));