pure-data/mac/patches/tk8.6.10_zombiewindows.patch

diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h
index 9417b62e6..dff6a200d 100644
--- a/macosx/tkMacOSXPrivate.h
+++ b/macosx/tkMacOSXPrivate.h
@@ -433,6 +433,18 @@ VISIBILITY_HIDDEN
 - (void) setAppleMenu: (NSMenu *) menu;
 @end
 
+/*
+ * These methods are exposed because they are needed to prevent zombie windows
+ * on systems with a TouchBar.  The TouchBar Key-Value observer holds a
+ * reference to the key window, which prevents deallocation of the key window
+ * when it is closed.
+ */
+
+@interface NSApplication(TkWm)
+- (id) _setKeyWindow: (NSWindow *) window;
+- (id) _setMainWindow: (NSWindow *) window;
+@end
+
 #endif /* _TKMACPRIV */
 
 int TkMacOSXGetAppPath(ClientData cd, Tcl_Interp *ip, int objc, Tcl_Obj *const objv[]);
diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c
index ceb3f3f7e..f24f198f0 100644
--- a/macosx/tkMacOSXWm.c
+++ b/macosx/tkMacOSXWm.c
@@ -879,6 +879,7 @@ TkWmDeadWindow(
     TkWindow *winPtr)		/* Top-level window that's being deleted. */
 {
     WmInfo *wmPtr = winPtr->wmInfoPtr, *wmPtr2;
+    NSWindow *ourNSWindow;
 
     if (wmPtr == NULL) {
 	return;
@@ -952,77 +953,87 @@ TkWmDeadWindow(
     }
 
     /*
-     * Delete the Mac window and remove it from the windowTable. The window
-     * could be nil if the window was never mapped. However, we don't do this
-     * for embedded windows, they don't go in the window list, and they do not
-     * own their portPtr's.
+     * Unregister the NSWindow and remove all references to it from the Tk
+     * data structures.  If the NSWindow is a child, disassociate it from
+     * the parent.  Then close and release the NSWindow.
      */
 
-    NSWindow *window = wmPtr->window;
-
-    if (window && !Tk_IsEmbedded(winPtr)) {
-	NSWindow *parent = [window parentWindow];
+    ourNSWindow = wmPtr->window;
+    if (ourNSWindow && !Tk_IsEmbedded(winPtr)) {
+	NSWindow *parent = [ourNSWindow parentWindow];
+	TkMacOSXUnregisterMacWindow(ourNSWindow);
+        if (winPtr->window) {
+            ((MacDrawable *) winPtr->window)->view = nil;
+        }
+	wmPtr->window = NULL;
 
 	if (parent) {
-	    [parent removeChildWindow:window];
+	    [parent removeChildWindow:ourNSWindow];
 	}
-#if DEBUG_ZOMBIES > 0
+
+#if DEBUG_ZOMBIES > 1
 	{
-	    const char *title = [[window title] UTF8String];
+	    const char *title = [[ourNSWindow title] UTF8String];
 	    if (title == nil) {
 		title = "unnamed window";
 	    }
 	    fprintf(stderr, ">>>> Closing <%s>. Count is: %lu\n", title,
-		    [window retainCount]);
+		    [ourNSWindow retainCount]);
 	}
 #endif
-	[window close];
-	TkMacOSXUnregisterMacWindow(window);
-        if (winPtr->window) {
-            ((MacDrawable *) winPtr->window)->view = nil;
-        }
-	wmPtr->window = NULL;
-        [window release];
-
-	/* Activate the highest window left on the screen. */
-	NSArray *windows = [NSApp orderedWindows];
-	for (id nswindow in windows) {
-	    TkWindow *winPtr2 = TkMacOSXGetTkWindow(nswindow);
 
-	    if (winPtr2 && nswindow != window) {
-		WmInfo *wmPtr = winPtr2->wmInfoPtr;
-		BOOL minimized = (wmPtr->hints.initial_state == IconicState
-			|| wmPtr->hints.initial_state == WithdrawnState);
+	/*
+	 * When a window is closed we want to move the focus to the next
+	 * highest window.  Apple's documentation says that calling the
+	 * orderOut method of the key window will accomplish this.  But
+	 * experiment shows that this is not the case.  So we have to reset the
+	 * key window ourselves.  When the window is the last one on the screen
+	 * there is no choice for a new key window.  Moreover, if the host
+	 * computer has a TouchBar then the TouchBar holds a reference to the
+	 * key window which prevents it from being deallocated until it stops
+	 * being the key window.  On these systems the only option for
+	 * preventing zombies is to set the key window to nil.
+	 */
 
-		/*
-		 * If no windows are left on the screen and the next window is
-		 * iconified or withdrawn, we don't want to make it be the
-		 * KeyWindow because that would cause it to be displayed on the
-		 * screen.
-		 */
+	for (NSWindow *w in [NSApp orderedWindows]) {
+	    TkWindow *winPtr2 = TkMacOSXGetTkWindow(w);
+	    BOOL isOnScreen;
 
-		if ([nswindow canBecomeKeyWindow] && !minimized) {
-		    [nswindow makeKeyAndOrderFront:NSApp];
-		    break;
-		}
+	    if (!winPtr2 || !winPtr2->wmInfoPtr) {
+		continue;
+	    }
+	    wmPtr2 = winPtr2->wmInfoPtr;
+	    isOnScreen = (wmPtr2->hints.initial_state != IconicState &&
+			  wmPtr2->hints.initial_state != WithdrawnState);
+	    if (w != ourNSWindow && isOnScreen && [w canBecomeKeyWindow]) {
+		[w makeKeyAndOrderFront:NSApp];
+		break;
 	    }
 	}
 
 	/*
-	 * Process all window events immediately to force the closed window to
-	 * be deallocated.  But don't do this for the root window as that is
-	 * unnecessary and can lead to segfaults.
+	 * Prevent zombies on systems with a TouchBar.
 	 */
 
-	if (winPtr->parentPtr) {
-	    while (Tcl_DoOneEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {}
+	if (ourNSWindow == [NSApp keyWindow]) {
+	    [NSApp _setKeyWindow:nil];
+	    [NSApp _setMainWindow:nil];
 	}
+	[ourNSWindow close];
+	[ourNSWindow release];
 	[NSApp _resetAutoreleasePool];
-#if DEBUG_ZOMBIES > 0
+
+#if DEBUG_ZOMBIES > 1
 	fprintf(stderr, "================= Pool dump ===================\n");
 	[NSAutoreleasePool showPools];
 #endif
+
     }
+
+    /*
+     * Deallocate the wmInfo and clear the wmInfoPtr.
+     */
+
     ckfree(wmPtr);
     winPtr->wmInfoPtr = NULL;
 }