Subject: bug#25408: Remove Decorations Around Emacs Frame (NS port)

From: Alan Third <[email-masked]>
Date: Wed, 19 Apr 2017 15:33:16 +0100

On Wed, Apr 19, 2017 at 09:26:23AM +0200, martin rudalics wrote:
> If at this time I do within the just started Emacs
> 
> (set-frame-parameter nil 'undecorated t)
> 
> I get
> 
> /home/martin/emacs-git/trunk/obj-ns/src/emacs: Uncaught exception NSInvalidArgumentException, reason: -[EmacsFSWindow setStyleMask:]: unrecognized selector sent to instance 0x30a37d0

It turns out that GNUStep doesn’t let you change the decorated state
of an existing window. It should be able to create a new undecorated
window.

> Setting the 'parent-frame' parameter of a frame has no impact, neither
> when I do it during frame creation not when doing it at any later time.

I believe it is setting the parent/child relationship, but in GNUStep
it doesn’t seem to mean the child moves with the parent. I think this
is a bug in GNUStep, but the behaviour isn’t documented yet, so I’m
not sure if it’s intentional.

The child will minimise and close with the parent and moving it to (0
0) will put it in the top left corner of the parent.

Except it doesn’t quite, because there’s a hard‐coded titlebar height
for GNUStep which is guaranteed to be wrong for every WM. At least I
think that’s what going on.

Z‐groups are working, but again in GNUStep it seems a bit hit and miss
as the frames seem to forget their state if you click on their
titlebars.
-- 
Alan Third
>From 5584fbe6402083853b71b3de251670752a5d7af2 Mon Sep 17 00:00:00 2001
From: Alan Third <[email-masked]>
Date: Fri, 14 Apr 2017 10:02:38 +0100
Subject: [PATCH] Add new frame functionality to NS port
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* lisp/frame.el (frame-restack): Call ns-frame-restack.
* src/keyboard.c (kbd_buffer_get_event) [HAVE_NS]: Enable
MOVE_FRAME_EVENT handling.
* src/frame.h:
* src/frame.c: Enable 'z-group', 'undecorated' and 'parent' frame
definitions.
* src/nsfns.m: Add x_set_z_group, x_set_parent_frame and
x_set_undecorated (Cocoa only) to handler struct.
(Fx-create_frame): Handle 'z-code', 'parent-frame' and 'undecorated'
frame parameter.
(Fns_frame_restack): New function.
* src/nsmenu.m (free_frame_tool_bar, update_frame_tool_bar):
FRAME_TOOLBAR_HEIGHT is no longer a variable.
* src/nsterm.h (NS_PARENT_WINDOW_LEFT_POS, NS_PARENT_WINDOW_TOP_POS):
Add #defines to find the screen position of the parent frame.
(NS_TOP_POS): Remove defun.
(EmacsView): Remove redundant toolbar variables and add createToolbar
method.
(FRAME_NS_TITLEBAR_HEIGHT, FRAME_TOOLBAR_HEIGHT): Always calculate the
values instead of storing them in a variable.
* src/nsterm.m (x_set_offset, windowDidMove): Take parent frame
position into account when positioning frames.
(initFrameFromEmacs): Remove toolbar creation code and handle new
frame parameters.
(x_set_window_size): Remove toolbar height calculation.
(x_set_z_group):
(x_set_parent_frame):
(x_set_undecorated) [NS_IMPL_COCOA]: New function.
(x_destroy_window): Detach parent if child closes.
(updateFrameSize): Change NSTRACE message to reflect new reality and
no longer reset frame size.
(windowWillResize): Don’t change NS window name when the titlebar
is invisible.
(createToolbar): Move toolbar creation code into it’s own method.
(toggleFullScreen): FRAME_TOOLBAR_HEIGHT and FRAME_NS_TITLEBAR_HEIGHT
are no longer variables.
(windowDidMove): Fire MOVE_FRAME_EVENT Emacs event.
---
 lisp/frame.el  |   4 +-
 src/frame.c    |   2 -
 src/frame.h    |  14 +--
 src/keyboard.c |   2 +-
 src/nsfns.m    |  63 +++++++++++-
 src/nsmenu.m   |   8 --
 src/nsterm.h   |  47 +++++++--
 src/nsterm.m   | 302 ++++++++++++++++++++++++++++++++++++++++++++-------------
 8 files changed, 341 insertions(+), 101 deletions(-)

diff --git a/lisp/frame.el b/lisp/frame.el
index 86a0e26e39..e632b5943f 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1548,7 +1548,9 @@ frame-restack
          ((eq frame-type 'x)
           (x-frame-restack frame1 frame2 above))
          ((eq frame-type 'w32)
-          (w32-frame-restack frame1 frame2 above))))
+          (w32-frame-restack frame1 frame2 above))
+         ((eq frame-type 'ns)
+          (ns-frame-restack frame1 frame2 above))))
     (error "Cannot restack frames")))
 
 (defun frame-size-changed-p (&optional frame)
diff --git a/src/frame.c b/src/frame.c
index 282b691c27..681a245ee0 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -683,7 +683,6 @@ make_frame (bool mini_p)
   f->vertical_scroll_bar_type = vertical_scroll_bar_none;
   f->horizontal_scroll_bars = false;
   f->want_fullscreen = FULLSCREEN_NONE;
-#if ! defined (HAVE_NS)
   f->undecorated = false;
 #ifndef HAVE_NTGUI
   f->override_redirect = false;
@@ -692,7 +691,6 @@ make_frame (bool mini_p)
   f->no_focus_on_map = false;
   f->no_accept_focus = false;
   f->z_group = z_group_none;
-#endif
 #if ! defined (USE_GTK) && ! defined (HAVE_NS)
   f->last_tool_bar_item = -1;
 #endif
diff --git a/src/frame.h b/src/frame.h
index 36af6e6780..4aa7c34a29 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -75,10 +75,10 @@ struct frame
      Usually it is nil.  */
   Lisp_Object title;
 
-#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS)
+#if defined (HAVE_WINDOW_SYSTEM)
   /* This frame's parent frame, if it has one.  */
   Lisp_Object parent_frame;
-#endif /* HAVE_WINDOW_SYSTEM and not HAVE_NS */
+#endif /* HAVE_WINDOW_SYSTEM */
 
   /* The frame which should receive keystrokes that occur in this
      frame, or nil if they should go to the frame itself.  This is
@@ -332,7 +332,7 @@ struct frame
   bool_bf horizontal_scroll_bars : 1;
 #endif /* HAVE_WINDOW_SYSTEM */
 
-#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS)
+#if defined (HAVE_WINDOW_SYSTEM)
   /* True if this is an undecorated frame.  */
   bool_bf undecorated : 1;
 
@@ -570,7 +570,7 @@ fset_face_alist (struct frame *f, Lisp_Object val)
 {
   f->face_alist = val;
 }
-#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS)
+#if defined (HAVE_WINDOW_SYSTEM)
 INLINE void
 fset_parent_frame (struct frame *f, Lisp_Object val)
 {
@@ -914,7 +914,7 @@ default_pixels_per_inch_y (void)
 #define FRAME_HAS_VERTICAL_SCROLL_BARS_ON_RIGHT(f) ((void) f, 0)
 #endif /* HAVE_WINDOW_SYSTEM */
 
-#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_NS)
+#if defined (HAVE_WINDOW_SYSTEM)
 #define FRAME_UNDECORATED(f) ((f)->undecorated)
 #ifdef HAVE_NTGUI
 #define FRAME_OVERRIDE_REDIRECT(f) ((void) f, 0)
@@ -934,7 +934,7 @@ default_pixels_per_inch_y (void)
 #define FRAME_Z_GROUP_ABOVE_SUSPENDED(f)	\
   ((f)->z_group == z_group_above_suspended)
 #define FRAME_Z_GROUP_BELOW(f) ((f)->z_group == z_group_below)
-#else /* not HAVE_WINDOW_SYSTEM or HAVE_NS */
+#else /* not HAVE_WINDOW_SYSTEM */
 #define FRAME_UNDECORATED(f) ((void) f, 0)
 #define FRAME_OVERRIDE_REDIRECT(f) ((void) f, 0)
 #define FRAME_PARENT_FRAME(f) ((void) f, NULL)
@@ -945,7 +945,7 @@ default_pixels_per_inch_y (void)
 #define FRAME_Z_GROUP_NONE(f) ((void) f, true)
 #define FRAME_Z_GROUP_ABOVE(f) ((void) f, false)
 #define FRAME_Z_GROUP_BELOW(f) ((void) f, false)
-#endif /* HAVE_WINDOW_SYSTEM and not HAVE_NS */
+#endif /* HAVE_WINDOW_SYSTEM */
 
 /* Whether horizontal scroll bars are currently enabled for frame F.  */
 #if USE_HORIZONTAL_SCROLL_BARS
diff --git a/src/keyboard.c b/src/keyboard.c
index 3e50142f7c..c9fa2a9f5e 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -4056,7 +4056,7 @@ kbd_buffer_get_event (KBOARD **kbp,
 	  kbd_fetch_ptr = event + 1;
 	}
 #endif
-#if defined (HAVE_X11) || defined (HAVE_NTGUI)
+#if defined (HAVE_X11) || defined (HAVE_NTGUI) || defined (HAVE_NS)
       else if (event->kind == MOVE_FRAME_EVENT)
 	{
 	  /* Make an event (move-frame (FRAME)).  */
diff --git a/src/nsfns.m b/src/nsfns.m
index 8a923dd393..f1a5df8f27 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -972,12 +972,16 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side
   0, /* x_set_sticky */
   0, /* x_set_tool_bar_position */
   0, /* x_set_inhibit_double_buffering */
-  0, /* x_set_undecorated */
-  0, /* x_set_parent_frame */
+#ifdef NS_IMPL_COCOA
+  x_set_undecorated, /* x_set_undecorated */
+#else
+  0, /*x_set_undecorated */
+#endif
+  x_set_parent_frame, /* x_set_parent_frame */
   0, /* x_set_skip_taskbar */
   0, /* x_set_no_focus_on_map */
   0, /* x_set_no_accept_focus */
-  0, /* x_set_z_group */
+  x_set_z_group, /* x_set_z_group */
   0, /* x_set_override_redirect */
 };
 
@@ -1087,7 +1091,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side
   ptrdiff_t count = specpdl_ptr - specpdl;
   Lisp_Object display;
   struct ns_display_info *dpyinfo = NULL;
-  Lisp_Object parent;
+  Lisp_Object parent, parent_frame;
   struct kboard *kb;
   static int desc_ctr = 1;
   int x_width = 0, x_height = 0;
@@ -1265,6 +1269,25 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side
 		     FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, 1,
 		     Qx_create_frame_1);
 
+  tem = x_get_arg (dpyinfo, parms, Qundecorated, NULL, NULL, RES_TYPE_BOOLEAN);
+  FRAME_UNDECORATED (f) = !NILP (tem) && !EQ (tem, Qunbound);
+  store_frame_param (f, Qundecorated, FRAME_UNDECORATED (f) ? Qt : Qnil);
+
+  parent_frame = x_get_arg (dpyinfo, parms, Qparent_frame, NULL, NULL,
+			    RES_TYPE_SYMBOL);
+  /* Accept parent-frame iff parent-id was not specified.  */
+  if (!NILP (parent)
+      || EQ (parent_frame, Qunbound)
+      || NILP (parent_frame)
+      || !FRAMEP (parent_frame)
+      || !FRAME_LIVE_P (XFRAME (parent_frame)))
+    parent_frame = Qnil;
+
+  fset_parent_frame (f, parent_frame);
+  store_frame_param (f, Qparent_frame, parent_frame);
+
+  x_default_parameter (f, parms, Qz_group, Qnil, NULL, NULL, RES_TYPE_SYMBOL);
+
   /* The resources controlling the menu-bar and tool-bar are
      processed specially at startup, and reflected in the mode
      variables; ignore them here.  */
@@ -1405,6 +1428,37 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side
     }
 }
 
