Subject: bug#29953: 26.0.90; Moving a child frame before making it visible makes the child frame not showing any more on macOS

From: Alan Third <[email-masked]>
Date: Wed, 3 Jan 2018 14:07:04 +0000

On Wed, Jan 03, 2018 at 07:55:12AM +0800, Jiege Chen wrote:
> 
> Snippet:
> 
> This code uses `oddp', which might not be available from `emacs -Q'.

You can replace it with (= 1 (mod count 2))

> On my macOS Sierra, the frame only shows once and never shows again.

There were two issues interacting here: invisible child frames lose
track of their parents in NS, and frames that are off‐screen don’t
have an associated screen but our positioning code will only position
a frame if it knows it’s screen.

I’ve fixed the first issue so that we look up the parent through the
Emacs frame structs, and the second by positioning according to the
main screen if the frame doesn’t have a screen.

I also fixed what I think is a bug when child frames are positioned
with negative values. On X it looks as though a negative value always
results in positioning according to the bottom‐right of the parent
frame, but on NS it would be positioned to the left and above the
parent frame. NS now behaves like X.

Patch attached.

> Related issue: https://github.com/emacs-lsp/lsp-ui/issues/21 .

Are you using threads? GUI calls and threads don’t mix well yet,
unfortunately.
-- 
Alan Third
>From b54c5c18df91cbf887d653846edddeb1c853c141 Mon Sep 17 00:00:00 2001
From: Alan Third <[email-masked]>
Date: Wed, 3 Jan 2018 13:45:03 +0000
Subject: [PATCH] Fix child frame placement issues (bug#29953)

* src/nsterm.h (NS_PARENT_WINDOW_LEFT_POS):
(NS_PARENT_WINDOW_TOP_POS): Get the parent frame through the frame
struct as invisible child windows are detached from their parents in
NS.
* src/nsterm.m (x_set_offset): Offscreen frames have `nil' screen
value, so handle that gracefully.  Child frames with negative left and
top should be positioned relative to the bottom right of the parent
frame.
---
 src/nsterm.h |  6 +++---
 src/nsterm.m | 46 ++++++++++++++++++++++++++++++----------------
 2 files changed, 33 insertions(+), 19 deletions(-)

diff --git a/src/nsterm.h b/src/nsterm.h
index c40ddf3284..588b9fc644 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -1073,11 +1073,11 @@ struct x_output
    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 (f) window] parentWindow].frame.origin.x : 0)
+   ? [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 (f) window] parentWindow].frame.origin.y          \
-      + [[FRAME_NS_VIEW (f) window] parentWindow].frame.size.height     \
+   ? ([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)
 
diff --git a/src/nsterm.m b/src/nsterm.m
index 5798f4fd0b..419a37033f 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1736,7 +1736,6 @@ -(void)remove
 {
   NSView *view = FRAME_NS_VIEW (f);
   NSArray *screens = [NSScreen screens];
-  NSScreen *fscreen = [screens objectAtIndex: 0];
   NSScreen *screen = [[view window] screen];
 
   NSTRACE ("x_set_offset");
@@ -1746,26 +1745,41 @@ -(void)remove
   f->left_pos = xoff;
   f->top_pos = yoff;
 
-  if (view != nil && screen && fscreen)
+  if (view != nil)
     {
-      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;
+      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 (FRAME_PARENT_FRAME (f) == NULL)
-	{
 	  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);
+
+          /* 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->top_pos < 0)
+            f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent)
+              - FRAME_PIXEL_HEIGHT (f) + f->top_pos;
+        }
+
       /* Constrain the setFrameTopLeftPoint so we don't move behind the
          menu bar.  */
       NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos
-- 
2.14.3