File: tk8.6.10_zombiewindows.patch

package info (click to toggle)
puredata 0.55.2%2Bds-1~bpo12%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-backports
  • size: 20,336 kB
  • sloc: ansic: 118,788; tcl: 10,221; cpp: 9,327; makefile: 1,632; sh: 1,476; python: 152; xml: 98; awk: 13
file content (167 lines) | stat: -rw-r--r-- 5,527 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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;
 }