]> code.delx.au - gnu-emacs/blobdiff - src/nsfns.m
Rework C source files to avoid ^(
[gnu-emacs] / src / nsfns.m
index f8863e6d40015eb28ff2d72b8a33cdd739990ed4..ea099081a7ff3480276ff919b8df3695b5a2521d 100644 (file)
@@ -1,14 +1,14 @@
 /* Functions for the NeXT/Open/GNUstep and MacOSX window system.
 
-Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2015 Free Software
+Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2016 Free Software
 Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -49,13 +49,6 @@ GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
 #include "macfont.h"
 #endif
 
-#if 0
-int fns_trace_num = 1;
-#define NSTRACE(x)        fprintf (stderr, "%s:%d: [%d] " #x "\n",        \
-                                  __FILE__, __LINE__, ++fns_trace_num)
-#else
-#define NSTRACE(x)
-#endif
 
 #ifdef HAVE_NS
 
@@ -70,9 +63,7 @@ Lisp_Object Fx_open_connection (Lisp_Object, Lisp_Object, Lisp_Object);
 static Lisp_Object as_script, *as_result;
 static int as_status;
 
-#ifdef GLYPH_DEBUG
 static ptrdiff_t image_cache_refcount;
-#endif
 
 
 /* ==========================================================================
@@ -366,7 +357,7 @@ static void
 x_set_icon_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 {
   NSView *view = FRAME_NS_VIEW (f);
-  NSTRACE (x_set_icon_name);
+  NSTRACE ("x_set_icon_name");
 
   /* see if it's changed */
   if (STRINGP (arg))
@@ -410,17 +401,16 @@ x_set_icon_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 static void
 ns_set_name_internal (struct frame *f, Lisp_Object name)
 {
-  struct gcpro gcpro1;
   Lisp_Object encoded_name, encoded_icon_name;
   NSString *str;
   NSView *view = FRAME_NS_VIEW (f);
 
-  GCPRO1 (name);
+
   encoded_name = ENCODE_UTF_8 (name);
-  UNGCPRO;
 
   str = [NSString stringWithUTF8String: SSDATA (encoded_name)];
 
+
   /* Don't change the name if it's already NAME.  */
   if (! [[[view window] title] isEqualToString: str])
     [[view window] setTitle: str];
@@ -441,7 +431,7 @@ ns_set_name_internal (struct frame *f, Lisp_Object name)
 static void
 ns_set_name (struct frame *f, Lisp_Object name, int explicit)
 {
-  NSTRACE (ns_set_name);
+  NSTRACE ("ns_set_name");
 
   /* Make sure that requests from lisp code override requests from
      Emacs redisplay code.  */
@@ -482,7 +472,7 @@ ns_set_name (struct frame *f, Lisp_Object name, int explicit)
 static void
 x_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 {
-  NSTRACE (x_explicitly_set_name);
+  NSTRACE ("x_explicitly_set_name");
   ns_set_name (f, arg, 1);
 }
 
@@ -493,11 +483,16 @@ x_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 void
 x_implicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 {
-  NSTRACE (x_implicitly_set_name);
+  NSTRACE ("x_implicitly_set_name");
+
+  Lisp_Object frame_title = buffer_local_value
+    (Qframe_title_format, XWINDOW (f->selected_window)->contents);
+  Lisp_Object icon_title = buffer_local_value
+    (Qicon_title_format, XWINDOW (f->selected_window)->contents);
 
   /* Deal with NS specific format t.  */
-  if (FRAME_NS_P (f) && ((FRAME_ICONIFIED_P (f) && EQ (Vicon_title_format, Qt))
-                         || EQ (Vframe_title_format, Qt)))
+  if (FRAME_NS_P (f) && ((FRAME_ICONIFIED_P (f) && EQ (icon_title, Qt))
+                         || EQ (frame_title, Qt)))
     ns_set_name_as_filename (f);
   else
     ns_set_name (f, arg, 0);
@@ -510,7 +505,7 @@ x_implicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 static void
 x_set_title (struct frame *f, Lisp_Object name, Lisp_Object old_name)
 {
-  NSTRACE (x_set_title);
+  NSTRACE ("x_set_title");
   /* Don't change the title if it's already NAME.  */
   if (EQ (name, f->title))
     return;
@@ -536,10 +531,9 @@ ns_set_name_as_filename (struct frame *f)
   Lisp_Object buf = XWINDOW (f->selected_window)->contents;
   const char *title;
   NSAutoreleasePool *pool;
-  struct gcpro gcpro1;
   Lisp_Object encoded_name, encoded_filename;
   NSString *str;
-  NSTRACE (ns_set_name_as_filename);
+  NSTRACE ("ns_set_name_as_filename");
 
   if (f->explicit_name || ! NILP (f->title))
     return;
@@ -557,9 +551,7 @@ ns_set_name_as_filename (struct frame *f)
         name = build_string ([ns_app_name UTF8String]);
     }
 
-  GCPRO1 (name);
   encoded_name = ENCODE_UTF_8 (name);
-  UNGCPRO;
 
   view = FRAME_NS_VIEW (f);
 
@@ -584,9 +576,7 @@ ns_set_name_as_filename (struct frame *f)
 
       if (! NILP (filename))
         {
-          GCPRO1 (filename);
           encoded_filename = ENCODE_UTF_8 (filename);
-          UNGCPRO;
 
           fstr = [NSString stringWithUTF8String: SSDATA (encoded_filename)];
           if (fstr == nil) fstr = @"";
@@ -665,8 +655,15 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
 void
 x_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
 {
+  /* Currently, when the tool bar change state, the frame is resized.
+
+     TODO: It would be better if this didn't occur when 1) the frame
+     is full height or maximized or 2) when specified by
+     `frame-inhibit-implied-resize'. */
   int nlines;
 
+  NSTRACE ("x_set_tool_bar_lines");
+
   if (FRAME_MINIBUF_ONLY_P (f))
     return;
 
@@ -686,10 +683,40 @@ x_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
         {
           free_frame_tool_bar (f);
           FRAME_EXTERNAL_TOOL_BAR (f) = 0;
-        }
+
+          {
+            EmacsView *view = FRAME_NS_VIEW (f);
+            int fs_state = [view fullscreenState];
+
+            if (fs_state == FULLSCREEN_MAXIMIZED)
+              {
+                [view setFSValue:FULLSCREEN_WIDTH];
+              }
+            else if (fs_state == FULLSCREEN_HEIGHT)
+              {
+                [view setFSValue:FULLSCREEN_NONE];
+              }
+          }
+       }
     }
 
-  x_set_window_size (f, 0, f->text_cols, f->text_lines, 0);
+  {
+    int inhibit
+      = ((f->after_make_frame
+         && !f->tool_bar_resized
+         && (EQ (frame_inhibit_implied_resize, Qt)
+             || (CONSP (frame_inhibit_implied_resize)
+                 && !NILP (Fmemq (Qtool_bar_lines,
+                                  frame_inhibit_implied_resize))))
+         && NILP (get_frame_param (f, Qfullscreen)))
+        ? 0
+        : 2);
+
+    NSTRACE_MSG ("inhibit:%d", inhibit);
+
+    frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
+    adjust_frame_size (f, -1, -1, inhibit, 0, Qtool_bar_lines);
+  }
 }
 
 
@@ -723,7 +750,7 @@ ns_implicitly_set_icon_type (struct frame *f)
   NSAutoreleasePool *pool;
   BOOL setMini = YES;
 
-  NSTRACE (ns_implicitly_set_icon_type);
+  NSTRACE ("ns_implicitly_set_icon_type");
 
   block_input ();
   pool = [[NSAutoreleasePool alloc] init];
@@ -749,7 +776,7 @@ ns_implicitly_set_icon_type (struct frame *f)
        chain = XCDR (chain))
     {
       elt = XCAR (chain);
-      /* special case: 't' means go by file type */
+      /* special case: t means go by file type */
       if (SYMBOLP (elt) && EQ (elt, Qt) && SSDATA (f->name)[0] == '/')
         {
           NSString *str
@@ -791,7 +818,7 @@ x_set_icon_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
   id image = nil;
   BOOL setMini = YES;
 
-  NSTRACE (x_set_icon_type);
+  NSTRACE ("x_set_icon_type");
 
   if (!NILP (arg) && SYMBOLP (arg))
     {
@@ -1005,10 +1032,21 @@ unwind_create_frame (Lisp_Object frame)
       struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
 #endif
 
+      /* If the frame's image cache refcount is still the same as our
+        private shadow variable, it means we are unwinding a frame
+        for which we didn't yet call init_frame_faces, where the
+        refcount is incremented.  Therefore, we increment it here, so
+        that free_frame_faces, called in x_free_frame_resources
+        below, will not mistakenly decrement the counter that was not
+        incremented yet to account for this new frame.  */
+      if (FRAME_IMAGE_CACHE (f) != NULL
+         && FRAME_IMAGE_CACHE (f)->refcount == image_cache_refcount)
+       FRAME_IMAGE_CACHE (f)->refcount++;
+
       x_free_frame_resources (f);
       free_glyphs (f);
 
-#ifdef GLYPH_DEBUG
+#if defined GLYPH_DEBUG && defined ENABLE_CHECKING
       /* Check that reference counts are indeed correct.  */
       eassert (dpyinfo->terminal->image_cache->refcount == image_cache_refcount);
 #endif
@@ -1075,14 +1113,13 @@ This function is an internal primitive--use `make-frame' instead.  */)
   Lisp_Object name;
   int minibuffer_only = 0;
   long window_prompting = 0;
-  int width, height;
   ptrdiff_t count = specpdl_ptr - specpdl;
-  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
   Lisp_Object display;
   struct ns_display_info *dpyinfo = NULL;
   Lisp_Object parent;
   struct kboard *kb;
   static int desc_ctr = 1;
+  int x_width = 0, x_height = 0;
 
   /* x_get_arg modifies parms.  */
   parms = Fcopy_alist (parms);
@@ -1119,7 +1156,6 @@ This function is an internal primitive--use `make-frame' instead.  */)
   /* No need to protect DISPLAY because that's not used after passing
      it to make_frame_without_minibuffer.  */
   frame = Qnil;
-  GCPRO4 (parms, parent, name, frame);
   tem = x_get_arg (dpyinfo, parms, Qminibuffer, "minibuffer", "Minibuffer",
                   RES_TYPE_SYMBOL);
   if (EQ (tem, Qnone) || NILP (tem))
@@ -1188,6 +1224,9 @@ This function is an internal primitive--use `make-frame' instead.  */)
     register_font_driver (&nsfont_driver, f);
 #endif
 
+  image_cache_refcount =
+    FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
+
   x_default_parameter (f, parms, Qfont_backend, Qnil,
                        "fontBackend", "FontBackend", RES_TYPE_STRING);
 
@@ -1242,11 +1281,6 @@ This function is an internal primitive--use `make-frame' instead.  */)
   x_default_parameter (f, parms, Qright_fringe, Qnil,
                       "rightFringe", "RightFringe", RES_TYPE_NUMBER);
 
-#ifdef GLYPH_DEBUG
-  image_cache_refcount =
-    FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
-#endif
-
   init_frame_faces (f);
 
   /* Read comment about this code in corresponding place in xfns.c.  */
@@ -1272,7 +1306,7 @@ This function is an internal primitive--use `make-frame' instead.  */)
                        RES_TYPE_STRING);
 
   parms = get_geometry_from_preferences (dpyinfo, parms);
-  window_prompting = x_figure_window_size (f, parms, 1);
+  window_prompting = x_figure_window_size (f, parms, true, &x_width, &x_height);
 
   tem = x_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, RES_TYPE_BOOLEAN);
   f->no_split = minibuffer_only || (!EQ (tem, Qunbound) && !EQ (tem, Qnil));
@@ -1292,6 +1326,8 @@ This function is an internal primitive--use `make-frame' instead.  */)
      = [NSCursor arrowCursor];
   f->output_data.ns->current_pointer = f->output_data.ns->text_cursor;
 
+  f->output_data.ns->in_animation = NO;
+
   [[EmacsView alloc] initFrameFromEmacs: f];
 
   x_icon (f, parms);
@@ -1326,6 +1362,11 @@ This function is an internal primitive--use `make-frame' instead.  */)
   /* Allow x_set_window_size, now.  */
   f->can_x_set_window_size = true;
 
+  if (x_width > 0)
+    SET_FRAME_WIDTH (f, x_width);
+  if (x_height > 0)
+    SET_FRAME_HEIGHT (f, x_height);
+
   adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), 0, 1,
                     Qx_create_frame_2);
 
@@ -1362,8 +1403,6 @@ This function is an internal primitive--use `make-frame' instead.  */)
     if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem))))
       fset_param_alist (f, Fcons (XCAR (tem), f->param_alist));
 
-  UNGCPRO;
-
   if (window_prompting & USPosition)
     x_set_offset (f, f->left_pos, f->top_pos, 1);
 
@@ -1455,6 +1494,15 @@ ns_run_file_dialog (void)
   ns_fd_data.panel = nil;
 }
 