+DEFUN ("ns-frame-restack", Fns_frame_restack, Sns_frame_restack, 2, 3, 0,
+       doc: /* Restack FRAME1 below FRAME2.
+This means that if both frames are visible and the display areas of
+these frames overlap, FRAME2 (partially) obscures FRAME1.  If optional
+third argument ABOVE is non-nil, restack FRAME1 above FRAME2.  This
+means that if both frames are visible and the display areas of these
+frames overlap, FRAME1 (partially) obscures FRAME2.
+
+Some window managers may refuse to restack windows.  */)
+     (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object above)
+{
+  struct frame *f1 = decode_live_frame (frame1);
+  struct frame *f2 = decode_live_frame (frame2);
+
+  if (FRAME_NS_VIEW (f1) && FRAME_NS_VIEW (f2))
+    {
+      NSWindow *window = [FRAME_NS_VIEW (f1) window];
+      NSInteger window2 = [[FRAME_NS_VIEW (f2) window] windowNumber];
+      NSWindowOrderingMode flag = NILP (above) ? NSWindowBelow : NSWindowAbove;
+
+      [window orderWindow: flag
+               relativeTo: window2];
+
+      return Qt;
+    }
+  else
+    {
+      error ("Cannot restack frames");
+      return Qnil;
+    }
+}
 
 DEFUN ("ns-popup-font-panel", Fns_popup_font_panel, Sns_popup_font_panel,
        0, 1, "",
@@ -3134,6 +3188,7 @@ - (NSString *)panel: (id)sender userEnteredFilename: (NSString *)filename
   defsubr (&Sns_display_monitor_attributes_list);
   defsubr (&Sns_frame_geometry);
   defsubr (&Sns_frame_edges);
+  defsubr (&Sns_frame_restack);
   defsubr (&Sx_display_mm_width);
   defsubr (&Sx_display_mm_height);
   defsubr (&Sx_display_screens);
diff --git a/src/nsmenu.m b/src/nsmenu.m
index 59ea3855ed..1262c9cb4d 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -995,8 +995,6 @@ - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
   block_input ();
   view->wait_for_tool_bar = NO;
 
-  FRAME_TOOLBAR_HEIGHT (f) = 0;
-
   /* Note: This trigger an animation, which calls windowDidResize
      repeatedly. */
   f->output_data.ns->in_animation = 1;
@@ -1129,12 +1127,6 @@ - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f
     }
 #endif
 
