fix window memory management (Cocoa)

Sat, 06 Jun 2026 18:04:54 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 06 Jun 2026 18:04:54 +0200
changeset 1174
8b8da1adef7d
parent 1173
ce0e443174d7
child 1175
1943f7411e50

fix window memory management (Cocoa)

make/xcode/toolkit/toolkit.xcodeproj/project.pbxproj file | annotate | diff | comparison | revisions
make/xcode/toolkit/toolkit/main.m file | annotate | diff | comparison | revisions
ui/cocoa/MainWindow.h file | annotate | diff | comparison | revisions
ui/cocoa/MainWindow.m file | annotate | diff | comparison | revisions
ui/cocoa/Toolbar.h file | annotate | diff | comparison | revisions
ui/cocoa/WindowManager.h file | annotate | diff | comparison | revisions
ui/cocoa/WindowManager.m file | annotate | diff | comparison | revisions
ui/cocoa/toolkit.m file | annotate | diff | comparison | revisions
ui/cocoa/webview.m file | annotate | diff | comparison | revisions
ui/cocoa/window.m file | annotate | diff | comparison | revisions
ui/common/types.c file | annotate | diff | comparison | revisions
--- a/make/xcode/toolkit/toolkit.xcodeproj/project.pbxproj	Sat Jun 06 16:53:39 2026 +0200
+++ b/make/xcode/toolkit/toolkit.xcodeproj/project.pbxproj	Sat Jun 06 18:04:54 2026 +0200
@@ -158,7 +158,7 @@
 		ED6581522CFF3CA000F5402F /* range.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = range.h; path = ../../../ui/ui/range.h; sourceTree = "<absolute>"; };
 		ED6581542CFF3CA000F5402F /* text.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = text.h; path = ../../../ui/ui/text.h; sourceTree = "<absolute>"; };
 		ED6581552CFF3CA000F5402F /* toolbar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolbar.h; path = ../../../ui/ui/toolbar.h; sourceTree = "<absolute>"; };
-		ED6581562CFF3CA000F5402F /* toolkit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolkit.h; path = ../../../ui/ui/toolkit.h; sourceTree = "<absolute>"; };
+		ED6581562CFF3CA000F5402F /* toolkit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolkit.h; path = /Users/olaf/Projekte/toolkit/ui/ui/toolkit.h; sourceTree = "<absolute>"; };
 		ED6581582CFF3CA000F5402F /* ui.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ui.h; path = ../../../ui/ui/ui.h; sourceTree = "<absolute>"; };
 		ED6581592CFF3CA000F5402F /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = window.h; path = ../../../ui/ui/window.h; sourceTree = "<absolute>"; };
 		ED65815A2CFF3EE900F5402F /* MainWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MainWindow.h; path = ../../../ui/cocoa/MainWindow.h; sourceTree = "<absolute>"; };
@@ -356,6 +356,7 @@
 				EDBC4D262EAD4BB0005CDF38 /* webview.m */,
 				ED895DA82EA0CACC00040078 /* TabView.h */,
 				ED895DA92EA0CACC00040078 /* TabView.m */,
+				ED6581562CFF3CA000F5402F /* toolkit.h */,
 				ED83C2BD2E8EA49200054B22 /* BoxContainer.h */,
 				ED83C2BE2E8EA49200054B22 /* BoxContainer.m */,
 				ED18C9212E76CA5500B64EA5 /* entry.h */,
@@ -421,7 +422,6 @@
 				ED6581522CFF3CA000F5402F /* range.h */,
 				ED6581542CFF3CA000F5402F /* text.h */,
 				ED6581552CFF3CA000F5402F /* toolbar.h */,
-				ED6581562CFF3CA000F5402F /* toolkit.h */,
 				ED6581582CFF3CA000F5402F /* ui.h */,
 				ED6581592CFF3CA000F5402F /* window.h */,
 			);
--- a/make/xcode/toolkit/toolkit/main.m	Sat Jun 06 16:53:39 2026 +0200
+++ b/make/xcode/toolkit/toolkit/main.m	Sat Jun 06 18:04:54 2026 +0200
@@ -162,12 +162,35 @@
     ui_list_update(doc->srclist);
 }
 