+#ifdef NS_IMPL_COCOA
+#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_9
+#define MODAL_OK_RESPONSE NSModalResponseOK
+#endif
+#endif
+#ifndef MODAL_OK_RESPONSE
+#define MODAL_OK_RESPONSE NSOKButton
+#endif
+
 DEFUN ("ns-read-file-name", Fns_read_file_name, Sns_read_file_name, 1, 5, 0,
        doc: /* Use a graphical panel to read a file name, using prompt PROMPT.
 Optional arg DIR, if non-nil, supplies a default directory.
@@ -1466,10 +1514,9 @@ Optional arg DIR_ONLY_P, if non-nil, means choose only directories.  */)
    Lisp_Object init, Lisp_Object dir_only_p)
 {
   static id fileDelegate = nil;
-  BOOL ret;
   BOOL isSave = NILP (mustmatch) && NILP (dir_only_p);
   id panel;
-  Lisp_Object fname;
+  Lisp_Object fname = Qnil;
 
   NSString *promptS = NILP (prompt) || !STRINGP (prompt) ? nil :
     [NSString stringWithUTF8String: SSDATA (prompt)];
@@ -1549,20 +1596,17 @@ Optional arg DIR_ONLY_P, if non-nil, means choose only directories.  */)
   while (ns_fd_data.panel != nil)
     [NSApp run];
 