-  FRAME_TOOLBAR_HEIGHT (f) =
-    NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
-    - FRAME_NS_TITLEBAR_HEIGHT (f);
-  if (FRAME_TOOLBAR_HEIGHT (f) < 0) // happens if frame is fullscreen.
-    FRAME_TOOLBAR_HEIGHT (f) = 0;
-
   if (oldh != FRAME_TOOLBAR_HEIGHT (f))
     [view updateFrameSize:YES];
   if (view->wait_for_tool_bar && FRAME_TOOLBAR_HEIGHT (f) > 0)
diff --git a/src/nsterm.h b/src/nsterm.h
index 53d9344cc7..2f8c4269b0 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -430,7 +430,7 @@ char const * nstrace_fullscreen_type_name (int);
    NSString *workingText;
    BOOL processingCompose;
    int fs_state, fs_before_fs, next_maximized;
-   int tibar_height, tobar_height, bwidth;
+   int bwidth;
    int maximized_width, maximized_height;
    NSWindow *nonfs_window;
    BOOL fs_is_native;
@@ -454,6 +454,7 @@ char const * nstrace_fullscreen_type_name (int);
 
 /* Emacs-side interface */
 - initFrameFromEmacs: (struct frame *) f;
+- (void) createToolbar: (struct frame *)f;
 - (void) setRows: (int) r andColumns: (int) c;
 - (void) setWindowClosing: (BOOL)closing;
 - (EmacsToolbar *) toolbar;