+static void action_window_close(UiEvent *event, void *data) {
+    ui_close(event->obj);
+}
+
+static void action_window_ref(UiEvent *event, void *data) {
+    ui_object_ref(data);
+}
+
+static void action_window_unref(UiEvent *event, void *data) {
+    ui_object_unref(data);
+}
+
+static void action_objref_window(UiEvent *event, void *data) {
+    UiObject *obj = ui_simple_window("Ref Window");
+    ui_vbox(obj, .spacing = 10, .margin = 10, .fill = TRUE) {
+        ui_button(obj, .label = "Ref", .onclick = action_window_ref, .onclickdata = data);
+        ui_button(obj, .label = "Unref", .onclick = action_window_unref, .onclickdata = data);
+    }
+    ui_show(obj);
+    
+}
+
 static void next_tab(UiEvent *event, void *data) {
     ui_var_set_int(event->obj->ctx, "tabview", 1);
 }
 
 void application_startup(UiEvent *event, void *data) {
     UiObject *obj = ui_splitview_window("My Window", TRUE);
+    
     //WindowData *wdata = ui_malloc(obj->ctx, sizeof(WindowData));
     //wdata->tbtoggle = ui_int_new(obj->ctx, "tbtoggle");
     //obj->window = wdata;
@@ -196,7 +219,9 @@
     
     ui_left_panel0(obj) {
         ui_grid(obj, .margin_left = 10, .margin_right = 10, .columnspacing = 10, .rowspacing = 10, .fill = TRUE) {
-            ui_button(obj, .label = "left", .hexpand = TRUE, .hfill = TRUE);
+            ui_button(obj, .label = "Close Window", .hexpand = TRUE, .hfill = TRUE, .onclick = action_window_close);
+            ui_newline(obj);
+            ui_button(obj, .label = "Ref/Unref Window", .onclick = action_objref_window, .onclickdata = obj, .hexpand = TRUE, .hfill = TRUE);
             ui_newline(obj);
             ui_linkbutton(obj, .varname = "link", .hexpand = TRUE, .hfill = TRUE);
             ui_newline(obj);
--- a/ui/cocoa/MainWindow.h	Sat Jun 06 16:53:39 2026 +0200
+++ b/ui/cocoa/MainWindow.h	Sat Jun 06 18:04:54 2026 +0200
@@ -29,7 +29,7 @@
 #import "toolkit.h"
 #import "../ui/window.h"
 
-@interface MainWindow : NSWindow<UiToplevelObject>
+@interface MainWindow : NSWindow<UiToplevelObject, NSWindowDelegate>
 
 @property UiObject *obj;
 @property (strong) NSSplitView *splitview;
--- a/ui/cocoa/MainWindow.m	Sat Jun 06 16:53:39 2026 +0200
+++ b/ui/cocoa/MainWindow.m	Sat Jun 06 18:04:54 2026 +0200
@@ -27,6 +27,7 @@
  */
 
 #import "MainWindow.h"
+#import "WindowManager.h"
 #import "container.h"
 #import "GridLayout.h"
 #import "BoxContainer.h"
@@ -38,6 +39,8 @@
 #import "menu.h"
 #import "Toolbar.h"
 
+
+
 @implementation MainWindow
 
 - (MainWindow*)init:(UiObject*)obj withSidebar:(BOOL)hasSidebar withSplitview:(BOOL)hasSplitview{
@@ -51,7 +54,7 @@
                              backing:NSBackingStoreBuffered
                                defer:false];
     _obj = obj;
-    
+    self.delegate = self;
     
     int top = 4;
     NSView *content = self.contentView;
@@ -137,7 +140,6 @@
         [self setToolbar:toolbar];
     }
     
-    
     return self;
 }
 
@@ -153,7 +155,37 @@
     }
 }
 
+- (void) windowWillClose:(NSNotification *) notification {
+    UiObject *obj = _obj;
+    
+    if(obj->onclose) {
+        UiEvent event;
+        memset(&event, 0, sizeof(UiEvent));
+        event.obj = obj;
+        event.window = obj->window;
+        event.document = obj->ctx->document;
+        obj->onclose(&event, obj->onclosedata);
+    }
+    
+    if(obj->ref > 0) {
+        obj->ref--;
+    } else {
+        // warn about invalid reference counting
+        NSLog(@"Error: UiObject %p ref == 0\n", obj);
+    }
+    
+    if(obj->ref == 0) {
+        // this cleans up any widget references from the context
+        uic_context_prepare_close(obj->ctx);
+        
+        [[WindowManager sharedWindowManager] closeWindow:self];
+    }
+}
 
+- (void) dealloc {
+    uic_object_destroy(_obj);
+    NSLog(@"MainWindow dealloc");
+}
 @end
 
 
--- a/ui/cocoa/Toolbar.h	Sat Jun 06 16:53:39 2026 +0200
+++ b/ui/cocoa/Toolbar.h	Sat Jun 06 18:04:54 2026 +0200
@@ -50,7 +50,7 @@
     NSMutableArray<NSString*> *defaultItems;
 }
 
