Subject: Re: bug#39883: 28.0.50; macOS blank frame

From: Alan Third <[email-masked]>
Date: Sat, 11 Apr 2020 14:29:25 +0100

On Thu, Apr 02, 2020 at 11:49:45AM +0530, Ashish SHUKLA wrote:
> On 4/2/20 12:23 AM, Alan Third wrote:
> > 
> > It’s quite possible we’ll need to provide some sort of option to force
> > this, or to do run‐time feature detection. I don’t much fancy trying
> > to code up the runtime detection for this feature, but if we need it
> > we need it.
> 
> That definitely seems more foolproof. Let me know if you like me to test
> your diff.

Can you please try the attached patches? The fix for this is built on
the one for fixing frame resizing. Ultimately they should both end up
on master, so I didn’t want to make separate branches and have to
rework them later.

You may have to set this CFLAG to enable the changes:

    -DMAC_OS_X_VERSION_MAX_ALLOWED=101500
-- 
Alan Third
>From 9fd40868af7e8c298dc844662b9942404ef43bd8 Mon Sep 17 00:00:00 2001
From: Alan Third <[email-masked]>
Date: Sat, 11 Apr 2020 14:18:39 +0100
Subject: [PATCH] Allow dynamic choice of drawing path on NS (bug#39883)

* src/nsterm.h (NS_DRAW_TO_BUFFER): Let this be enabled on versions
older than 10.14.
* src/nsterm.m (ns_update_begin):
(ns_update_end):
(ns_focus):
(ns_unfocus):
([EmacsView viewDidResize:]):
([EmacsView createDrawingBuffer]):
([EmacsView windowDidChangeBackingProperties:]):
([EmacsView copyRect:to:]):
([EmacsView wantsUpdateLayer]): Dynamically switch between drawing to a
buffer and drawing to the screen, depending on the version of AppKit
in use.
---
 src/nsterm.h |  10 +--
 src/nsterm.m | 227 ++++++++++++++++++++++++++++++++++++---------------
 2 files changed, 165 insertions(+), 72 deletions(-)

diff --git a/src/nsterm.h b/src/nsterm.h
index e142dbd4f0..f5d3c32b8b 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -343,14 +343,8 @@ #define NSTRACE_UNSILENCE()
    therefore we draw to an offscreen buffer and swap it in when the
    toolkit wants to draw the frame. GNUstep and macOS 10.7 and below
    do not support this method, so we revert to drawing directly to the
-   glass.
-
-   FIXME: Should we make this macOS 10.8+, or macOS 10.14+?  I'm
-   inclined to go with 10.14+ as there have been some reports of funny
-   behaviour on 10.13 and below.  It may be worth adding a variable to
-   allow people in the overlapping region to switch between drawing
-   paths.  */
-#if defined (NS_IMPL_COCOA) && defined (MAC_OS_X_VERSION_10_14)
+   glass.  */
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
 #define NS_DRAW_TO_BUFFER 1
 #endif
 
diff --git a/src/nsterm.m b/src/nsterm.m
index 9dd1a89d0c..f8ed7e6659 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1144,10 +1144,25 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
 
   ns_updating_frame = f;
 #ifdef NS_DRAW_TO_BUFFER
-  [view focusOnDrawingBuffer];
-#else
-  [view lockFocus];
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+  if ([self wantsUpdateLayer])
+    {
 #endif
+      [view focusOnDrawingBuffer];
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+    }
+  else
+    {
+#endif
+#endif /* NS_DRAW_TO_BUFFER */
+
+#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+      [view lockFocus];
+#endif
+#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+    }
+#endif
+
 }

@@ -1166,15 +1181,29 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
 
 #ifdef NS_DRAW_TO_BUFFER
-  [NSGraphicsContext setCurrentContext:nil];
-  [view setNeedsDisplay:YES];
-#else
-  block_input ();
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+  if ([self wantsUpdateLayer])
+    {
+#endif
+      [NSGraphicsContext setCurrentContext:nil];
+      [view setNeedsDisplay:YES];
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+    }
+  else
+    {
+#endif
+#endif /* NS_DRAW_TO_BUFFER */
 
-  [view unlockFocus];
-  [[view window] flushWindow];
+#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+      block_input ();
 
-  unblock_input ();
+      [view unlockFocus];
+      [[view window] flushWindow];
+
+      unblock_input ();
+#endif
+#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+    }
 #endif
   ns_updating_frame = NULL;
 }
@@ -1199,24 +1228,39 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
     }
 
   if (f != ns_updating_frame)