@@ -1012,8 +1013,6 @@ struct x_output
 
 #define NS_FACE_FOREGROUND(f) ((f)->foreground)
 #define NS_FACE_BACKGROUND(f) ((f)->background)
-#define FRAME_NS_TITLEBAR_HEIGHT(f) ((f)->output_data.ns->titlebar_height)
-#define FRAME_TOOLBAR_HEIGHT(f) ((f)->output_data.ns->toolbar_height)
 
 #define FRAME_DEFAULT_FACE(f) FACE_FROM_ID_OR_NULL (f, DEFAULT_FACE_ID)
 
@@ -1029,6 +1028,25 @@ struct x_output
 #define XNS_SCROLL_BAR(vec) XSAVE_POINTER (vec, 0)
 #endif
 
+/* Compute pixel height of the frame's titlebar. */
+#define FRAME_NS_TITLEBAR_HEIGHT(f)                                     \
+  (NSHeight([FRAME_NS_VIEW (f) frame]) == 0 ?                           \
+   0                                                                    \
+   : (int)(NSHeight([FRAME_NS_VIEW (f) window].frame)                   \
+           - NSHeight([NSWindow contentRectForFrameRect:                \
+                       [[FRAME_NS_VIEW (f) window] frame]               \
+                       styleMask:[[FRAME_NS_VIEW (f) window] styleMask]])))
+
+/* Compute pixel height of the toolbar. */
+#define FRAME_TOOLBAR_HEIGHT(f)                                         \
+  (([[FRAME_NS_VIEW (f) window] toolbar] == nil                         \
+    || ! [[FRAME_NS_VIEW (f) window] toolbar].isVisible) ?		\
+   0                                                                    \
+   : (int)(NSHeight([NSWindow contentRectForFrameRect:                  \
+                     [[FRAME_NS_VIEW (f) window] frame]                 \
+                     styleMask:[[FRAME_NS_VIEW (f) window] styleMask]]) \
+           - NSHeight([[[FRAME_NS_VIEW (f) window] contentView] frame])))
+
 /* Compute pixel size for vertical scroll bars */
 #define NS_SCROLL_BAR_WIDTH(f)						\
   (FRAME_HAS_VERTICAL_SCROLL_BARS (f)					\
@@ -1059,12 +1077,17 @@ struct x_output
    (FRAME_SCROLL_BAR_LINES (f) * FRAME_LINE_HEIGHT (f)	\
     - NS_SCROLL_BAR_HEIGHT (f)) : 0)
 