-@property MainWindow *window;
+@property (weak) MainWindow *window;
 @property UiObject *obj;
 
 - (UiToolbar*) initWithWindow:(MainWindow*)window;
--- a/ui/cocoa/WindowManager.h	Sat Jun 06 16:53:39 2026 +0200
+++ b/ui/cocoa/WindowManager.h	Sat Jun 06 18:04:54 2026 +0200
@@ -28,14 +28,16 @@
 
 #import "toolkit.h"
 
-@interface WindowManager : NSObject<NSWindowDelegate>
+@interface WindowManager : NSObject
 
-@property NSMutableArray<NSWindow*> *windows;
+@property NSMutableArray<NSWindowController*> *windows;
 
 + (WindowManager*) sharedWindowManager;
 
 - (WindowManager*)init;
 
-- (void)addWindow:(NSWindow*)win;
+- (void)addWindow:(NSWindowController*)win;
+
+- (void)closeWindow:(NSWindow*)win;
 
 @end
--- a/ui/cocoa/WindowManager.m	Sat Jun 06 16:53:39 2026 +0200
+++ b/ui/cocoa/WindowManager.m	Sat Jun 06 18:04:54 2026 +0200
@@ -44,14 +44,12 @@
     return self;
 }
 
-- (void)addWindow:(NSWindow*)win {
+- (void)addWindow:(NSWindowController*)win {
     [_windows addObject:win];
-    [win setDelegate:self];
 }
 
-- (void) windowWillClose:(NSNotification *) notification {
-    NSWindow *window = notification.object;
-    [_windows removeObject:window];
+- (void)closeWindow:(NSWindow*)win {
+    [_windows removeObject:win.windowController];
 }
 
 @end
--- a/ui/cocoa/toolkit.m	Sat Jun 06 16:53:39 2026 +0200
+++ b/ui/cocoa/toolkit.m	Sat Jun 06 18:04:54 2026 +0200
@@ -147,7 +147,7 @@
 void ui_main(void) {
     main_thr_check("ui_main");
     
-    NSApplicationMain(app_argc, app_argv);
+    NSApplicationMain(app_argc,app_argv);
     //[NSApp finishLaunching];
     //[NSApp activateIgnoringOtherApps:YES];
     //[NSApp run];
--- a/ui/cocoa/webview.m	Sat Jun 06 16:53:39 2026 +0200
+++ b/ui/cocoa/webview.m	Sat Jun 06 18:04:54 2026 +0200
@@ -52,7 +52,7 @@
             data->webview = (__bridge void*)webview;
             data->javascript = TRUE;
             data->zoom = 1;
-            value->value = value;
+            value->value = data;
         }
     }
     
--- a/ui/cocoa/window.m	Sat Jun 06 16:53:39 2026 +0200
+++ b/ui/cocoa/window.m	Sat Jun 06 18:04:54 2026 +0200
@@ -50,19 +50,23 @@
 static int splitview_window_default_pos = -1;
 static UiBool splitview_window_use_prop = TRUE;
 
+static void main_window_destroy(UiObject *obj) {
+    MainWindow *window = (__bridge MainWindow*)obj->wobj;
+    [[WindowManager sharedWindowManager] closeWindow:window];
+}
+
 static UiObject* create_window(const char *title, BOOL simple, BOOL sidebar, BOOL splitview) {
     UiObject *obj = uic_object_new_toplevel();
     
     MainWindow *window = [[MainWindow alloc] init:obj withSidebar:sidebar withSplitview:splitview];
-    [[WindowManager sharedWindowManager] addWindow:window];
-    window.releasedWhenClosed = false; // TODO: we still need a cleanup strategy
     
     obj->wobj = (__bridge void*)window;
+    obj->destroy = main_window_destroy;
     
     MainWindowController *controller = [[MainWindowController alloc] initWithWindow:obj window:window];
     window.windowController = controller;
     [window setNextResponder:(NSResponder*)controller];
-    objc_setAssociatedObject(window, "windowcontroller", controller, OBJC_ASSOCIATION_RETAIN);
+    [[WindowManager sharedWindowManager] addWindow:controller];
     
     return obj;
 }
--- a/ui/common/types.c	Sat Jun 06 16:53:39 2026 +0200
+++ b/ui/common/types.c	Sat Jun 06 18:04:54 2026 +0200
@@ -760,6 +760,9 @@
 
 void uic_generic_save(UiGeneric *g) {
     if(!g->obj) return;
+    // TODO: this leaks memory
+    // we can't just use get on Generic and ignore the result
+    // probably a separate save method is required
     g->get(g);
 }
 

mercurial