From ed79116517a5979ed583a4b0245e64a13db47502 Mon Sep 17 00:00:00 2001 From: Kevin Walzer Date: Sat, 21 Mar 2015 01:22:36 +0000 Subject: Cleanup and simplification of memory management in event loop; now works more smoothly; thanks to Marc Culler for patches --- macosx/tkMacOSXMenu.c | 9 +++- macosx/tkMacOSXNotify.c | 116 +++++++++++++++++++------------------------ macosx/tkMacOSXSubwindows.c | 6 +++ macosx/tkMacOSXWindowEvent.c | 33 ++++++++++-- macosx/tkMacOSXWm.c | 14 +++++- 5 files changed, 106 insertions(+), 72 deletions(-) diff --git a/macosx/tkMacOSXMenu.c b/macosx/tkMacOSXMenu.c index 472520f..5ae38f8 100644 --- a/macosx/tkMacOSXMenu.c +++ b/macosx/tkMacOSXMenu.c @@ -375,6 +375,13 @@ 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 @@ -466,7 +473,7 @@ static int ModifierCharWidth(Tk_Font tkfont); [servicesMenuItem setSubmenu:_servicesMenu]; } [self setAppleMenu:applicationMenu]; - [self setMainMenu:menu]; + [self safeSetMainMenu:menu]; } @end diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index d35427a..6a4e6c1 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -18,9 +18,9 @@ #include #import +/* This is not used for anything at the moment. */ typedef struct ThreadSpecificData { - int initialized, sendEventNestingLevel; - NSEvent *currentEvent; + int initialized; } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; @@ -34,6 +34,7 @@ static void TkMacOSXEventsCheckProc(ClientData clientData, int flags); #pragma mark TKApplication(TKNotify) @interface NSApplication(TKNotify) +/* We need to declare this hidden method. */ - (void)_modalSession:(NSModalSession)session sendEvent:(NSEvent *)event; @end @@ -47,35 +48,28 @@ static void TkMacOSXEventsCheckProc(ClientData clientData, int flags); @end @implementation TKApplication(TKNotify) +/* Redisplay all of our windows, then call super. */ - (NSEvent *)nextEventMatchingMask:(NSUInteger)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)deqFlag { NSAutoreleasePool *pool = [NSAutoreleasePool new]; [NSApp makeWindowsPerform:@selector(tkDisplayIfNeeded) inOrder:NO]; - int oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); - NSEvent *event = [[super nextEventMatchingMask:mask untilDate:expiration - inMode:mode dequeue:deqFlag] retain]; - Tcl_SetServiceMode(oldMode); - if (event) { - TSD_INIT(); - if (tsdPtr->sendEventNestingLevel) { - if (![NSApp tkProcessEvent:event]) { - [event release]; - event = nil; - } - } - } [pool drain]; - return [event autorelease]; + NSEvent *event = [super nextEventMatchingMask:mask + untilDate:expiration + inMode:mode + dequeue:deqFlag]; + return event; } + + /* + * Call super then check the pasteboard. + */ - (void)sendEvent:(NSEvent *)theEvent { - TSD_INIT(); - int oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); - tsdPtr->sendEventNestingLevel++; + NSAutoreleasePool *pool = [NSAutoreleasePool new]; [super sendEvent:theEvent]; - tsdPtr->sendEventNestingLevel--; - Tcl_SetServiceMode(oldMode); [NSApp tkCheckPasteboard]; + [pool drain]; } @end @@ -152,7 +146,8 @@ Tk_MacOSXSetupTkNotifier(void) "first [load] of TkAqua has to occur in the main thread!"); } Tcl_CreateEventSource(TkMacOSXEventsSetupProc, - TkMacOSXEventsCheckProc, GetMainEventQueue()); + TkMacOSXEventsCheckProc, + GetMainEventQueue()); TkCreateExitHandler(TkMacOSXNotifyExitHandler, NULL); Tcl_SetServiceMode(TCL_SERVICE_ALL); TclMacOSXNotifierAddRunLoopMode(NSEventTrackingRunLoopMode); @@ -184,7 +179,8 @@ TkMacOSXNotifyExitHandler( { TSD_INIT(); Tcl_DeleteEventSource(TkMacOSXEventsSetupProc, - TkMacOSXEventsCheckProc, GetMainEventQueue()); + TkMacOSXEventsCheckProc, + GetMainEventQueue()); tsdPtr->initialized = 0; } @@ -193,16 +189,19 @@ TkMacOSXNotifyExitHandler( * * TkMacOSXEventsSetupProc -- * - * This procedure implements the setup part of the TkAqua Events event - * source. It is invoked by Tcl_DoOneEvent before entering the notifier - * to check for events. + * This procedure implements the setup part of the MacOSX event + * source. It is invoked by Tcl_DoOneEvent before calling + * TkMacOSXEventsProc to process all queued NSEvents. In our + * case, all we need to do is to set the Tcl MaxBlockTime to + * 0 before starting the loop to process all queued NSEvents. * * Results: * None. * * Side effects: - * If TkAqua events are queued, then the maximum block time will be set - * to 0 to ensure that the notifier returns control to Tcl. + * + * If NSEvents are queued, then the maximum block time will be set + * to 0 to ensure that control returns immediately to Tcl. * *---------------------------------------------------------------------- */ @@ -213,20 +212,14 @@ TkMacOSXEventsSetupProc( int flags) { if (flags & TCL_WINDOW_EVENTS && - ![[NSRunLoop currentRunLoop] currentMode]) { + ![[NSRunLoop currentRunLoop] currentMode]) { static Tcl_Time zeroBlockTime = { 0, 0 }; - - TSD_INIT(); - if (!tsdPtr->currentEvent) { - NSEvent *currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate distantPast] - inMode:GetRunLoopMode(TkMacOSXGetModalSession()) - dequeue:YES]; - if (currentEvent) { - tsdPtr->currentEvent = [currentEvent retain]; - } - } - if (tsdPtr->currentEvent) { + /* 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) { Tcl_SetMaxBlockTime(&zeroBlockTime); } } @@ -237,17 +230,18 @@ TkMacOSXEventsSetupProc( * * TkMacOSXEventsCheckProc -- * - * This procedure processes events sitting in the TkAqua event queue. + * This procedure loops through all NSEvents waiting in the + * TKApplication event queue, generating X events from them. * * Results: * None. * * Side effects: - * Moves applicable queued TkAqua events onto the Tcl event queue. + * NSevents are used to generate X events, which are added to the + * Tcl event queue. * *---------------------------------------------------------------------- */ - static void TkMacOSXEventsCheckProc( ClientData clientData, @@ -256,31 +250,23 @@ TkMacOSXEventsCheckProc( if (flags & TCL_WINDOW_EVENTS && ![[NSRunLoop currentRunLoop] currentMode]) { NSEvent *currentEvent = nil; - NSAutoreleasePool *pool = nil; NSModalSession modalSession; - TSD_INIT(); - if (tsdPtr->currentEvent) { - currentEvent = tsdPtr->currentEvent; - [currentEvent autorelease]; - } do { modalSession = TkMacOSXGetModalSession(); + currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(modalSession) + dequeue:YES]; if (!currentEvent) { - currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate distantPast] - inMode:GetRunLoopMode(modalSession) dequeue:YES]; + break; /* No more events. */ } - if (!currentEvent) { - break; - } - [currentEvent retain]; - pool = [NSAutoreleasePool new]; - if (![NSApp tkProcessEvent:currentEvent]) { - [currentEvent release]; - currentEvent = nil; - } - if (currentEvent) { + 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. */ #ifdef TK_MAC_DEBUG_EVENTS TKLog(@" event: %@", currentEvent); #endif @@ -289,14 +275,12 @@ TkMacOSXEventsCheckProc( } else { [NSApp sendEvent:currentEvent]; } - [currentEvent release]; - currentEvent = nil; } [pool drain]; - pool = nil; } while (1); } } + /* * Local Variables: diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 95cc338..a9703c1 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -131,6 +131,11 @@ 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 @@ -189,6 +194,7 @@ XMapWindow( event.xvisibility.type = VisibilityNotify; event.xvisibility.state = VisibilityUnobscured; NotifyVisibility(macWin->winPtr, &event); + [pool drain]; } /* diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 86a1960..d68a933 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -796,7 +796,6 @@ Tk_MacOSXIsAppInFront(void) @implementation TKContentView @end - /*Restrict event processing to Expose events.*/ static Tk_RestrictAction ExposeRestrictProc( @@ -846,9 +845,37 @@ ExposeRestrictProc( } +-(void) setFrameSize: (NSSize)newsize +{ + if ( [self inLiveResize] ) { + NSWindow *window = [self window]; + TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); + Tk_Window tkwin = (Tk_Window) winPtr; + unsigned int width = (unsigned int)newsize.width; + unsigned int height=(unsigned int)newsize.height; + + /* Resize the Tk Window to the requested size.*/ + TkGenWMConfigureEvent(tkwin, Tk_X(tkwin), Tk_Y(tkwin), width, height, + TK_SIZE_CHANGED | TK_MACOSX_HANDLE_EVENT_IMMEDIATELY); + + /* Then resize the NSView to the actual window size*/ + newsize.width = (CGFloat)Tk_Width(tkwin); + newsize.height = (CGFloat)Tk_Height(tkwin); + [super setFrameSize: newsize]; + + /* Process all pending events to update the window. */ + while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {} + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} + + } else { + [super setFrameSize: newsize]; + } +} + /* * As insurance against bugs that might cause layout glitches during a live - * resize, we redraw the window at the end of the resize operation. + * resize, we redraw the window one more time at the end of the resize + * operation. */ - (void)viewDidEndLiveResize @@ -872,7 +899,6 @@ ExposeRestrictProc( return; } - HIShapeGetBounds(shape, &updateBounds); serial = LastKnownRequestProcessed(Tk_Display(winPtr)); if (GenerateUpdates(shape, &updateBounds, winPtr) && @@ -900,7 +926,6 @@ ExposeRestrictProc( Tk_RestrictEvents(oldProc, oldArg, &oldArg); while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} - } } diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index a852ac9..8459bdd 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -22,6 +22,8 @@ #include "tkMacOSXDebug.h" #include +#define DEBUG_ZOMBIES 0 + /* #ifdef TK_MAC_DEBUG #define TK_MAC_DEBUG_WINDOWS @@ -338,6 +340,17 @@ static void RemapWindows(TkWindow *winPtr, { id _i1, _i2; } + +- (id) retain +{ +#if DEBUG_ZOMBIES + const char *title = [[self title] UTF8String]; + if (title != NULL) { + printf("Retaining %s with count %lu\n", title, [self retainCount]); + } +#endif + return [super retain]; +} @end @implementation TKWindow @@ -785,7 +798,6 @@ TkWmDeadWindow( if (parent) { [parent removeChildWindow:window]; } - [window setExcludedFromWindowsMenu:YES]; [window close]; TkMacOSXUnregisterMacWindow(window); if (winPtr->window) { -- cgit v0.12