-/* XXX: fix for GNUstep inconsistent accounting for titlebar */
-#ifdef NS_IMPL_GNUSTEP
-#define NS_TOP_POS(f) ((f)->top_pos + 18)
-#else
-#define NS_TOP_POS(f) ((f)->top_pos)
-#endif
+/* 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 (f) window] parentWindow].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_TITLEBAR_HEIGHT (FRAME_PARENT_FRAME (f)))              \
+   : [[[FRAME_NS_VIEW (f) window] screen] frame].size.height)
 
 #define FRAME_NS_FONT_TABLE(f) (FRAME_DISPLAY_INFO (f)->font_table)
 
@@ -1185,6 +1208,12 @@ extern int x_display_pixel_width (struct ns_display_info *);
 /* This in nsterm.m */
 extern float ns_antialias_threshold;
 extern void x_destroy_window (struct frame *f);
+extern void x_set_undecorated (struct frame *f, Lisp_Object new_value,
+                               Lisp_Object old_value);
+extern void x_set_parent_frame (struct frame *f, Lisp_Object new_value,
+                                Lisp_Object old_value);
+extern void x_set_z_group (struct frame *f, Lisp_Object new_value,
+                           Lisp_Object old_value);
 extern int ns_select (int nfds, fd_set *readfds, fd_set *writefds,
 		      fd_set *exceptfds, struct timespec const *timeout,
 		      sigset_t const *sigmask);
diff --git a/src/nsterm.m b/src/nsterm.m
index 4725991aff..fbbcdbe4bc 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1668,6 +1668,17 @@ -(void)remove
    -------------------------------------------------------------------------- */
 {
   NSTRACE ("x_destroy_window");
+
+  /* If this frame has a parent window, detach it as not doing so can
+     cause a crash in GNUStep. */
+  if (FRAME_PARENT_FRAME (f) != NULL)
+    {
+      NSWindow *child = [FRAME_NS_VIEW (f) window];
+      NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
+
+      [parent removeChildWindow: child];
+    }
+
   check_window_system (f);
   x_free_frame_resources (f);
   ns_window_num--;
@@ -1706,14 +1717,18 @@ -(void)remove
            - FRAME_TOOLBAR_HEIGHT (f))
         : f->top_pos;
 #ifdef NS_IMPL_GNUSTEP
-      if (f->left_pos < 100)
-        f->left_pos = 100;  /* don't overlap menu */
+      if (FRAME_PARENT_FRAME (f) == NULL)
+	{
+	  if (f->left_pos < 100)
+	    f->left_pos = 100;  /* don't overlap menu */
+	}
 #endif
       /* Constrain the setFrameTopLeftPoint so we don't move behind the
          menu bar.  */
-      NSPoint pt = NSMakePoint (SCREENMAXBOUND (f->left_pos),
-                                SCREENMAXBOUND ([fscreen frame].size.height
-                                                - NS_TOP_POS (f)));
+      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);
@@ -1738,7 +1753,6 @@ -(void)remove
   EmacsView *view = FRAME_NS_VIEW (f);
   NSWindow *window = [view window];
   NSRect wr = [window frame];
-  int tb = FRAME_EXTERNAL_TOOL_BAR (f);
   int pixelwidth, pixelheight;
   int orig_height = wr.size.height;
 
@@ -1764,25 +1778,6 @@ -(void)remove
       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
     }
 