-#ifdef NS_DRAW_TO_BUFFER
-    [view focusOnDrawingBuffer];
-#else
     {
-      if (view != focus_view)
+#ifdef NS_DRAW_TO_BUFFER
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+      if ([self wantsUpdateLayer])
         {
-          if (focus_view != NULL)
+#endif
+          [view focusOnDrawingBuffer];
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+        }
+      else
+        {
+#endif
+#endif /* NS_DRAW_TO_BUFFER */
+
+#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+          if (view != focus_view)
             {
-              [focus_view unlockFocus];
-              [[focus_view window] flushWindow];
-            }
+              if (focus_view != NULL)
+                {
+                  [focus_view unlockFocus];
+                  [[focus_view window] flushWindow];
+                }
 
-          if (view)
-            [view lockFocus];
-          focus_view = view;
+              if (view)
+                [view lockFocus];
+              focus_view = view;
+            }
+#endif
+#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
         }
-    }
 #endif
+    }
+
 
   /* clipping */
   if (r)
@@ -1246,16 +1290,30 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
     }
 
 #ifdef NS_DRAW_TO_BUFFER
-  [FRAME_NS_VIEW (f) setNeedsDisplay:YES];
-#else
-  if (f != ns_updating_frame)
+  #if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+  if ([self wantsUpdateLayer])
+    {
+#endif
+      [FRAME_NS_VIEW (f) setNeedsDisplay:YES];
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+    }
+  else
     {
-      if (focus_view != NULL)
+#endif
+#endif /* NS_DRAW_TO_BUFFER */
+
+#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+      if (f != ns_updating_frame)
         {
-          [focus_view unlockFocus];
-          [[focus_view window] flushWindow];
-          focus_view = NULL;
+          if (focus_view != NULL)
+            {
+              [focus_view unlockFocus];
+              [[focus_view window] flushWindow];
+              focus_view = NULL;
+            }
         }
+#endif
+#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
     }
 #endif
 }
@@ -7253,13 +7311,27 @@ - (void)viewDidResize:(NSNotification *)notification
     return;
 
 #ifdef NS_DRAW_TO_BUFFER
