mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 18:05:35 +00:00
app: [macOS] defer Window destroy to after window close
The windowWillClose callback is too soon to destroy our Window: at least draw callbacks may be called after windowWillClose but before the window is gone. This change moves cleanup to the viewDidMoveToWindow callback where we're sure the NSView is no longer active. Fixes: https://todo.sr.ht/~eliasnaur/gio/466 Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+40
-35
@@ -186,6 +186,11 @@ static CFTypeRef layerForView(CFTypeRef viewRef) {
|
|||||||
return (__bridge CFTypeRef)view.layer;
|
return (__bridge CFTypeRef)view.layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CFTypeRef windowForView(CFTypeRef viewRef) {
|
||||||
|
NSView *view = (__bridge NSView *)viewRef;
|
||||||
|
return (__bridge CFTypeRef)view.window;
|
||||||
|
}
|
||||||
|
|
||||||
static void raiseWindow(CFTypeRef windowRef) {
|
static void raiseWindow(CFTypeRef windowRef) {
|
||||||
NSWindow* window = (__bridge NSWindow *)windowRef;
|
NSWindow* window = (__bridge NSWindow *)windowRef;
|
||||||
[window makeKeyAndOrderFront:nil];
|
[window makeKeyAndOrderFront:nil];
|
||||||
@@ -237,7 +242,6 @@ type ViewEvent struct {
|
|||||||
|
|
||||||
type window struct {
|
type window struct {
|
||||||
view C.CFTypeRef
|
view C.CFTypeRef
|
||||||
window C.CFTypeRef
|
|
||||||
w *callbacks
|
w *callbacks
|
||||||
stage system.Stage
|
stage system.Stage
|
||||||
displayLink *displayLink
|
displayLink *displayLink
|
||||||
@@ -305,7 +309,7 @@ func (w *window) WriteClipboard(s string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) updateWindowMode() {
|
func (w *window) updateWindowMode() {
|
||||||
style := int(C.getWindowStyleMask(w.window))
|
style := int(C.getWindowStyleMask(C.windowForView(w.view)))
|
||||||
if style&C.NSWindowStyleMaskFullScreen != 0 {
|
if style&C.NSWindowStyleMaskFullScreen != 0 {
|
||||||
w.config.Mode = Fullscreen
|
w.config.Mode = Fullscreen
|
||||||
} else {
|
} else {
|
||||||
@@ -321,70 +325,71 @@ func (w *window) Configure(options []Option) {
|
|||||||
w.updateWindowMode()
|
w.updateWindowMode()
|
||||||
cnf := w.config
|
cnf := w.config
|
||||||
cnf.apply(cfg, options)
|
cnf.apply(cfg, options)
|
||||||
|
window := C.windowForView(w.view)
|
||||||
|
|
||||||
switch cnf.Mode {
|
switch cnf.Mode {
|
||||||
case Fullscreen:
|
case Fullscreen:
|
||||||
switch prev.Mode {
|
switch prev.Mode {
|
||||||
case Fullscreen:
|
case Fullscreen:
|
||||||
case Minimized:
|
case Minimized:
|
||||||
C.unhideWindow(w.window)
|
C.unhideWindow(window)
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
w.config.Mode = Fullscreen
|
w.config.Mode = Fullscreen
|
||||||
C.toggleFullScreen(w.window)
|
C.toggleFullScreen(window)
|
||||||
}
|
}
|
||||||
case Minimized:
|
case Minimized:
|
||||||
switch prev.Mode {
|
switch prev.Mode {
|
||||||
case Minimized, Fullscreen:
|
case Minimized, Fullscreen:
|
||||||
default:
|
default:
|
||||||
w.config.Mode = Minimized
|
w.config.Mode = Minimized
|
||||||
C.hideWindow(w.window)
|
C.hideWindow(window)
|
||||||
}
|
}
|
||||||
case Maximized:
|
case Maximized:
|
||||||
switch prev.Mode {
|
switch prev.Mode {
|
||||||
case Fullscreen:
|
case Fullscreen:
|
||||||
case Minimized:
|
case Minimized:
|
||||||
C.unhideWindow(w.window)
|
C.unhideWindow(window)
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
w.config.Mode = Maximized
|
w.config.Mode = Maximized
|
||||||
w.setTitle(prev, cnf)
|
w.setTitle(prev, cnf)
|
||||||
if C.isWindowZoomed(w.window) == 0 {
|
if C.isWindowZoomed(window) == 0 {
|
||||||
C.zoomWindow(w.window)
|
C.zoomWindow(window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Windowed:
|
case Windowed:
|
||||||
switch prev.Mode {
|
switch prev.Mode {
|
||||||
case Fullscreen:
|
case Fullscreen:
|
||||||
C.toggleFullScreen(w.window)
|
C.toggleFullScreen(window)
|
||||||
case Minimized:
|
case Minimized:
|
||||||
C.unhideWindow(w.window)
|
C.unhideWindow(window)
|
||||||
case Maximized:
|
case Maximized:
|
||||||
}
|
}
|
||||||
w.config.Mode = Windowed
|
w.config.Mode = Windowed
|
||||||
if C.isWindowZoomed(w.window) != 0 {
|
if C.isWindowZoomed(window) != 0 {
|
||||||
C.zoomWindow(w.window)
|
C.zoomWindow(window)
|
||||||
}
|
}
|
||||||
w.setTitle(prev, cnf)
|
w.setTitle(prev, cnf)
|
||||||
if prev.Size != cnf.Size {
|
if prev.Size != cnf.Size {
|
||||||
w.config.Size = cnf.Size
|
w.config.Size = cnf.Size
|
||||||
cnf.Size = cnf.Size.Div(int(screenScale))
|
cnf.Size = cnf.Size.Div(int(screenScale))
|
||||||
C.setSize(w.window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
|
C.setSize(window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
|
||||||
}
|
}
|
||||||
if prev.MinSize != cnf.MinSize {
|
if prev.MinSize != cnf.MinSize {
|
||||||
w.config.MinSize = cnf.MinSize
|
w.config.MinSize = cnf.MinSize
|
||||||
cnf.MinSize = cnf.MinSize.Div(int(screenScale))
|
cnf.MinSize = cnf.MinSize.Div(int(screenScale))
|
||||||
C.setMinSize(w.window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y))
|
C.setMinSize(window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y))
|
||||||
}
|
}
|
||||||
if prev.MaxSize != cnf.MaxSize {
|
if prev.MaxSize != cnf.MaxSize {
|
||||||
w.config.MaxSize = cnf.MaxSize
|
w.config.MaxSize = cnf.MaxSize
|
||||||
cnf.MaxSize = cnf.MaxSize.Div(int(screenScale))
|
cnf.MaxSize = cnf.MaxSize.Div(int(screenScale))
|
||||||
C.setMaxSize(w.window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y))
|
C.setMaxSize(window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cnf.Decorated != prev.Decorated {
|
if cnf.Decorated != prev.Decorated {
|
||||||
w.config.Decorated = cnf.Decorated
|
w.config.Decorated = cnf.Decorated
|
||||||
mask := C.getWindowStyleMask(w.window)
|
mask := C.getWindowStyleMask(window)
|
||||||
style := C.NSWindowStyleMask(C.NSWindowStyleMaskTitled | C.NSWindowStyleMaskResizable | C.NSWindowStyleMaskMiniaturizable | C.NSWindowStyleMaskClosable)
|
style := C.NSWindowStyleMask(C.NSWindowStyleMaskTitled | C.NSWindowStyleMaskResizable | C.NSWindowStyleMaskMiniaturizable | C.NSWindowStyleMaskClosable)
|
||||||
style = C.NSWindowStyleMaskFullSizeContentView
|
style = C.NSWindowStyleMaskFullSizeContentView
|
||||||
mask &^= style
|
mask &^= style
|
||||||
@@ -395,12 +400,12 @@ func (w *window) Configure(options []Option) {
|
|||||||
barTrans = C.YES
|
barTrans = C.YES
|
||||||
titleVis = C.NSWindowTitleHidden
|
titleVis = C.NSWindowTitleHidden
|
||||||
}
|
}
|
||||||
C.setWindowTitlebarAppearsTransparent(w.window, barTrans)
|
C.setWindowTitlebarAppearsTransparent(window, barTrans)
|
||||||
C.setWindowTitleVisibility(w.window, titleVis)
|
C.setWindowTitleVisibility(window, titleVis)
|
||||||
C.setWindowStyleMask(w.window, mask)
|
C.setWindowStyleMask(window, mask)
|
||||||
C.setWindowStandardButtonHidden(w.window, C.NSWindowCloseButton, barTrans)
|
C.setWindowStandardButtonHidden(window, C.NSWindowCloseButton, barTrans)
|
||||||
C.setWindowStandardButtonHidden(w.window, C.NSWindowMiniaturizeButton, barTrans)
|
C.setWindowStandardButtonHidden(window, C.NSWindowMiniaturizeButton, barTrans)
|
||||||
C.setWindowStandardButtonHidden(w.window, C.NSWindowZoomButton, barTrans)
|
C.setWindowStandardButtonHidden(window, C.NSWindowZoomButton, barTrans)
|
||||||
}
|
}
|
||||||
w.w.Event(ConfigEvent{Config: w.config})
|
w.w.Event(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
@@ -410,25 +415,26 @@ func (w *window) setTitle(prev, cnf Config) {
|
|||||||
w.config.Title = cnf.Title
|
w.config.Title = cnf.Title
|
||||||
title := stringToNSString(cnf.Title)
|
title := stringToNSString(cnf.Title)
|
||||||
defer C.CFRelease(title)
|
defer C.CFRelease(title)
|
||||||
C.setTitle(w.window, title)
|
C.setTitle(C.windowForView(w.view), title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Perform(acts system.Action) {
|
func (w *window) Perform(acts system.Action) {
|
||||||
|
window := C.windowForView(w.view)
|
||||||
walkActions(acts, func(a system.Action) {
|
walkActions(acts, func(a system.Action) {
|
||||||
switch a {
|
switch a {
|
||||||
case system.ActionCenter:
|
case system.ActionCenter:
|
||||||
r := C.getScreenFrame(w.window) // the screen size of the window
|
r := C.getScreenFrame(window) // the screen size of the window
|
||||||
sz := w.config.Size
|
sz := w.config.Size
|
||||||
x := (int(r.size.width) - sz.X) / 2
|
x := (int(r.size.width) - sz.X) / 2
|
||||||
y := (int(r.size.height) - sz.Y) / 2
|
y := (int(r.size.height) - sz.Y) / 2
|
||||||
C.setScreenFrame(w.window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y))
|
C.setScreenFrame(window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y))
|
||||||
case system.ActionRaise:
|
case system.ActionRaise:
|
||||||
C.raiseWindow(w.window)
|
C.raiseWindow(window)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if acts&system.ActionClose != 0 {
|
if acts&system.ActionClose != 0 {
|
||||||
C.closeWindow(w.window)
|
C.closeWindow(window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +537,7 @@ func gio_onMouse(view, evt C.CFTypeRef, cdir C.int, cbtn C.NSInteger, x, y, dx,
|
|||||||
if ok && w.config.Mode != Fullscreen {
|
if ok && w.config.Mode != Fullscreen {
|
||||||
switch act {
|
switch act {
|
||||||
case system.ActionMove:
|
case system.ActionMove:
|
||||||
C.performWindowDragWithEvent(w.window, evt)
|
C.performWindowDragWithEvent(C.windowForView(w.view), evt)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -781,14 +787,12 @@ func configFor(scale float32) unit.Metric {
|
|||||||
//export gio_onClose
|
//export gio_onClose
|
||||||
func gio_onClose(view C.CFTypeRef) {
|
func gio_onClose(view C.CFTypeRef) {
|
||||||
w := mustView(view)
|
w := mustView(view)
|
||||||
|
w.displayLink.Close()
|
||||||
w.w.Event(ViewEvent{})
|
w.w.Event(ViewEvent{})
|
||||||
deleteView(view)
|
deleteView(view)
|
||||||
w.w.Event(system.DestroyEvent{})
|
w.w.Event(system.DestroyEvent{})
|
||||||
w.displayLink.Close()
|
|
||||||
C.CFRelease(w.view)
|
C.CFRelease(w.view)
|
||||||
C.CFRelease(w.window)
|
|
||||||
w.view = 0
|
w.view = 0
|
||||||
w.window = 0
|
|
||||||
w.displayLink = nil
|
w.displayLink = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,17 +852,18 @@ func newWindow(win *callbacks, options []Option) error {
|
|||||||
}
|
}
|
||||||
errch <- nil
|
errch <- nil
|
||||||
w.w = win
|
w.w = win
|
||||||
w.window = C.gio_createWindow(w.view, 0, 0, 0, 0, 0, 0)
|
window := C.gio_createWindow(w.view, 0, 0, 0, 0, 0, 0)
|
||||||
w.updateWindowMode()
|
w.updateWindowMode()
|
||||||
win.SetDriver(w)
|
win.SetDriver(w)
|
||||||
w.Configure(options)
|
w.Configure(options)
|
||||||
if nextTopLeft.x == 0 && nextTopLeft.y == 0 {
|
if nextTopLeft.x == 0 && nextTopLeft.y == 0 {
|
||||||
// cascadeTopLeftFromPoint treats (0, 0) as a no-op,
|
// cascadeTopLeftFromPoint treats (0, 0) as a no-op,
|
||||||
// and just returns the offset we need for the first window.
|
// and just returns the offset we need for the first window.
|
||||||
nextTopLeft = C.cascadeTopLeftFromPoint(w.window, nextTopLeft)
|
nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft)
|
||||||
}
|
}
|
||||||
nextTopLeft = C.cascadeTopLeftFromPoint(w.window, nextTopLeft)
|
nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft)
|
||||||
C.makeKeyAndOrderFront(w.window)
|
// makeKeyAndOrderFront assumes ownership of our window reference.
|
||||||
|
C.makeKeyAndOrderFront(window)
|
||||||
layer := C.layerForView(w.view)
|
layer := C.layerForView(w.view)
|
||||||
w.w.Event(ViewEvent{View: uintptr(w.view), Layer: uintptr(layer)})
|
w.w.Event(ViewEvent{View: uintptr(w.view), Layer: uintptr(layer)})
|
||||||
})
|
})
|
||||||
|
|||||||
+5
-6
@@ -45,11 +45,6 @@ __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(void);
|
|||||||
NSWindow *window = (NSWindow *)[notification object];
|
NSWindow *window = (NSWindow *)[notification object];
|
||||||
gio_onFocus((__bridge CFTypeRef)window.contentView, 0);
|
gio_onFocus((__bridge CFTypeRef)window.contentView, 0);
|
||||||
}
|
}
|
||||||
- (void)windowWillClose:(NSNotification *)notification {
|
|
||||||
NSWindow *window = (NSWindow *)[notification object];
|
|
||||||
window.delegate = nil;
|
|
||||||
gio_onClose((__bridge CFTypeRef)window.contentView);
|
|
||||||
}
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) {
|
static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) {
|
||||||
@@ -86,6 +81,11 @@ static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFlo
|
|||||||
layer.delegate = self;
|
layer.delegate = self;
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
- (void)viewDidMoveToWindow {
|
||||||
|
if (self.window == nil) {
|
||||||
|
gio_onClose((__bridge CFTypeRef)self);
|
||||||
|
}
|
||||||
|
}
|
||||||
- (void)mouseDown:(NSEvent *)event {
|
- (void)mouseDown:(NSEvent *)event {
|
||||||
handleMouse(self, event, MOUSE_DOWN, 0, 0);
|
handleMouse(self, event, MOUSE_DOWN, 0, 0);
|
||||||
}
|
}
|
||||||
@@ -357,7 +357,6 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGF
|
|||||||
NSView *view = (__bridge NSView *)viewRef;
|
NSView *view = (__bridge NSView *)viewRef;
|
||||||
[window setContentView:view];
|
[window setContentView:view];
|
||||||
[window makeFirstResponder:view];
|
[window makeFirstResponder:view];
|
||||||
window.releasedWhenClosed = NO;
|
|
||||||
window.delegate = globalWindowDel;
|
window.delegate = globalWindowDel;
|
||||||
return (__bridge_retained CFTypeRef)window;
|
return (__bridge_retained CFTypeRef)window;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user