-  /* If we have a toolbar, take its height into account. */
-  if (tb && ! [view isFullscreen])
-    {
-    /* NOTE: previously this would generate wrong result if toolbar not
-             yet displayed and fixing toolbar_height=32 helped, but
-             now (200903) seems no longer needed */
-    FRAME_TOOLBAR_HEIGHT (f) =
-      NSHeight ([window frameRectForContentRect: NSMakeRect (0, 0, 0, 0)])
-        - FRAME_NS_TITLEBAR_HEIGHT (f);
-#if 0
-      /* Only breaks things here, removed by martin 2015-09-30.  */
-#ifdef NS_IMPL_GNUSTEP
-      FRAME_TOOLBAR_HEIGHT (f) -= 3;
-#endif
-#endif
-    }
-  else
-    FRAME_TOOLBAR_HEIGHT (f) = 0;
-
   wr.size.width = pixelwidth + f->border_width;
   wr.size.height = pixelheight;
   if (! [view isFullscreen])
@@ -1811,6 +1806,150 @@ -(void)remove
   unblock_input ();
 }
 
+#ifdef NS_IMPL_COCOA
+void
+x_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
+/* --------------------------------------------------------------------------
+     Set frame F's `undecorated' parameter.  If non-nil, F's window-system
+     window is drawn without decorations, title, minimize/maximize boxes
+     and external borders.  This usually means that the window cannot be
+     dragged, resized, iconified, maximized or deleted with the mouse.  If
+     nil, draw the frame with all the elements listed above unless these
+     have been suspended via window manager settings.
+
+     GNUStep cannot change an existing window's style.
+   -------------------------------------------------------------------------- */
+{
+  EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
+  NSWindow *window = [view window];
+
+  if (!EQ (new_value, old_value))
+    {
+      block_input ();
+
+      if (NILP (new_value))
+        {
+          FRAME_UNDECORATED (f) = false;
+          [window setStyleMask: ((window.styleMask
+                                  | NSWindowStyleMaskTitled
+                                  | NSWindowStyleMaskResizable
+                                  | NSWindowStyleMaskMiniaturizable
+                                  | NSWindowStyleMaskClosable)
+                                 ^ NSWindowStyleMaskBorderless)];
+
+          [view createToolbar: f];
+        }
+      else
+        {
+          [window setToolbar: nil];
+          /* Do I need to release the toolbar here? */
+
+          FRAME_UNDECORATED (f) = true;
+          [window setStyleMask: ((window.styleMask | NSWindowStyleMaskBorderless)
+                                 ^ (NSWindowStyleMaskTitled
+                                    | NSWindowStyleMaskResizable
+                                    | NSWindowStyleMaskMiniaturizable
+                                    | NSWindowStyleMaskClosable))];
+        }
+
+      /* At this point it seems we don't have an active NSResponder,
+         so some key presses (TAB) are swallowed by the system. */
+      [window makeFirstResponder: view];
+
+      [view updateFrameSize: NO];
+      unblock_input ();
+    }
+}
+#endif /* NS_IMPL_COCOA */
+
+void
+x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
+/* --------------------------------------------------------------------------
+     Set frame F's `parent-frame' parameter.  If non-nil, make F a child
+     frame of the frame specified by that parameter.  Technically, this
+     makes F's window-system window a child window of the parent frame's
+     window-system window.  If nil, make F's window-system window a
+     top-level window--a child of its display's root window.
+
+     A child frame's `left' and `top' parameters specify positions
+     relative to the top-left corner of its parent frame's native
+     rectangle.  On macOS moving a parent frame moves all its child
+     frames too, keeping their position relative to the parent
+     unaltered.  When a parent frame is iconified or made invisible, its
+     child frames are made invisible.  When a parent frame is deleted,
+     its child frames are deleted too.
+
+     Whether a child frame has a tool bar may be window-system or window
+     manager dependent.  It's advisable to disable it via the frame
+     parameter settings.
+
+     Some window managers may not honor this parameter.
+   -------------------------------------------------------------------------- */
+{
+  struct frame *p = NULL;
+  NSWindow *parent, *child;
+
+  if (!NILP (new_value)
+      && (!FRAMEP (new_value)
+	  || !FRAME_LIVE_P (p = XFRAME (new_value))
+	  || !FRAME_X_P (p)))
+    {
+      store_frame_param (f, Qparent_frame, old_value);
+      error ("Invalid specification of `parent-frame'");
+    }
+
+  if (p != FRAME_PARENT_FRAME (f))
+    {
+      parent = [FRAME_NS_VIEW (p) window];
+      child = [FRAME_NS_VIEW (f) window];
+
+      block_input ();
+      [parent addChildWindow: child
+                     ordered: NSWindowAbove];
+      unblock_input ();
+
+      fset_parent_frame (f, new_value);
+    }
+}
+
+void
+x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
+/* Set frame F's `z-group' parameter.  If `above', F's window-system
+   window is displayed above all windows that do not have the `above'
+   property set.  If nil, F's window is shown below all windows that
+   have the `above' property set and above all windows that have the
+   `below' property set.  If `below', F's window is displayed below
+   all windows that do.
+
+   Some window managers may not honor this parameter. */
+{
+  EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
+  NSWindow *window = [view window];
+
+  if (NILP (new_value))
+    {
+      window.level = NSNormalWindowLevel;
+      FRAME_Z_GROUP (f) = z_group_none;
+    }
+  else if (EQ (new_value, Qabove))
+    {
+      window.level = NSNormalWindowLevel + 1;
+      FRAME_Z_GROUP (f) = z_group_above;
+    }
+  else if (EQ (new_value, Qabove_suspended))
+    {
+      /* Not sure what level this should be. */
+      window.level = NSNormalWindowLevel + 1;
+      FRAME_Z_GROUP (f) = z_group_above_suspended;
+    }
+  else if (EQ (new_value, Qbelow))
+    {
+      window.level = NSNormalWindowLevel - 1;
+      FRAME_Z_GROUP (f) = z_group_below;
+    }
+  else
+    error ("Invalid z-group specification");
+}
 
 static void
 ns_fullscreen_hook (struct frame *f)