-  CGFloat scale = [[self window] backingScaleFactor];
-  oldw = (CGFloat)CGBitmapContextGetWidth (drawingBuffer) / scale;
-  oldh = (CGFloat)CGBitmapContextGetHeight (drawingBuffer) / scale;
-#else
-  oldw = FRAME_PIXEL_WIDTH (emacsframe);
-  oldh = FRAME_PIXEL_HEIGHT (emacsframe);
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+  if ([self wantsUpdateLayer])
+    {
 #endif
+      CGFloat scale = [[self window] backingScaleFactor];
+      oldw = (CGFloat)CGBitmapContextGetWidth (drawingBuffer) / scale;
+      oldh = (CGFloat)CGBitmapContextGetHeight (drawingBuffer) / scale;
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+    }
+  else
+    {
+#endif
+#endif /* NS_DRAW_TO_BUFFER */
+#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+      oldw = FRAME_PIXEL_WIDTH (emacsframe);
+      oldh = FRAME_PIXEL_HEIGHT (emacsframe);
+#endif
+#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+    }
+#endif
+
   neww = (int)NSWidth (frame);
   newh = (int)NSHeight (frame);
 
@@ -8289,6 +8361,9 @@ - (void)createDrawingBuffer
 {
   NSTRACE ("EmacsView createDrawingBuffer]");
 
+  if (! [self wantsUpdateLayer])
+    return;
+
   NSGraphicsContext *screen;
   CGColorSpaceRef colorSpace = [[[self window] colorSpace] CGColorSpace];
   CGFloat scale = [[self window] backingScaleFactor];
@@ -8324,6 +8399,9 @@ - (void)windowDidChangeBackingProperties:(NSNotification *)notification
 {
   NSTRACE ("EmacsView windowDidChangeBackingProperties:]");
 
+  if (! [self wantsUpdateLayer])
+    return;
+
   CGFloat old = [[[notification userInfo]
                     objectForKey:@"NSBackingPropertyOldScaleFactorKey"]
                   doubleValue];
@@ -8347,41 +8425,56 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect
   NSTRACE_RECT ("Destination", dstRect);
 
 #ifdef NS_DRAW_TO_BUFFER
-  CGImageRef copy;
-  NSRect frame = [self frame];
-  NSAffineTransform *setOrigin = [NSAffineTransform transform];
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+  if ([self wantsUpdateLayer])
+    {
+#endif
+      CGImageRef copy;
+      NSRect frame = [self frame];
+      NSAffineTransform *setOrigin = [NSAffineTransform transform];
 
-  [[NSGraphicsContext currentContext] saveGraphicsState];
+      [[NSGraphicsContext currentContext] saveGraphicsState];
 
-  /* Set the clipping before messing with the buffer's
-     orientation.  */
-  NSRectClip (dstRect);
+      /* Set the clipping before messing with the buffer's
+         orientation.  */
+      NSRectClip (dstRect);
 
-  /* Unflip the buffer as the copied image will be unflipped, and
-     offset the top left so when we draw back into the buffer the
-     correct part of the image is drawn.  */
-  CGContextScaleCTM(drawingBuffer, 1, -1);
-  CGContextTranslateCTM(drawingBuffer,
-                        NSMinX (dstRect) - NSMinX (srcRect),
-                        -NSHeight (frame) - (NSMinY (dstRect) - NSMinY (srcRect)));
+      /* Unflip the buffer as the copied image will be unflipped, and
+         offset the top left so when we draw back into the buffer the
+         correct part of the image is drawn.  */
+      CGContextScaleCTM(drawingBuffer, 1, -1);
+      CGContextTranslateCTM(drawingBuffer,
+                            NSMinX (dstRect) - NSMinX (srcRect),
+                            -NSHeight (frame) - (NSMinY (dstRect) - NSMinY (srcRect)));
 
-  /* Take a copy of the buffer and then draw it back to the buffer,
-     limited by the clipping rectangle.  */
-  copy = CGBitmapContextCreateImage (drawingBuffer);
-  CGContextDrawImage (drawingBuffer, frame, copy);
+      /* Take a copy of the buffer and then draw it back to the buffer,
+         limited by the clipping rectangle.  */
+      copy = CGBitmapContextCreateImage (drawingBuffer);
+      CGContextDrawImage (drawingBuffer, frame, copy);
 
-  CGImageRelease (copy);
+      CGImageRelease (copy);
 
-  [[NSGraphicsContext currentContext] restoreGraphicsState];
-  [self setNeedsDisplayInRect:dstRect];
-#else
-  hide_bell();              // Ensure the bell image isn't scrolled.
+      [[NSGraphicsContext currentContext] restoreGraphicsState];
+      [self setNeedsDisplayInRect:dstRect];
 
-  ns_focus (emacsframe, &dstRect, 1);
-  [self scrollRect: srcRect
-                by: NSMakeSize (dstRect.origin.x - srcRect.origin.x,
-                                dstRect.origin.y - srcRect.origin.y)];
-  ns_unfocus (emacsframe);
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+    }
+  else
+    {
+#endif
+#endif /* NS_DRAW_TO_BUFFER */
+
+#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+      hide_bell();              // Ensure the bell image isn't scrolled.
+
+      ns_focus (emacsframe, &dstRect, 1);
+      [self scrollRect: srcRect
+                    by: NSMakeSize (dstRect.origin.x - srcRect.origin.x,
+                                    dstRect.origin.y - srcRect.origin.y)];
+      ns_unfocus (emacsframe);
+#endif
+#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+    }
 #endif
 }
 
@@ -8389,7 +8482,13 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect
 #ifdef NS_DRAW_TO_BUFFER
 - (BOOL)wantsUpdateLayer
 {
-    return YES;
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+  if (NSAppKitVersionNumber < 1671)
+    return NO;
+#endif
+
+  /* Running on macOS 10.14 or above.  */
+  return YES;
 }

-- 
2.26.0

>From 579f006d912d6a2ce2bf4bb5629dbcb6fba076b5 Mon Sep 17 00:00:00 2001
From: Alan Third <[email-masked]>
Date: Sat, 4 Apr 2020 15:13:58 +0100
Subject: [PATCH v5] Fix NS frame resizing issues (bug#40200, bug#28872)

* src/nsmenu.m (update_frame_tool_bar): Remove reference to
updateFrameSize.
* src/nsterm.h: ([EmacsView updateFrameSize]):
([EmacsView setRows:andColumns:]): Remove unused
method definitions.
(NS_PARENT_WINDOW_LEFT_POS):
(NS_PARENT_WINDOW_TOP_POS): Move to nsterm.m.
* src/nsterm.m (ns_parent_window_rect): New function.
(NS_PARENT_WINDOW_LEFT_POS):
(NS_PARENT_WINDOW_TOP_POS): Move to nsterm.m and simplify.
(ns_set_offset): Fix strange behaviours when using negative values.
(ns_set_window_size):
(ns_set_undecorated):
([EmacsView windowDidResize:]):
([EmacsView windowDidExitFullScreen]):
(ns_judge_scroll_bars): Remove references to updateFrameSize.
([EmacsView dealloc]): Unset resize notification and release buffer.
([EmacsView updateFrameSize:]): Remove function.
([EmacsView windowWillResize:toSize:]): Move some code to
viewDidResize.
([EmacsView viewDidResize]): New function.
([EmacsView initFrameFromEmacs:]): Set up resize notification and move
buffer creation until after the prerequisite objects are created.
([EmacsView toggleFullScreen:]): Set frame to the size of the
contentview, not the whole window, and remove reference to
updateFrameSize.
([EmacsView setRows:andColumns:]): Remove unused method.
([EmacsView windowDidMove:]): Tidy up.
---
 src/nsmenu.m |   2 -
 src/nsterm.h |  15 ---
 src/nsterm.m | 318 ++++++++++++++++++++++++---------------------------
 3 files changed, 150 insertions(+), 185 deletions(-)

diff --git a/src/nsmenu.m b/src/nsmenu.m
index 67f9a45a40..b7e4cbd565 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -1141,8 +1141,6 @@ - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
     }
 #endif
 
-  if (oldh != FRAME_TOOLBAR_HEIGHT (f))
-    [view updateFrameSize:YES];
   if (view->wait_for_tool_bar && FRAME_TOOLBAR_HEIGHT (f) > 0)
     {
       view->wait_for_tool_bar = NO;
diff --git a/src/nsterm.h b/src/nsterm.h
index 8396a542f7..e142dbd4f0 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -439,7 +439,6 @@ #define NS_DRAW_TO_BUFFER 1
 #endif
 @public
    struct frame *emacsframe;
-   int rows, cols;
    int scrollbarsNeedingUpdate;
    EmacsToolbar *toolbar;
    NSRect ns_userRect;
@@ -458,11 +457,9 @@ #define NS_DRAW_TO_BUFFER 1
 /* Emacs-side interface */
 - (instancetype) initFrameFromEmacs: (struct frame *) f;
 - (void) createToolbar: (struct frame *)f;
-- (void) setRows: (int) r andColumns: (int) c;
 - (void) setWindowClosing: (BOOL)closing;
 - (EmacsToolbar *) toolbar;
 - (void) deleteWorkingText;
-- (void) updateFrameSize: (BOOL) delay;
 - (void) handleFS;
 - (void) setFSValue: (int)value;
 - (void) toggleFullScreen: (id) sender;
@@ -1084,18 +1081,6 @@ #define NS_SCROLL_BAR_ADJUST_HORIZONTALLY(w, f)		\
    (FRAME_SCROLL_BAR_LINES (f) * FRAME_LINE_HEIGHT (f)	\
     - NS_SCROLL_BAR_HEIGHT (f)) : 0)
 
-/* Calculate system coordinates of the left and top of the parent
-   window or, if there is no parent window, the screen.  */
-#define NS_PARENT_WINDOW_LEFT_POS(f)                                    \
-  (FRAME_PARENT_FRAME (f) != NULL                                       \
-   ? [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window].frame.origin.x : 0)
-#define NS_PARENT_WINDOW_TOP_POS(f)                                     \
-  (FRAME_PARENT_FRAME (f) != NULL                                       \
-   ? ([FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window].frame.origin.y    \
-      + [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window].frame.size.height \
-      - FRAME_NS_TITLEBAR_HEIGHT (FRAME_PARENT_FRAME (f)))              \
-   : [[[NSScreen screens] objectAtIndex: 0] frame].size.height)
-
 #define FRAME_NS_FONT_TABLE(f) (FRAME_DISPLAY_INFO (f)->font_table)
 
 #define FRAME_FONTSET(f) ((f)->output_data.ns->fontset)
diff --git a/src/nsterm.m b/src/nsterm.m
index 3ce223307b..9dd1a89d0c 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -843,6 +843,32 @@ Free a pool and temporary objects it refers to (callable from C)
 }

+/* Get the frame rect, in system coordinates, of the parent window or,
+   if there is no parent window, the main screen.  */
+static inline NSRect
+ns_parent_window_rect (struct frame *f)
+{
+  NSRect parentRect;
+
+  if (FRAME_PARENT_FRAME (f) != NULL)
+    {
+      EmacsView *parentView = FRAME_NS_VIEW (FRAME_PARENT_FRAME (f));
+      parentRect = [parentView convertRect:[parentView frame]
+                                    toView:nil];
+      parentRect = [[parentView window] convertRectToScreen:parentRect];
+    }
+  else
+    parentRect = [[[NSScreen screens] objectAtIndex:0] frame];
+
+  return parentRect;
+}
+
+/* Calculate system coordinates of the left and top of the parent
+   window or, if there is no parent window, the main screen.  */
+#define NS_PARENT_WINDOW_LEFT_POS(f) NSMinX (ns_parent_window_rect (f))
+#define NS_PARENT_WINDOW_TOP_POS(f) NSMaxY (ns_parent_window_rect (f))
+
+
 static NSRect
 ns_row_rect (struct window *w, struct glyph_row *row,
                enum glyph_row_area area)
@@ -1741,61 +1767,64 @@ Hide the window (X11 semantics)
    -------------------------------------------------------------------------- */
 {
   NSView *view = FRAME_NS_VIEW (f);
-  NSScreen *screen = [[view window] screen];
+  NSRect windowFrame = [[view window] frame];
+  NSPoint topLeft;
 
   NSTRACE ("ns_set_offset");
 
   block_input ();
 
-  f->left_pos = xoff;
-  f->top_pos = yoff;
+  if (FRAME_PARENT_FRAME (f))
+    {
+      /* Convert the parent frame's view rectangle into screen
+         coords.  */
+      EmacsView *parentView = FRAME_NS_VIEW (FRAME_PARENT_FRAME (f));
+      NSRect parentRect = [parentView convertRect:[parentView frame]
+                                           toView:nil];
+      parentRect = [[parentView window] convertRectToScreen:parentRect];
+
+      if (f->size_hint_flags & XNegative)
+        topLeft.x = NSMaxX (parentRect) - NSWidth (windowFrame) + xoff;
+      else
+        topLeft.x = NSMinX (parentRect) + xoff;
 
-  if (view != nil)
+      if (f->size_hint_flags & YNegative)
+        topLeft.y = NSMinY (parentRect) + NSHeight (windowFrame) - yoff;
+      else
+        topLeft.y = NSMaxY (parentRect) - yoff;
+    }
+  else
     {
-      if (FRAME_PARENT_FRAME (f) == NULL && screen)
-        {
-          f->left_pos = f->size_hint_flags & XNegative
-            ? [screen visibleFrame].size.width + f->left_pos - FRAME_PIXEL_WIDTH (f)
-            : f->left_pos;
-          /* We use visibleFrame here to take menu bar into account.
-             Ideally we should also adjust left/top with visibleFrame.origin.  */
-
-          f->top_pos = f->size_hint_flags & YNegative
-            ? ([screen visibleFrame].size.height + f->top_pos
-               - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f)
-               - FRAME_TOOLBAR_HEIGHT (f))
-            : f->top_pos;
-#ifdef NS_IMPL_GNUSTEP
-	  if (f->left_pos < 100)
-	    f->left_pos = 100;  /* don't overlap menu */
-#endif
-        }
-      else if (FRAME_PARENT_FRAME (f) != NULL)
-        {
-          struct frame *parent = FRAME_PARENT_FRAME (f);
+      /* If there is no parent frame then just convert to screen
+         coordinates, UNLESS we have negative values, in which case I
+         think it's best to position from the bottom and right of the
+         current screen rather than the main screen or whole
+         display.  */
+      NSRect screenFrame = [[[view window] screen] frame];
 
-          /* On X negative values for child frames always result in
-             positioning relative to the bottom right corner of the
-             parent frame.  */
-          if (f->left_pos < 0)
-            f->left_pos = FRAME_PIXEL_WIDTH (parent) - FRAME_PIXEL_WIDTH (f) + f->left_pos;
+      if (f->size_hint_flags & XNegative)
+        topLeft.x = NSMaxX (screenFrame) - NSWidth (windowFrame) + xoff;
+      else
+        topLeft.x = xoff;
 
-          if (f->top_pos < 0)
-            f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent)
-              - FRAME_PIXEL_HEIGHT (f) + f->top_pos;
-        }
+      if (f->size_hint_flags & YNegative)
+        topLeft.y = NSMinY (screenFrame) + NSHeight (windowFrame) - yoff;
+      else
+        topLeft.y = NSMaxY ([[[NSScreen screens] objectAtIndex:0] frame]) - yoff;
+
+#ifdef NS_IMPL_GNUSTEP
+      /* Don't overlap the menu.
 
-      /* Constrain the setFrameTopLeftPoint so we don't move behind the
-         menu bar.  */
-      NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos
-                                                + NS_PARENT_WINDOW_LEFT_POS (f)),
-                                SCREENMAXBOUND (NS_PARENT_WINDOW_TOP_POS (f)
-                                                - f->top_pos));
-      NSTRACE_POINT ("setFrameTopLeftPoint", pt);
-      [[view window] setFrameTopLeftPoint: pt];
-      f->size_hint_flags &= ~(XNegative|YNegative);
+         FIXME: Surely there's a better way than just hardcoding 100
+         in here?  */
+      boundsRect.origin.x = 100;
+#endif
     }
 