-  ret = (ns_fd_data.ret == NSOKButton);
-
-  if (ret)
+  if (ns_fd_data.ret == MODAL_OK_RESPONSE)
     {
       NSString *str = ns_filename_from_panel (panel);
       if (! str) str = ns_directory_from_panel (panel);
-      if (! str) ret = NO;
-      else fname = build_string ([str UTF8String]);
+      if (str) fname = build_string ([str UTF8String]);
     }
 
   [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
   unblock_input ();
 
-  return ret ? fname : Qnil;
+  return fname;
 }
 
 const char *
@@ -1686,7 +1730,7 @@ If omitted or nil, that stands for the selected frame's display.
 
 Note: "screen" here is not in Nextstep terminology but in X11's.  For
 the number of physical monitors, use `(length
-(display-monitor-attributes-list TERMINAL))' instead.  */)
+\(display-monitor-attributes-list TERMINAL))' instead.  */)
   (Lisp_Object terminal)
 {
   check_ns_display_info (terminal);
@@ -2055,39 +2099,6 @@ there was no result.  */)
 }
 
 
-DEFUN ("ns-convert-utf8-nfd-to-nfc", Fns_convert_utf8_nfd_to_nfc,
-       Sns_convert_utf8_nfd_to_nfc, 1, 1, 0,
-       doc: /* Return an NFC string that matches the UTF-8 NFD string STR.  */)
-     (Lisp_Object str)
-{
-/* TODO: If GNUstep ever implements precomposedStringWithCanonicalMapping,
-         remove this. */
-  NSString *utfStr;
-  Lisp_Object ret = Qnil;
-  NSAutoreleasePool *pool;
-
-  CHECK_STRING (str);
-  pool = [[NSAutoreleasePool alloc] init];
-  utfStr = [NSString stringWithUTF8String: SSDATA (str)];
-#ifdef NS_IMPL_COCOA
-  if (utfStr)
-    utfStr = [utfStr precomposedStringWithCanonicalMapping];
-#endif
-  if (utfStr)
-    {
-      const char *cstr = [utfStr UTF8String];
-      if (cstr)
-        ret = build_string (cstr);
-    }
-
-  [pool release];
-  if (NILP (ret))
-    error ("Invalid UTF-8");
-
-  return ret;
-}
-
-
 #ifdef NS_IMPL_COCOA
 
 /* Compile and execute the AppleScript SCRIPT and return the error
@@ -2662,7 +2673,7 @@ compute_tip_xy (struct frame *f,
                 int *root_x,
                 int *root_y)
 {
-  Lisp_Object left, top;
+  Lisp_Object left, top, right, bottom;
   EmacsView *view = FRAME_NS_VIEW (f);
   struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
   NSPoint pt;
@@ -2670,25 +2681,38 @@ compute_tip_xy (struct frame *f,
   /* Start with user-specified or mouse position.  */
   left = Fcdr (Fassq (Qleft, parms));
   top = Fcdr (Fassq (Qtop, parms));
+  right = Fcdr (Fassq (Qright, parms));
+  bottom = Fcdr (Fassq (Qbottom, parms));
 
-  if (!INTEGERP (left) || !INTEGERP (top))
+  if ((!INTEGERP (left) && !INTEGERP (right))
+      || (!INTEGERP (top) && !INTEGERP (bottom)))
     {
       pt.x = dpyinfo->last_mouse_motion_x;
       pt.y = dpyinfo->last_mouse_motion_y;
       /* Convert to screen coordinates */
       pt = [view convertPoint: pt toView: nil];
+#if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
       pt = [[view window] convertBaseToScreen: pt];
+#else
+      {
+        NSRect r = NSMakeRect (pt.x, pt.y, 0, 0);
+        r = [[view window] convertRectToScreen: r];
+        pt.x = r.origin.x;
+        pt.y = r.origin.y;
+      }
+#endif
     }
   else
     {
       /* Absolute coordinates.  */
-      pt.x = XINT (left);
-      pt.y = x_display_pixel_height (FRAME_DISPLAY_INFO (f)) - XINT (top)
-        - height;
+      pt.x = INTEGERP (left) ? XINT (left) : XINT (right);
+      pt.y = (x_display_pixel_height (FRAME_DISPLAY_INFO (f))
+             - (INTEGERP (top) ? XINT (top) : XINT (bottom))
+             - height);
     }
 
   /* Ensure in bounds.  (Note, screen origin = lower left.) */
-  if (INTEGERP (left))
+  if (INTEGERP (left) || INTEGERP (right))
     *root_x = pt.x;
   else if (pt.x + XINT (dx) <= 0)
     *root_x = 0; /* Can happen for negative dx */
@@ -2703,7 +2727,7 @@ compute_tip_xy (struct frame *f,
     /* Put it left justified on the screen -- it ought to fit that way.  */
     *root_x = 0;
 
-  if (INTEGERP (top))
+  if (INTEGERP (top) || INTEGERP (bottom))
     *root_y = pt.y;
   else if (pt.y - XINT (dy) - height >= 0)
     /* It fits below the pointer.  */
@@ -2733,19 +2757,24 @@ Automatically hide the tooltip after TIMEOUT seconds.  TIMEOUT nil
 means use the default timeout of 5 seconds.
 
 If the list of frame parameters PARMS contains a `left' parameter,