@@ -2683,7 +2822,7 @@ -(void)remove
 
           for (i = 0; i < full_height; i++)
             cbits[i] = bits[i];
-          img = [[EmacsImage alloc] initFromXBM: cbits width: 8
+          img = [[EmacsImage alloc] XBM: cbits width: 8
                                          height: full_height
                                              fg: 0 bg: 0];
           bimgs[p->which - 1] = img;
@@ -6399,7 +6538,8 @@ - (void) updateFrameSize: (BOOL) delay;
   newh = (int)wr.size.height - extra;
 
   NSTRACE_SIZE ("New size", NSMakeSize (neww, newh));
-  NSTRACE_MSG ("tool_bar_height: %d", emacsframe->tool_bar_height);
+  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);
@@ -6424,9 +6564,11 @@ - (void) updateFrameSize: (BOOL) delay;
       SET_FRAME_GARBAGED (emacsframe);
       cancel_mouse_face (emacsframe);
 
-      wr = NSMakeRect (0, 0, neww, newh);
+      /* The next two lines appear to be setting the frame to the same
+         size as it already is.  Why are they there? */
+      // wr = NSMakeRect (0, 0, neww, newh);
 
-      [view setFrame: wr];
+      // [view setFrame: wr];
 
       // to do: consider using [NSNotificationCenter postNotificationName:].
       [self windowDidMove: // Update top/left.
@@ -6489,7 +6631,8 @@ - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
             old_title = 0;
           }
       }
-    else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize)
+    else if (fs_state == FULLSCREEN_NONE && ! maximizing_resize
+             && [[self window] titleVisibility])
       {
         char *size_title;
         NSWindow *window = [self window];
@@ -6692,6 +6835,34 @@ - (BOOL)isOpaque
 }