+  NSTRACE_POINT ("setFrameTopLeftPoint", topLeft);
+  [[view window] setFrameTopLeftPoint:topLeft];
+  f->size_hint_flags &= ~(XNegative|YNegative);
+
   unblock_input ();
 }
 
@@ -1862,9 +1891,16 @@ Hide the window (X11 semantics)
 	   make_fixnum (FRAME_NS_TITLEBAR_HEIGHT (f)),
 	   make_fixnum (FRAME_TOOLBAR_HEIGHT (f))));
 
-  [window setFrame: wr display: YES];
+ /* Usually it seems safe to delay changing the frame size, but when a
+    series of actions are taken with no redisplay between them then we
+    can end up using old values so don't delay here.  */
+ change_frame_size (f,
+                    FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth),
+                    FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight),
+                    0, NO, 0, 1);
+
+  [window setFrame:wr display:NO];
 
-  [view updateFrameSize: NO];
   unblock_input ();
 }
 
@@ -1913,7 +1949,6 @@ Hide the window (X11 semantics)
          so some key presses (TAB) are swallowed by the system.  */
       [window makeFirstResponder: view];
 
-      [view updateFrameSize: NO];
       unblock_input ();
     }
 }
@@ -5026,9 +5061,6 @@ in certain situations (rapid incoming events).
       if ([view judge])
         removed = YES;
     }