-the tooltip is displayed at that x-position.  Otherwise it is
-displayed at the mouse position, with offset DX added (default is 5 if
-DX isn't specified).  Likewise for the y-position; if a `top' frame
-parameter is specified, it determines the y-position of the tooltip
-window, otherwise it is displayed at the mouse position, with offset
-DY added (default is -10).
+display the tooltip at that x-position.  If the list of frame parameters
+PARMS contains no `left' but a `right' parameter, display the tooltip
+right-adjusted at that x-position. Otherwise display it at the
+x-position of the mouse, with offset DX added (default is 5 if DX isn't
+specified).
+
+Likewise for the y-position: If a `top' frame parameter is specified, it
+determines the position of the upper edge of the tooltip window.  If a
+`bottom' parameter but no `top' frame parameter is specified, it
+determines the position of the lower edge of the tooltip window.
+Otherwise display the tooltip window at the y-position of the mouse,
+with offset DY added (default is -10).
 
 A tooltip's maximum size is specified by `x-max-tooltip-size'.
 Text larger than the specified size is clipped.  */)
      (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy)
 {
   int root_x, root_y;
-  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
   ptrdiff_t count = SPECPDL_INDEX ();
   struct frame *f;
   char *str;
@@ -2753,8 +2782,6 @@ Text larger than the specified size is clipped.  */)
 
   specbind (Qinhibit_redisplay, Qt);
 
