From 225b04b45f1bbb626bbc4df6d2bd8d59848a607f Mon Sep 17 00:00:00 2001 From: marc_culler Date: Sat, 18 Dec 2021 19:41:46 +0000 Subject: Fix[6be8b0b48c]: Aqua can crash if a toplevel is destroyed with Cmd-W during a drag which began in the toplevel. --- macosx/tkMacOSXMouseEvent.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index faba2dc..29a57aa 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -292,7 +292,19 @@ enum { } } else { if (isDragging) { - winPtr = TkMacOSXGetHostToplevel((TkWindow *)dragTarget)->winPtr; + + /* + * The dragTarget window can be destroyed during a mouse drag by + * pressing Cmd-W without releasing the mouse button. This can + * lead to a crash. See [6be8b0b48c]. + */ + + TkWindow *dragPtr = (TkWindow *) dragTarget; + if(dragPtr == NULL || dragPtr->flags & TK_ALREADY_DEAD) { + target = (TkWindow *) TkMacOSXGetTkWindow([NSApp keyWindow]); + return theEvent; + } + winPtr = TkMacOSXGetHostToplevel(dragPtr)->winPtr; } else if (eventType == NSScrollWheel) { winPtr = scrollTarget; } else { -- cgit v0.12 From a95f9f1b08a27165ffce03db777aad7e1660a3b0 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Sun, 19 Dec 2021 22:01:59 +0000 Subject: Add a property to TKWindow which indicates that the associated Tk window is being destroyed. --- macosx/tkMacOSXMouseEvent.c | 32 ++++++++++++++++++++------------ macosx/tkMacOSXPrivate.h | 2 ++ macosx/tkMacOSXWm.c | 2 ++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index 29a57aa..b96d487 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -124,6 +124,14 @@ enum { if (!isTestingEvent && !isMotionEvent) { return theEvent; } + } else if ([(TKWindow *) eventWindow isDead]) { + if ([eventWindow isKeyWindow]) { + [eventWindow resignKey]; + } + dragTarget = NULL; + target = TkMacOSXGetTkWindow([NSApp keyWindow]); + isDragging = NO; + [NSApp setTkEventTarget: target]; } else if (!NSPointInRect(viewLocation, [contentView bounds])) { isOutside = YES; } @@ -142,6 +150,7 @@ enum { } isDragging = YES; dragTarget = target; + break; case NSRightMouseDragged: case NSOtherMouseDragged: isMotionEvent = YES; @@ -291,20 +300,19 @@ enum { return theEvent; } } else { - if (isDragging) { - - /* - * The dragTarget window can be destroyed during a mouse drag by - * pressing Cmd-W without releasing the mouse button. This can - * lead to a crash. See [6be8b0b48c]. - */ - + if (isDragging && dragTarget) { TkWindow *dragPtr = (TkWindow *) dragTarget; - if(dragPtr == NULL || dragPtr->flags & TK_ALREADY_DEAD) { - target = (TkWindow *) TkMacOSXGetTkWindow([NSApp keyWindow]); - return theEvent; + TKWindow *dragWindow = nil; + if (dragPtr) { + dragWindow = (TKWindow *)TkMacOSXGetNSWindowForDrawable( + dragPtr->window); + } + if (!dragWindow) { + dragTarget = NULL; + target = NULL; + return theEvent; } - winPtr = TkMacOSXGetHostToplevel(dragPtr)->winPtr; + winPtr = TkMacOSXGetHostToplevel((TkWindow *) dragTarget)->winPtr; } else if (eventType == NSScrollWheel) { winPtr = scrollTarget; } else { diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index de0f5fd..c3b364f 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -451,9 +451,11 @@ VISIBILITY_HIDDEN #ifdef __i386__ /* The Objective C runtime used on i386 requires this. */ Window _tkWindow; + Bool _isDead; #endif } @property Window tkWindow; +@property Bool isDead; @end @interface TKWindow(TKWm) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index c065d6c..cdfc1a1 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -363,6 +363,7 @@ static void RemoveTransient(TkWindow *winPtr); @implementation TKWindow: NSWindow @synthesize tkWindow = _tkWindow; +@synthesize isDead = _isDead; @end #pragma mark TKWindow(TKWm) @@ -1057,6 +1058,7 @@ TkWmDeadWindow( */ deadNSWindow = (TKWindow *)wmPtr->window; + [deadNSWindow setIsDead:YES]; if (deadNSWindow && !Tk_IsEmbedded(winPtr)) { NSWindow *parent = [deadNSWindow parentWindow]; [deadNSWindow setTkWindow:None]; -- cgit v0.12 From c78ed6cd94f2aefa3268c4901af10cafae29c3d3 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Mon, 20 Dec 2021 01:07:47 +0000 Subject: Make target not be static in processMouseEvent. --- macosx/tkMacOSXMouseEvent.c | 8 ++++---- macosx/tkMacOSXWm.c | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index b96d487..1a72c65 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -83,8 +83,8 @@ enum { NSPoint location = [theEvent locationInWindow]; NSPoint viewLocation = [contentView convertPoint:location fromView:nil]; TkWindow *winPtr = NULL, *grabWinPtr, *scrollTarget = NULL; - Tk_Window tkwin = NULL, capture; - static Tk_Window target = NULL, dragTarget = NULL; + Tk_Window tkwin = NULL, capture, target; + static Tk_Window dragTarget = NULL; NSPoint local, global; NSInteger button; TkWindow *newFocus = NULL; @@ -130,8 +130,8 @@ enum { } dragTarget = NULL; target = TkMacOSXGetTkWindow([NSApp keyWindow]); - isDragging = NO; [NSApp setTkEventTarget: target]; + isDragging = NO; } else if (!NSPointInRect(viewLocation, [contentView bounds])) { isOutside = YES; } @@ -149,7 +149,7 @@ enum { return theEvent; } isDragging = YES; - dragTarget = target; + dragTarget = [NSApp tkEventTarget]; break; case NSRightMouseDragged: case NSOtherMouseDragged: diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index cdfc1a1..8d8a8c1 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1058,7 +1058,9 @@ TkWmDeadWindow( */ deadNSWindow = (TKWindow *)wmPtr->window; - [deadNSWindow setIsDead:YES]; + if ([deadNSWindow respondsToSelector:@selector(setIsDead)]) { + [deadNSWindow setIsDead:YES]; + } if (deadNSWindow && !Tk_IsEmbedded(winPtr)) { NSWindow *parent = [deadNSWindow parentWindow]; [deadNSWindow setTkWindow:None]; @@ -1107,6 +1109,7 @@ TkWmDeadWindow( wmPtr2->hints.initial_state != WithdrawnState); if (w != deadNSWindow && isOnScreen && [w canBecomeKeyWindow]) { [w makeKeyAndOrderFront:NSApp]; + [NSApp setTkEventTarget:TkMacOSXGetTkWindow(w)]; break; } } @@ -6646,6 +6649,7 @@ TkpChangeFocus( } if (win && [win canBecomeKeyWindow]) { [win makeKeyAndOrderFront:NSApp]; + [NSApp setTkEventTarget:TkMacOSXGetTkWindow(win)]; } } -- cgit v0.12 From 82054a7dfb2eda4270d178916eeb775f8c1dddf7 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Mon, 20 Dec 2021 03:28:32 +0000 Subject: Remove one more static variable. Now TkWmDeadWindow can clear the drag target if necessary. --- macosx/tkMacOSXInit.c | 1 + macosx/tkMacOSXMouseEvent.c | 33 ++++++++++++++------------------- macosx/tkMacOSXPrivate.h | 2 ++ 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/macosx/tkMacOSXInit.c b/macosx/tkMacOSXInit.c index d44bea3..d9f3d12 100644 --- a/macosx/tkMacOSXInit.c +++ b/macosx/tkMacOSXInit.c @@ -46,6 +46,7 @@ static int TkMacOSXGetAppPathCmd(ClientData cd, Tcl_Interp *ip, @synthesize tkLiveResizeEnded = _tkLiveResizeEnded; @synthesize tkPointerWindow = _tkPointerWindow; @synthesize tkEventTarget = _tkEventTarget; +@synthesize tkDragTarget = _tkDragTarget; @synthesize tkButtonState = _tkButtonState; @end diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index 1a72c65..6457890 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -84,7 +84,6 @@ enum { NSPoint viewLocation = [contentView convertPoint:location fromView:nil]; TkWindow *winPtr = NULL, *grabWinPtr, *scrollTarget = NULL; Tk_Window tkwin = NULL, capture, target; - static Tk_Window dragTarget = NULL; NSPoint local, global; NSInteger button; TkWindow *newFocus = NULL; @@ -93,7 +92,6 @@ enum { Bool isTestingEvent = NO; Bool isMotionEvent = NO; Bool isOutside = NO; - static Bool isDragging = NO; static Bool ignoreDrags = NO; static Bool ignoreUpDown = NO; static NSTimeInterval timestamp = 0; @@ -128,10 +126,10 @@ enum { if ([eventWindow isKeyWindow]) { [eventWindow resignKey]; } - dragTarget = NULL; + [NSApp setTkDragTarget: nil]; target = TkMacOSXGetTkWindow([NSApp keyWindow]); [NSApp setTkEventTarget: target]; - isDragging = NO; + [NSApp setTkDragTarget: nil]; } else if (!NSPointInRect(viewLocation, [contentView bounds])) { isOutside = YES; } @@ -142,14 +140,13 @@ enum { buttonState &= ~TkGetButtonMask(button); break; case NSLeftMouseDragged: - if (isOutside && !isDragging) { + if (isOutside && ![NSApp tkDragTarget]) { ignoreDrags = YES; } if (ignoreDrags) { return theEvent; } - isDragging = YES; - dragTarget = [NSApp tkEventTarget]; + [NSApp setTkDragTarget: [NSApp tkEventTarget]]; break; case NSRightMouseDragged: case NSOtherMouseDragged: @@ -172,8 +169,7 @@ enum { [NSApp setTkPointerWindow:nil]; break; case NSLeftMouseUp: - isDragging = NO; - dragTarget = NULL; + [NSApp setTkDragTarget: nil]; if ([theEvent clickCount] == 2) { ignoreUpDown = NO; } @@ -209,7 +205,6 @@ enum { if ([theEvent timestamp] - timestamp > 1) { ignoreUpDown = NO; } - if ([theEvent clickCount] == 2) { if (ignoreUpDown == YES) { return theEvent; @@ -300,19 +295,19 @@ enum { return theEvent; } } else { - if (isDragging && dragTarget) { - TkWindow *dragPtr = (TkWindow *) dragTarget; + if ([NSApp tkDragTarget]) { + TkWindow *dragPtr = (TkWindow *) [NSApp tkDragTarget]; TKWindow *dragWindow = nil; if (dragPtr) { dragWindow = (TKWindow *)TkMacOSXGetNSWindowForDrawable( dragPtr->window); } if (!dragWindow) { - dragTarget = NULL; + [NSApp setTkDragTarget: nil]; target = NULL; return theEvent; } - winPtr = TkMacOSXGetHostToplevel((TkWindow *) dragTarget)->winPtr; + winPtr = TkMacOSXGetHostToplevel((TkWindow *) [NSApp tkDragTarget])->winPtr; } else if (eventType == NSScrollWheel) { winPtr = scrollTarget; } else { @@ -375,8 +370,8 @@ enum { * when the mouse is outside of the focused toplevel. */ - if (isDragging) { - TkWindow *w = (TkWindow *) dragTarget; + if ([NSApp tkDragTarget]) { + TkWindow *w = (TkWindow *) [NSApp tkDragTarget]; win_x = global.x; win_y = global.y; for (; w != NULL; w = w->parentPtr) { @@ -393,7 +388,7 @@ enum { break; } } - target = dragTarget; + target = (Tk_Window) [NSApp tkDragTarget]; } else { target = Tk_TopCoordsToWindow(tkwin, local.x, local.y, &win_x, &win_y); } @@ -459,7 +454,7 @@ enum { */ if (eventType != NSScrollWheel) { - if (isDragging) { + if ([NSApp tkDragTarget]) { /* * When dragging the mouse into the resize area Apple shows the @@ -474,7 +469,7 @@ enum { Tk_UpdatePointer((Tk_Window) [NSApp tkPointerWindow], global.x, global.y, state); } else if (eventType == NSMouseExited) { - if (isDragging) { + if ([NSApp tkDragTarget]) { Tk_UpdatePointer((Tk_Window) [NSApp tkPointerWindow], global.x, global.y, state); } else { diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index c3b364f..22efbbf 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -336,6 +336,7 @@ VISIBILITY_HIDDEN Bool _tkLiveResizeEnded; TkWindow *_tkPointerWindow; TkWindow *_tkEventTarget; + TkWindow *_tkDragTarget; unsigned int _tkButtonState; #endif @@ -347,6 +348,7 @@ VISIBILITY_HIDDEN @property Bool tkLiveResizeEnded; @property TkWindow *tkPointerWindow; @property TkWindow *tkEventTarget; +@property TkWindow *tkDragTarget; @property unsigned int tkButtonState; @end -- cgit v0.12 From c7c1a7de57504e28e2660de0900bf63cb0260ec4 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Mon, 20 Dec 2021 14:25:07 +0000 Subject: Prevent stale references in the persistent mouse event processing state. --- macosx/tkMacOSXSubwindows.c | 10 ++++++++++ macosx/tkMacOSXWm.c | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index dbae21e..68ae599 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -89,6 +89,16 @@ XDestroyWindow( } ckfree(macWin); return Success; + } else { + TkWindow *winPtr = macWin->winPtr; + if (winPtr == [NSApp tkDragTarget]) { + [NSApp setTkDragTarget:nil]; + printf("Clearing DragTarget\n"); + } + if (winPtr == [NSApp tkEventTarget]) { + printf("Clearing EventTarget\n"); + [NSApp setTkEventTarget:nil]; + } } if (macWin->visRgn) { CFRelease(macWin->visRgn); diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 8d8a8c1..eaf19e9 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1051,16 +1051,42 @@ TkWmDeadWindow( ckfree(transientPtr); } + deadNSWindow = (TKWindow *)wmPtr->window; + if ([deadNSWindow respondsToSelector:@selector(setIsDead)]) { + [deadNSWindow setIsDead:YES]; + } + + /* + * Remove references to the Tk window from the mouse event processing + * state which is recorded in the NSApplication object. + */ + + if (winPtr == [NSApp tkDragTarget]) { + [NSApp setTkDragTarget:nil]; + } + if (winPtr == [NSApp tkEventTarget]) { + [NSApp setTkEventTarget:nil]; + } + if (winPtr == [NSApp tkPointerWindow]) { + NSWindow *w; + [NSApp setTkPointerWindow:nil]; + for (w in [NSApp orderedWindows]) { + if (w == deadNSWindow) { + continue; + } + if (NSPointInRect([NSEvent mouseLocation], [w frame])) { + [NSApp setTkPointerWindow:TkMacOSXGetTkWindow(w)]; + break; + } + } + } + /* * 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. */ - deadNSWindow = (TKWindow *)wmPtr->window; - if ([deadNSWindow respondsToSelector:@selector(setIsDead)]) { - [deadNSWindow setIsDead:YES]; - } if (deadNSWindow && !Tk_IsEmbedded(winPtr)) { NSWindow *parent = [deadNSWindow parentWindow]; [deadNSWindow setTkWindow:None]; -- cgit v0.12 From ca555898f2505f32064a101ce8e2dcf4a06198e1 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Mon, 20 Dec 2021 15:54:41 +0000 Subject: Clean up. --- macosx/tkMacOSXPrivate.h | 5 +++++ macosx/tkMacOSXSubwindows.c | 10 ---------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index 22efbbf..c7a547e 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -346,6 +346,11 @@ VISIBILITY_HIDDEN @property Bool isDrawing; @property Bool needsToDraw; @property Bool tkLiveResizeEnded; + +/* + * Persistent state variables used by processMouseEvent. + */ + @property TkWindow *tkPointerWindow; @property TkWindow *tkEventTarget; @property TkWindow *tkDragTarget; diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 68ae599..dbae21e 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -89,16 +89,6 @@ XDestroyWindow( } ckfree(macWin); return Success; - } else { - TkWindow *winPtr = macWin->winPtr; - if (winPtr == [NSApp tkDragTarget]) { - [NSApp setTkDragTarget:nil]; - printf("Clearing DragTarget\n"); - } - if (winPtr == [NSApp tkEventTarget]) { - printf("Clearing EventTarget\n"); - [NSApp setTkEventTarget:nil]; - } } if (macWin->visRgn) { CFRelease(macWin->visRgn); -- cgit v0.12 From 32ad249136c01232bce166b33015f783f613bca2 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Mon, 20 Dec 2021 16:09:22 +0000 Subject: Remove the isDead property from TKWindow since it is no longer needed. --- macosx/tkMacOSXMouseEvent.c | 8 -------- macosx/tkMacOSXPrivate.h | 2 -- macosx/tkMacOSXWm.c | 4 ---- 3 files changed, 14 deletions(-) diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index 6457890..4ed954d 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -122,14 +122,6 @@ enum { if (!isTestingEvent && !isMotionEvent) { return theEvent; } - } else if ([(TKWindow *) eventWindow isDead]) { - if ([eventWindow isKeyWindow]) { - [eventWindow resignKey]; - } - [NSApp setTkDragTarget: nil]; - target = TkMacOSXGetTkWindow([NSApp keyWindow]); - [NSApp setTkEventTarget: target]; - [NSApp setTkDragTarget: nil]; } else if (!NSPointInRect(viewLocation, [contentView bounds])) { isOutside = YES; } diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index c7a547e..3b03139 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -458,11 +458,9 @@ VISIBILITY_HIDDEN #ifdef __i386__ /* The Objective C runtime used on i386 requires this. */ Window _tkWindow; - Bool _isDead; #endif } @property Window tkWindow; -@property Bool isDead; @end @interface TKWindow(TKWm) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index eaf19e9..be50f96 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -363,7 +363,6 @@ static void RemoveTransient(TkWindow *winPtr); @implementation TKWindow: NSWindow @synthesize tkWindow = _tkWindow; -@synthesize isDead = _isDead; @end #pragma mark TKWindow(TKWm) @@ -1052,9 +1051,6 @@ TkWmDeadWindow( } deadNSWindow = (TKWindow *)wmPtr->window; - if ([deadNSWindow respondsToSelector:@selector(setIsDead)]) { - [deadNSWindow setIsDead:YES]; - } /* * Remove references to the Tk window from the mouse event processing -- cgit v0.12 From 774c6317321143cc524d608e9b78e8ee51cfb84e Mon Sep 17 00:00:00 2001 From: marc_culler Date: Mon, 20 Dec 2021 16:50:54 +0000 Subject: Call TkUpdatePointer when a toplevel is destroyed. --- macosx/tkMacOSXWm.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index be50f96..0d52e43 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -1060,18 +1060,20 @@ TkWmDeadWindow( if (winPtr == [NSApp tkDragTarget]) { [NSApp setTkDragTarget:nil]; } - if (winPtr == [NSApp tkEventTarget]) { - [NSApp setTkEventTarget:nil]; - } if (winPtr == [NSApp tkPointerWindow]) { NSWindow *w; + NSPoint mouse = [NSEvent mouseLocation]; [NSApp setTkPointerWindow:nil]; for (w in [NSApp orderedWindows]) { if (w == deadNSWindow) { continue; } - if (NSPointInRect([NSEvent mouseLocation], [w frame])) { - [NSApp setTkPointerWindow:TkMacOSXGetTkWindow(w)]; + if (NSPointInRect(mouse, [w frame])) { + TkWindow *winPtr2 = TkMacOSXGetTkWindow(w); + int x = mouse.x, y = TkMacOSXZeroScreenHeight() - mouse.y; + [NSApp setTkPointerWindow:winPtr2]; + Tk_UpdatePointer((Tk_Window) winPtr2, x, y, + [NSApp tkButtonState]); break; } } -- cgit v0.12