-
-  if (removed)
-    [eview updateFrameSize: NO];
 }
 
 /* ==========================================================================
@@ -6200,6 +6232,15 @@ - (void) setWindowClosing: (BOOL)closing
 - (void)dealloc
 {
   NSTRACE ("[EmacsView dealloc]");
+
+  /* Clear the view resize notification.  */
+  [[NSNotificationCenter defaultCenter]
+    removeObserver:self
+              name:NSViewFrameDidChangeNotification
+            object:nil];
+
+  CGContextRelease (drawingBuffer);
+
   [toolbar release];
   if (fs_state == FULLSCREEN_BOTH)
     [nonfs_window release];
@@ -7041,108 +7082,12 @@ - (BOOL)windowShouldClose: (id)sender
   return NO;
 }
 
-- (void) updateFrameSize: (BOOL) delay
-{
-  NSWindow *window = [self window];
-  NSRect wr = [window frame];
-  int extra = 0;
-  int oldc = cols, oldr = rows;
-  int oldw = FRAME_PIXEL_WIDTH (emacsframe);
-  int oldh = FRAME_PIXEL_HEIGHT (emacsframe);
-  int neww, newh;
-
-  NSTRACE ("[EmacsView updateFrameSize:]");
-  NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
-  NSTRACE_RECT ("Original frame", wr);
-  NSTRACE_MSG  ("Original columns: %d", cols);
-  NSTRACE_MSG  ("Original rows: %d", rows);
-
-  if (! [self isFullscreen])
-    {
-      int toolbar_height;
-#ifdef NS_IMPL_GNUSTEP
-      // GNUstep does not always update the tool bar height.  Force it.
-      if (toolbar && [toolbar isVisible])
-          update_frame_tool_bar (emacsframe);
-#endif
-
-      toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe);
-      if (toolbar_height < 0)
-        toolbar_height = 35;
-
-      extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe)
-        + toolbar_height;
-    }
-
-  if (wait_for_tool_bar)
-    {
-      /* The toolbar height is always 0 in fullscreen and undecorated
-         frames, so don't wait for it to become available.  */
-      if (FRAME_TOOLBAR_HEIGHT (emacsframe) == 0
-          && FRAME_UNDECORATED (emacsframe) == false
-          && ! [self isFullscreen])
-        {
-          NSTRACE_MSG ("Waiting for toolbar");
-          return;
-        }
-      wait_for_tool_bar = NO;
-    }
-
-  neww = (int)wr.size.width - emacsframe->border_width;
-  newh = (int)wr.size.height - extra;
-
-  NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
-  NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe));
-  NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe));
-
-  cols = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (emacsframe, neww);
-  rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (emacsframe, newh);
-
-  if (cols < MINWIDTH)
-    cols = MINWIDTH;
-
-  if (rows < MINHEIGHT)
-    rows = MINHEIGHT;
-
-  NSTRACE_MSG ("New columns: %d", cols);
-  NSTRACE_MSG ("New rows: %d", rows);
-
-  if (oldr != rows || oldc != cols || neww != oldw || newh != oldh)
-    {
-      NSView *view = FRAME_NS_VIEW (emacsframe);
-
-      change_frame_size (emacsframe,
-                         FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
-                         FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
-                         0, delay, 0, 1);
-      SET_FRAME_GARBAGED (emacsframe);
-      cancel_mouse_face (emacsframe);
-
-      /* The next two lines set the frame to the same size as we've
-         already set above.  We need to do this when we switch back
-         from non-native fullscreen, in other circumstances it appears
-         to be a noop.  (bug#28872) */
-      wr = NSMakeRect (0, 0, neww, newh);
-      [view setFrame: wr];
-#ifdef NS_DRAW_TO_BUFFER
-      [self createDrawingBuffer];
-#endif
-
-      // To do: consider using [NSNotificationCenter postNotificationName:].
-      [self windowDidMove: // Update top/left.
-	      [NSNotification notificationWithName:NSWindowDidMoveNotification
-					    object:[view window]]];
-    }
-  else
-    {
-      NSTRACE_MSG ("No change");
-    }
-}
 
 - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
 /* Normalize frame to gridded text size.  */
 {
   int extra = 0;
+  int cols, rows;
 
   NSTRACE ("[EmacsView windowWillResize:toSize: " NSTRACE_FMT_SIZE "]",
            NSTRACE_ARG_SIZE (frameSize));
@@ -7279,11 +7224,6 @@ - (void)windowDidResize: (NSNotification *)notification
   sz = [self windowWillResize: theWindow toSize: sz];
 #endif /* NS_IMPL_GNUSTEP */
 
-  if (cols > 0 && rows > 0)
-    {
-      [self updateFrameSize: YES];
-    }
-
   ns_send_appdefined (-1);
 }
 