-  GCPRO4 (string, parms, frame, timeout);
-
   CHECK_STRING (string);
   str = SSDATA (string);
   f = decode_window_system_frame (frame);
@@ -2790,7 +2817,6 @@ Text larger than the specified size is clipped.  */)
   [ns_tooltip showAtX: root_x Y: root_y for: XINT (timeout)];
   unblock_input ();
 
-  UNGCPRO;
   return unbind_to (count, Qnil);
 }
 
@@ -2806,87 +2832,143 @@ Value is t if tooltip was open, nil otherwise.  */)
   return Qt;
 }
 
-DEFUN ("x-frame-geometry", Fx_frame_geometry, Sx_frame_geometry, 0, 1, 0,
-       doc: /* Return geometric attributes of frame FRAME.
-
-FRAME must be a live frame and defaults to the selected one.
-
-The return value is an association list containing the following
-elements (all size values are in pixels).
-
-- `frame-outer-size' is a cons of the outer width and height of FRAME.
-  The outer size include the title bar and the external borders as well
-  as any menu and/or tool bar of frame.
-
-- `border' is a cons of the horizontal and vertical width of FRAME's
-  external borders.
-
-- `title-bar-height' is the height of the title bar of FRAME.
-
-- `menu-bar-external' if `t' means the menu bar is external (not
+/* Return geometric attributes of FRAME.  According to the value of
+   ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner
+   edges of FRAME, the root window edges of frame (Qroot_edges).  Any
+   other value means to return the geometry as returned by
+   Fx_frame_geometry.  */
+static Lisp_Object
+frame_geometry (Lisp_Object frame, Lisp_Object attribute)
+{
+  struct frame *f = decode_live_frame (frame);
+  Lisp_Object fullscreen_symbol = Fframe_parameter (frame, Qfullscreen);
+  bool fullscreen = (EQ (fullscreen_symbol, Qfullboth)
+                    || EQ (fullscreen_symbol, Qfullscreen));
+  int border = fullscreen ? 0 : f->border_width;
+  int title_height = fullscreen ? 0 : FRAME_NS_TITLEBAR_HEIGHT (f);
+  int native_width = FRAME_PIXEL_WIDTH (f);
+  int native_height = FRAME_PIXEL_HEIGHT (f);
+  int outer_width = native_width + 2 * border;
+  int outer_height = native_height + 2 * border + title_height;
+  int native_left = f->left_pos + border;
+  int native_top = f->top_pos + border + title_height;
+  int native_right = f->left_pos + outer_width - border;
+  int native_bottom = f->top_pos + outer_height - border;
+  int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
+  int tool_bar_height = FRAME_TOOLBAR_HEIGHT (f);
+  int tool_bar_width = (tool_bar_height
+                       ? outer_width - 2 * internal_border_width
+                       : 0);
+
+  /* Construct list.  */
+  if (EQ (attribute, Qouter_edges))
+    return list4 (make_number (f->left_pos), make_number (f->top_pos),
+                 make_number (f->left_pos + outer_width),
+                 make_number (f->top_pos + outer_height));
+  else if (EQ (attribute, Qnative_edges))
+    return list4 (make_number (native_left), make_number (native_top),
+                 make_number (native_right), make_number (native_bottom));
+  else if (EQ (attribute, Qinner_edges))
+    return list4 (make_number (native_left + internal_border_width),
+                 make_number (native_top
+                              + tool_bar_height
+                              + internal_border_width),
+                 make_number (native_right - internal_border_width),
+                 make_number (native_bottom - internal_border_width));
+  else
+    return
+      listn (CONSTYPE_HEAP, 10,
+            Fcons (Qouter_position,
+                   Fcons (make_number (f->left_pos),
+                          make_number (f->top_pos))),
+            Fcons (Qouter_size,
+                   Fcons (make_number (outer_width),
+                          make_number (outer_height))),
+            Fcons (Qexternal_border_size,
+                   (fullscreen
+                    ? Fcons (make_number (0), make_number (0))
+                    : Fcons (make_number (border), make_number (border)))),
+            Fcons (Qtitle_bar_size,
+                   Fcons (make_number (0), make_number (title_height))),
+            Fcons (Qmenu_bar_external, Qnil),
+            Fcons (Qmenu_bar_size, Fcons (make_number (0), make_number (0))),
+            Fcons (Qtool_bar_external,
+                   FRAME_EXTERNAL_TOOL_BAR (f) ? Qt : Qnil),
+            Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
+            Fcons (Qtool_bar_size,
+                   Fcons (make_number (tool_bar_width),
+                          make_number (tool_bar_height))),
+            Fcons (Qinternal_border_width,
+                   make_number (internal_border_width)));
+}
+
+DEFUN ("ns-frame-geometry", Fns_frame_geometry, Sns_frame_geometry, 0, 1, 0,
+       doc: /* Return geometric attributes of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is an association list of the attributes listed below.  All height
+and width values are in pixels.
+
+`outer-position' is a cons of the outer left and top edges of FRAME
+  relative to the origin - the position (0, 0) - of FRAME's display.
+
+`outer-size' is a cons of the outer width and height of FRAME.  The
+  outer size includes the title bar and the external borders as well as
+  any menu and/or tool bar of frame.
+
+`external-border-size' is a cons of the horizontal and vertical width of
+  FRAME's external borders as supplied by the window manager.
+
+`title-bar-size' is a cons of the width and height of the title bar of
+  FRAME as supplied by the window manager.  If both of them are zero,
+  FRAME has no title bar.  If only the width is zero, Emacs was not
+  able to retrieve the width information.
+
+`menu-bar-external', if non-nil, means the menu bar is external (never
   included in the inner edges of FRAME).
 
-`menu-bar-size' is a cons of the width and height of the menu bar of
+`menu-bar-size' is a cons of the width and height of the menu bar of
   FRAME.
 
-- `tool-bar-external' if `t' means the tool bar is external (not
+`tool-bar-external', if non-nil, means the tool bar is external (never
   included in the inner edges of FRAME).
 
-- `tool-bar-side' tells tells on which side the tool bar on FRAME is and
-  can be one of `left', `top', `right' or `bottom'.
+`tool-bar-position' tells on which side the tool bar on FRAME is and can
+  be one of `left', `top', `right' or `bottom'.  If this is nil, FRAME
+  has no tool bar.
 
-`tool-bar-size' is a cons of the width and height of the tool bar of
+`tool-bar-size' is a cons of the width and height of the tool bar of
   FRAME.
 
-- `frame-inner-size' is a cons of the inner width and height of FRAME.
-  This excludes FRAME's title bar and external border as well as any
-  external menu and/or tool bar.  */)
+`internal-border-width' is the width of the internal border of
+  FRAME.  */)
   (Lisp_Object frame)
 {
-  struct frame *f = decode_live_frame (frame);
-  int inner_width = FRAME_PIXEL_WIDTH (f);
-  int inner_height = FRAME_PIXEL_HEIGHT (f);
-  Lisp_Object fullscreen = Fframe_parameter (frame, Qfullscreen);
-  int border = f->border_width;
-  int title = FRAME_NS_TITLEBAR_HEIGHT (f);
-  int outer_width = FRAME_PIXEL_WIDTH (f) + 2 * border;
-  int outer_height = FRAME_PIXEL_HEIGHT (f) + 2 * border;
-  int tool_bar_height = FRAME_TOOLBAR_HEIGHT (f);
-  int tool_bar_width = tool_bar_height > 0
-    ? outer_width - 2 * FRAME_INTERNAL_BORDER_WIDTH (f)
-    : 0;
-  // Always 0 on NS.
-  int menu_bar_height = 0;
-  int menu_bar_width = 0;
-
-  return
-    listn (CONSTYPE_HEAP, 10,
-          Fcons (Qframe_position,
-                 Fcons (make_number (f->left_pos), make_number (f->top_pos))),
-          Fcons (Qframe_outer_size,
-                 Fcons (make_number (outer_width), make_number (outer_height))),
-          Fcons (Qexternal_border_size,
-                 ((EQ (fullscreen, Qfullboth) || EQ (fullscreen, Qfullscreen))
-                  ? Fcons (make_number (0), make_number (0))
-                  : Fcons (make_number (border), make_number (border)))),
-           Fcons (Qtitle_height,
-                 ((EQ (fullscreen, Qfullboth) || EQ (fullscreen, Qfullscreen))
-                  ? make_number (0)
-                  : make_number (title))),
-          Fcons (Qmenu_bar_external, FRAME_EXTERNAL_MENU_BAR (f) ? Qt : Qnil),
-          Fcons (Qmenu_bar_size,
-                 Fcons (make_number (menu_bar_width),
-                        make_number (menu_bar_height))),
-          Fcons (Qtool_bar_external, FRAME_EXTERNAL_TOOL_BAR (f) ? Qt : Qnil),
-          Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
-          Fcons (Qtool_bar_size,
-                 Fcons (make_number (tool_bar_width),
-                        make_number (tool_bar_height))),
-          Fcons (Qframe_inner_size,
-                 Fcons (make_number (inner_width),
-                        make_number (inner_height))));
+  return frame_geometry (frame, Qnil);
 }
 
+DEFUN ("ns-frame-edges", Fns_frame_edges, Sns_frame_edges, 0, 2, 0,
+       doc: /* Return edge coordinates of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is a list of the form (LEFT, TOP, RIGHT, BOTTOM).  All values are
+in pixels relative to the origin - the position (0, 0) - of FRAME's
+display.
+
+If optional argument TYPE is the symbol `outer-edges', return the outer
+edges of FRAME.  The outer edges comprise the decorations of the window
+manager (like the title bar or external borders) as well as any external
+menu or tool bar of FRAME.  If optional argument TYPE is the symbol
+`native-edges' or nil, return the native edges of FRAME.  The native
+edges exclude the decorations of the window manager and any external
+menu or tool bar of FRAME.  If TYPE is the symbol `inner-edges', return
+the inner edges of FRAME.  These edges exclude title bar, any borders,
+menu bar or tool bar of FRAME.  */)
+  (Lisp_Object frame, Lisp_Object type)
+{
+  return frame_geometry (frame, ((EQ (type, Qouter_edges)
+                                 || EQ (type, Qinner_edges))
+                                ? type
+                                : Qnative_edges));
+}
 
 /* ==========================================================================
 
@@ -3026,6 +3108,8 @@ void
 syms_of_nsfns (void)
 {
   DEFSYM (Qfontsize, "fontsize");
+  DEFSYM (Qframe_title_format, "frame-title-format");
+  DEFSYM (Qicon_title_format, "icon-title-format");
 
   DEFVAR_LISP ("ns-icon-type-alist", Vns_icon_type_alist,
                doc: /* Alist of elements (REGEXP . IMAGE) for images of icons associated to frames.
@@ -3070,7 +3154,8 @@ be used as the image of the icon representing the frame.  */);
   defsubr (&Sx_display_pixel_width);
   defsubr (&Sx_display_pixel_height);
   defsubr (&Sns_display_monitor_attributes_list);
-  defsubr (&Sx_frame_geometry);
+  defsubr (&Sns_frame_geometry);
+  defsubr (&Sns_frame_edges);
   defsubr (&Sx_display_mm_width);
   defsubr (&Sx_display_mm_height);
   defsubr (&Sx_display_screens);
@@ -3089,7 +3174,6 @@ be used as the image of the icon representing the frame.  */);
   defsubr (&Sns_emacs_info_panel);
   defsubr (&Sns_list_services);
   defsubr (&Sns_perform_service);
-  defsubr (&Sns_convert_utf8_nfd_to_nfc);
   defsubr (&Sns_popup_font_panel);
   defsubr (&Sns_popup_color_panel);