+- (void)createToolbar: (struct frame *)f
+{
+  EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
+  NSWindow *window = [view window];
+
+  toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
+                   [NSString stringWithFormat: @"Emacs Frame %d",
+                             ns_window_num]];
+  [toolbar setVisible: NO];
+  [window setToolbar: toolbar];
+
+  /* Don't set frame garbaged until tool bar is up to date?
+     This avoids an extra clear and redraw (flicker) at frame creation.  */
+  if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
+  else wait_for_tool_bar = NO;
+
+
+#ifdef NS_IMPL_COCOA
+  {
+    NSButton *toggleButton;
+    toggleButton = [window standardWindowButton: NSWindowToolbarButton];
+    [toggleButton setTarget: self];
+    [toggleButton setAction: @selector (toggleToolbar: )];
+  }
+#endif
+}
+
+
 - initFrameFromEmacs: (struct frame *)f
 {
   NSRect r, wr;
@@ -6729,14 +6900,14 @@ - (BOOL)isOpaque
   maximizing_resize = NO;
 #endif
 
-  win = [[EmacsWindow alloc]
+  win = [[EmacsFSWindow alloc]
             initWithContentRect: r
-                      styleMask: (NSWindowStyleMaskResizable |
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
-                                  NSWindowStyleMaskTitled |
-#endif
-                                  NSWindowStyleMaskMiniaturizable |
-                                  NSWindowStyleMaskClosable)
+                      styleMask: (FRAME_UNDECORATED (f)
+                                  ? NSWindowStyleMaskBorderless
+                                  : NSWindowStyleMaskTitled
+                                  | NSWindowStyleMaskResizable
+                                  | NSWindowStyleMaskMiniaturizable
+                                  | NSWindowStyleMaskClosable)
                         backing: NSBackingStoreBuffered
                           defer: YES];
 
@@ -6746,7 +6917,6 @@ - (BOOL)isOpaque
 
   wr = [win frame];
   bwidth = f->border_width = wr.size.width - r.size.width;
-  tibar_height = FRAME_NS_TITLEBAR_HEIGHT (f) = wr.size.height - r.size.height;
 
   [win setAcceptsMouseMovedEvents: YES];
   [win setDelegate: self];
@@ -6766,42 +6936,36 @@ - (BOOL)isOpaque
   [win setTitle: name];
 
   /* toolbar support */
-  toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier:
-                         [NSString stringWithFormat: @"Emacs Frame %d",
-                                   ns_window_num]];
-  [win setToolbar: toolbar];
-  [toolbar setVisible: NO];
-
-  /* Don't set frame garbaged until tool bar is up to date?
-     This avoids an extra clear and redraw (flicker) at frame creation.  */
-  if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES;
-  else wait_for_tool_bar = NO;
-
-
-#ifdef NS_IMPL_COCOA
-  {
-    NSButton *toggleButton;
-  toggleButton = [win standardWindowButton: NSWindowToolbarButton];
-  [toggleButton setTarget: self];
-  [toggleButton setAction: @selector (toggleToolbar: )];
-  }
-#endif
-  FRAME_TOOLBAR_HEIGHT (f) = 0;
+  if (! FRAME_UNDECORATED (f))
+    [self createToolbar: f];
 
   tem = f->icon_name;
   if (!NILP (tem))
     [win setMiniwindowTitle:
            [NSString stringWithUTF8String: SSDATA (tem)]];
 
+  if (FRAME_PARENT_FRAME (f) != NULL)
+    {
+      NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
+      [parent addChildWindow: win
+                     ordered: NSWindowAbove];
+    }
+
+  if (!NILP (FRAME_Z_GROUP (f)))
+      win.level = NSNormalWindowLevel
+        + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1);
+
   {
     NSScreen *screen = [win screen];
 
     if (screen != 0)
       {
         NSPoint pt = NSMakePoint
-          (IN_BOUND (-SCREENMAX, f->left_pos, SCREENMAX),
+          (IN_BOUND (-SCREENMAX, f->left_pos
+                     + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX),
            IN_BOUND (-SCREENMAX,
-                     [screen frame].size.height - NS_TOP_POS (f), SCREENMAX));
+                     NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos,
+                     SCREENMAX));
 
         [win setFrameTopLeftPoint: pt];
 
@@ -6843,9 +7007,15 @@ - (void)windowDidMove: sender
     return;
   if (screen != nil)
     {
-      emacsframe->left_pos = r.origin.x;
+      emacsframe->left_pos = r.origin.x - NS_PARENT_WINDOW_LEFT_POS (emacsframe);
       emacsframe->top_pos =
-        [screen frame].size.height - (r.origin.y + r.size.height);
+        NS_PARENT_WINDOW_TOP_POS (emacsframe) - (r.origin.y + r.size.height);
+
+      if (emacs_event)
+        {
+          emacs_event->kind = MOVE_FRAME_EVENT;
+          EV_TRAILER ((id)nil);
+        }
     }
 }
 
@@ -7262,9 +7432,6 @@ - (void)toggleFullScreen: (id)sender
         [fw setOpaque: NO];
 
       f->border_width = 0;
-      FRAME_NS_TITLEBAR_HEIGHT (f) = 0;
-      tobar_height = FRAME_TOOLBAR_HEIGHT (f);
-      FRAME_TOOLBAR_HEIGHT (f) = 0;
 
       nonfs_window = w;
 
@@ -7298,9 +7465,6 @@ - (void)toggleFullScreen: (id)sender
         [w setOpaque: NO];
 
       f->border_width = bwidth;
-      FRAME_NS_TITLEBAR_HEIGHT (f) = tibar_height;
-      if (FRAME_EXTERNAL_TOOL_BAR (f))
-        FRAME_TOOLBAR_HEIGHT (f) = tobar_height;
 
       // to do: consider using [NSNotificationCenter postNotificationName:] to send notifications.
 
-- 
2.12.0