@@ -7304,6 +7244,50 @@ - (void)viewDidEndLiveResize
 #endif /* NS_IMPL_COCOA */

+- (void)viewDidResize:(NSNotification *)notification
+{
+  NSRect frame = [self frame];
+  int oldw, oldh, neww, newh;
+
+  if (! FRAME_LIVE_P (emacsframe))
+    return;
+
+#ifdef NS_DRAW_TO_BUFFER
+  CGFloat scale = [[self window] backingScaleFactor];
+  oldw = (CGFloat)CGBitmapContextGetWidth (drawingBuffer) / scale;
+  oldh = (CGFloat)CGBitmapContextGetHeight (drawingBuffer) / scale;
+#else
+  oldw = FRAME_PIXEL_WIDTH (emacsframe);
+  oldh = FRAME_PIXEL_HEIGHT (emacsframe);
+#endif
+  neww = (int)NSWidth (frame);
+  newh = (int)NSHeight (frame);
+
+  NSTRACE ("[EmacsView viewDidResize]");
+
+  /* Don't want to do anything when the view size hasn't changed. */
+  if ((oldh == newh && oldw == neww))
+    {
+      NSTRACE_MSG ("No change");
+      return;
+    }
+
+  NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
+  NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
+
+  change_frame_size (emacsframe,
+                     FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
+                     FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
+                     0, YES, 0, 1);
+
+#ifdef NS_DRAW_TO_BUFFER
+  [self createDrawingBuffer];
+#endif
+  SET_FRAME_GARBAGED (emacsframe);
+  cancel_mouse_face (emacsframe);
+}
+
+
 - (void)windowDidBecomeKey: (NSNotification *)notification
 /* cf. x_detect_focus_change(), x_focus_changed(), x_new_focus_frame() */
 {
@@ -7465,10 +7449,6 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
   maximizing_resize = NO;
 #endif
 
-#ifdef NS_DRAW_TO_BUFFER
-  [self createDrawingBuffer];
-#endif
-
   win = [[EmacsWindow alloc]
             initWithContentRect: r
                       styleMask: (FRAME_UNDECORATED (f)
@@ -7574,6 +7554,17 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
   [NSApp registerServicesMenuSendTypes: ns_send_types
                            returnTypes: [NSArray array]];
 
+#ifdef NS_DRAW_TO_BUFFER
+  [self createDrawingBuffer];
+#endif
+
+  /* Set up view resize notifications.  */
+  [self setPostsFrameChangedNotifications:YES];
+  [[NSNotificationCenter defaultCenter]
+      addObserver:self
+         selector:@selector (viewDidResize:)
+             name:NSViewFrameDidChangeNotification object:nil];
+
   /* macOS Sierra automatically enables tabbed windows.  We can't
      allow this to be enabled until it's available on a Free system.
      Currently it only happens by accident and is buggy anyway.  */
@@ -7603,9 +7594,8 @@ - (void)windowDidMove: sender
     return;
   if (screen != nil)
     {
-      emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
-      emacsframe->top_pos =
-        NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
+      emacsframe->left_pos = NSMinX (r) - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
+      emacsframe->top_pos = NS_PARENT_WINDOW_TOP_POS (emacsframe) - NSMaxY (r);
 
       // FIXME: after event part below didExitFullScreen is not received
       // if (emacs_event)
@@ -7904,7 +7894,6 @@ - (void)windowDidExitFullScreen /* provided for direct calls */
     {
       [toolbar setVisible:YES];
       update_frame_tool_bar (emacsframe);
-      [self updateFrameSize:YES];
       [[self window] display];
     }
   else
@@ -8117,11 +8106,11 @@ - (void)toggleFullScreen: (id)sender
       // send notifications.
 
       [self windowWillExitFullScreen];
-      [fw setFrame: [w frame] display:YES animate:ns_use_fullscreen_animation];
+      [fw setFrame:[[w contentView] frame]
+                    display:YES animate:ns_use_fullscreen_animation];
       [fw close];
       [w makeKeyAndOrderFront:NSApp];
       [self windowDidExitFullScreen];
-      [self updateFrameSize:YES];
     }
 }
 
@@ -8630,13 +8619,6 @@ - (instancetype)setMiniwindowImage: (BOOL) setMini
 }

-- (void) setRows: (int) r andColumns: (int) c
-{
-  NSTRACE ("[EmacsView setRows:%d andColumns:%d]", r, c);
-  rows = r;
-  cols = c;
-}
-
 - (int) fullscreenState
 {
   return fs_state;
-- 
2.26.0