unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#51411: NS port cleanups
       [not found] <87mtmwt3mc.fsf.ref@yahoo.com>
@ 2021-10-26 11:41 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-26 12:39   ` Alan Third
  0 siblings, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-26 11:41 UTC (permalink / raw)
  To: 51411

[-- Attachment #1: Type: text/plain, Size: 1368 bytes --]

I did not notice that the thread previously titled "NS port
improvements" was moved off bug#51251 onto emacs-devel, but now that
there are concrete changes I think it would be a good opportunity to
post them on bug-gnu-emacs, per the CONTRIBUTE document.

The changes, with the generic improvements split from the changes to
font rendering are attached.

Alan raised several questions, which I answered.  I've reproduced that
part of the discussion below:

> Is there any reason to nest ns_focus? There are (according to Apple)
> performance reasons to not save the context unless you really need to.

Right now it's used in the code that clips to the exact bounds of a
string, if a string's overhangs are already drawn.

I couldn't find a cleaner way to do this, and that situation is rare, so
I think the performance problems will usually be avoided.

> I mean alt as defined by GNUstep in the quote I sent you in my last
> email. I don't think it's our job to say that GNUstep's choice of
> defaults is wrong and therefore do something that would be unexpected
> for a GNUstep user.

Well, on every other platform Meta is on the alt on the users' keyboard.
Emacs in general doesn't conform to the platform expectations WRT to key
bindings, so I think being consistent with Emacs on other platforms is
more important here.

But in the end, it's your call.  Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0002-General-improvements-to-NS-port.patch --]
[-- Type: text/x-patch, Size: 13201 bytes --]

From 861f2bc781d459118e318b90277e88d52af6a6ca Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:51:31 +0800
Subject: [PATCH 2/2] General improvements to NS port

* src/dispextern.h: Remove some !HAVE_NS conditionals around grab
related code.
* src/frame.c (gui_mouse_grabbed, gui_redo_mouse_highlight): Remove
!HAVE_NS conditionals around code.

* src/nsmenu.m (ns_update_menubar): Prevent recursive calls and enable
shallow updates on GNUstep.
(menuNeedsUpdate): Prevent recursive calls.
(ns_menu_show): Fix mysterious GC-related bug.
(update_frame_tool_bar_1): Work around mysterious toolbar sizing bug
on GNUstep.

* src/nsterm.h (struct ns_output): New field for tracking toolbar
visibility changes.

* src/nsterm.m (frame_set_mouse_pixel_position): Implement for
GNUstep.
(ns_redraw_scroll_bars): Enable for GNUstep.
(ns_clear_frame): Redraw scrollbars on GNUstep.
(ns_update_window_end): New function.
(ns_redisplay_interface): Add ns_update_window_end on GNUstep.
(- keyDown): Remove debug code that doesn't work on GNUstep.
(- mouseDown): Enable grab tracking on NS port.
(- resizeWithOldSuperviewSize): Fix build with NSTRACE.
(ns_alternate_modifier, ns_command_modifier): Fix default values for
GNUstep.

* src/xdisp.c (note_tab_bar_highlight): Enable some code for NS port.
---
 src/dispextern.h |  2 --
 src/frame.c      |  4 ---
 src/nsmenu.m     | 76 +++++++++++++++++++++++++++++++++++++++---------
 src/nsterm.h     |  6 ++++
 src/nsterm.m     | 50 ++++++++++++++++++++++++++-----
 src/xdisp.c      |  2 --
 6 files changed, 110 insertions(+), 30 deletions(-)

diff --git a/src/dispextern.h b/src/dispextern.h
index 08dac5d455..58e9048556 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3722,10 +3722,8 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask)			      \
                                           const char *, const char *,
                                           enum resource_types);
 
-#ifndef HAVE_NS /* These both used on W32 and X only.  */
 extern bool gui_mouse_grabbed (Display_Info *);
 extern void gui_redo_mouse_highlight (Display_Info *);
-#endif /* HAVE_NS */
 
 #endif /* HAVE_WINDOW_SYSTEM */
 
diff --git a/src/frame.c b/src/frame.c
index 2b1cb452ef..79a7c89e0d 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -5028,8 +5028,6 @@ gui_set_no_special_glyphs (struct frame *f, Lisp_Object new_value, Lisp_Object o
 }
 
 
-#ifndef HAVE_NS
-
 /* Non-zero if mouse is grabbed on DPYINFO
    and we know the frame where it is.  */
 
@@ -5054,8 +5052,6 @@ gui_redo_mouse_highlight (Display_Info *dpyinfo)
 			  dpyinfo->last_mouse_motion_y);
 }
 
-#endif /* HAVE_NS */
-
 /* Subroutines of creating an X frame.  */
 
 /* Make sure that Vx_resource_name is set to a reasonable value.
diff --git a/src/nsmenu.m b/src/nsmenu.m
index a2540c1663..65cf53fa21 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -101,6 +101,15 @@
 static void
 ns_update_menubar (struct frame *f, bool deep_p)
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+
+  if (inside)
+    return;
+
+  inside++;
+#endif
+
   BOOL needsSet = NO;
   id menu = [NSApp mainMenu];
   bool owfi;
@@ -120,7 +129,12 @@
   NSTRACE ("ns_update_menubar");
 
   if (f != SELECTED_FRAME () || FRAME_EXTERNAL_MENU_BAR (f) == 0)
+    {
+#ifdef NS_IMPL_GNUSTEP
+      inside--;
+#endif
       return;
+    }
   XSETFRAME (Vmenu_updating_frame, f);
 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
 
@@ -144,10 +158,6 @@
   t = -(1000*tb.time+tb.millitm);
 #endif
 
-#ifdef NS_IMPL_GNUSTEP
-  deep_p = 1; /* See comment in menuNeedsUpdate.  */
-#endif
-
   if (deep_p)
     {
       /* Make a widget-value tree representing the entire menu trees.  */
@@ -275,6 +285,9 @@
 	  free_menubar_widget_value_tree (first_wv);
 	  discard_menu_items ();
 	  unbind_to (specpdl_count, Qnil);
+#ifdef NS_IMPL_GNUSTEP
+	  inside--;
+#endif
 	  return;
 	}
 
@@ -408,6 +421,10 @@
   if (needsSet)
     [NSApp setMainMenu: menu];
 
+#ifdef NS_IMPL_GNUSTEP
+  inside--;
+#endif
+
   unblock_input ();
 
 }
@@ -452,17 +469,34 @@ - (instancetype)initWithTitle: (NSString *)title
    call to ns_update_menubar.  */
 - (void)menuNeedsUpdate: (NSMenu *)menu
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+#endif
+
   if (!FRAME_LIVE_P (SELECTED_FRAME ()))
     return;
 
-#ifdef NS_IMPL_COCOA
-/* TODO: GNUstep calls this method when the menu is still being built
-   which results in a recursive stack overflow.  One possible solution
-   is to use menuWillOpen instead, but the Apple docs explicitly warn
-   against changing the contents of the menu in it.  I don't know what
-   the right thing to do for GNUstep is.  */
+#ifdef NS_IMPL_GNUSTEP
+  /* GNUstep calls this method when the menu is still being built
+     which results in a recursive stack overflow, which this variable
+     prevents.  */
+
+  if (!inside)
+    ++inside;
+  else
+    return;
+#endif
+
   if (needsUpdate)
-    ns_update_menubar (SELECTED_FRAME (), true);
+    {
+#ifdef NS_IMPL_GNUSTEP
+      needsUpdate = NO;
+#endif
+      ns_update_menubar (SELECTED_FRAME (), true);
+    }
+
+#ifdef NS_IMPL_GNUSTEP
+  --inside;
 #endif
 }
 
@@ -789,6 +823,9 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   p.x = x; p.y = y;
 
+  /* Don't GC due to a mysterious bug.  */
+  inhibit_garbage_collection ();
+
   /* now parse stage 2 as in ns_update_menubar */
   wv = make_widget_value ("contextmenu", NULL, true, Qnil);
   wv->button_type = BUTTON_TYPE_NONE;
@@ -960,15 +997,17 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   pmenu = [[EmacsMenu alloc] initWithTitle:
                    NILP (title) ? @"" : [NSString stringWithLispString: title]];
+  /* On GNUstep, this call makes menu_items nil for whatever reason
+     when displaying a context menu from `context-menu-mode'.  */
+  Lisp_Object items = menu_items;
   [pmenu fillWithWidgetValue: first_wv->contents];
+  menu_items = items;
   free_menubar_widget_value_tree (first_wv);
-  unbind_to (specpdl_count, Qnil);
-
   popup_activated_flag = 1;
   tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
   popup_activated_flag = 0;
   [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
-
+  unbind_to (specpdl_count, Qnil);
   unblock_input ();
   return tem;
 }
@@ -1019,6 +1058,15 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
   [toolbar clearActive];
 #else
   [toolbar clearAll];
+  /* It takes at least 3 such adjustments to fix an issue where the
+     tool bar is 2x too tall when a frame's tool bar is first shown.
+     This is ugly, but I have no other solution for this problem.  */
+  if (FRAME_OUTPUT_DATA (f)->tool_bar_adjusted < 3)
+    {
+      [toolbar setVisible: NO];
+      FRAME_OUTPUT_DATA (f)->tool_bar_adjusted++;
+      [toolbar setVisible: YES];
+    }
 #endif
 
   /* Update EmacsToolbar as in GtkUtils, build items list.  */
diff --git a/src/nsterm.h b/src/nsterm.h
index 944dbd727c..8175f99664 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -978,6 +978,12 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
 
   /* Non-zero if we are doing an animation, e.g. toggling the tool bar.  */
   int in_animation;
+
+#ifdef NS_IMPL_GNUSTEP
+  /* Zero if this is the first time a toolbar has been updated on this
+     frame. */
+  int tool_bar_adjusted;
+#endif
 };
 
 /* This dummy declaration needed to support TTYs.  */
diff --git a/src/nsterm.m b/src/nsterm.m
index 957cd815a0..d9c28cb191 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -65,6 +65,7 @@ Updated by Christian Limpach (chris@nice.ch)
 
 #ifdef NS_IMPL_GNUSTEP
 #include "process.h"
+#import <GNUstepGUI/GSDisplayServer.h>
 #endif
 
 #ifdef NS_IMPL_COCOA
@@ -2259,13 +2260,19 @@ Hide the window (X11 semantics)
 {
   NSTRACE ("frame_set_mouse_pixel_position");
 
-  /* FIXME: what about GNUstep?  */
 #ifdef NS_IMPL_COCOA
   CGPoint mouse_pos =
     CGPointMake(f->left_pos + pix_x,
                 f->top_pos + pix_y +
                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
   CGWarpMouseCursorPosition (mouse_pos);
+#else
+  GSDisplayServer *server = GSServerForWindow ([FRAME_NS_VIEW (f) window]);
+  [server setMouseLocation: NSMakePoint (f->left_pos + pix_x,
+					 f->top_pos + pix_y
+					 + FRAME_NS_TITLEBAR_HEIGHT(f)
+					 + FRAME_TOOLBAR_HEIGHT(f))
+		  onScreen: [[[FRAME_NS_VIEW (f) window] screen] screenNumber]];
 #endif
 }
 
@@ -2578,8 +2585,7 @@ Hide the window (X11 semantics)
    ========================================================================== */
 
 
-#if 0
-/* FIXME: Remove this function. */
+#ifdef NS_IMPL_GNUSTEP
 static void
 ns_redraw_scroll_bars (struct frame *f)
 {
@@ -2624,10 +2630,9 @@ Hide the window (X11 semantics)
   NSRectFill (r);
   ns_unfocus (f);
 
-  /* as of 2006/11 or so this is now needed */
-  /* FIXME: I don't see any reason for this and removing it makes no
-     difference here.  Do we need it for GNUstep?  */
-  //ns_redraw_scroll_bars (f);
+#ifdef NS_IMPL_GNUSTEP
+  ns_redraw_scroll_bars (f);
+#endif
   unblock_input ();
 }
 
@@ -4933,6 +4938,17 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
 {
 }
 
+#ifdef NS_IMPL_GNUSTEP
+static void
+ns_update_window_end (struct window *w, bool cursor_on_p,
+		      bool mouse_face_overwritten_p)
+{
+  NSTRACE ("ns_update_window_end (cursor_on_p = %d)", cursor_on_p);
+
+  ns_redraw_scroll_bars (WINDOW_XFRAME (w));
+}
+#endif
+
 /* This and next define (many of the) public functions in this file.  */
 /* gui_* are generic versions in xdisp.c that we, and other terms, get away
          with using despite presence in the "system dependent" redisplay
@@ -4949,7 +4965,11 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
   ns_scroll_run,
   ns_after_update_window_line,
   NULL, /* update_window_begin */
+#ifndef NS_IMPL_GNUSTEP
   NULL, /* update_window_end   */
+#else
+  ns_update_window_end,
+#endif
   0, /* flush_display */
   gui_clear_window_mouse_face,
   gui_get_glyph_overhangs,
@@ -6177,9 +6197,11 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
       Lisp_Object kind = fnKeysym ? QCfunction : QCordinary;
       emacs_event->modifiers = EV_MODIFIERS2 (flags, kind);
 
+#ifndef NS_IMPL_GNUSTEP
       if (NS_KEYLOG)
         fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
                  code, fnKeysym, flags, emacs_event->modifiers);
+#endif
 
       /* If it was a function key or had control-like modifiers, pass
          it directly to Emacs.  */
@@ -6692,6 +6714,11 @@ - (void)mouseDown: (NSEvent *)theEvent
       emacs_event->code = EV_BUTTON (theEvent);
       emacs_event->modifiers = EV_MODIFIERS (theEvent)
                              | EV_UDMODIFIERS (theEvent);
+
+      if (emacs_event->modifiers & down_modifier)
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed |= 1 << EV_BUTTON (theEvent);
+      else
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed &= ~(1 << EV_BUTTON (theEvent));
     }
 
   XSETINT (emacs_event->x, lrint (p.x));
@@ -6992,7 +7019,6 @@ - (void)resizeWithOldSuperviewSize: (NSSize)oldSize
   height = (int)NSHeight (frame);
 
   NSTRACE_SIZE ("New size", NSMakeSize (width, height));
-  NSTRACE_SIZE ("Original size", size);
 
   /* Reset the frame size to match the bounds of the superview (the
      NSWindow's contentView).  We need to do this as sometimes the
@@ -9854,7 +9880,11 @@ Convert an X font name (XLFD) to an NS font name.
 \n\
 Each SYMBOL is `control', `meta', `alt', `super', `hyper' or `none'.\n\
 If `none', the key is ignored by Emacs and retains its standard meaning.");
+#ifdef NS_IMPL_GNUSTEP
+  ns_alternate_modifier = Qalt;
+#else
   ns_alternate_modifier = Qmeta;
+#endif
 
   DEFVAR_LISP ("ns-right-alternate-modifier", ns_right_alternate_modifier,
                "This variable describes the behavior of the right alternate or option key.\n\
@@ -9875,7 +9905,11 @@ Convert an X font name (XLFD) to an NS font name.
 \n\
 Each SYMBOL is `control', `meta', `alt', `super', `hyper' or `none'.\n\
 If `none', the key is ignored by Emacs and retains its standard meaning.");
+#ifdef NS_IMPL_GNUSTEP
+  ns_command_modifier = Qmeta;
+#else
   ns_command_modifier = Qsuper;
+#endif
 
   DEFVAR_LISP ("ns-right-command-modifier", ns_right_command_modifier,
                "This variable describes the behavior of the right command key.\n\
diff --git a/src/xdisp.c b/src/xdisp.c
index 9998677262..bfe7c571ab 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13891,7 +13891,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   clear_mouse_face (hlinfo);
 
   bool mouse_down_p = false;
-#ifndef HAVE_NS
   /* Mouse is down, but on different tab-bar item?  Or alternatively,
      the mouse might've been pressed somewhere we don't know about,
      and then have moved onto the tab bar.  In this case,
@@ -13904,7 +13903,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   if (mouse_down_p && f->last_tab_bar_item != prop_idx
       && f->last_tab_bar_item != -1)
     return;
-#endif
   draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED;
 
   /* If tab-bar item is not enabled, don't highlight it.  */
-- 
2.31.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-Improve-font-display-on-NS-port.patch --]
[-- Type: text/x-patch, Size: 76639 bytes --]

From a908e305c824fe466e381cdb6c279046d7cce781 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:44:03 +0800
Subject: [PATCH 1/2] Improve font display on NS port

* src/nsfns.m (Fx_create_frame): Use "fixed" for the default font on
GNUstep.

* src/nsfont.m (LCD_SMOOTHING_MARGIN, ns_escape_name)
(ns_unescape_name, ns_attribute_fvalue)
(STYLE_REF): Remove unused defines and functions.

(struct ns_glyph_layout, enum lgstring_direction).
(enum gs_font_slant, enum gs_font_weight, enum gs_font_width)
(enum gs_specified, struct gs_font_data): New enumerators and
structures.

(ns_font_descs_match_p)
(ns_done_font_data, ns_get_font_data): New functions.
(ns_glyph_metrics): Stop escaping names.

(ns_spec_to_descriptor): Fix font descriptor creation for symbolic
font spec entires.
(ns_descriptor_to_entity): Create entries with the correct symbolic
styles.

(ns_fallback_entity): Fix fallback entity selection.
(ns_findfonts): Use our own font matcher instead of the broken GNUstep
matcher.

(ns_list_family): Remove obsolete comment.
(nsfont_open): Remove obsolete code, comments, and synthItal logic
which doesn't work on GNUstep.

(nsfont_encode_char): Use a type that can fit NSGlyph
(nsfont_draw): Chose correct font, remove obsolete mouse face logic,
obsolete comments, and switch to using glyph-based drawing instead of
character-based drawing.

(ns_font_shape, nsfont_shape): New functions.

(ns_uni_to_glyphs_1): New function.
(ns_uni_to_glyphs): Return glyphs instead of unicode codepoints.

(ns_glyph_metrics): Use NSGlyphs instead of unicode codepoints and fix
left bearing, right bearing, ascent and descent computation.

(struct nsfont_driver): Add shaping capability.

* src/nsterm.h (struct nsfont_info): Use unsigned int for glyph cache.

* src/nsterm.m (gsaved): Make integer.
(ns_focus, ns_unfocus): Set DPS clipping on GNUstep and always save GC.

(ns_compute_glyph_string_overhangs): Fix overhang computation by using
xterm code.
(ns_draw_window_cursor): Simplify cursor drawing.
(ns_maybe_dumpglyphs_background): Test for cursor HL and remove
obsolete mouse face logic.
(ns_dumpglyphs_image)
(ns_dumpglyphs_box_or_relief)
(ns_dumpglyphs_stretch): Rectify for new cursor logic.
(ns_draw_glyph_string_foreground): Remove mouse face logic.
(ns_draw_glyph_strings): Implement overhangs and remove obsolete
comment.
(ns_draw_text_decoration): Add condition for DRAW_CURSOR and simplify
color selection.
(ns_define_frame_cursor): Remove nonsensical code (define_frame_cursor
has nothing to do with the text cursor, aka caret).

* src/xdisp.c (draw_glyphs): Enable code for NS port to fix mouse face
cursor display.

* src/macfont.m (get_cgcolor_from_nscolor): New function.
(macfont_draw): Remove obsolete mouse-face code and enable cursor
display.
---
 src/macfont.m |   36 +-
 src/nsfns.m   |    6 +
 src/nsfont.m  | 1184 ++++++++++++++++++++++++++++++++++++-------------
 src/nsterm.h  |    2 +-
 src/nsterm.m  |  373 ++++++++--------
 src/xdisp.c   |    2 -
 6 files changed, 1085 insertions(+), 518 deletions(-)

diff --git a/src/macfont.m b/src/macfont.m
index d86f09f485..df552400e3 100644
--- a/src/macfont.m
+++ b/src/macfont.m
@@ -613,6 +613,21 @@ static void mac_font_get_glyphs_for_variants (CFDataRef, UTF32Char,
   return cgColor;
 }
 
+static CGColorRef
+get_cgcolor_from_nscolor (NSColor *nsColor, struct frame *f)
+{
+  [nsColor set];
+  CGColorSpaceRef colorSpace = [[nsColor colorSpace] CGColorSpace];
+  NSInteger noc = [nsColor numberOfComponents];
+  CGFloat *components = xmalloc (sizeof(CGFloat)*(1+noc));
+  CGColorRef cgColor;
+
+  [nsColor getComponents: components];
+  cgColor = CGColorCreate (colorSpace, components);
+  xfree (components);
+  return cgColor;
+}
+
 #define CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND(context, face, f)        \
   do {                                                                  \
     CGColorRef refcol_ = get_cgcolor (NS_FACE_FOREGROUND (face), f);    \
@@ -2907,14 +2922,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
 
   if (!CGRectIsNull (background_rect))
     {
-      if (s->hl == DRAW_MOUSE_FACE)
+      if (s->hl == DRAW_CURSOR)
         {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-				       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_CURSOR_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
         }
-      CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
       CGContextFillRects (context, &background_rect, 1);
     }
 
@@ -2923,7 +2938,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
       CGAffineTransform atfm;
 
       CGContextScaleCTM (context, 1, -1);
-      CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
+      if (s->hl == DRAW_CURSOR)
+        {
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_BACKGROUND_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
+        }
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
       if (macfont_info->synthetic_italic_p)
         atfm = synthetic_italic_atfm;
       else
diff --git a/src/nsfns.m b/src/nsfns.m
index 797d0ce782..f4d8172246 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -1236,6 +1236,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                          "fontBackend", "FontBackend", RES_TYPE_STRING);
 
   {
+#ifdef NS_IMPL_COCOA
     /* use for default font name */
     id font = [NSFont userFixedPitchFontOfSize: -1.0]; /* default */
     gui_default_parameter (f, parms, Qfontsize,
@@ -1250,6 +1251,11 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                            build_string (fontname),
                            "font", "Font", RES_TYPE_STRING);
     xfree (fontname);
+#else
+    gui_default_parameter (f, parms, Qfont,
+                           build_string ("fixed"),
+                           "font", "Font", RES_TYPE_STRING);
+#endif
   }
   unblock_input ();
 
diff --git a/src/nsfont.m b/src/nsfont.m
index 5a9cdfebc0..f8208b4577 100644
--- a/src/nsfont.m
+++ b/src/nsfont.m
@@ -1,4 +1,4 @@
-/* Font back-end driver for the NeXT/Open/GNUstep and macOS window system.
+/* Font back-end driver for the GNUstep window system.
    See font.h
    Copyright (C) 2006-2021 Free Software Foundation, Inc.
 
@@ -38,47 +38,269 @@
 #include "termchar.h"
 #include "pdumper.h"
 
-/* TODO: Drop once we can assume gnustep-gui 0.17.1.  */
+#import <Foundation/NSException.h>
 #import <AppKit/NSFontDescriptor.h>
+#import <AppKit/NSLayoutManager.h>
+#import <GNUstepGUI/GSLayoutManager.h>
+#import <GNUstepGUI/GSFontInfo.h>
 
 #define NSFONT_TRACE 0
-#define LCD_SMOOTHING_MARGIN 2
 
-/* Font glyph and metrics caching functions, implemented at end.  */
-static void ns_uni_to_glyphs (struct nsfont_info *font_info,
-                              unsigned char block);
-static void ns_glyph_metrics (struct nsfont_info *font_info,
-                              unsigned char block);
+/* Structure used by GS `shape' functions for storing layout
+   information for each glyph.  Borrowed from macfont.h.  */
+struct ns_glyph_layout
+{
+  /* Range of indices of the characters composed into the group of
+     glyphs that share the cursor position with this glyph.  The
+     members `location' and `length' are in UTF-16 indices.  */
+  NSRange comp_range;
 
-#define INVALID_GLYPH 0xFFFF
+  /* UTF-16 index in the source string for the first character
+     associated with this glyph.  */
+  NSUInteger string_index;
 
-/* ==========================================================================
+  /* Horizontal and vertical adjustments of glyph position.  The
+     coordinate space is that of Core Text.  So, the `baseline_delta'
+     value is negative if the glyph should be placed below the
+     baseline.  */
+  CGFloat advance_delta, baseline_delta;
 
-    Utilities
+  /* Typographical width of the glyph.  */
+  CGFloat advance;
 
-   ========================================================================== */
+  /* Glyph ID of the glyph.  */
+  NSGlyph glyph_id;
+};
+
+
+enum lgstring_direction
+  {
+    DIR_R2L = -1, DIR_UNKNOWN = 0, DIR_L2R = 1
+  };
+
+enum gs_font_slant
+  {
+    GS_FONT_SLANT_ITALIC,
+    GS_FONT_SLANT_REVERSE_ITALIC,
+    GS_FONT_SLANT_NORMAL
+  };
+
+enum gs_font_weight
+  {
+    GS_FONT_WEIGHT_LIGHT,
+    GS_FONT_WEIGHT_BOLD,
+    GS_FONT_WEIGHT_NORMAL
+  };
 
+enum gs_font_width
+  {
+    GS_FONT_WIDTH_CONDENSED,
+    GS_FONT_WIDTH_EXPANDED,
+    GS_FONT_WIDTH_NORMAL
+  };
+
+enum gs_specified
+  {
+    GS_SPECIFIED_SLANT = 1,
+    GS_SPECIFIED_WEIGHT = 1 << 1,
+    GS_SPECIFIED_WIDTH = 1 << 2,
+    GS_SPECIFIED_FAMILY = 1 << 3,
+    GS_SPECIFIED_SPACING = 1 << 4
+  };
+
+struct gs_font_data
+{
+  int specified;
+  enum gs_font_slant slant;
+  enum gs_font_weight weight;
+  enum gs_font_width width;
+  bool monospace_p;
+  char *family_name;
+};
 
-/* Replace spaces w/another character so emacs core font parsing routines
-   aren't thrown off.  */
 static void
-ns_escape_name (char *name)
+ns_done_font_data (struct gs_font_data *data)
 {
-  for (; *name; name++)
-    if (*name == ' ')
-      *name = '_';
+  if (data->specified & GS_SPECIFIED_FAMILY)
+    xfree (data->family_name);
 }
 
-
-/* Reconstruct spaces in a font family name passed through emacs.  */
 static void
-ns_unescape_name (char *name)
+ns_get_font_data (NSFontDescriptor *desc, struct gs_font_data *dat)
+{
+  NSNumber *tem;
+  NSFontSymbolicTraits traits = [desc symbolicTraits];
+  NSDictionary *dict = [desc objectForKey: NSFontTraitsAttribute];
+  NSString *family = [desc objectForKey: NSFontFamilyAttribute];
+
+  dat->specified = 0;
+
+  if (family != nil)
+    {
+      dat->specified |= GS_SPECIFIED_FAMILY;
+      dat->family_name = xstrdup ([family cStringUsingEncoding: NSUTF8StringEncoding]);
+    }
+
+  tem = [desc objectForKey: NSFontFixedAdvanceAttribute];
+
+  if ((tem != nil && [tem boolValue] != NO)
+      || (traits & NSFontMonoSpaceTrait))
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = true;
+    }
+  else if (tem != nil && [tem boolValue] == NO)
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = false;
+    }
+
+  if (traits & NSFontBoldTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WEIGHT;
+      dat->weight = GS_FONT_WEIGHT_BOLD;
+    }
+
+  if (traits & NSFontItalicTrait)
+    {
+      dat->specified |= GS_SPECIFIED_SLANT;
+      dat->slant = GS_FONT_SLANT_ITALIC;
+    }
+
+  if (traits & NSFontCondensedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_CONDENSED;
+    }
+  else if (traits & NSFontExpandedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_EXPANDED;
+    }
+
+  if (dict != nil)
+    {
+      tem = [dict objectForKey: NSFontSlantTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_SLANT;
+
+	  dat->slant = [tem floatValue] > 0
+	    ? GS_FONT_SLANT_ITALIC
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_SLANT_REVERSE_ITALIC
+	       : GS_FONT_SLANT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWeightTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WEIGHT;
+
+	  dat->weight = [tem floatValue] > 0
+	    ? GS_FONT_WEIGHT_BOLD
+	    : ([tem floatValue] < -0.4f
+	       ? GS_FONT_WEIGHT_LIGHT
+	       : GS_FONT_WEIGHT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWidthTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WIDTH;
+
+	  dat->width = [tem floatValue] > 0
+	    ? GS_FONT_WIDTH_EXPANDED
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_WIDTH_NORMAL
+	       : GS_FONT_WIDTH_CONDENSED);
+	}
+    }
+}
+
+static bool
+ns_font_descs_match_p (NSFontDescriptor *desc, NSFontDescriptor *target)
 {
-  for (; *name; name++)
-    if (*name == '_')
-      *name = ' ';
+  struct gs_font_data dat;
+  struct gs_font_data t;
+
+  ns_get_font_data (desc, &dat);
+  ns_get_font_data (target, &t);
+
+  if (!(t.specified & GS_SPECIFIED_WIDTH))
+    t.width = GS_FONT_WIDTH_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_WEIGHT))
+    t.weight = GS_FONT_WEIGHT_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_SPACING))
+    t.monospace_p = false;
+  if (!(t.specified & GS_SPECIFIED_SLANT))
+    t.slant = GS_FONT_SLANT_NORMAL;
+
+  if (!(t.specified & GS_SPECIFIED_FAMILY))
+    emacs_abort ();
+
+  bool match_p = true;
+
+  if (dat.specified & GS_SPECIFIED_WIDTH
+      && dat.width != t.width)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_WEIGHT
+      && dat.weight != t.weight)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SPACING
+      && dat.monospace_p != t.monospace_p)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SLANT
+      && dat.monospace_p != t.monospace_p)
+    {
+      if (NSFONT_TRACE)
+	printf ("Matching monospace for %s: %d %d\n",
+		t.family_name, dat.monospace_p,
+		t.monospace_p);
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_FAMILY
+      && strcmp (dat.family_name, t.family_name))
+    match_p = false;
+
+ gout:
+  ns_done_font_data (&dat);
+  ns_done_font_data (&t);
+
+  return match_p;
 }
 
+/* Font glyph and metrics caching functions, implemented at end.  */
+static void ns_uni_to_glyphs (struct nsfont_info *font_info,
+                              unsigned char block);
+static void ns_glyph_metrics (struct nsfont_info *font_info,
+                              unsigned int block);
+
+#define INVALID_GLYPH 0xFFFF
+
+/* ==========================================================================
+
+    Utilities
+
+   ========================================================================== */
+
 
 /* Extract family name from a font spec.  */
 static NSString *
@@ -91,66 +313,116 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
     {
       char *tmp = xlispstrdup (SYMBOL_NAME (tem));
       NSString *family;
-      ns_unescape_name (tmp);
       family = [NSString stringWithUTF8String: tmp];
       xfree (tmp);
       return family;
     }
 }
 
-
-/* Return 0 if attr not set, else value (which might also be 0).
-   On Leopard 0 gets returned even on descriptors where the attribute
-   was never set, so there's no way to distinguish between unspecified
-   and set to not have.  Callers should assume 0 means unspecified.  */
-static float
-ns_attribute_fvalue (NSFontDescriptor *fdesc, NSString *trait)
-{
-    NSDictionary *tdict = [fdesc objectForKey: NSFontTraitsAttribute];
-    NSNumber *val = [tdict objectForKey: trait];
-    return val == nil ? 0.0F : [val floatValue];
-}
-
-
 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
    to NSFont descriptor.  Information under extra only needed for matching.  */
-#define STYLE_REF 100
 static NSFontDescriptor *
 ns_spec_to_descriptor (Lisp_Object font_spec)
 {
     NSFontDescriptor *fdesc;
     NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
-    NSMutableDictionary *tdict = [NSMutableDictionary new];
     NSString *family = ns_get_family (font_spec);
-    float n;
-
-    /* Add each attr in font_spec to fdAttrs.  */
-    n = min (FONT_WEIGHT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWeightTrait];
-    n = min (FONT_SLANT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontSlantTrait];
-    n = min (FONT_WIDTH_NUMERIC (font_spec), 200);
-    if (n > -1 && (n > STYLE_REF + 10 || n < STYLE_REF - 10))
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWidthTrait];
-    if ([tdict count] > 0)
-	[fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+    NSMutableDictionary *tdict = [NSMutableDictionary new];
 
-    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
-               retain] autorelease];
+    Lisp_Object tem;
+
+    tem = FONT_SLANT_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontSlantTrait];
+	else if (EQ (tem, intern ("reverse-italic")) ||
+		 EQ (tem, intern ("reverse-oblique")))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontSlantTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontSlantTrait];
+      }
+
+    tem = FONT_WIDTH_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qcondensed))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontWidthTrait];
+	else if (EQ (tem, Qexpanded))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontWidthTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontWidthTrait];
+      }
+
+    tem = FONT_WEIGHT_SYMBOLIC (font_spec);
+
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qbold))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else if (EQ (tem, Qlight))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		      forKey: NSFontWeightTrait];
+	  }
+      }
+
+    tem = AREF (font_spec, FONT_SPACING_INDEX);
 
     if (family != nil)
       {
-        NSFontDescriptor *fdesc2 = [fdesc fontDescriptorWithFamily: family];
-        fdesc = [[fdesc2 retain] autorelease];
+	[fdAttrs setObject: family
+		    forKey: NSFontFamilyAttribute];
       }
 
-    [fdAttrs release];
+    if (FIXNUMP (tem))
+      {
+	if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:YES]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+	else
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:NO]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+      }
+
+    /* Handle special families such as ``fixed'' or ``Sans Serif''.  */
+
+    if ([family isEqualToString: @"fixed"])
+      {
+	[fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+    else if ([family isEqualToString: @"Sans Serif"])
+      {
+	[fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+
+    [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+
+    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
+               retain] autorelease];
+
     [tdict release];
+    [fdAttrs release];
     return fdesc;
 }
 
@@ -161,61 +433,64 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
                          Lisp_Object extra,
                          const char *style)
 {
-    Lisp_Object font_entity = font_make_entity ();
-    /*   NSString *psName = [desc postscriptName]; */
-    NSString *family = [desc objectForKey: NSFontFamilyAttribute];
-    unsigned int traits = [desc symbolicTraits];
-    char *escapedFamily;
-
-    /* Shouldn't happen, but on Tiger fallback desc gets name but no family.  */
-    if (family == nil)
-      family = [desc objectForKey: NSFontNameAttribute];
-    if (family == nil)
-      family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-
-    escapedFamily = xstrdup ([family UTF8String]);
-    ns_escape_name (escapedFamily);
-
-    ASET (font_entity, FONT_TYPE_INDEX, Qns);
-    ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
-    ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
-    ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
-    ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
-
-    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    traits & NSFontBoldTrait ? Qbold : Qmedium);
-/*    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    make_fixnum (100 + 100
-			* ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    traits & NSFontItalicTrait ? Qitalic : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-                    traits & NSFontCondensedTrait ? Qcondensed :
-                    traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
-
-    ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_SPACING_INDEX,
-	  make_fixnum([desc symbolicTraits] & NSFontMonoSpaceTrait
-	      ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
-
-    ASET (font_entity, FONT_EXTRA_INDEX, extra);
-    ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+  Lisp_Object font_entity = font_make_entity ();
+  struct gs_font_data data;
+  ns_get_font_data (desc, &data);
+
+  ASET (font_entity, FONT_TYPE_INDEX, Qns);
+  ASET (font_entity, FONT_FOUNDRY_INDEX, Qns);
+  if (data.specified & GS_SPECIFIED_FAMILY)
+    ASET (font_entity, FONT_FAMILY_INDEX, intern (data.family_name));
+  ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
+  ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
+
+  if (data.specified & GS_SPECIFIED_WEIGHT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
+		      data.weight == GS_FONT_WEIGHT_BOLD
+		      ? Qbold : (data.weight == GS_FONT_WEIGHT_LIGHT
+				 ? Qlight : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX, Qnormal);
 
-    if (NSFONT_TRACE)
-      {
-	fputs ("created font_entity:\n    ", stderr);
-	debug_print (font_entity);
-      }
+  if (data.specified & GS_SPECIFIED_SLANT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
+		      data.slant == GS_FONT_SLANT_ITALIC
+		      ? Qitalic : (data.slant == GS_FONT_SLANT_REVERSE_ITALIC
+				   ? intern ("reverse-italic") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX, Qnormal);
 
-    xfree (escapedFamily);
-    return font_entity;
+  if (data.specified & GS_SPECIFIED_WIDTH)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
+		      data.width == GS_FONT_WIDTH_CONDENSED
+		      ? Qcondensed : (data.width == GS_FONT_WIDTH_EXPANDED
+				      ? intern ("expanded") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX, Qnormal);
+
+  ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_SPACING_INDEX,
+	make_fixnum ((data.specified & GS_SPECIFIED_WIDTH && data.monospace_p)
+		     ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
+
+  ASET (font_entity, FONT_EXTRA_INDEX, extra);
+  ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+
+  if (NSFONT_TRACE)
+    {
+      fputs ("created font_entity:\n    ", stderr);
+      debug_print (font_entity);
+    }
+
+  ns_done_font_data (&data);
+  return font_entity;
 }
 
 
@@ -223,8 +498,7 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
 static Lisp_Object
 ns_fallback_entity (void)
 {
-  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
-      fontDescriptor], Qnil, NULL);
+  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 1] fontDescriptor], Qnil, NULL);
 }
 
 
@@ -510,21 +784,20 @@ but also for ascii (which causes unnecessary font substitution).  */
     return families;
 }
 
+/* GNUstep font matching is very mediocre (it can't even compare
+   symbolic styles correctly), which is why our own font matching
+   mechanism must be implemented.  */
 
-/* Implementation for list() and match().  List() can return nil, match()
-must return something.  Strategy is to drop family name from attribute
-matching set for match.  */
+/* Implementation for list and match.  */
 static Lisp_Object
 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
 {
     Lisp_Object tem, list = Qnil;
-    NSFontDescriptor *fdesc, *desc;
-    NSMutableSet *fkeys;
-    NSArray *matchingDescs;
-    NSEnumerator *dEnum;
-    NSString *family;
+    NSFontDescriptor *fdesc;
+    NSArray *all_descs;
+    GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
+
     NSSet *cFamilies;
-    BOOL foundItal = NO;
 
     block_input ();
     if (NSFONT_TRACE)
@@ -537,43 +810,22 @@ but also for ascii (which causes unnecessary font substitution).  */
     cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
 
     fdesc = ns_spec_to_descriptor (font_spec);
-    fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
-    if (isMatch)
-	[fkeys removeObject: NSFontFamilyAttribute];
-
-    matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
+    all_descs = [enumerator availableFontDescriptors];
 
-    if (NSFONT_TRACE)
-	NSLog(@"Got desc %@ and found %lu matching fonts from it: ", fdesc,
-	      (unsigned long)[matchingDescs count]);
-
-    for (dEnum = [matchingDescs objectEnumerator]; (desc = [dEnum nextObject]);)
+    for (NSFontDescriptor *desc in all_descs)
       {
 	if (![cFamilies containsObject:
 	         [desc objectForKey: NSFontFamilyAttribute]])
 	    continue;
+	if (!ns_font_descs_match_p (fdesc, desc))
+	  continue;
+
         tem = ns_descriptor_to_entity (desc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
+				       AREF (font_spec, FONT_EXTRA_INDEX),
                                        NULL);
         if (isMatch)
           return tem;
 	list = Fcons (tem, list);
-	if (fabs (ns_attribute_fvalue (desc, NSFontSlantTrait)) > 0.05)
-	    foundItal = YES;
-      }
-
-    /* Add synthItal member if needed.  */
-    family = [fdesc objectForKey: NSFontFamilyAttribute];
-    if (family != nil && !foundItal && !NILP (list))
-      {
-        NSFontDescriptor *s1 = [NSFontDescriptor new];
-        NSFontDescriptor *sDesc
-          = [[s1 fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
-              fontDescriptorWithFamily: family];
-	list = Fcons (ns_descriptor_to_entity (sDesc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
-					 "synthItal"), list);
-        [s1 release];
       }
 
     unblock_input ();
@@ -652,7 +904,6 @@ Properties to be considered are same as for list().  */
                objectEnumerator];
   while ((family = [families nextObject]))
       list = Fcons (intern ([family UTF8String]), list);
-  /* FIXME: escape the name?  */
 
   if (NSFONT_TRACE)
     fprintf (stderr, "nsfont: list families returning %"pD"d entries\n",
@@ -668,18 +919,15 @@ Properties to be considered are same as for list().  */
 static Lisp_Object
 nsfont_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
 {
-  BOOL synthItal;
-  unsigned int traits = 0;
   struct nsfont_info *font_info;
   struct font *font;
   NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
   NSFontManager *fontMgr = [NSFontManager sharedFontManager];
   NSString *family;
   NSFont *nsfont, *sfont;
-  Lisp_Object tem;
   NSRect brect;
   Lisp_Object font_object;
-  int fixLeopardBug;
+  Lisp_Object tem;
 
   block_input ();
 
@@ -692,42 +940,20 @@ Properties to be considered are same as for list().  */
   if (pixel_size <= 0)
     {
       /* try to get it out of frame params */
-        Lisp_Object tem = get_frame_param (f, Qfontsize);
-        pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
+      tem = get_frame_param (f, Qfontsize);
+      pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
     }
 
   tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
-  synthItal = !NILP (tem) && !strncmp ("synthItal", SSDATA (SYMBOL_NAME (tem)),
-                                       9);
   family = ns_get_family (font_entity);
   if (family == nil)
     family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-  /* Should be > 0.23 as some font descriptors (e.g. Terminus) set to that
-     when setting family in ns_spec_to_descriptor().  */
-  if (ns_attribute_fvalue (fontDesc, NSFontWeightTrait) > 0.50F)
-      traits |= NSBoldFontMask;
-  if (ns_attribute_fvalue (fontDesc, NSFontSlantTrait) > 0.05F)
-      traits |= NSItalicFontMask;
-
-  /* see https://web.archive.org/web/20100201175731/http://cocoadev.com/forums/comments.php?DiscussionID=74 */
-  fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
-  nsfont = [fontMgr fontWithFamily: family
-                            traits: traits weight: fixLeopardBug
-			      size: pixel_size];
-  /* if didn't find, try synthetic italic */
-  if (nsfont == nil && synthItal)
-    {
-      nsfont = [fontMgr fontWithFamily: family
-                                traits: traits & ~NSItalicFontMask
-                                weight: fixLeopardBug size: pixel_size];
-    }
+
+  nsfont = [NSFont fontWithDescriptor: fontDesc
+				 size: pixel_size];
 
   if (nsfont == nil)
-    {
-      message_with_string ("*** Warning: font in family `%s' not found",
-                          build_string ([family UTF8String]), 1);
-      nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
-    }
+    nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
 
   if (NSFONT_TRACE)
     NSLog (@"%@\n", nsfont);
@@ -740,7 +966,7 @@ when setting family in ns_spec_to_descriptor().  */
   if (!font)
     {
       unblock_input ();
-      return Qnil; /* FIXME: other terms do, but returning Qnil causes segfault.  */
+      return Qnil;
     }
 
   font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs);
@@ -781,7 +1007,7 @@ when setting family in ns_spec_to_descriptor().  */
     font_info->name = xstrdup (fontName);
     font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
     font_info->ital =
-      synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
+      ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
 
     /* Metrics etc.; some fonts return an unusually large max advance, so we
        only use it for fonts that have wide characters.  */
@@ -808,8 +1034,6 @@ when setting family in ns_spec_to_descriptor().  */
       lrint (brect.size.width - (CGFloat) font_info->width);
 
     /* set up metrics portion of font struct */
-    font->ascent = lrint([sfont ascender]);
-    font->descent = -lrint(floor(adjusted_descender));
     font->space_width = lrint (ns_char_width (sfont, ' '));
     font->max_width = lrint (font_info->max_bounds.width);
     font->min_width = font->space_width;  /* Approximate.  */
@@ -871,7 +1095,7 @@ when setting family in ns_spec_to_descriptor().  */
 {
   struct nsfont_info *font_info = (struct nsfont_info *)font;
   unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
-  unsigned short g;
+  unsigned int g;
 
   if (c > 0xFFFF)
     return FONT_INVALID_CODE;
@@ -934,51 +1158,26 @@ is false when (FROM > 0 || TO < S->nchars).  */
 static int
 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
              bool with_background)
-/* NOTE: focus and clip must be set.  */
 {
-  static unsigned char cbuf[1024];
-  unsigned char *c = cbuf;
-#if GNUSTEP_GUI_MAJOR_VERSION > 0 || GNUSTEP_GUI_MINOR_VERSION > 22
-  static CGFloat advances[1024];
-  CGFloat *adv = advances;
-#else
-  static float advances[1024];
-  float *adv = advances;
-#endif
+  NSGlyph *c = alloca ((to - from) * sizeof *c);
+  NSSize *adv = alloca ((to - from) * sizeof *adv);
+  NSSize *advances = adv;
+
   struct face *face;
   NSRect r;
   struct nsfont_info *font;
-  NSColor *col, *bgCol;
+  NSColor *col;
   unsigned *t = s->char2b;
-  int i, len, flags;
+  int i, len;
   char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
 
   block_input ();
 
-  font = (struct nsfont_info *)s->face->font;
+  font = (struct nsfont_info *) s->font;
   if (font == NULL)
     font = (struct nsfont_info *)FRAME_FONT (s->f);
 
-  /* Select face based on input flags.  */
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
-  switch (flags)
-    {
-    case NS_DUMPGLYPH_CURSOR:
-      face = s->face;
-      break;
-    case NS_DUMPGLYPH_MOUSEFACE:
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-      break;
-    default:
-      face = s->face;
-    }
+  face = s->face;
 
   r.origin.x = s->x;
   if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
@@ -987,16 +1186,15 @@ is false when (FROM > 0 || TO < S->nchars).  */
   r.origin.y = s->y;
   r.size.height = FONT_HEIGHT (font);
 
-  /* Convert UTF-16 (?) to UTF-8 and determine advances.  Note if we just ask
-     NS to render the string, it will come out differently from the individual
-     character widths added up because of layout processing.  */
+  /* Determine advances.  */
   {
     int cwidth, twidth = 0;
     int hi, lo;
-    /* FIXME: composition: no vertical displacement is considered.  */
+
     t += from; /* advance into composition */
     for (i = from; i < to; i++, t++)
       {
+	c[i - from] = *t;
         hi = (*t & 0xFF00) >> 8;
         lo = *t & 0x00FF;
         if (isComposite)
@@ -1012,7 +1210,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 		else
 		  {
 		    cwidth = LGLYPH_WADJUST (glyph);
-		    *(adv-1) += LGLYPH_XOFF (glyph);
+		    (adv - 1)->width += LGLYPH_XOFF (glyph);
 		  }
 	      }
           }
@@ -1023,55 +1221,27 @@ is false when (FROM > 0 || TO < S->nchars).  */
             cwidth = font->metrics[hi][lo].width;
           }
         twidth += cwidth;
-        *adv++ = cwidth;
-        c += CHAR_STRING (*t, c); /* This converts the char to UTF-8.  */
+        adv++->width = cwidth;
       }
     len = adv - advances;
     r.size.width = twidth;
-    *c = 0;
   }
 
   /* Fill background if requested.  */
   if (with_background && !isComposite)
     {
-      NSRect br = r;
-      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
-      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
-
-      if (s->row->full_width_p)
-        {
-          if (br.origin.x <= fibw + 1 + mbox_line_width)
-            {
-              br.size.width += br.origin.x - mbox_line_width;
-              br.origin.x = mbox_line_width;
-            }
-          if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
-                <= fibw+1)
-            br.size.width += fibw;
-        }
-      if (s->face->box == FACE_NO_BOX)
-        {
-          /* Expand unboxed top row over internal border.  */
-          if (br.origin.y <= fibw + 1 + mbox_line_width)
-            {
-              br.size.height += br.origin.y;
-              br.origin.y = 0;
-            }
-        }
-      else
-        {
-          int correction = abs (s->face->box_horizontal_line_width)+1;
-          br.origin.y += correction;
-          br.size.height -= 2*correction;
-          correction = abs (s->face->box_vertical_line_width)+1;
-          br.origin.x += correction;
-          br.size.width -= 2*correction;
-        }
+      NSRect br = NSMakeRect (x, y - FONT_BASE (s->font),
+			      s->width, FONT_HEIGHT (s->font));
 
       if (!s->face->stipple)
-        [(NS_FACE_BACKGROUND (face) != 0
-          ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-          : FRAME_BACKGROUND_COLOR (s->f)) set];
+	{
+	  if (s->hl != DRAW_CURSOR)
+	    [(NS_FACE_BACKGROUND (face) != 0
+	      ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+	      : FRAME_BACKGROUND_COLOR (s->f)) set];
+	  else
+	    [FRAME_CURSOR_COLOR (s->f) set];
+	}
       else
         {
           struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
@@ -1080,43 +1250,32 @@ is false when (FROM > 0 || TO < S->nchars).  */
       NSRectFill (br);
     }
 
-
   /* set up for character rendering */
   r.origin.y = y;
 
-  col = (NS_FACE_FOREGROUND (face) != 0
-         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
-         : FRAME_FOREGROUND_COLOR (s->f));
-
-  bgCol = (flags != NS_DUMPGLYPH_FOREGROUND ? nil
-           : (NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)));
+  if (s->hl == DRAW_CURSOR)
+    col = FRAME_BACKGROUND_COLOR (s->f);
+  else
+    col = (NS_FACE_FOREGROUND (face) != 0
+	   ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
+	   : FRAME_FOREGROUND_COLOR (s->f));
 
   /* render under GNUstep using DPS */
   {
-    NSGraphicsContext *context = GSCurrentContext ();
-
+    NSGraphicsContext *context = [NSGraphicsContext currentContext];
     DPSgsave (context);
-    [font->nsfont set];
-
-    /* do erase if "foreground" mode */
-    if (bgCol != nil)
+    if (s->clip_head)
       {
-        [bgCol set];
-        DPSmoveto (context, r.origin.x, r.origin.y);
-/*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
-        DPSxshow (context, (const char *) cbuf, advances, len);
-        DPSstroke (context);
-        [col set];
-/*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
+	DPSrectclip (context, s->clip_head->x, 0,
+		     FRAME_PIXEL_WIDTH (s->f),
+		     FRAME_PIXEL_HEIGHT (s->f));
       }
+    [font->nsfont set];
 
     [col set];
 
-    /* draw with DPSxshow () */
     DPSmoveto (context, r.origin.x, r.origin.y);
-    DPSxshow (context, (const char *) cbuf, advances, len);
+    GSShowGlyphsWithAdvances (context, c, advances, len);
     DPSstroke (context);
 
     DPSgrestore (context);
@@ -1126,6 +1285,360 @@ is false when (FROM > 0 || TO < S->nchars).  */
   return to-from;
 }
 
+static NSUInteger
+ns_font_shape (NSFont *font, NSString *string,
+	       struct ns_glyph_layout *glyph_layouts, NSUInteger glyph_len,
+	       enum lgstring_direction dir)
+{
+  NSUInteger i;
+  NSUInteger result = 0;
+  NSTextStorage *textStorage;
+  NSLayoutManager *layoutManager;
+  NSTextContainer *textContainer;
+  NSUInteger stringLength;
+  NSPoint spaceLocation;
+  /* numberOfGlyphs can't actually be 0, but this pacifies GCC */
+  NSUInteger used, numberOfGlyphs = 0;
+
+  textStorage = [[NSTextStorage alloc] initWithString:string];
+  layoutManager = [[NSLayoutManager alloc] init];
+  textContainer = [[NSTextContainer alloc] init];
+
+  /* Append a trailing space to measure baseline position.  */
+  [textStorage appendAttributedString:([[[NSAttributedString alloc]
+                                          initWithString:@" "] autorelease])];
+  [textStorage setFont:font];
+  [textContainer setLineFragmentPadding:0];
+
+  [layoutManager addTextContainer:textContainer];
+  [textContainer release];
+  [textStorage addLayoutManager:layoutManager];
+  [layoutManager release];
+
+  if (!(textStorage && layoutManager && textContainer))
+    emacs_abort ();
+
+  stringLength = [string length];
+
+  /* Force layout.  */
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  spaceLocation = [layoutManager locationForGlyphAtIndex:stringLength];
+
+  /* Remove the appended trailing space because otherwise it may
+     generate a wrong result for a right-to-left text.  */
+  [textStorage beginEditing];
+  [textStorage deleteCharactersInRange:(NSMakeRange (stringLength, 1))];
+  [textStorage endEditing];
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  i = 0;
+  while (i < stringLength)
+    {
+      NSRange range;
+      NSFont *fontInTextStorage =
+        [textStorage attribute: NSFontAttributeName
+		       atIndex:i
+                     longestEffectiveRange: &range
+                       inRange: NSMakeRange (0, stringLength)];
+
+      if (!(fontInTextStorage == font
+            || [[fontInTextStorage fontName] isEqualToString:[font fontName]]))
+        break;
+      i = NSMaxRange (range);
+    }
+  if (i < stringLength)
+    /* Make the test `used <= glyph_len' below fail if textStorage
+       contained some fonts other than the specified one.  */
+    used = glyph_len + 1;
+  else
+    {
+      NSRange range = NSMakeRange (0, stringLength);
+
+      range = [layoutManager glyphRangeForCharacterRange:range
+                                    actualCharacterRange:NULL];
+      numberOfGlyphs = NSMaxRange (range);
+      used = numberOfGlyphs;
+      for (i = 0; i < numberOfGlyphs; i++)
+        if ([layoutManager notShownAttributeForGlyphAtIndex:i])
+          used--;
+    }
+
+  if (0 < used && used <= glyph_len)
+    {
+      NSUInteger glyphIndex, prevGlyphIndex;
+      NSUInteger *permutation;
+      NSRange compRange, range;
+      CGFloat totalAdvance;
+
+      glyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      permutation = NULL;
+#define RIGHT_TO_LEFT_P permutation
+
+      /* Fill the `comp_range' member of struct mac_glyph_layout, and
+         setup a permutation for right-to-left text.  */
+      compRange = NSMakeRange (0, 0);
+      for (range = NSMakeRange (0, 0); NSMaxRange (range) < used;
+           range.length++)
+        {
+          struct ns_glyph_layout *gl = glyph_layouts + NSMaxRange (range);
+          NSUInteger characterIndex =
+            [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
+
+          gl->string_index = characterIndex;
+
+          if (characterIndex >= NSMaxRange (compRange))
+            {
+              compRange.location = NSMaxRange (compRange);
+              do
+                {
+                  NSRange characterRange =
+                    [string
+                      rangeOfComposedCharacterSequenceAtIndex:characterIndex];
+
+                  compRange.length =
+                    NSMaxRange (characterRange) - compRange.location;
+                  [layoutManager glyphRangeForCharacterRange:compRange
+                                        actualCharacterRange:&characterRange];
+                  characterIndex = NSMaxRange (characterRange) - 1;
+                }
+              while (characterIndex >= NSMaxRange (compRange));
+
+              if (RIGHT_TO_LEFT_P)
+                for (i = 0; i < range.length; i++)
+                  permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+              range = NSMakeRange (NSMaxRange (range), 0);
+            }
+
+          gl->comp_range.location = compRange.location;
+          gl->comp_range.length = compRange.length;
+
+          while (++glyphIndex < numberOfGlyphs)
+            if (![layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+              break;
+        }
+      if (RIGHT_TO_LEFT_P)
+        for (i = 0; i < range.length; i++)
+          permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+      /* Then fill the remaining members.  */
+      glyphIndex = prevGlyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      if (!RIGHT_TO_LEFT_P)
+        totalAdvance = 0;
+      else
+        {
+          NSUInteger nrects;
+          NSRect *glyphRects =
+            [layoutManager
+              rectArrayForGlyphRange:(NSMakeRange (0, numberOfGlyphs))
+              withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                     inTextContainer:textContainer rectCount:&nrects];
+
+          totalAdvance = NSMaxX (glyphRects[0]);
+        }
+
+      for (i = 0; i < used; i++)
+        {
+          struct ns_glyph_layout *gl;
+          NSPoint location;
+          NSUInteger nextGlyphIndex;
+          NSRange glyphRange;
+          NSRect *glyphRects;
+          NSUInteger nrects;
+
+          if (!RIGHT_TO_LEFT_P)
+            gl = glyph_layouts + i;
+          else
+            {
+              NSUInteger dest = permutation[i];
+
+              gl = glyph_layouts + dest;
+              if (i < dest)
+                {
+                  NSUInteger tmp = gl->string_index;
+
+                  gl->string_index = glyph_layouts[i].string_index;
+                  glyph_layouts[i].string_index = tmp;
+                }
+            }
+          gl->glyph_id = [layoutManager glyphAtIndex: glyphIndex];
+
+          location = [layoutManager locationForGlyphAtIndex:glyphIndex];
+          gl->baseline_delta = spaceLocation.y - location.y;
+
+          for (nextGlyphIndex = glyphIndex + 1; nextGlyphIndex < numberOfGlyphs;
+               nextGlyphIndex++)
+            if (![layoutManager
+                   notShownAttributeForGlyphAtIndex:nextGlyphIndex])
+              break;
+
+          if (!RIGHT_TO_LEFT_P)
+            {
+              CGFloat maxX;
+
+              if (prevGlyphIndex == 0)
+                glyphRange = NSMakeRange (0, nextGlyphIndex);
+              else
+                glyphRange = NSMakeRange (glyphIndex,
+                                          nextGlyphIndex - glyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              maxX = max (NSMaxX (glyphRects[0]), totalAdvance);
+              gl->advance_delta = location.x - totalAdvance;
+              gl->advance = maxX - totalAdvance;
+              totalAdvance = maxX;
+            }
+          else
+            {
+              CGFloat minX;
+
+              if (nextGlyphIndex == numberOfGlyphs)
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          numberOfGlyphs - prevGlyphIndex);
+              else
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          glyphIndex + 1 - prevGlyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              minX = min (NSMinX (glyphRects[0]), totalAdvance);
+              gl->advance = totalAdvance - minX;
+              totalAdvance = minX;
+              gl->advance_delta = location.x - totalAdvance;
+            }
+
+          prevGlyphIndex = glyphIndex + 1;
+          glyphIndex = nextGlyphIndex;
+        }
+
+      if (RIGHT_TO_LEFT_P)
+        xfree (permutation);
+
+#undef RIGHT_TO_LEFT_P
+
+      result = used;
+    }
+  [textStorage release];
+
+  return result;
+}
+
+static Lisp_Object
+nsfont_shape (Lisp_Object lgstring, Lisp_Object direction)
+{
+  struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  struct nsfont_info *font_info = (struct nsfont_info *) font;
+  struct ns_glyph_layout *glyph_layouts;
+  NSFont *nsfont = font_info->nsfont;
+  ptrdiff_t glyph_len, len, i;
+  Lisp_Object tem;
+  unichar *mb_buf;
+  NSUInteger used;
+
+  glyph_len = LGSTRING_GLYPH_LEN (lgstring);
+  for (i = 0; i < glyph_len; ++i)
+    {
+      tem = LGSTRING_GLYPH (lgstring, i);
+
+      if (NILP (tem))
+	break;
+    }
+
+  len = i;
+
+  if (INT_MAX / 2 < len)
+    memory_full (SIZE_MAX);
+
+  block_input ();
+
+  mb_buf = alloca (len * sizeof *mb_buf);
+
+  for (i = 0; i < len; ++i)
+    {
+      uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
+      mb_buf[i] = (unichar) c;
+    }
+
+  NSString *string = [NSString stringWithCharacters: mb_buf
+					     length: len];
+  unblock_input ();
+
+  if (!string)
+    return Qnil;
+
+  block_input ();
+
+  enum lgstring_direction dir = DIR_UNKNOWN;
+
+  if (EQ (direction, QL2R))
+    dir = DIR_L2R;
+  else if (EQ (direction, QR2L))
+    dir = DIR_R2L;
+  glyph_layouts = alloca (sizeof (struct ns_glyph_layout) * glyph_len);
+  used = ns_font_shape (nsfont, string, glyph_layouts, glyph_len, dir);
+
+  for (i = 0; i < used; i++)
+    {
+      Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
+      struct ns_glyph_layout *gl = glyph_layouts + i;
+      EMACS_INT from, to;
+      struct font_metrics metrics;
+
+      if (NILP (lglyph))
+        {
+          lglyph = LGLYPH_NEW ();
+          LGSTRING_SET_GLYPH (lgstring, i, lglyph);
+        }
+
+      from = gl->comp_range.location;
+      LGLYPH_SET_FROM (lglyph, from);
+
+      to = gl->comp_range.location + gl->comp_range.length;
+      LGLYPH_SET_TO (lglyph, to - 1);
+
+      /* LGLYPH_CHAR is used in `describe-char' for checking whether
+         the composition is trivial.  */
+      {
+        UTF32Char c;
+
+        if (mb_buf[gl->string_index] >= 0xD800
+            && mb_buf[gl->string_index] < 0xDC00)
+          c = (((mb_buf[gl->string_index] - 0xD800) << 10)
+               + (mb_buf[gl->string_index + 1] - 0xDC00) + 0x10000);
+        else
+          c = mb_buf[gl->string_index];
+
+        LGLYPH_SET_CHAR (lglyph, c);
+      }
+
+      {
+        unsigned long cc = gl->glyph_id;
+        LGLYPH_SET_CODE (lglyph, cc);
+      }
+
+      nsfont_text_extents (font, &gl->glyph_id, 1, &metrics);
+      LGLYPH_SET_WIDTH (lglyph, metrics.width);
+      LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
+      LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
+      LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
+      LGLYPH_SET_DESCENT (lglyph, metrics.descent);
+    }
+  unblock_input ();
+
+  return make_fixnum (used);
+}
 
 
 /* ==========================================================================
@@ -1134,6 +1647,50 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
    ========================================================================== */
 
+static NSGlyph
+ns_uni_to_glyphs_1 (struct nsfont_info *info, unsigned int c)
+{
+  unichar characters[] = { c };
+  NSString *string =
+    [NSString stringWithCharacters: characters
+			    length: 1];
+  NSDictionary *attributes =
+    [NSDictionary dictionaryWithObjectsAndKeys:
+		    info->nsfont, NSFontAttributeName, nil];
+  NSTextStorage *storage = [[NSTextStorage alloc] initWithString: string
+						      attributes: attributes];
+  NSTextContainer *text_container = [[NSTextContainer alloc] init];
+  NSLayoutManager *manager = [[NSLayoutManager alloc] init];
+
+  [manager addTextContainer: text_container];
+  [text_container release]; /* Retained by manager */
+  [storage addLayoutManager: manager];
+  [manager release]; /* Retained by storage */
+
+  NSFont *font_in_storage = [storage attribute: NSFontAttributeName
+				       atIndex:0
+				effectiveRange: NULL];
+  NSGlyph glyph = FONT_INVALID_CODE;
+
+  if ((font_in_storage == info->nsfont
+       || [[font_in_storage fontName] isEqualToString: [info->nsfont fontName]]))
+    {
+      @try
+	{
+	  glyph = [manager glyphAtIndex: 0];
+	}
+      @catch (NSException *e)
+	{
+	  /* GNUstep bug? */
+	  glyph = 'X';
+	}
+    }
+
+  [storage release];
+
+  return glyph;
+}
+
 /* Find and cache corresponding glyph codes for unicode values in given
    hi-byte block of 256.  */
 static void
@@ -1141,7 +1698,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   unichar *unichars = xmalloc (0x101 * sizeof (unichar));
   unsigned int i, g, idx;
-  unsigned short *glyphs;
+  unsigned int *glyphs;
 
   if (NSFONT_TRACE)
     fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
@@ -1149,7 +1706,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
   block_input ();
 
-  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
+  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned int));
   if (!unichars || !(font_info->glyphs[block]))
     emacs_abort ();
 
@@ -1166,7 +1723,8 @@ is false when (FROM > 0 || TO < S->nchars).  */
     for (i = 0; i < 0x100; i++, glyphs++)
       {
         g = unichars[i];
-        *glyphs = g;
+	NSGlyph glyph = ns_uni_to_glyphs_1 (font_info, g);
+        *glyphs = glyph;
       }
   }
 
@@ -1175,18 +1733,19 @@ is false when (FROM > 0 || TO < S->nchars).  */
 }
 
 
-/* Determine and cache metrics for corresponding glyph codes in given
-   hi-byte block of 256.  */
+/* Determine and cache metrics for glyphs in given hi-byte block of
+   256.  */
 static void
-ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
+ns_glyph_metrics (struct nsfont_info *font_info, unsigned int block)
 {
-  unsigned int i, g;
+  unsigned int i;
+  NSGlyph g;
   unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
   NSFont *sfont;
   struct font_metrics *metrics;
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
+    fprintf (stderr, "%p\tComputing metrics for glyphs in block %u\n",
             font_info, block);
 
   /* not implemented yet (as of startup 0.18), so punt */
@@ -1209,19 +1768,14 @@ is false when (FROM > 0 || TO < S->nchars).  */
       w = max ([sfont advancementForGlyph: g].width, 2.0);
       metrics->width = lrint (w);
 
-      lb = r.origin.x;
-      rb = r.size.width - w;
-      // Add to bearing for LCD smoothing.  We don't know if it is there.
-      if (lb < 0)
-        metrics->lbearing = round (lb - LCD_SMOOTHING_MARGIN);
-      if (font_info->ital)
-        rb += (CGFloat) (0.22F * font_info->height);
-      metrics->rbearing = lrint (w + rb + LCD_SMOOTHING_MARGIN);
-
-      metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
-      /* lrint (hshrink * [sfont ascender] + expand * hd/2); */
-      metrics->ascent = r.size.height - metrics->descent;
-      /* -lrint (hshrink* [sfont descender] - expand * hd/2); */
+      lb = NSMinX (r);
+      rb = NSMaxX (r);
+
+      metrics->rbearing = lrint (rb);
+      metrics->lbearing = lrint (lb);
+
+      metrics->descent = NSMinY (r);
+      metrics->ascent = NSMaxY (r);
     }
   unblock_input ();
 }
@@ -1257,6 +1811,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
   .has_char = nsfont_has_char,
   .encode_char = nsfont_encode_char,
   .text_extents = nsfont_text_extents,
+  .shape = nsfont_shape,
   .draw = nsfont_draw,
   };
 
@@ -1265,7 +1820,6 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   DEFSYM (Qcondensed, "condensed");
   DEFSYM (Qexpanded, "expanded");
-  DEFSYM (Qapple, "apple");
   DEFSYM (Qmedium, "medium");
   DEFVAR_LISP ("ns-reg-to-script", Vns_reg_to_script,
                doc: /* Internal use: maps font registry to Unicode script.  */);
diff --git a/src/nsterm.h b/src/nsterm.h
index 4bbcf43973..944dbd727c 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -820,7 +820,7 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
   XCharStruct max_bounds;
   /* We compute glyph codes and metrics on-demand in blocks of 256 indexed
      by hibyte, lobyte.  */
-  unsigned short **glyphs; /* map Unicode index to glyph */
+  unsigned int **glyphs; /* map Unicode index to glyph */
   struct font_metrics **metrics;
 };
 #endif
diff --git a/src/nsterm.m b/src/nsterm.m
index ed692f95a2..957cd815a0 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -270,7 +270,7 @@ - (NSColor *)colorUsingDefaultColorSpace
 /* display update */
 static struct frame *ns_updating_frame;
 static int ns_window_num = 0;
-static BOOL gsaved = NO;
+static int gsaved = 0;
 #ifdef NS_IMPL_COCOA
 static BOOL ns_menu_bar_is_hidden = NO;
 #endif
@@ -1074,15 +1074,25 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
       [view lockFocus];
     }
 
+  NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+  [ctx saveGraphicsState];
+  gsaved++;
+
   /* clipping */
   if (r)
     {
-      [[NSGraphicsContext currentContext] saveGraphicsState];
       if (n == 2)
         NSRectClipList (r, 2);
       else
         NSRectClip (*r);
-      gsaved = YES;
+#ifdef NS_IMPL_GNUSTEP
+      DPSrectclip (ctx, NSMinX (*r), NSMinY (*r),
+		   NSWidth (*r), NSHeight (*r));
+
+      if (n == 2)
+	DPSrectclip (ctx, NSMinX (r[1]), NSMinY (r[1]),
+		     NSWidth (r[1]), NSHeight (r[1]));
+#endif
     }
 }
 
@@ -1098,7 +1108,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
   if (gsaved)
     {
       [[NSGraphicsContext currentContext] restoreGraphicsState];
-      gsaved = NO;
+      gsaved--;
     }
 
   if (f != ns_updating_frame)
@@ -2433,9 +2443,6 @@ Hide the window (X11 semantics)
       EmacsView *view = FRAME_NS_VIEW (f);
       FRAME_POINTER_TYPE (f) = cursor;
       [[view window] invalidateCursorRectsForView: view];
-      /* Redisplay assumes this function also draws the changed frame
-         cursor, but this function doesn't, so do it explicitly.  */
-      gui_update_cursor (f, 1);
     }
 }
 
@@ -2847,31 +2854,31 @@ Hide the window (X11 semantics)
      External (RIF); compute left/right overhang of whole string and set in s
    -------------------------------------------------------------------------- */
 {
-  struct font *font = s->font;
-
-  if (s->char2b)
+  if (s->cmp == NULL
+      && (s->first_glyph->type == CHAR_GLYPH
+	  || s->first_glyph->type == COMPOSITE_GLYPH))
     {
       struct font_metrics metrics;
-      unsigned int codes[2];
-      codes[0] = *(s->char2b);
-      codes[1] = *(s->char2b + s->nchars - 1);
 
-      font->driver->text_extents (font, codes, 2, &metrics);
-      s->left_overhang = -metrics.lbearing;
-      s->right_overhang
-	= metrics.rbearing > metrics.width
-	? metrics.rbearing - metrics.width : 0;
+      if (s->first_glyph->type == CHAR_GLYPH)
+	{
+	  struct font *font = s->font;
+	  font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+	}
+      else
+	{
+	  Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+	  composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+	}
+      s->right_overhang = (metrics.rbearing > metrics.width
+			   ? metrics.rbearing - metrics.width : 0);
+      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
     }
-  else
+  else if (s->cmp)
     {
-      s->left_overhang = 0;
-#ifdef NS_IMPL_GNUSTEP
-      if (EQ (font->driver->type, Qns))
-        s->right_overhang = ((struct nsfont_info *)font)->ital ?
-          FONT_HEIGHT (font) * 0.2 : 0;
-      else
-#endif
-        s->right_overhang = 0;
+      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+      s->left_overhang = - s->cmp->lbearing;
     }
 }
 
@@ -3011,14 +3018,13 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   struct frame *f = WINDOW_XFRAME (w);
   struct glyph *phys_cursor_glyph;
   struct glyph *cursor_glyph;
-  struct face *face;
-  NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
 
   /* If cursor is out of bounds, don't draw garbage.  This can happen
      in mini-buffer windows when switching between echo area glyphs
      and mini-buffer.  */
 
-  NSTRACE ("ns_draw_window_cursor");
+  NSTRACE ("ns_draw_window_cursor (on = %d, cursor_type = %d)",
+	   on_p, cursor_type);
 
   if (!on_p)
     return;
@@ -3034,6 +3040,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
     {
+      NSTRACE_MSG ("No phys cursor glyph was found!");
+
       if (glyph_row->exact_window_width_line_p
           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
         {
@@ -3043,10 +3051,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       return;
     }
 
-  /* We draw the cursor (with NSRectFill), then draw the glyph on top
-     (other terminals do it the other way round).  We must set
-     w->phys_cursor_width to the cursor width.  For bar cursors, that
-     is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
 
   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
@@ -3080,15 +3084,7 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   ns_focus (f, &r, 1);
 
-  face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
-  if (face && NS_FACE_BACKGROUND (face)
-      == ns_index_color (FRAME_CURSOR_COLOR (f), f))
-    {
-      [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
-      hollow_color = FRAME_CURSOR_COLOR (f);
-    }
-  else
-    [FRAME_CURSOR_COLOR (f) set];
+  [FRAME_CURSOR_COLOR (f) set];
 
   switch (cursor_type)
     {
@@ -3096,13 +3092,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     case NO_CURSOR:
       break;
     case FILLED_BOX_CURSOR:
-      NSRectFill (r);
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
       break;
     case HOLLOW_BOX_CURSOR:
-      NSRectFill (r);
-      [hollow_color set];
-      NSRectFill (NSInsetRect (r, 1, 1));
-      [FRAME_CURSOR_COLOR (f) set];
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_NORMAL_TEXT);
+      [NSBezierPath strokeRect: r];
       break;
     case HBAR_CURSOR:
       NSRectFill (r);
@@ -3119,11 +3113,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       break;
     }
   ns_unfocus (f);
-
-  /* Draw the character under the cursor.  Other terms only draw
-     the character on top of box cursors, so do the same here.  */
-  if (cursor_type == FILLED_BOX_CURSOR || cursor_type == HOLLOW_BOX_CURSOR)
-    draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
 }
 
 
@@ -3303,16 +3292,18 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   if (s->for_overlaps)
     return;
 
+  if (s->hl == DRAW_CURSOR)
+    [FRAME_BACKGROUND_COLOR (s->f) set];
+  else if (face->underline_defaulted_p)
+    [defaultCol set];
+  else
+    [ns_lookup_indexed_color (face->underline_color, s->f) set];
+
   /* Do underline.  */
   if (face->underline)
     {
       if (s->face->underline == FACE_UNDER_WAVE)
         {
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
-
           ns_draw_underwave (s, width, x);
         }
       else if (s->face->underline == FACE_UNDER_LINE)
@@ -3383,11 +3374,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
           s->underline_position = position;
 
           r = NSMakeRect (x, s->ybase + position, width, thickness);
-
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
           NSRectFill (r);
         }
     }
@@ -3397,11 +3383,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     {
       NSRect r;
       r = NSMakeRect (x, s->y, width, 1);
-
-      if (face->overline_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->overline_color, s->f) set];
       NSRectFill (r);
     }
 
@@ -3424,10 +3405,6 @@ larger if there are taller display elements (e.g., characters
       dy = lrint ((glyph_height - h) / 2);
       r = NSMakeRect (x, glyph_y + dy, width, 1);
 
-      if (face->strike_through_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
       NSRectFill (r);
     }
 }
@@ -3575,17 +3552,7 @@ Function modeled after x_draw_glyph_string_box ().
   struct glyph *last_glyph;
   NSRect r;
   int hthickness, vthickness;
-  struct face *face;
-
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = s->face;
+  struct face *face = s->face;
 
   vthickness = face->box_vertical_line_width;
   hthickness = face->box_horizontal_line_width;
@@ -3659,34 +3626,26 @@ Function modeled after x_draw_glyph_string_box ().
 	  || FONT_TOO_HIGH (s->font)
           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
 	{
-          struct face *face;
-          if (s->hl == DRAW_MOUSE_FACE)
-            {
-              face
-		= FACE_FROM_ID_OR_NULL (s->f,
-					MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-              if (!face)
-                face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-            }
-          else
-            face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+          struct face *face = s->face;
           if (!face->stipple)
-            [(NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)) set];
+	    {
+	      if (s->hl != DRAW_CURSOR)
+		[(NS_FACE_BACKGROUND (face) != 0
+		  ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+		  : FRAME_BACKGROUND_COLOR (s->f)) set];
+	      else
+		[FRAME_CURSOR_COLOR (s->f) set];
+	    }
           else
             {
               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
             }
 
-          if (s->hl != DRAW_CURSOR)
-            {
-              NSRect r = NSMakeRect (s->x, s->y + box_line_width,
-                                    s->background_width,
-                                    s->height-2*box_line_width);
-              NSRectFill (r);
-            }
+	  NSRect r = NSMakeRect (s->x, s->y + box_line_width,
+				 s->background_width,
+				 s->height-2*box_line_width);
+	  NSRectFill (r);
 
 	  s->background_filled_p = 1;
 	}
@@ -3707,7 +3666,7 @@ Function modeled after x_draw_glyph_string_box ().
   int th;
   char raised_p;
   NSRect br;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *tdCol;
 
   NSTRACE ("ns_dumpglyphs_image");
@@ -3728,15 +3687,6 @@ Function modeled after x_draw_glyph_string_box ().
   /* Draw BG: if we need larger area than image itself cleared, do that,
      otherwise, since we composite the image under NS (instead of mucking
      with its background color), we must clear just the image area.  */
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-       face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
 
   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
 
@@ -3807,16 +3757,8 @@ Function modeled after x_draw_glyph_string_box ().
 
   if (s->hl == DRAW_CURSOR)
     {
-    [FRAME_CURSOR_COLOR (s->f) set];
-    if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
+      [FRAME_CURSOR_COLOR (s->f) set];
       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
-    else
-      /* Currently on NS img->mask is always 0.  Since
-         get_window_cursor_type specifies a hollow box cursor when on
-         a non-masked image we never reach this clause.  But we put it
-         in, in anticipation of better support for image masks on
-         NS.  */
-      tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
     }
   else
     {
@@ -3871,7 +3813,7 @@ Function modeled after x_draw_glyph_string_box ().
   NSRect r[2];
   NSRect glyphRect;
   int n;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *fgCol, *bgCol;
 
   if (!s->background_filled_p)
@@ -3879,48 +3821,22 @@ Function modeled after x_draw_glyph_string_box ().
       n = ns_get_glyph_string_clip_rect (s, r);
       ns_focus (s->f, r, n);
 
-      if (s->hl == DRAW_MOUSE_FACE)
-        {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-        }
-      else
-        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+      face = s->face;
 
       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
 
+      if (s->hl == DRAW_CURSOR)
+	{
+	  fgCol = bgCol;
+	  bgCol = FRAME_CURSOR_COLOR (s->f);
+	}
+
       glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
 
       [bgCol set];
 
-      /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
-         overwriting cursor (usually when cursor on a tab) */
-      if (s->hl == DRAW_CURSOR)
-        {
-          CGFloat x, width;
-
-          /* FIXME: This looks like it will only work for left to
-             right languages.  */
-          x = NSMinX (glyphRect);
-          width = s->w->phys_cursor_width;
-          glyphRect.size.width -= width;
-          glyphRect.origin.x += width;
-
-          NSRectFill (glyphRect);
-
-          /* Draw overlining, etc. on the cursor. */
-          if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-            ns_draw_text_decoration (s, face, bgCol, width, x);
-          else
-            ns_draw_text_decoration (s, face, fgCol, width, x);
-        }
-      else
-        {
-          NSRectFill (glyphRect);
-        }
+      NSRectFill (glyphRect);
 
       /* Draw overlining, etc. on the stretch glyph (or the part
          of the stretch glyph after the cursor). */
@@ -3936,7 +3852,7 @@ overwriting cursor (usually when cursor on a tab) */
 static void
 ns_draw_glyph_string_foreground (struct glyph_string *s)
 {
-  int x, flags;
+  int x;
   struct font *font = s->font;
 
   /* If first glyph of S has a left box line, start drawing the text
@@ -3947,15 +3863,9 @@ overwriting cursor (usually when cursor on a tab) */
   else
     x = s->x;
 
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
   font->driver->draw
     (s, s->cmp_from, s->nchars, x, s->ybase,
-     (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
-     || flags == NS_DUMPGLYPH_MOUSEFACE);
+     !s->for_overlaps && !s->background_filled_p);
 }
 
 
@@ -4062,9 +3972,9 @@ overwriting cursor (usually when cursor on a tab) */
   struct font *font = s->face->font;
   if (! font) font = FRAME_FONT (s->f);
 
-  NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
+  NSTRACE ("ns_draw_glyph_string (hl = %u)", s->hl);
 
-  if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
+  if (s->next && s->right_overhang && !s->for_overlaps)
     {
       int width;
       struct glyph_string *next;
@@ -4101,6 +4011,18 @@ overwriting cursor (usually when cursor on a tab) */
       box_drawn_p = 1;
     }
 
+  bool must_unfocus = false;
+
+  if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+      && !s->clip_tail
+      && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+	  || (s->next && s->next->hl != s->hl && s->right_overhang)))
+    {
+      NSRect rect = NSMakeRect (s->x, s->y, s->width, s->height);
+      ns_focus (s->f, &rect, 1);
+      must_unfocus = true;
+    }
+
   switch (s->first_glyph->type)
     {
 
@@ -4124,28 +4046,19 @@ overwriting cursor (usually when cursor on a tab) */
       n = ns_get_glyph_string_clip_rect (s, r);
       ns_focus (s->f, r, n);
 
-      if (s->for_overlaps || (s->cmp_from > 0
-			      && ! s->first_glyph->u.cmp.automatic))
-        s->background_filled_p = 1;
+      BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+      if (s->for_overlaps || (isComposite
+			      && (s->cmp_from > 0
+				  && ! s->first_glyph->u.cmp.automatic)))
+	s->background_filled_p = 1;
       else
         ns_maybe_dumpglyphs_background
           (s, s->first_glyph->type == COMPOSITE_GLYPH);
 
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
-      {
-        BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
-
-        if (isComposite)
-          ns_draw_composite_glyph_string_foreground (s);
-        else
-          ns_draw_glyph_string_foreground (s);
-      }
+      if (isComposite)
+	ns_draw_composite_glyph_string_foreground (s);
+      else
+	ns_draw_glyph_string_foreground (s);
 
       {
         NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
@@ -4158,13 +4071,6 @@ overwriting cursor (usually when cursor on a tab) */
         ns_draw_text_decoration (s, s->face, col, s->width, s->x);
       }
 
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
       ns_unfocus (s->f);
       break;
 
@@ -4197,7 +4103,88 @@ overwriting cursor (usually when cursor on a tab) */
       ns_unfocus (s->f);
     }
 
+  /* Draw surrounding overhangs. */
+  if (s->prev)
+    {
+      struct glyph_string *prev;
+
+      for (prev = s->prev; prev; prev = prev->prev)
+	if (prev->hl != s->hl
+	    && prev->x + prev->width + prev->right_overhang > s->x)
+	  {
+	    /* As prev was drawn while clipped to its own area, we
+	       must draw the right_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = prev->hl;
+	    struct face *save_face = prev->face;
+
+	    prev->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    ns_focus (s->f, &r, 1);
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    prev->num_clips = 1;
+	    prev->hl = s->hl;
+	    if (prev->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (prev);
+	    else
+	      ns_draw_composite_glyph_string_foreground (prev);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    ns_unfocus (s->f);
+	    prev->hl = save;
+	    prev->face = save_face;
+	    prev->num_clips = 0;
+	  }
+    }
+
+  if (s->next)
+    {
+      struct glyph_string *next;
+
+      for (next = s->next; next; next = next->next)
+	if (next->hl != s->hl
+	    && next->x - next->left_overhang < s->x + s->width)
+	  {
+	    /* As next will be drawn while clipped to its own area,
+	       we must draw the left_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = next->hl;
+	    struct face *save_face = next->face;
+
+	    next->hl = s->hl;
+	    next->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    ns_focus (s->f, NULL, 0);
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    next->num_clips = 1;
+	    if (next->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (next);
+	    else
+	      ns_draw_composite_glyph_string_foreground (next);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    ns_unfocus (s->f);
+	    next->hl = save;
+	    next->num_clips = 0;
+	    next->face = save_face;
+	    next->clip_head = next;
+	    next->background_filled_p = 0;
+	  }
+    }
   s->num_clips = 0;
+
+  if (must_unfocus)
+    ns_unfocus (s->f);
 }
 
 
diff --git a/src/xdisp.c b/src/xdisp.c
index bbe7e2701b..9998677262 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -29297,7 +29297,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
   for (s = head; s; s = s->next)
     FRAME_RIF (f)->draw_glyph_string (s);
 
-#ifndef HAVE_NS
   /* When focus a sole frame and move horizontally, this clears on_p
      causing a failure to erase prev cursor position. */
   if (area == TEXT_AREA
@@ -29316,7 +29315,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
       notice_overwritten_cursor (w, TEXT_AREA, x0, x1,
 				 row->y, MATRIX_ROW_BOTTOM_Y (row));
     }
-#endif
 
   /* Value is the x-position up to which drawn, relative to AREA of W.
      This doesn't include parts drawn because of overhangs.  */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-26 11:41 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-26 12:39   ` Alan Third
  2021-10-26 12:50     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 22+ messages in thread
From: Alan Third @ 2021-10-26 12:39 UTC (permalink / raw)
  To: Po Lu; +Cc: 51411

On Tue, Oct 26, 2021 at 07:41:31PM +0800, Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors wrote:
> > Is there any reason to nest ns_focus? There are (according to Apple)
> > performance reasons to not save the context unless you really need to.
> 
> Right now it's used in the code that clips to the exact bounds of a
> string, if a string's overhangs are already drawn.
> 
> I couldn't find a cleaner way to do this, and that situation is rare, so
> I think the performance problems will usually be avoided.

Is this the code in ns_draw_glyph_string?

> > I mean alt as defined by GNUstep in the quote I sent you in my last
> > email. I don't think it's our job to say that GNUstep's choice of
> > defaults is wrong and therefore do something that would be unexpected
> > for a GNUstep user.
> 
> Well, on every other platform Meta is on the alt on the users' keyboard.
> Emacs in general doesn't conform to the platform expectations WRT to key
> bindings, so I think being consistent with Emacs on other platforms is
> more important here.

I think we normally accept what the system tells us is Alt, or the
nearest equivalent. In this case GNUstep tells us what it's
understanding of the nearest equivalent (option) is, and I don't see
any reason to override that.

So please remove the changes to modifier keys.
-- 
Alan Third





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-26 12:39   ` Alan Third
@ 2021-10-26 12:50     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-27 17:20       ` Alan Third
  0 siblings, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-26 12:50 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

[-- Attachment #1: Type: text/plain, Size: 426 bytes --]

Alan Third <alan@idiocy.org> writes:

> Is this the code in ns_draw_glyph_string?

Yes, it is.

> I think we normally accept what the system tells us is Alt, or the
> nearest equivalent. In this case GNUstep tells us what it's
> understanding of the nearest equivalent (option) is, and I don't see
> any reason to override that.

> So please remove the changes to modifier keys.

Fair enough, thanks for the feedback.  Done.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-General-improvements-to-NS-port.patch --]
[-- Type: text/x-patch, Size: 12119 bytes --]

From b4060ca7357091ef0ba6abf3faf0314321cd85be Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:51:31 +0800
Subject: [PATCH] General improvements to NS port

* src/dispextern.h: Remove some !HAVE_NS conditionals around grab
related code.
* src/frame.c (gui_mouse_grabbed, gui_redo_mouse_highlight): Remove
!HAVE_NS conditionals around code.

* src/nsmenu.m (ns_update_menubar): Prevent recursive calls and enable
shallow updates on GNUstep.
(menuNeedsUpdate): Prevent recursive calls.
(ns_menu_show): Fix mysterious GC-related bug.
(update_frame_tool_bar_1): Work around mysterious toolbar sizing bug
on GNUstep.

* src/nsterm.h (struct ns_output): New field for tracking toolbar
visibility changes.

* src/nsterm.m (frame_set_mouse_pixel_position): Implement for
GNUstep.
(ns_redraw_scroll_bars): Enable for GNUstep.
(ns_clear_frame): Redraw scrollbars on GNUstep.
(ns_update_window_end): New function.
(ns_redisplay_interface): Add ns_update_window_end on GNUstep.
(- keyDown): Remove debug code that doesn't work on GNUstep.
(- mouseDown): Enable grab tracking on NS port.
(- resizeWithOldSuperviewSize): Fix build with NSTRACE.

* src/xdisp.c (note_tab_bar_highlight): Enable some code for NS port.
---
 src/dispextern.h |  2 --
 src/frame.c      |  4 ---
 src/nsmenu.m     | 76 +++++++++++++++++++++++++++++++++++++++---------
 src/nsterm.h     |  6 ++++
 src/nsterm.m     | 42 +++++++++++++++++++++-----
 src/xdisp.c      |  2 --
 6 files changed, 102 insertions(+), 30 deletions(-)

diff --git a/src/dispextern.h b/src/dispextern.h
index 08dac5d455..58e9048556 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3722,10 +3722,8 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask)			      \
                                           const char *, const char *,
                                           enum resource_types);
 
-#ifndef HAVE_NS /* These both used on W32 and X only.  */
 extern bool gui_mouse_grabbed (Display_Info *);
 extern void gui_redo_mouse_highlight (Display_Info *);
-#endif /* HAVE_NS */
 
 #endif /* HAVE_WINDOW_SYSTEM */
 
diff --git a/src/frame.c b/src/frame.c
index 2b1cb452ef..79a7c89e0d 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -5028,8 +5028,6 @@ gui_set_no_special_glyphs (struct frame *f, Lisp_Object new_value, Lisp_Object o
 }
 
 
-#ifndef HAVE_NS
-
 /* Non-zero if mouse is grabbed on DPYINFO
    and we know the frame where it is.  */
 
@@ -5054,8 +5052,6 @@ gui_redo_mouse_highlight (Display_Info *dpyinfo)
 			  dpyinfo->last_mouse_motion_y);
 }
 
-#endif /* HAVE_NS */
-
 /* Subroutines of creating an X frame.  */
 
 /* Make sure that Vx_resource_name is set to a reasonable value.
diff --git a/src/nsmenu.m b/src/nsmenu.m
index 05b89c2f56..b93d3a79bd 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -101,6 +101,15 @@
 static void
 ns_update_menubar (struct frame *f, bool deep_p)
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+
+  if (inside)
+    return;
+
+  inside++;
+#endif
+
   BOOL needsSet = NO;
   id menu = [NSApp mainMenu];
   bool owfi;
@@ -120,7 +129,12 @@
   NSTRACE ("ns_update_menubar");
 
   if (f != SELECTED_FRAME () || FRAME_EXTERNAL_MENU_BAR (f) == 0)
+    {
+#ifdef NS_IMPL_GNUSTEP
+      inside--;
+#endif
       return;
+    }
   XSETFRAME (Vmenu_updating_frame, f);
 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
 
@@ -144,10 +158,6 @@
   t = -(1000*tb.time+tb.millitm);
 #endif
 
-#ifdef NS_IMPL_GNUSTEP
-  deep_p = 1; /* See comment in menuNeedsUpdate.  */
-#endif
-
   if (deep_p)
     {
       /* Make a widget-value tree representing the entire menu trees.  */
@@ -275,6 +285,9 @@
 	  free_menubar_widget_value_tree (first_wv);
 	  discard_menu_items ();
 	  unbind_to (specpdl_count, Qnil);
+#ifdef NS_IMPL_GNUSTEP
+	  inside--;
+#endif
 	  return;
 	}
 
@@ -408,6 +421,10 @@
   if (needsSet)
     [NSApp setMainMenu: menu];
 
+#ifdef NS_IMPL_GNUSTEP
+  inside--;
+#endif
+
   unblock_input ();
 
 }
@@ -490,17 +507,34 @@ - (instancetype)initWithTitle: (NSString *)title
    call to ns_update_menubar.  */
 - (void)menuNeedsUpdate: (NSMenu *)menu
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+#endif
+
   if (!FRAME_LIVE_P (SELECTED_FRAME ()))
     return;
 
-#ifdef NS_IMPL_COCOA
-/* TODO: GNUstep calls this method when the menu is still being built
-   which results in a recursive stack overflow.  One possible solution
-   is to use menuWillOpen instead, but the Apple docs explicitly warn
-   against changing the contents of the menu in it.  I don't know what
-   the right thing to do for GNUstep is.  */
+#ifdef NS_IMPL_GNUSTEP
+  /* GNUstep calls this method when the menu is still being built
+     which results in a recursive stack overflow, which this variable
+     prevents.  */
+
+  if (!inside)
+    ++inside;
+  else
+    return;
+#endif
+
   if (needsUpdate)
-    ns_update_menubar (SELECTED_FRAME (), true);
+    {
+#ifdef NS_IMPL_GNUSTEP
+      needsUpdate = NO;
+#endif
+      ns_update_menubar (SELECTED_FRAME (), true);
+    }
+
+#ifdef NS_IMPL_GNUSTEP
+  --inside;
 #endif
 }
 
@@ -827,6 +861,9 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   p.x = x; p.y = y;
 
+  /* Don't GC due to a mysterious bug.  */
+  inhibit_garbage_collection ();
+
   /* now parse stage 2 as in ns_update_menubar */
   wv = make_widget_value ("contextmenu", NULL, true, Qnil);
   wv->button_type = BUTTON_TYPE_NONE;
@@ -998,15 +1035,17 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   pmenu = [[EmacsMenu alloc] initWithTitle:
                    NILP (title) ? @"" : [NSString stringWithLispString: title]];
+  /* On GNUstep, this call makes menu_items nil for whatever reason
+     when displaying a context menu from `context-menu-mode'.  */
+  Lisp_Object items = menu_items;
   [pmenu fillWithWidgetValue: first_wv->contents];
+  menu_items = items;
   free_menubar_widget_value_tree (first_wv);
-  unbind_to (specpdl_count, Qnil);
-
   popup_activated_flag = 1;
   tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
   popup_activated_flag = 0;
   [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
-
+  unbind_to (specpdl_count, Qnil);
   unblock_input ();
   return tem;
 }
@@ -1057,6 +1096,15 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
   [toolbar clearActive];
 #else
   [toolbar clearAll];
+  /* It takes at least 3 such adjustments to fix an issue where the
+     tool bar is 2x too tall when a frame's tool bar is first shown.
+     This is ugly, but I have no other solution for this problem.  */
+  if (FRAME_OUTPUT_DATA (f)->tool_bar_adjusted < 3)
+    {
+      [toolbar setVisible: NO];
+      FRAME_OUTPUT_DATA (f)->tool_bar_adjusted++;
+      [toolbar setVisible: YES];
+    }
 #endif
 
   /* Update EmacsToolbar as in GtkUtils, build items list.  */
diff --git a/src/nsterm.h b/src/nsterm.h
index 944dbd727c..8175f99664 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -978,6 +978,12 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
 
   /* Non-zero if we are doing an animation, e.g. toggling the tool bar.  */
   int in_animation;
+
+#ifdef NS_IMPL_GNUSTEP
+  /* Zero if this is the first time a toolbar has been updated on this
+     frame. */
+  int tool_bar_adjusted;
+#endif
 };
 
 /* This dummy declaration needed to support TTYs.  */
diff --git a/src/nsterm.m b/src/nsterm.m
index 957cd815a0..cb308ea23c 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -65,6 +65,7 @@ Updated by Christian Limpach (chris@nice.ch)
 
 #ifdef NS_IMPL_GNUSTEP
 #include "process.h"
+#import <GNUstepGUI/GSDisplayServer.h>
 #endif
 
 #ifdef NS_IMPL_COCOA
@@ -2259,13 +2260,19 @@ Hide the window (X11 semantics)
 {
   NSTRACE ("frame_set_mouse_pixel_position");
 
-  /* FIXME: what about GNUstep?  */
 #ifdef NS_IMPL_COCOA
   CGPoint mouse_pos =
     CGPointMake(f->left_pos + pix_x,
                 f->top_pos + pix_y +
                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
   CGWarpMouseCursorPosition (mouse_pos);
+#else
+  GSDisplayServer *server = GSServerForWindow ([FRAME_NS_VIEW (f) window]);
+  [server setMouseLocation: NSMakePoint (f->left_pos + pix_x,
+					 f->top_pos + pix_y
+					 + FRAME_NS_TITLEBAR_HEIGHT(f)
+					 + FRAME_TOOLBAR_HEIGHT(f))
+		  onScreen: [[[FRAME_NS_VIEW (f) window] screen] screenNumber]];
 #endif
 }
 
@@ -2578,8 +2585,7 @@ Hide the window (X11 semantics)
    ========================================================================== */
 
 
-#if 0
-/* FIXME: Remove this function. */
+#ifdef NS_IMPL_GNUSTEP
 static void
 ns_redraw_scroll_bars (struct frame *f)
 {
@@ -2624,10 +2630,9 @@ Hide the window (X11 semantics)
   NSRectFill (r);
   ns_unfocus (f);
 
-  /* as of 2006/11 or so this is now needed */
-  /* FIXME: I don't see any reason for this and removing it makes no
-     difference here.  Do we need it for GNUstep?  */
-  //ns_redraw_scroll_bars (f);
+#ifdef NS_IMPL_GNUSTEP
+  ns_redraw_scroll_bars (f);
+#endif
   unblock_input ();
 }
 
@@ -4933,6 +4938,17 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
 {
 }
 
+#ifdef NS_IMPL_GNUSTEP
+static void
+ns_update_window_end (struct window *w, bool cursor_on_p,
+		      bool mouse_face_overwritten_p)
+{
+  NSTRACE ("ns_update_window_end (cursor_on_p = %d)", cursor_on_p);
+
+  ns_redraw_scroll_bars (WINDOW_XFRAME (w));
+}
+#endif
+
 /* This and next define (many of the) public functions in this file.  */
 /* gui_* are generic versions in xdisp.c that we, and other terms, get away
          with using despite presence in the "system dependent" redisplay
@@ -4949,7 +4965,11 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
   ns_scroll_run,
   ns_after_update_window_line,
   NULL, /* update_window_begin */
+#ifndef NS_IMPL_GNUSTEP
   NULL, /* update_window_end   */
+#else
+  ns_update_window_end,
+#endif
   0, /* flush_display */
   gui_clear_window_mouse_face,
   gui_get_glyph_overhangs,
@@ -6177,9 +6197,11 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
       Lisp_Object kind = fnKeysym ? QCfunction : QCordinary;
       emacs_event->modifiers = EV_MODIFIERS2 (flags, kind);
 
+#ifndef NS_IMPL_GNUSTEP
       if (NS_KEYLOG)
         fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
                  code, fnKeysym, flags, emacs_event->modifiers);
+#endif
 
       /* If it was a function key or had control-like modifiers, pass
          it directly to Emacs.  */
@@ -6692,6 +6714,11 @@ - (void)mouseDown: (NSEvent *)theEvent
       emacs_event->code = EV_BUTTON (theEvent);
       emacs_event->modifiers = EV_MODIFIERS (theEvent)
                              | EV_UDMODIFIERS (theEvent);
+
+      if (emacs_event->modifiers & down_modifier)
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed |= 1 << EV_BUTTON (theEvent);
+      else
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed &= ~(1 << EV_BUTTON (theEvent));
     }
 
   XSETINT (emacs_event->x, lrint (p.x));
@@ -6992,7 +7019,6 @@ - (void)resizeWithOldSuperviewSize: (NSSize)oldSize
   height = (int)NSHeight (frame);
 
   NSTRACE_SIZE ("New size", NSMakeSize (width, height));
-  NSTRACE_SIZE ("Original size", size);
 
   /* Reset the frame size to match the bounds of the superview (the
      NSWindow's contentView).  We need to do this as sometimes the
diff --git a/src/xdisp.c b/src/xdisp.c
index 9998677262..bfe7c571ab 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13891,7 +13891,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   clear_mouse_face (hlinfo);
 
   bool mouse_down_p = false;
-#ifndef HAVE_NS
   /* Mouse is down, but on different tab-bar item?  Or alternatively,
      the mouse might've been pressed somewhere we don't know about,
      and then have moved onto the tab bar.  In this case,
@@ -13904,7 +13903,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   if (mouse_down_p && f->last_tab_bar_item != prop_idx
       && f->last_tab_bar_item != -1)
     return;
-#endif
   draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED;
 
   /* If tab-bar item is not enabled, don't highlight it.  */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-26 12:50     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-27 17:20       ` Alan Third
  2021-10-28  1:09         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 22+ messages in thread
From: Alan Third @ 2021-10-27 17:20 UTC (permalink / raw)
  To: Po Lu; +Cc: 51411

On Tue, Oct 26, 2021 at 08:50:52PM +0800, Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors wrote:
> Alan Third <alan@idiocy.org> writes:
> 
> > Is this the code in ns_draw_glyph_string?
> 
> Yes, it is.

That function is a real mess of calls to ns_focus and ns_unfocus. I
think there's no good reason for them to be called as often as they
are, and some related functions (ns_dumpglyphs_stretch) don't need to
call them themselves either.

I think we need to have a rethink of how clipping is handled here. We
don't need to use ns_focus to clip and we repeatedly call ns_focus on
the same rectangle. For a lot of those calls we could replace them
with a single clipping rectangle and adjust it as required for
overhangs or whatever.

I also think you misunderstood what I was saying about the performance
problems with calling saveGraphicsState. Calling it when not required
is frowned upon by Apple as they say it causes performance problems.
Your code is now calling it in ns_focus whether it's required or not.
Ideally we only call it when modifying the clipping rectangle, because
there are times ns_focus is called where we don't modify the clipping
rectangle and therefore we don't need to save the graphics context.

I understand what you did, but I think the better solution is for us
is to try to untangle ns_draw_glyph_string, even if that means saving
the context there directly on occasion.

If you want me to have a look at it let me know, I think I know what
needs to be done, but I probably won't get round to it as soon as you
could.
-- 
Alan Third





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-27 17:20       ` Alan Third
@ 2021-10-28  1:09         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-28 10:17           ` Alan Third
  0 siblings, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-28  1:09 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

[-- Attachment #1: Type: text/plain, Size: 1789 bytes --]

Alan Third <alan@idiocy.org> writes:

> That function is a real mess of calls to ns_focus and ns_unfocus. I
> think there's no good reason for them to be called as often as they
> are, and some related functions (ns_dumpglyphs_stretch) don't need to
> call them themselves either.

> I think we need to have a rethink of how clipping is handled here. We
> don't need to use ns_focus to clip and we repeatedly call ns_focus on
> the same rectangle. For a lot of those calls we could replace them
> with a single clipping rectangle and adjust it as required for
> overhangs or whatever.

> I also think you misunderstood what I was saying about the performance
> problems with calling saveGraphicsState. Calling it when not required
> is frowned upon by Apple as they say it causes performance problems.
> Your code is now calling it in ns_focus whether it's required or not.
> Ideally we only call it when modifying the clipping rectangle, because
> there are times ns_focus is called where we don't modify the clipping
> rectangle and therefore we don't need to save the graphics context.

I understand what you mean now, thanks.

> I understand what you did, but I think the better solution is for us
> is to try to untangle ns_draw_glyph_string, even if that means saving
> the context there directly on occasion.

> If you want me to have a look at it let me know, I think I know what
> needs to be done, but I probably won't get round to it as soon as you
> could.

Thanks.  I had a try at it, cleaning up much of what appeared obviously
unnecessary.  (Though I did not dare change what seemed to be mysterious
to me.)

Though, OTOH, I think calling saveGraphicsState inside the overhang draw
process is not too much of a problem as it happens infrequently enough
to not be relevant.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-font-display-on-NS-port.patch --]
[-- Type: text/x-patch, Size: 78636 bytes --]

From 6e21a00e22a4fccecfc0dd41617217adcdb7e724 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:44:03 +0800
Subject: [PATCH] Improve font display on NS port

* src/nsfns.m (Fx_create_frame): Use "fixed" for the default font on
GNUstep.

* src/nsfont.m (LCD_SMOOTHING_MARGIN, ns_escape_name)
(ns_unescape_name, ns_attribute_fvalue)
(STYLE_REF): Remove unused defines and functions.

(struct ns_glyph_layout, enum lgstring_direction).
(enum gs_font_slant, enum gs_font_weight, enum gs_font_width)
(enum gs_specified, struct gs_font_data): New enumerators and
structures.

(ns_font_descs_match_p)
(ns_done_font_data, ns_get_font_data): New functions.
(ns_glyph_metrics): Stop escaping names.

(ns_spec_to_descriptor): Fix font descriptor creation for symbolic
font spec entires.
(ns_descriptor_to_entity): Create entries with the correct symbolic
styles.

(ns_fallback_entity): Fix fallback entity selection.
(ns_findfonts): Use our own font matcher instead of the broken GNUstep
matcher.

(ns_list_family): Remove obsolete comment.
(nsfont_open): Remove obsolete code, comments, and synthItal logic
which doesn't work on GNUstep.

(nsfont_encode_char): Use a type that can fit NSGlyph
(nsfont_draw): Chose correct font, remove obsolete mouse face logic,
obsolete comments, and switch to using glyph-based drawing instead of
character-based drawing.

(ns_font_shape, nsfont_shape): New functions.

(ns_uni_to_glyphs_1): New function.
(ns_uni_to_glyphs): Return glyphs instead of unicode codepoints.

(ns_glyph_metrics): Use NSGlyphs instead of unicode codepoints and fix
left bearing, right bearing, ascent and descent computation.

(struct nsfont_driver): Add shaping capability.

* src/nsterm.h (struct nsfont_info): Use unsigned int for glyph cache.

* src/nsterm.c (ns_focus): Set DPS clipping on GNUstep.

(ns_compute_glyph_string_overhangs): Fix overhang computation by using
xterm code.
(ns_draw_window_cursor): Simplify cursor drawing.
(ns_maybe_dumpglyphs_background): Test for cursor HL and remove
obsolete mouse face logic.
(ns_dumpglyphs_image)
(ns_dumpglyphs_box_or_relief): Rectify for new cursor logic.
(ns_dumpglyphs_stretch): Rectify for new cursor logic and rely on
ns_draw_glyph_string to set focus.
(ns_draw_glyph_string_foreground): Remove mouse face logic.
(ns_draw_glyph_strings): Implement overhangs, remove obsolete
comment, and always focus before dumping glyphs.
(ns_draw_text_decoration): Add condition for DRAW_CURSOR and simplify
color selection.
(ns_define_frame_cursor): Remove nonsensical code (define_frame_cursor
has nothing to do with the text cursor, aka caret).

* src/xdisp.c (draw_glyphs): Enable code for NS port to fix mouse face
cursor display.

* src/macfont.m (get_cgcolor_from_nscolor): New function.
(macfont_draw): Remove obsolete mouse-face code and enable cursor
display.
---
 src/macfont.m |   36 +-
 src/nsfns.m   |    6 +
 src/nsfont.m  | 1215 +++++++++++++++++++++++++++++++++++--------------
 src/nsterm.h  |    2 +-
 src/nsterm.m  |  415 ++++++++---------
 src/xdisp.c   |    2 -
 6 files changed, 1093 insertions(+), 583 deletions(-)

diff --git a/src/macfont.m b/src/macfont.m
index d86f09f485..df552400e3 100644
--- a/src/macfont.m
+++ b/src/macfont.m
@@ -613,6 +613,21 @@ static void mac_font_get_glyphs_for_variants (CFDataRef, UTF32Char,
   return cgColor;
 }
 
+static CGColorRef
+get_cgcolor_from_nscolor (NSColor *nsColor, struct frame *f)
+{
+  [nsColor set];
+  CGColorSpaceRef colorSpace = [[nsColor colorSpace] CGColorSpace];
+  NSInteger noc = [nsColor numberOfComponents];
+  CGFloat *components = xmalloc (sizeof(CGFloat)*(1+noc));
+  CGColorRef cgColor;
+
+  [nsColor getComponents: components];
+  cgColor = CGColorCreate (colorSpace, components);
+  xfree (components);
+  return cgColor;
+}
+
 #define CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND(context, face, f)        \
   do {                                                                  \
     CGColorRef refcol_ = get_cgcolor (NS_FACE_FOREGROUND (face), f);    \
@@ -2907,14 +2922,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
 
   if (!CGRectIsNull (background_rect))
     {
-      if (s->hl == DRAW_MOUSE_FACE)
+      if (s->hl == DRAW_CURSOR)
         {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-				       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_CURSOR_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
         }
-      CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
       CGContextFillRects (context, &background_rect, 1);
     }
 
@@ -2923,7 +2938,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
       CGAffineTransform atfm;
 
       CGContextScaleCTM (context, 1, -1);
-      CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
+      if (s->hl == DRAW_CURSOR)
+        {
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_BACKGROUND_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
+        }
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
       if (macfont_info->synthetic_italic_p)
         atfm = synthetic_italic_atfm;
       else
diff --git a/src/nsfns.m b/src/nsfns.m
index 797d0ce782..f4d8172246 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -1236,6 +1236,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                          "fontBackend", "FontBackend", RES_TYPE_STRING);
 
   {
+#ifdef NS_IMPL_COCOA
     /* use for default font name */
     id font = [NSFont userFixedPitchFontOfSize: -1.0]; /* default */
     gui_default_parameter (f, parms, Qfontsize,
@@ -1250,6 +1251,11 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                            build_string (fontname),
                            "font", "Font", RES_TYPE_STRING);
     xfree (fontname);
+#else
+    gui_default_parameter (f, parms, Qfont,
+                           build_string ("fixed"),
+                           "font", "Font", RES_TYPE_STRING);
+#endif
   }
   unblock_input ();
 
diff --git a/src/nsfont.m b/src/nsfont.m
index 5a9cdfebc0..b3224629f0 100644
--- a/src/nsfont.m
+++ b/src/nsfont.m
@@ -1,4 +1,4 @@
-/* Font back-end driver for the NeXT/Open/GNUstep and macOS window system.
+/* Font back-end driver for the GNUstep window system.
    See font.h
    Copyright (C) 2006-2021 Free Software Foundation, Inc.
 
@@ -38,47 +38,269 @@
 #include "termchar.h"
 #include "pdumper.h"
 
-/* TODO: Drop once we can assume gnustep-gui 0.17.1.  */
+#import <Foundation/NSException.h>
 #import <AppKit/NSFontDescriptor.h>
+#import <AppKit/NSLayoutManager.h>
+#import <GNUstepGUI/GSLayoutManager.h>
+#import <GNUstepGUI/GSFontInfo.h>
 
 #define NSFONT_TRACE 0
-#define LCD_SMOOTHING_MARGIN 2
 
-/* Font glyph and metrics caching functions, implemented at end.  */
-static void ns_uni_to_glyphs (struct nsfont_info *font_info,
-                              unsigned char block);
-static void ns_glyph_metrics (struct nsfont_info *font_info,
-                              unsigned char block);
+/* Structure used by GS `shape' functions for storing layout
+   information for each glyph.  Borrowed from macfont.h.  */
+struct ns_glyph_layout
+{
+  /* Range of indices of the characters composed into the group of
+     glyphs that share the cursor position with this glyph.  The
+     members `location' and `length' are in UTF-16 indices.  */
+  NSRange comp_range;
 
-#define INVALID_GLYPH 0xFFFF
+  /* UTF-16 index in the source string for the first character
+     associated with this glyph.  */
+  NSUInteger string_index;
 
-/* ==========================================================================
+  /* Horizontal and vertical adjustments of glyph position.  The
+     coordinate space is that of Core Text.  So, the `baseline_delta'
+     value is negative if the glyph should be placed below the
+     baseline.  */
+  CGFloat advance_delta, baseline_delta;
 
-    Utilities
+  /* Typographical width of the glyph.  */
+  CGFloat advance;
 
-   ========================================================================== */
+  /* Glyph ID of the glyph.  */
+  NSGlyph glyph_id;
+};
+
+
+enum lgstring_direction
+  {
+    DIR_R2L = -1, DIR_UNKNOWN = 0, DIR_L2R = 1
+  };
+
+enum gs_font_slant
+  {
+    GS_FONT_SLANT_ITALIC,
+    GS_FONT_SLANT_REVERSE_ITALIC,
+    GS_FONT_SLANT_NORMAL
+  };
+
+enum gs_font_weight
+  {
+    GS_FONT_WEIGHT_LIGHT,
+    GS_FONT_WEIGHT_BOLD,
+    GS_FONT_WEIGHT_NORMAL
+  };
+
+enum gs_font_width
+  {
+    GS_FONT_WIDTH_CONDENSED,
+    GS_FONT_WIDTH_EXPANDED,
+    GS_FONT_WIDTH_NORMAL
+  };
+
+enum gs_specified
+  {
+    GS_SPECIFIED_SLANT = 1,
+    GS_SPECIFIED_WEIGHT = 1 << 1,
+    GS_SPECIFIED_WIDTH = 1 << 2,
+    GS_SPECIFIED_FAMILY = 1 << 3,
+    GS_SPECIFIED_SPACING = 1 << 4
+  };
 
+struct gs_font_data
+{
+  int specified;
+  enum gs_font_slant slant;
+  enum gs_font_weight weight;
+  enum gs_font_width width;
+  bool monospace_p;
+  char *family_name;
+};
 
-/* Replace spaces w/another character so emacs core font parsing routines
-   aren't thrown off.  */
 static void
-ns_escape_name (char *name)
+ns_done_font_data (struct gs_font_data *data)
 {
-  for (; *name; name++)
-    if (*name == ' ')
-      *name = '_';
+  if (data->specified & GS_SPECIFIED_FAMILY)
+    xfree (data->family_name);
 }
 
-
-/* Reconstruct spaces in a font family name passed through emacs.  */
 static void
-ns_unescape_name (char *name)
+ns_get_font_data (NSFontDescriptor *desc, struct gs_font_data *dat)
 {
-  for (; *name; name++)
-    if (*name == '_')
-      *name = ' ';
+  NSNumber *tem;
+  NSFontSymbolicTraits traits = [desc symbolicTraits];
+  NSDictionary *dict = [desc objectForKey: NSFontTraitsAttribute];
+  NSString *family = [desc objectForKey: NSFontFamilyAttribute];
+
+  dat->specified = 0;
+
+  if (family != nil)
+    {
+      dat->specified |= GS_SPECIFIED_FAMILY;
+      dat->family_name = xstrdup ([family cStringUsingEncoding: NSUTF8StringEncoding]);
+    }
+
+  tem = [desc objectForKey: NSFontFixedAdvanceAttribute];
+
+  if ((tem != nil && [tem boolValue] != NO)
+      || (traits & NSFontMonoSpaceTrait))
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = true;
+    }
+  else if (tem != nil && [tem boolValue] == NO)
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = false;
+    }
+
+  if (traits & NSFontBoldTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WEIGHT;
+      dat->weight = GS_FONT_WEIGHT_BOLD;
+    }
+
+  if (traits & NSFontItalicTrait)
+    {
+      dat->specified |= GS_SPECIFIED_SLANT;
+      dat->slant = GS_FONT_SLANT_ITALIC;
+    }
+
+  if (traits & NSFontCondensedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_CONDENSED;
+    }
+  else if (traits & NSFontExpandedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_EXPANDED;
+    }
+
+  if (dict != nil)
+    {
+      tem = [dict objectForKey: NSFontSlantTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_SLANT;
+
+	  dat->slant = [tem floatValue] > 0
+	    ? GS_FONT_SLANT_ITALIC
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_SLANT_REVERSE_ITALIC
+	       : GS_FONT_SLANT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWeightTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WEIGHT;
+
+	  dat->weight = [tem floatValue] > 0
+	    ? GS_FONT_WEIGHT_BOLD
+	    : ([tem floatValue] < -0.4f
+	       ? GS_FONT_WEIGHT_LIGHT
+	       : GS_FONT_WEIGHT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWidthTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WIDTH;
+
+	  dat->width = [tem floatValue] > 0
+	    ? GS_FONT_WIDTH_EXPANDED
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_WIDTH_NORMAL
+	       : GS_FONT_WIDTH_CONDENSED);
+	}
+    }
+}
+
+static bool
+ns_font_descs_match_p (NSFontDescriptor *desc, NSFontDescriptor *target)
+{
+  struct gs_font_data dat;
+  struct gs_font_data t;
+
+  ns_get_font_data (desc, &dat);
+  ns_get_font_data (target, &t);
+
+  if (!(t.specified & GS_SPECIFIED_WIDTH))
+    t.width = GS_FONT_WIDTH_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_WEIGHT))
+    t.weight = GS_FONT_WEIGHT_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_SPACING))
+    t.monospace_p = false;
+  if (!(t.specified & GS_SPECIFIED_SLANT))
+    t.slant = GS_FONT_SLANT_NORMAL;
+
+  if (!(t.specified & GS_SPECIFIED_FAMILY))
+    emacs_abort ();
+
+  bool match_p = true;
+
+  if (dat.specified & GS_SPECIFIED_WIDTH
+      && dat.width != t.width)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_WEIGHT
+      && dat.weight != t.weight)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SPACING
+      && dat.monospace_p != t.monospace_p)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SLANT
+      && dat.monospace_p != t.monospace_p)
+    {
+      if (NSFONT_TRACE)
+	printf ("Matching monospace for %s: %d %d\n",
+		t.family_name, dat.monospace_p,
+		t.monospace_p);
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_FAMILY
+      && strcmp (dat.family_name, t.family_name))
+    match_p = false;
+
+ gout:
+  ns_done_font_data (&dat);
+  ns_done_font_data (&t);
+
+  return match_p;
 }
 
+/* Font glyph and metrics caching functions, implemented at end.  */
+static void ns_uni_to_glyphs (struct nsfont_info *font_info,
+                              unsigned char block);
+static void ns_glyph_metrics (struct nsfont_info *font_info,
+                              unsigned int block);
+
+#define INVALID_GLYPH 0xFFFF
+
+/* ==========================================================================
+
+    Utilities
+
+   ========================================================================== */
+
 
 /* Extract family name from a font spec.  */
 static NSString *
@@ -91,66 +313,116 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
     {
       char *tmp = xlispstrdup (SYMBOL_NAME (tem));
       NSString *family;
-      ns_unescape_name (tmp);
       family = [NSString stringWithUTF8String: tmp];
       xfree (tmp);
       return family;
     }
 }
 
-
-/* Return 0 if attr not set, else value (which might also be 0).
-   On Leopard 0 gets returned even on descriptors where the attribute
-   was never set, so there's no way to distinguish between unspecified
-   and set to not have.  Callers should assume 0 means unspecified.  */
-static float
-ns_attribute_fvalue (NSFontDescriptor *fdesc, NSString *trait)
-{
-    NSDictionary *tdict = [fdesc objectForKey: NSFontTraitsAttribute];
-    NSNumber *val = [tdict objectForKey: trait];
-    return val == nil ? 0.0F : [val floatValue];
-}
-
-
 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
    to NSFont descriptor.  Information under extra only needed for matching.  */
-#define STYLE_REF 100
 static NSFontDescriptor *
 ns_spec_to_descriptor (Lisp_Object font_spec)
 {
     NSFontDescriptor *fdesc;
     NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
-    NSMutableDictionary *tdict = [NSMutableDictionary new];
     NSString *family = ns_get_family (font_spec);
-    float n;
-
-    /* Add each attr in font_spec to fdAttrs.  */
-    n = min (FONT_WEIGHT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWeightTrait];
-    n = min (FONT_SLANT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontSlantTrait];
-    n = min (FONT_WIDTH_NUMERIC (font_spec), 200);
-    if (n > -1 && (n > STYLE_REF + 10 || n < STYLE_REF - 10))
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWidthTrait];
-    if ([tdict count] > 0)
-	[fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+    NSMutableDictionary *tdict = [NSMutableDictionary new];
 
-    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
-               retain] autorelease];
+    Lisp_Object tem;
+
+    tem = FONT_SLANT_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontSlantTrait];
+	else if (EQ (tem, intern ("reverse-italic")) ||
+		 EQ (tem, intern ("reverse-oblique")))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontSlantTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontSlantTrait];
+      }
+
+    tem = FONT_WIDTH_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qcondensed))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontWidthTrait];
+	else if (EQ (tem, Qexpanded))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontWidthTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontWidthTrait];
+      }
+
+    tem = FONT_WEIGHT_SYMBOLIC (font_spec);
+
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qbold))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else if (EQ (tem, Qlight))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		      forKey: NSFontWeightTrait];
+	  }
+      }
+
+    tem = AREF (font_spec, FONT_SPACING_INDEX);
 
     if (family != nil)
       {
-        NSFontDescriptor *fdesc2 = [fdesc fontDescriptorWithFamily: family];
-        fdesc = [[fdesc2 retain] autorelease];
+	[fdAttrs setObject: family
+		    forKey: NSFontFamilyAttribute];
       }
 
-    [fdAttrs release];
+    if (FIXNUMP (tem))
+      {
+	if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:YES]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+	else
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:NO]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+      }
+
+    /* Handle special families such as ``fixed'' or ``Sans Serif''.  */
+
+    if ([family isEqualToString: @"fixed"])
+      {
+	[fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+    else if ([family isEqualToString: @"Sans Serif"])
+      {
+	[fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+
+    [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+
+    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
+               retain] autorelease];
+
     [tdict release];
+    [fdAttrs release];
     return fdesc;
 }
 
@@ -161,61 +433,64 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
                          Lisp_Object extra,
                          const char *style)
 {
-    Lisp_Object font_entity = font_make_entity ();
-    /*   NSString *psName = [desc postscriptName]; */
-    NSString *family = [desc objectForKey: NSFontFamilyAttribute];
-    unsigned int traits = [desc symbolicTraits];
-    char *escapedFamily;
-
-    /* Shouldn't happen, but on Tiger fallback desc gets name but no family.  */
-    if (family == nil)
-      family = [desc objectForKey: NSFontNameAttribute];
-    if (family == nil)
-      family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-
-    escapedFamily = xstrdup ([family UTF8String]);
-    ns_escape_name (escapedFamily);
-
-    ASET (font_entity, FONT_TYPE_INDEX, Qns);
-    ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
-    ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
-    ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
-    ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
-
-    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    traits & NSFontBoldTrait ? Qbold : Qmedium);
-/*    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    make_fixnum (100 + 100
-			* ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    traits & NSFontItalicTrait ? Qitalic : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-                    traits & NSFontCondensedTrait ? Qcondensed :
-                    traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
-
-    ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_SPACING_INDEX,
-	  make_fixnum([desc symbolicTraits] & NSFontMonoSpaceTrait
-	      ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
-
-    ASET (font_entity, FONT_EXTRA_INDEX, extra);
-    ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+  Lisp_Object font_entity = font_make_entity ();
+  struct gs_font_data data;
+  ns_get_font_data (desc, &data);
+
+  ASET (font_entity, FONT_TYPE_INDEX, Qns);
+  ASET (font_entity, FONT_FOUNDRY_INDEX, Qns);
+  if (data.specified & GS_SPECIFIED_FAMILY)
+    ASET (font_entity, FONT_FAMILY_INDEX, intern (data.family_name));
+  ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
+  ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
+
+  if (data.specified & GS_SPECIFIED_WEIGHT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
+		      data.weight == GS_FONT_WEIGHT_BOLD
+		      ? Qbold : (data.weight == GS_FONT_WEIGHT_LIGHT
+				 ? Qlight : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX, Qnormal);
 
-    if (NSFONT_TRACE)
-      {
-	fputs ("created font_entity:\n    ", stderr);
-	debug_print (font_entity);
-      }
+  if (data.specified & GS_SPECIFIED_SLANT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
+		      data.slant == GS_FONT_SLANT_ITALIC
+		      ? Qitalic : (data.slant == GS_FONT_SLANT_REVERSE_ITALIC
+				   ? intern ("reverse-italic") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX, Qnormal);
+
+  if (data.specified & GS_SPECIFIED_WIDTH)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
+		      data.width == GS_FONT_WIDTH_CONDENSED
+		      ? Qcondensed : (data.width == GS_FONT_WIDTH_EXPANDED
+				      ? intern ("expanded") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX, Qnormal);
 
-    xfree (escapedFamily);
-    return font_entity;
+  ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_SPACING_INDEX,
+	make_fixnum ((data.specified & GS_SPECIFIED_WIDTH && data.monospace_p)
+		     ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
+
+  ASET (font_entity, FONT_EXTRA_INDEX, extra);
+  ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+
+  if (NSFONT_TRACE)
+    {
+      fputs ("created font_entity:\n    ", stderr);
+      debug_print (font_entity);
+    }
+
+  ns_done_font_data (&data);
+  return font_entity;
 }
 
 
@@ -223,8 +498,7 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
 static Lisp_Object
 ns_fallback_entity (void)
 {
-  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
-      fontDescriptor], Qnil, NULL);
+  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 1] fontDescriptor], Qnil, NULL);
 }
 
 
@@ -510,21 +784,20 @@ but also for ascii (which causes unnecessary font substitution).  */
     return families;
 }
 
+/* GNUstep font matching is very mediocre (it can't even compare
+   symbolic styles correctly), which is why our own font matching
+   mechanism must be implemented.  */
 
-/* Implementation for list() and match().  List() can return nil, match()
-must return something.  Strategy is to drop family name from attribute
-matching set for match.  */
+/* Implementation for list and match.  */
 static Lisp_Object
 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
 {
     Lisp_Object tem, list = Qnil;
-    NSFontDescriptor *fdesc, *desc;
-    NSMutableSet *fkeys;
-    NSArray *matchingDescs;
-    NSEnumerator *dEnum;
-    NSString *family;
+    NSFontDescriptor *fdesc;
+    NSArray *all_descs;
+    GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
+
     NSSet *cFamilies;
-    BOOL foundItal = NO;
 
     block_input ();
     if (NSFONT_TRACE)
@@ -537,43 +810,22 @@ but also for ascii (which causes unnecessary font substitution).  */
     cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
 
     fdesc = ns_spec_to_descriptor (font_spec);
-    fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
-    if (isMatch)
-	[fkeys removeObject: NSFontFamilyAttribute];
-
-    matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
+    all_descs = [enumerator availableFontDescriptors];
 
-    if (NSFONT_TRACE)
-	NSLog(@"Got desc %@ and found %lu matching fonts from it: ", fdesc,
-	      (unsigned long)[matchingDescs count]);
-
-    for (dEnum = [matchingDescs objectEnumerator]; (desc = [dEnum nextObject]);)
+    for (NSFontDescriptor *desc in all_descs)
       {
 	if (![cFamilies containsObject:
 	         [desc objectForKey: NSFontFamilyAttribute]])
 	    continue;
+	if (!ns_font_descs_match_p (fdesc, desc))
+	  continue;
+
         tem = ns_descriptor_to_entity (desc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
+				       AREF (font_spec, FONT_EXTRA_INDEX),
                                        NULL);
         if (isMatch)
           return tem;
 	list = Fcons (tem, list);
-	if (fabs (ns_attribute_fvalue (desc, NSFontSlantTrait)) > 0.05)
-	    foundItal = YES;
-      }
-
-    /* Add synthItal member if needed.  */
-    family = [fdesc objectForKey: NSFontFamilyAttribute];
-    if (family != nil && !foundItal && !NILP (list))
-      {
-        NSFontDescriptor *s1 = [NSFontDescriptor new];
-        NSFontDescriptor *sDesc
-          = [[s1 fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
-              fontDescriptorWithFamily: family];
-	list = Fcons (ns_descriptor_to_entity (sDesc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
-					 "synthItal"), list);
-        [s1 release];
       }
 
     unblock_input ();
@@ -652,7 +904,6 @@ Properties to be considered are same as for list().  */
                objectEnumerator];
   while ((family = [families nextObject]))
       list = Fcons (intern ([family UTF8String]), list);
-  /* FIXME: escape the name?  */
 
   if (NSFONT_TRACE)
     fprintf (stderr, "nsfont: list families returning %"pD"d entries\n",
@@ -668,18 +919,15 @@ Properties to be considered are same as for list().  */
 static Lisp_Object
 nsfont_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
 {
-  BOOL synthItal;
-  unsigned int traits = 0;
   struct nsfont_info *font_info;
   struct font *font;
   NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
   NSFontManager *fontMgr = [NSFontManager sharedFontManager];
   NSString *family;
   NSFont *nsfont, *sfont;
-  Lisp_Object tem;
   NSRect brect;
   Lisp_Object font_object;
-  int fixLeopardBug;
+  Lisp_Object tem;
 
   block_input ();
 
@@ -692,42 +940,20 @@ Properties to be considered are same as for list().  */
   if (pixel_size <= 0)
     {
       /* try to get it out of frame params */
-        Lisp_Object tem = get_frame_param (f, Qfontsize);
-        pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
+      tem = get_frame_param (f, Qfontsize);
+      pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
     }
 
   tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
-  synthItal = !NILP (tem) && !strncmp ("synthItal", SSDATA (SYMBOL_NAME (tem)),
-                                       9);
   family = ns_get_family (font_entity);
   if (family == nil)
     family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-  /* Should be > 0.23 as some font descriptors (e.g. Terminus) set to that
-     when setting family in ns_spec_to_descriptor().  */
-  if (ns_attribute_fvalue (fontDesc, NSFontWeightTrait) > 0.50F)
-      traits |= NSBoldFontMask;
-  if (ns_attribute_fvalue (fontDesc, NSFontSlantTrait) > 0.05F)
-      traits |= NSItalicFontMask;
-
-  /* see https://web.archive.org/web/20100201175731/http://cocoadev.com/forums/comments.php?DiscussionID=74 */
-  fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
-  nsfont = [fontMgr fontWithFamily: family
-                            traits: traits weight: fixLeopardBug
-			      size: pixel_size];
-  /* if didn't find, try synthetic italic */
-  if (nsfont == nil && synthItal)
-    {
-      nsfont = [fontMgr fontWithFamily: family
-                                traits: traits & ~NSItalicFontMask
-                                weight: fixLeopardBug size: pixel_size];
-    }
+
+  nsfont = [NSFont fontWithDescriptor: fontDesc
+				 size: pixel_size];
 
   if (nsfont == nil)
-    {
-      message_with_string ("*** Warning: font in family `%s' not found",
-                          build_string ([family UTF8String]), 1);
-      nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
-    }
+    nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
 
   if (NSFONT_TRACE)
     NSLog (@"%@\n", nsfont);
@@ -740,7 +966,7 @@ when setting family in ns_spec_to_descriptor().  */
   if (!font)
     {
       unblock_input ();
-      return Qnil; /* FIXME: other terms do, but returning Qnil causes segfault.  */
+      return Qnil;
     }
 
   font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs);
@@ -781,7 +1007,7 @@ when setting family in ns_spec_to_descriptor().  */
     font_info->name = xstrdup (fontName);
     font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
     font_info->ital =
-      synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
+      ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
 
     /* Metrics etc.; some fonts return an unusually large max advance, so we
        only use it for fonts that have wide characters.  */
@@ -808,8 +1034,6 @@ when setting family in ns_spec_to_descriptor().  */
       lrint (brect.size.width - (CGFloat) font_info->width);
 
     /* set up metrics portion of font struct */
-    font->ascent = lrint([sfont ascender]);
-    font->descent = -lrint(floor(adjusted_descender));
     font->space_width = lrint (ns_char_width (sfont, ' '));
     font->max_width = lrint (font_info->max_bounds.width);
     font->min_width = font->space_width;  /* Approximate.  */
@@ -871,7 +1095,7 @@ when setting family in ns_spec_to_descriptor().  */
 {
   struct nsfont_info *font_info = (struct nsfont_info *)font;
   unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
-  unsigned short g;
+  unsigned int g;
 
   if (c > 0xFFFF)
     return FONT_INVALID_CODE;
@@ -934,51 +1158,23 @@ is false when (FROM > 0 || TO < S->nchars).  */
 static int
 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
              bool with_background)
-/* NOTE: focus and clip must be set.  */
 {
-  static unsigned char cbuf[1024];
-  unsigned char *c = cbuf;
-#if GNUSTEP_GUI_MAJOR_VERSION > 0 || GNUSTEP_GUI_MINOR_VERSION > 22
-  static CGFloat advances[1024];
-  CGFloat *adv = advances;
-#else
-  static float advances[1024];
-  float *adv = advances;
-#endif
+  NSGlyph *c = alloca ((to - from) * sizeof *c);
+
   struct face *face;
   NSRect r;
   struct nsfont_info *font;
-  NSColor *col, *bgCol;
-  unsigned *t = s->char2b;
-  int i, len, flags;
+  NSColor *col;
+  int len = to - from;
   char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
 
   block_input ();
 
-  font = (struct nsfont_info *)s->face->font;
+  font = (struct nsfont_info *) s->font;
   if (font == NULL)
     font = (struct nsfont_info *)FRAME_FONT (s->f);
 
-  /* Select face based on input flags.  */
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
-  switch (flags)
-    {
-    case NS_DUMPGLYPH_CURSOR:
-      face = s->face;
-      break;
-    case NS_DUMPGLYPH_MOUSEFACE:
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-      break;
-    default:
-      face = s->face;
-    }
+  face = s->face;
 
   r.origin.x = s->x;
   if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
@@ -987,91 +1183,24 @@ is false when (FROM > 0 || TO < S->nchars).  */
   r.origin.y = s->y;
   r.size.height = FONT_HEIGHT (font);
 
-  /* Convert UTF-16 (?) to UTF-8 and determine advances.  Note if we just ask
-     NS to render the string, it will come out differently from the individual
-     character widths added up because of layout processing.  */
-  {
-    int cwidth, twidth = 0;
-    int hi, lo;
-    /* FIXME: composition: no vertical displacement is considered.  */
-    t += from; /* advance into composition */
-    for (i = from; i < to; i++, t++)
-      {
-        hi = (*t & 0xFF00) >> 8;
-        lo = *t & 0x00FF;
-        if (isComposite)
-          {
-	    if (!s->first_glyph->u.cmp.automatic)
-		cwidth = s->cmp->offsets[i * 2] /* (H offset) */ - twidth;
-	    else
-	      {
-		Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
-		Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
-		if (NILP (LGLYPH_ADJUSTMENT (glyph)))
-		    cwidth = LGLYPH_WIDTH (glyph);
-		else
-		  {
-		    cwidth = LGLYPH_WADJUST (glyph);
-		    *(adv-1) += LGLYPH_XOFF (glyph);
-		  }
-	      }
-          }
-        else
-          {
-            if (!font->metrics[hi]) /* FIXME: why/how can we need this now?  */
-              ns_glyph_metrics (font, hi);
-            cwidth = font->metrics[hi][lo].width;
-          }
-        twidth += cwidth;
-        *adv++ = cwidth;
-        c += CHAR_STRING (*t, c); /* This converts the char to UTF-8.  */
-      }
-    len = adv - advances;
-    r.size.width = twidth;
-    *c = 0;
-  }
+  for (int i = from; i < to; ++i)
+    c[i] = s->char2b[i];
 
   /* Fill background if requested.  */
   if (with_background && !isComposite)
     {
-      NSRect br = r;
-      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
-      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
-
-      if (s->row->full_width_p)
-        {
-          if (br.origin.x <= fibw + 1 + mbox_line_width)
-            {
-              br.size.width += br.origin.x - mbox_line_width;
-              br.origin.x = mbox_line_width;
-            }
-          if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
-                <= fibw+1)
-            br.size.width += fibw;
-        }
-      if (s->face->box == FACE_NO_BOX)
-        {
-          /* Expand unboxed top row over internal border.  */
-          if (br.origin.y <= fibw + 1 + mbox_line_width)
-            {
-              br.size.height += br.origin.y;
-              br.origin.y = 0;
-            }
-        }
-      else
-        {
-          int correction = abs (s->face->box_horizontal_line_width)+1;
-          br.origin.y += correction;
-          br.size.height -= 2*correction;
-          correction = abs (s->face->box_vertical_line_width)+1;
-          br.origin.x += correction;
-          br.size.width -= 2*correction;
-        }
+      NSRect br = NSMakeRect (x, y - FONT_BASE (s->font),
+			      s->width, FONT_HEIGHT (s->font));
 
       if (!s->face->stipple)
-        [(NS_FACE_BACKGROUND (face) != 0
-          ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-          : FRAME_BACKGROUND_COLOR (s->f)) set];
+	{
+	  if (s->hl != DRAW_CURSOR)
+	    [(NS_FACE_BACKGROUND (face) != 0
+	      ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+	      : FRAME_BACKGROUND_COLOR (s->f)) set];
+	  else
+	    [FRAME_CURSOR_COLOR (s->f) set];
+	}
       else
         {
           struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
@@ -1080,43 +1209,32 @@ is false when (FROM > 0 || TO < S->nchars).  */
       NSRectFill (br);
     }
 
-
   /* set up for character rendering */
   r.origin.y = y;
 
-  col = (NS_FACE_FOREGROUND (face) != 0
-         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
-         : FRAME_FOREGROUND_COLOR (s->f));
-
-  bgCol = (flags != NS_DUMPGLYPH_FOREGROUND ? nil
-           : (NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)));
+  if (s->hl == DRAW_CURSOR)
+    col = FRAME_BACKGROUND_COLOR (s->f);
+  else
+    col = (NS_FACE_FOREGROUND (face) != 0
+	   ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
+	   : FRAME_FOREGROUND_COLOR (s->f));
 
   /* render under GNUstep using DPS */
   {
-    NSGraphicsContext *context = GSCurrentContext ();
-
+    NSGraphicsContext *context = [NSGraphicsContext currentContext];
     DPSgsave (context);
-    [font->nsfont set];
-
-    /* do erase if "foreground" mode */
-    if (bgCol != nil)
+    if (s->clip_head)
       {
-        [bgCol set];
-        DPSmoveto (context, r.origin.x, r.origin.y);
-/*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
-        DPSxshow (context, (const char *) cbuf, advances, len);
-        DPSstroke (context);
-        [col set];
-/*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
+	DPSrectclip (context, s->clip_head->x, 0,
+		     FRAME_PIXEL_WIDTH (s->f),
+		     FRAME_PIXEL_HEIGHT (s->f));
       }
+    [font->nsfont set];
 
     [col set];
 
-    /* draw with DPSxshow () */
     DPSmoveto (context, r.origin.x, r.origin.y);
-    DPSxshow (context, (const char *) cbuf, advances, len);
+    GSShowGlyphs (context, c, len);
     DPSstroke (context);
 
     DPSgrestore (context);
@@ -1126,6 +1244,360 @@ is false when (FROM > 0 || TO < S->nchars).  */
   return to-from;
 }
 
+static NSUInteger
+ns_font_shape (NSFont *font, NSString *string,
+	       struct ns_glyph_layout *glyph_layouts, NSUInteger glyph_len,
+	       enum lgstring_direction dir)
+{
+  NSUInteger i;
+  NSUInteger result = 0;
+  NSTextStorage *textStorage;
+  NSLayoutManager *layoutManager;
+  NSTextContainer *textContainer;
+  NSUInteger stringLength;
+  NSPoint spaceLocation;
+  /* numberOfGlyphs can't actually be 0, but this pacifies GCC */
+  NSUInteger used, numberOfGlyphs = 0;
+
+  textStorage = [[NSTextStorage alloc] initWithString:string];
+  layoutManager = [[NSLayoutManager alloc] init];
+  textContainer = [[NSTextContainer alloc] init];
+
+  /* Append a trailing space to measure baseline position.  */
+  [textStorage appendAttributedString:([[[NSAttributedString alloc]
+                                          initWithString:@" "] autorelease])];
+  [textStorage setFont:font];
+  [textContainer setLineFragmentPadding:0];
+
+  [layoutManager addTextContainer:textContainer];
+  [textContainer release];
+  [textStorage addLayoutManager:layoutManager];
+  [layoutManager release];
+
+  if (!(textStorage && layoutManager && textContainer))
+    emacs_abort ();
+
+  stringLength = [string length];
+
+  /* Force layout.  */
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  spaceLocation = [layoutManager locationForGlyphAtIndex:stringLength];
+
+  /* Remove the appended trailing space because otherwise it may
+     generate a wrong result for a right-to-left text.  */
+  [textStorage beginEditing];
+  [textStorage deleteCharactersInRange:(NSMakeRange (stringLength, 1))];
+  [textStorage endEditing];
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  i = 0;
+  while (i < stringLength)
+    {
+      NSRange range;
+      NSFont *fontInTextStorage =
+        [textStorage attribute: NSFontAttributeName
+		       atIndex:i
+                     longestEffectiveRange: &range
+                       inRange: NSMakeRange (0, stringLength)];
+
+      if (!(fontInTextStorage == font
+            || [[fontInTextStorage fontName] isEqualToString:[font fontName]]))
+        break;
+      i = NSMaxRange (range);
+    }
+  if (i < stringLength)
+    /* Make the test `used <= glyph_len' below fail if textStorage
+       contained some fonts other than the specified one.  */
+    used = glyph_len + 1;
+  else
+    {
+      NSRange range = NSMakeRange (0, stringLength);
+
+      range = [layoutManager glyphRangeForCharacterRange:range
+                                    actualCharacterRange:NULL];
+      numberOfGlyphs = NSMaxRange (range);
+      used = numberOfGlyphs;
+      for (i = 0; i < numberOfGlyphs; i++)
+        if ([layoutManager notShownAttributeForGlyphAtIndex:i])
+          used--;
+    }
+
+  if (0 < used && used <= glyph_len)
+    {
+      NSUInteger glyphIndex, prevGlyphIndex;
+      NSUInteger *permutation;
+      NSRange compRange, range;
+      CGFloat totalAdvance;
+
+      glyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      permutation = NULL;
+#define RIGHT_TO_LEFT_P permutation
+
+      /* Fill the `comp_range' member of struct mac_glyph_layout, and
+         setup a permutation for right-to-left text.  */
+      compRange = NSMakeRange (0, 0);
+      for (range = NSMakeRange (0, 0); NSMaxRange (range) < used;
+           range.length++)
+        {
+          struct ns_glyph_layout *gl = glyph_layouts + NSMaxRange (range);
+          NSUInteger characterIndex =
+            [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
+
+          gl->string_index = characterIndex;
+
+          if (characterIndex >= NSMaxRange (compRange))
+            {
+              compRange.location = NSMaxRange (compRange);
+              do
+                {
+                  NSRange characterRange =
+                    [string
+                      rangeOfComposedCharacterSequenceAtIndex:characterIndex];
+
+                  compRange.length =
+                    NSMaxRange (characterRange) - compRange.location;
+                  [layoutManager glyphRangeForCharacterRange:compRange
+                                        actualCharacterRange:&characterRange];
+                  characterIndex = NSMaxRange (characterRange) - 1;
+                }
+              while (characterIndex >= NSMaxRange (compRange));
+
+              if (RIGHT_TO_LEFT_P)
+                for (i = 0; i < range.length; i++)
+                  permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+              range = NSMakeRange (NSMaxRange (range), 0);
+            }
+
+          gl->comp_range.location = compRange.location;
+          gl->comp_range.length = compRange.length;
+
+          while (++glyphIndex < numberOfGlyphs)
+            if (![layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+              break;
+        }
+      if (RIGHT_TO_LEFT_P)
+        for (i = 0; i < range.length; i++)
+          permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+      /* Then fill the remaining members.  */
+      glyphIndex = prevGlyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      if (!RIGHT_TO_LEFT_P)
+        totalAdvance = 0;
+      else
+        {
+          NSUInteger nrects;
+          NSRect *glyphRects =
+            [layoutManager
+              rectArrayForGlyphRange:(NSMakeRange (0, numberOfGlyphs))
+              withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                     inTextContainer:textContainer rectCount:&nrects];
+
+          totalAdvance = NSMaxX (glyphRects[0]);
+        }
+
+      for (i = 0; i < used; i++)
+        {
+          struct ns_glyph_layout *gl;
+          NSPoint location;
+          NSUInteger nextGlyphIndex;
+          NSRange glyphRange;
+          NSRect *glyphRects;
+          NSUInteger nrects;
+
+          if (!RIGHT_TO_LEFT_P)
+            gl = glyph_layouts + i;
+          else
+            {
+              NSUInteger dest = permutation[i];
+
+              gl = glyph_layouts + dest;
+              if (i < dest)
+                {
+                  NSUInteger tmp = gl->string_index;
+
+                  gl->string_index = glyph_layouts[i].string_index;
+                  glyph_layouts[i].string_index = tmp;
+                }
+            }
+          gl->glyph_id = [layoutManager glyphAtIndex: glyphIndex];
+
+          location = [layoutManager locationForGlyphAtIndex:glyphIndex];
+          gl->baseline_delta = spaceLocation.y - location.y;
+
+          for (nextGlyphIndex = glyphIndex + 1; nextGlyphIndex < numberOfGlyphs;
+               nextGlyphIndex++)
+            if (![layoutManager
+                   notShownAttributeForGlyphAtIndex:nextGlyphIndex])
+              break;
+
+          if (!RIGHT_TO_LEFT_P)
+            {
+              CGFloat maxX;
+
+              if (prevGlyphIndex == 0)
+                glyphRange = NSMakeRange (0, nextGlyphIndex);
+              else
+                glyphRange = NSMakeRange (glyphIndex,
+                                          nextGlyphIndex - glyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              maxX = max (NSMaxX (glyphRects[0]), totalAdvance);
+              gl->advance_delta = location.x - totalAdvance;
+              gl->advance = maxX - totalAdvance;
+              totalAdvance = maxX;
+            }
+          else
+            {
+              CGFloat minX;
+
+              if (nextGlyphIndex == numberOfGlyphs)
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          numberOfGlyphs - prevGlyphIndex);
+              else
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          glyphIndex + 1 - prevGlyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              minX = min (NSMinX (glyphRects[0]), totalAdvance);
+              gl->advance = totalAdvance - minX;
+              totalAdvance = minX;
+              gl->advance_delta = location.x - totalAdvance;
+            }
+
+          prevGlyphIndex = glyphIndex + 1;
+          glyphIndex = nextGlyphIndex;
+        }
+
+      if (RIGHT_TO_LEFT_P)
+        xfree (permutation);
+
+#undef RIGHT_TO_LEFT_P
+
+      result = used;
+    }
+  [textStorage release];
+
+  return result;
+}
+
+static Lisp_Object
+nsfont_shape (Lisp_Object lgstring, Lisp_Object direction)
+{
+  struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  struct nsfont_info *font_info = (struct nsfont_info *) font;
+  struct ns_glyph_layout *glyph_layouts;
+  NSFont *nsfont = font_info->nsfont;
+  ptrdiff_t glyph_len, len, i;
+  Lisp_Object tem;
+  unichar *mb_buf;
+  NSUInteger used;
+
+  glyph_len = LGSTRING_GLYPH_LEN (lgstring);
+  for (i = 0; i < glyph_len; ++i)
+    {
+      tem = LGSTRING_GLYPH (lgstring, i);
+
+      if (NILP (tem))
+	break;
+    }
+
+  len = i;
+
+  if (INT_MAX / 2 < len)
+    memory_full (SIZE_MAX);
+
+  block_input ();
+
+  mb_buf = alloca (len * sizeof *mb_buf);
+
+  for (i = 0; i < len; ++i)
+    {
+      uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
+      mb_buf[i] = (unichar) c;
+    }
+
+  NSString *string = [NSString stringWithCharacters: mb_buf
+					     length: len];
+  unblock_input ();
+
+  if (!string)
+    return Qnil;
+
+  block_input ();
+
+  enum lgstring_direction dir = DIR_UNKNOWN;
+
+  if (EQ (direction, QL2R))
+    dir = DIR_L2R;
+  else if (EQ (direction, QR2L))
+    dir = DIR_R2L;
+  glyph_layouts = alloca (sizeof (struct ns_glyph_layout) * glyph_len);
+  used = ns_font_shape (nsfont, string, glyph_layouts, glyph_len, dir);
+
+  for (i = 0; i < used; i++)
+    {
+      Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
+      struct ns_glyph_layout *gl = glyph_layouts + i;
+      EMACS_INT from, to;
+      struct font_metrics metrics;
+
+      if (NILP (lglyph))
+        {
+          lglyph = LGLYPH_NEW ();
+          LGSTRING_SET_GLYPH (lgstring, i, lglyph);
+        }
+
+      from = gl->comp_range.location;
+      LGLYPH_SET_FROM (lglyph, from);
+
+      to = gl->comp_range.location + gl->comp_range.length;
+      LGLYPH_SET_TO (lglyph, to - 1);
+
+      /* LGLYPH_CHAR is used in `describe-char' for checking whether
+         the composition is trivial.  */
+      {
+        UTF32Char c;
+
+        if (mb_buf[gl->string_index] >= 0xD800
+            && mb_buf[gl->string_index] < 0xDC00)
+          c = (((mb_buf[gl->string_index] - 0xD800) << 10)
+               + (mb_buf[gl->string_index + 1] - 0xDC00) + 0x10000);
+        else
+          c = mb_buf[gl->string_index];
+
+        LGLYPH_SET_CHAR (lglyph, c);
+      }
+
+      {
+        unsigned long cc = gl->glyph_id;
+        LGLYPH_SET_CODE (lglyph, cc);
+      }
+
+      nsfont_text_extents (font, &gl->glyph_id, 1, &metrics);
+      LGLYPH_SET_WIDTH (lglyph, metrics.width);
+      LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
+      LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
+      LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
+      LGLYPH_SET_DESCENT (lglyph, metrics.descent);
+    }
+  unblock_input ();
+
+  return make_fixnum (used);
+}
 
 
 /* ==========================================================================
@@ -1134,6 +1606,50 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
    ========================================================================== */
 
+static NSGlyph
+ns_uni_to_glyphs_1 (struct nsfont_info *info, unsigned int c)
+{
+  unichar characters[] = { c };
+  NSString *string =
+    [NSString stringWithCharacters: characters
+			    length: 1];
+  NSDictionary *attributes =
+    [NSDictionary dictionaryWithObjectsAndKeys:
+		    info->nsfont, NSFontAttributeName, nil];
+  NSTextStorage *storage = [[NSTextStorage alloc] initWithString: string
+						      attributes: attributes];
+  NSTextContainer *text_container = [[NSTextContainer alloc] init];
+  NSLayoutManager *manager = [[NSLayoutManager alloc] init];
+
+  [manager addTextContainer: text_container];
+  [text_container release]; /* Retained by manager */
+  [storage addLayoutManager: manager];
+  [manager release]; /* Retained by storage */
+
+  NSFont *font_in_storage = [storage attribute: NSFontAttributeName
+				       atIndex:0
+				effectiveRange: NULL];
+  NSGlyph glyph = FONT_INVALID_CODE;
+
+  if ((font_in_storage == info->nsfont
+       || [[font_in_storage fontName] isEqualToString: [info->nsfont fontName]]))
+    {
+      @try
+	{
+	  glyph = [manager glyphAtIndex: 0];
+	}
+      @catch (NSException *e)
+	{
+	  /* GNUstep bug? */
+	  glyph = 'X';
+	}
+    }
+
+  [storage release];
+
+  return glyph;
+}
+
 /* Find and cache corresponding glyph codes for unicode values in given
    hi-byte block of 256.  */
 static void
@@ -1141,7 +1657,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   unichar *unichars = xmalloc (0x101 * sizeof (unichar));
   unsigned int i, g, idx;
-  unsigned short *glyphs;
+  unsigned int *glyphs;
 
   if (NSFONT_TRACE)
     fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
@@ -1149,7 +1665,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
   block_input ();
 
-  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
+  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned int));
   if (!unichars || !(font_info->glyphs[block]))
     emacs_abort ();
 
@@ -1166,7 +1682,8 @@ is false when (FROM > 0 || TO < S->nchars).  */
     for (i = 0; i < 0x100; i++, glyphs++)
       {
         g = unichars[i];
-        *glyphs = g;
+	NSGlyph glyph = ns_uni_to_glyphs_1 (font_info, g);
+        *glyphs = glyph;
       }
   }
 
@@ -1175,18 +1692,19 @@ is false when (FROM > 0 || TO < S->nchars).  */
 }
 
 
-/* Determine and cache metrics for corresponding glyph codes in given
-   hi-byte block of 256.  */
+/* Determine and cache metrics for glyphs in given hi-byte block of
+   256.  */
 static void
-ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
+ns_glyph_metrics (struct nsfont_info *font_info, unsigned int block)
 {
-  unsigned int i, g;
+  unsigned int i;
+  NSGlyph g;
   unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
   NSFont *sfont;
   struct font_metrics *metrics;
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
+    fprintf (stderr, "%p\tComputing metrics for glyphs in block %u\n",
             font_info, block);
 
   /* not implemented yet (as of startup 0.18), so punt */
@@ -1209,19 +1727,14 @@ is false when (FROM > 0 || TO < S->nchars).  */
       w = max ([sfont advancementForGlyph: g].width, 2.0);
       metrics->width = lrint (w);
 
-      lb = r.origin.x;
-      rb = r.size.width - w;
-      // Add to bearing for LCD smoothing.  We don't know if it is there.
-      if (lb < 0)
-        metrics->lbearing = round (lb - LCD_SMOOTHING_MARGIN);
-      if (font_info->ital)
-        rb += (CGFloat) (0.22F * font_info->height);
-      metrics->rbearing = lrint (w + rb + LCD_SMOOTHING_MARGIN);
-
-      metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
-      /* lrint (hshrink * [sfont ascender] + expand * hd/2); */
-      metrics->ascent = r.size.height - metrics->descent;
-      /* -lrint (hshrink* [sfont descender] - expand * hd/2); */
+      lb = NSMinX (r);
+      rb = NSMaxX (r);
+
+      metrics->rbearing = lrint (rb);
+      metrics->lbearing = lrint (lb);
+
+      metrics->descent = NSMinY (r);
+      metrics->ascent = NSMaxY (r);
     }
   unblock_input ();
 }
@@ -1257,6 +1770,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
   .has_char = nsfont_has_char,
   .encode_char = nsfont_encode_char,
   .text_extents = nsfont_text_extents,
+  .shape = nsfont_shape,
   .draw = nsfont_draw,
   };
 
@@ -1265,7 +1779,6 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   DEFSYM (Qcondensed, "condensed");
   DEFSYM (Qexpanded, "expanded");
-  DEFSYM (Qapple, "apple");
   DEFSYM (Qmedium, "medium");
   DEFVAR_LISP ("ns-reg-to-script", Vns_reg_to_script,
                doc: /* Internal use: maps font registry to Unicode script.  */);
diff --git a/src/nsterm.h b/src/nsterm.h
index c750d1bd99..8175f99664 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -820,7 +820,7 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
   XCharStruct max_bounds;
   /* We compute glyph codes and metrics on-demand in blocks of 256 indexed
      by hibyte, lobyte.  */
-  unsigned short **glyphs; /* map Unicode index to glyph */
+  unsigned int **glyphs; /* map Unicode index to glyph */
   struct font_metrics **metrics;
 };
 #endif
diff --git a/src/nsterm.m b/src/nsterm.m
index 4c2a3f287c..051ee511ca 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1078,11 +1078,20 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
   /* clipping */
   if (r)
     {
-      [[NSGraphicsContext currentContext] saveGraphicsState];
+      NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+      [ctx saveGraphicsState];
       if (n == 2)
         NSRectClipList (r, 2);
       else
         NSRectClip (*r);
+#ifdef NS_IMPL_GNUSTEP
+      DPSrectclip (ctx, NSMinX (*r), NSMinY (*r),
+		   NSWidth (*r), NSHeight (*r));
+
+      if (n == 2)
+	DPSrectclip (ctx, NSMinX (r[1]), NSMinY (r[1]),
+		     NSWidth (r[1]), NSHeight (r[1]));
+#endif
       gsaved = YES;
     }
 }
@@ -2440,9 +2449,6 @@ Hide the window (X11 semantics)
       EmacsView *view = FRAME_NS_VIEW (f);
       FRAME_POINTER_TYPE (f) = cursor;
       [[view window] invalidateCursorRectsForView: view];
-      /* Redisplay assumes this function also draws the changed frame
-         cursor, but this function doesn't, so do it explicitly.  */
-      gui_update_cursor (f, 1);
     }
 }
 
@@ -2852,31 +2858,31 @@ Hide the window (X11 semantics)
      External (RIF); compute left/right overhang of whole string and set in s
    -------------------------------------------------------------------------- */
 {
-  struct font *font = s->font;
-
-  if (s->char2b)
+  if (s->cmp == NULL
+      && (s->first_glyph->type == CHAR_GLYPH
+	  || s->first_glyph->type == COMPOSITE_GLYPH))
     {
       struct font_metrics metrics;
-      unsigned int codes[2];
-      codes[0] = *(s->char2b);
-      codes[1] = *(s->char2b + s->nchars - 1);
 
-      font->driver->text_extents (font, codes, 2, &metrics);
-      s->left_overhang = -metrics.lbearing;
-      s->right_overhang
-	= metrics.rbearing > metrics.width
-	? metrics.rbearing - metrics.width : 0;
+      if (s->first_glyph->type == CHAR_GLYPH)
+	{
+	  struct font *font = s->font;
+	  font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+	}
+      else
+	{
+	  Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+	  composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+	}
+      s->right_overhang = (metrics.rbearing > metrics.width
+			   ? metrics.rbearing - metrics.width : 0);
+      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
     }
-  else
+  else if (s->cmp)
     {
-      s->left_overhang = 0;
-#ifdef NS_IMPL_GNUSTEP
-      if (EQ (font->driver->type, Qns))
-        s->right_overhang = ((struct nsfont_info *)font)->ital ?
-          FONT_HEIGHT (font) * 0.2 : 0;
-      else
-#endif
-        s->right_overhang = 0;
+      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+      s->left_overhang = - s->cmp->lbearing;
     }
 }
 
@@ -3016,14 +3022,13 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   struct frame *f = WINDOW_XFRAME (w);
   struct glyph *phys_cursor_glyph;
   struct glyph *cursor_glyph;
-  struct face *face;
-  NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
 
   /* If cursor is out of bounds, don't draw garbage.  This can happen
      in mini-buffer windows when switching between echo area glyphs
      and mini-buffer.  */
 
-  NSTRACE ("ns_draw_window_cursor");
+  NSTRACE ("ns_draw_window_cursor (on = %d, cursor_type = %d)",
+	   on_p, cursor_type);
 
   if (!on_p)
     return;
@@ -3039,6 +3044,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
     {
+      NSTRACE_MSG ("No phys cursor glyph was found!");
+
       if (glyph_row->exact_window_width_line_p
           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
         {
@@ -3048,10 +3055,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       return;
     }
 
-  /* We draw the cursor (with NSRectFill), then draw the glyph on top
-     (other terminals do it the other way round).  We must set
-     w->phys_cursor_width to the cursor width.  For bar cursors, that
-     is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
 
   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
@@ -3083,17 +3086,15 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   /* Prevent the cursor from being drawn outside the text area.  */
   r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
 
-  ns_focus (f, &r, 1);
+  NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+  [ctx saveGraphicsState];
+  NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+  DPSrectclip (ctx, r.origin.x, r.origin.y,
+	       r.size.width, r.size.height);
+#endif
 
-  face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
-  if (face && NS_FACE_BACKGROUND (face)
-      == ns_index_color (FRAME_CURSOR_COLOR (f), f))
-    {
-      [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
-      hollow_color = FRAME_CURSOR_COLOR (f);
-    }
-  else
-    [FRAME_CURSOR_COLOR (f) set];
+  [FRAME_CURSOR_COLOR (f) set];
 
   switch (cursor_type)
     {
@@ -3101,13 +3102,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     case NO_CURSOR:
       break;
     case FILLED_BOX_CURSOR:
-      NSRectFill (r);
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
       break;
     case HOLLOW_BOX_CURSOR:
-      NSRectFill (r);
-      [hollow_color set];
-      NSRectFill (NSInsetRect (r, 1, 1));
-      [FRAME_CURSOR_COLOR (f) set];
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_NORMAL_TEXT);
+      [NSBezierPath strokeRect: r];
       break;
     case HBAR_CURSOR:
       NSRectFill (r);
@@ -3123,12 +3122,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       NSRectFill (s);
       break;
     }
-  ns_unfocus (f);
 
-  /* Draw the character under the cursor.  Other terms only draw
-     the character on top of box cursors, so do the same here.  */
-  if (cursor_type == FILLED_BOX_CURSOR || cursor_type == HOLLOW_BOX_CURSOR)
-    draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+  [ctx restoreGraphicsState];
 }
 
 
@@ -3308,16 +3303,18 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   if (s->for_overlaps)
     return;
 
+  if (s->hl == DRAW_CURSOR)
+    [FRAME_BACKGROUND_COLOR (s->f) set];
+  else if (face->underline_defaulted_p)
+    [defaultCol set];
+  else
+    [ns_lookup_indexed_color (face->underline_color, s->f) set];
+
   /* Do underline.  */
   if (face->underline)
     {
       if (s->face->underline == FACE_UNDER_WAVE)
         {
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
-
           ns_draw_underwave (s, width, x);
         }
       else if (s->face->underline == FACE_UNDER_LINE)
@@ -3388,11 +3385,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
           s->underline_position = position;
 
           r = NSMakeRect (x, s->ybase + position, width, thickness);
-
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
           NSRectFill (r);
         }
     }
@@ -3402,11 +3394,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     {
       NSRect r;
       r = NSMakeRect (x, s->y, width, 1);
-
-      if (face->overline_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->overline_color, s->f) set];
       NSRectFill (r);
     }
 
@@ -3429,10 +3416,6 @@ larger if there are taller display elements (e.g., characters
       dy = lrint ((glyph_height - h) / 2);
       r = NSMakeRect (x, glyph_y + dy, width, 1);
 
-      if (face->strike_through_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
       NSRectFill (r);
     }
 }
@@ -3580,17 +3563,7 @@ Function modeled after x_draw_glyph_string_box ().
   struct glyph *last_glyph;
   NSRect r;
   int hthickness, vthickness;
-  struct face *face;
-
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = s->face;
+  struct face *face = s->face;
 
   vthickness = face->box_vertical_line_width;
   hthickness = face->box_horizontal_line_width;
@@ -3664,34 +3637,26 @@ Function modeled after x_draw_glyph_string_box ().
 	  || FONT_TOO_HIGH (s->font)
           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
 	{
-          struct face *face;
-          if (s->hl == DRAW_MOUSE_FACE)
-            {
-              face
-		= FACE_FROM_ID_OR_NULL (s->f,
-					MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-              if (!face)
-                face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-            }
-          else
-            face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+          struct face *face = s->face;
           if (!face->stipple)
-            [(NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)) set];
+	    {
+	      if (s->hl != DRAW_CURSOR)
+		[(NS_FACE_BACKGROUND (face) != 0
+		  ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+		  : FRAME_BACKGROUND_COLOR (s->f)) set];
+	      else
+		[FRAME_CURSOR_COLOR (s->f) set];
+	    }
           else
             {
               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
             }
 
-          if (s->hl != DRAW_CURSOR)
-            {
-              NSRect r = NSMakeRect (s->x, s->y + box_line_width,
-                                    s->background_width,
-                                    s->height-2*box_line_width);
-              NSRectFill (r);
-            }
+	  NSRect r = NSMakeRect (s->x, s->y + box_line_width,
+				 s->background_width,
+				 s->height-2*box_line_width);
+	  NSRectFill (r);
 
 	  s->background_filled_p = 1;
 	}
@@ -3712,7 +3677,7 @@ Function modeled after x_draw_glyph_string_box ().
   int th;
   char raised_p;
   NSRect br;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *tdCol;
 
   NSTRACE ("ns_dumpglyphs_image");
@@ -3733,15 +3698,6 @@ Function modeled after x_draw_glyph_string_box ().
   /* Draw BG: if we need larger area than image itself cleared, do that,
      otherwise, since we composite the image under NS (instead of mucking
      with its background color), we must clear just the image area.  */
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-       face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
 
   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
 
@@ -3812,16 +3768,8 @@ Function modeled after x_draw_glyph_string_box ().
 
   if (s->hl == DRAW_CURSOR)
     {
-    [FRAME_CURSOR_COLOR (s->f) set];
-    if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
+      [FRAME_CURSOR_COLOR (s->f) set];
       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
-    else
-      /* Currently on NS img->mask is always 0.  Since
-         get_window_cursor_type specifies a hollow box cursor when on
-         a non-masked image we never reach this clause.  But we put it
-         in, in anticipation of better support for image masks on
-         NS.  */
-      tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
     }
   else
     {
@@ -3873,66 +3821,35 @@ Function modeled after x_draw_glyph_string_box ().
 static void
 ns_dumpglyphs_stretch (struct glyph_string *s)
 {
-  NSRect r[2];
   NSRect glyphRect;
-  int n;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *fgCol, *bgCol;
 
   if (!s->background_filled_p)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
 
-      if (s->hl == DRAW_MOUSE_FACE)
-        {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-        }
-      else
-        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+      face = s->face;
 
       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
 
-      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
-
-      [bgCol set];
-
-      /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
-         overwriting cursor (usually when cursor on a tab) */
       if (s->hl == DRAW_CURSOR)
-        {
-          CGFloat x, width;
+	{
+	  fgCol = bgCol;
+	  bgCol = FRAME_CURSOR_COLOR (s->f);
+	}
 
-          /* FIXME: This looks like it will only work for left to
-             right languages.  */
-          x = NSMinX (glyphRect);
-          width = s->w->phys_cursor_width;
-          glyphRect.size.width -= width;
-          glyphRect.origin.x += width;
+      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
 
-          NSRectFill (glyphRect);
+      [bgCol set];
 
-          /* Draw overlining, etc. on the cursor. */
-          if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-            ns_draw_text_decoration (s, face, bgCol, width, x);
-          else
-            ns_draw_text_decoration (s, face, fgCol, width, x);
-        }
-      else
-        {
-          NSRectFill (glyphRect);
-        }
+      NSRectFill (glyphRect);
 
       /* Draw overlining, etc. on the stretch glyph (or the part
          of the stretch glyph after the cursor). */
       ns_draw_text_decoration (s, face, fgCol, NSWidth (glyphRect),
                                NSMinX (glyphRect));
 
-      ns_unfocus (s->f);
       s->background_filled_p = 1;
     }
 }
@@ -3941,7 +3858,7 @@ overwriting cursor (usually when cursor on a tab) */
 static void
 ns_draw_glyph_string_foreground (struct glyph_string *s)
 {
-  int x, flags;
+  int x;
   struct font *font = s->font;
 
   /* If first glyph of S has a left box line, start drawing the text
@@ -3952,15 +3869,9 @@ overwriting cursor (usually when cursor on a tab) */
   else
     x = s->x;
 
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
   font->driver->draw
     (s, s->cmp_from, s->nchars, x, s->ybase,
-     (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
-     || flags == NS_DUMPGLYPH_MOUSEFACE);
+     !s->for_overlaps && !s->background_filled_p);
 }
 
 
@@ -4067,9 +3978,9 @@ overwriting cursor (usually when cursor on a tab) */
   struct font *font = s->face->font;
   if (! font) font = FRAME_FONT (s->f);
 
-  NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
+  NSTRACE ("ns_draw_glyph_string (hl = %u)", s->hl);
 
-  if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
+  if (s->next && s->right_overhang && !s->for_overlaps)
     {
       int width;
       struct glyph_string *next;
@@ -4106,14 +4017,21 @@ overwriting cursor (usually when cursor on a tab) */
       box_drawn_p = 1;
     }
 
+  n = ns_get_glyph_string_clip_rect (s, r);
+
+  if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+      && !s->clip_tail
+      && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+	  || (s->next && s->next->hl != s->hl && s->right_overhang)))
+    r[0] = NSIntersectionRect (r[0], NSMakeRect (s->x, s->y, s->width, s->height));
+
+  ns_focus (s->f, r, n);
+
   switch (s->first_glyph->type)
     {
 
     case IMAGE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
       ns_dumpglyphs_image (s, r[0]);
-      ns_unfocus (s->f);
       break;
 
     case XWIDGET_GLYPH:
@@ -4126,57 +4044,36 @@ overwriting cursor (usually when cursor on a tab) */
 
     case CHAR_GLYPH:
     case COMPOSITE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
-      if (s->for_overlaps || (s->cmp_from > 0
-			      && ! s->first_glyph->u.cmp.automatic))
-        s->background_filled_p = 1;
-      else
-        ns_maybe_dumpglyphs_background
-          (s, s->first_glyph->type == COMPOSITE_GLYPH);
-
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
       {
-        BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	if (s->for_overlaps || (isComposite
+				&& (s->cmp_from > 0
+				    && ! s->first_glyph->u.cmp.automatic)))
+	  s->background_filled_p = 1;
+	else
+	  ns_maybe_dumpglyphs_background
+	    (s, s->first_glyph->type == COMPOSITE_GLYPH);
 
-        if (isComposite)
-          ns_draw_composite_glyph_string_foreground (s);
-        else
-          ns_draw_glyph_string_foreground (s);
-      }
+	if (isComposite)
+	  ns_draw_composite_glyph_string_foreground (s);
+	else
+	  ns_draw_glyph_string_foreground (s);
 
-      {
-        NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
-                        ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
-                                                   s->f)
-                        : FRAME_FOREGROUND_COLOR (s->f));
-        [col set];
-
-        /* Draw underline, overline, strike-through. */
-        ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	{
+	  NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
+			  ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
+						     s->f)
+			  : FRAME_FOREGROUND_COLOR (s->f));
+	  [col set];
+
+	  /* Draw underline, overline, strike-through. */
+	  ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	}
       }
 
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
-      ns_unfocus (s->f);
       break;
 
     case GLYPHLESS_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
       if (s->for_overlaps || (s->cmp_from > 0
 			      && ! s->first_glyph->u.cmp.automatic))
         s->background_filled_p = 1;
@@ -4186,7 +4083,6 @@ overwriting cursor (usually when cursor on a tab) */
       /* ... */
       /* Not yet implemented.  */
       /* ... */
-      ns_unfocus (s->f);
       break;
 
     default:
@@ -4195,13 +4091,88 @@ overwriting cursor (usually when cursor on a tab) */
 
   /* Draw box if not done already.  */
   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
+    ns_dumpglyphs_box_or_relief (s);
+
+  ns_unfocus (s->f);
+
+  /* Draw surrounding overhangs. */
+  if (s->prev)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-      ns_dumpglyphs_box_or_relief (s);
-      ns_unfocus (s->f);
+      struct glyph_string *prev;
+
+      for (prev = s->prev; prev; prev = prev->prev)
+	if (prev->hl != s->hl
+	    && prev->x + prev->width + prev->right_overhang > s->x)
+	  {
+	    /* As prev was drawn while clipped to its own area, we
+	       must draw the right_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = prev->hl;
+	    struct face *save_face = prev->face;
+
+	    prev->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    prev->num_clips = 1;
+	    prev->hl = s->hl;
+	    if (prev->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (prev);
+	    else
+	      ns_draw_composite_glyph_string_foreground (prev);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    prev->hl = save;
+	    prev->face = save_face;
+	    prev->num_clips = 0;
+	  }
     }
 
+  if (s->next)
+    {
+      struct glyph_string *next;
+
+      for (next = s->next; next; next = next->next)
+	if (next->hl != s->hl
+	    && next->x - next->left_overhang < s->x + s->width)
+	  {
+	    /* As next will be drawn while clipped to its own area,
+	       we must draw the left_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = next->hl;
+	    struct face *save_face = next->face;
+
+	    next->hl = s->hl;
+	    next->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    next->num_clips = 1;
+	    if (next->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (next);
+	    else
+	      ns_draw_composite_glyph_string_foreground (next);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    next->hl = save;
+	    next->num_clips = 0;
+	    next->face = save_face;
+	    next->clip_head = next;
+	    next->background_filled_p = 0;
+	  }
+    }
   s->num_clips = 0;
 }
 
diff --git a/src/xdisp.c b/src/xdisp.c
index cb8b793cfb..bfe7c571ab 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -29295,7 +29295,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
   for (s = head; s; s = s->next)
     FRAME_RIF (f)->draw_glyph_string (s);
 
-#ifndef HAVE_NS
   /* When focus a sole frame and move horizontally, this clears on_p
      causing a failure to erase prev cursor position. */
   if (area == TEXT_AREA
@@ -29314,7 +29313,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
       notice_overwritten_cursor (w, TEXT_AREA, x0, x1,
 				 row->y, MATRIX_ROW_BOTTOM_Y (row));
     }
-#endif
 
   /* Value is the x-position up to which drawn, relative to AREA of W.
      This doesn't include parts drawn because of overhangs.  */
-- 
2.31.1


[-- Attachment #3: Type: text/plain, Size: 126 bytes --]


I think these changes are appropriate enough for the font display patch,
as they affect cursor display and overhang drawing.

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-28  1:09         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-28 10:17           ` Alan Third
  2021-10-28 11:25             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 22+ messages in thread
From: Alan Third @ 2021-10-28 10:17 UTC (permalink / raw)
  To: Po Lu; +Cc: 51411

On Thu, Oct 28, 2021 at 09:09:33AM +0800, Po Lu wrote:
> Alan Third <alan@idiocy.org> writes:
> 
> > I understand what you did, but I think the better solution is for us
> > is to try to untangle ns_draw_glyph_string, even if that means saving
> > the context there directly on occasion.
> 
> > If you want me to have a look at it let me know, I think I know what
> > needs to be done, but I probably won't get round to it as soon as you
> > could.
> 
> Thanks.  I had a try at it, cleaning up much of what appeared obviously
> unnecessary.  (Though I did not dare change what seemed to be mysterious
> to me.)
> 
> Though, OTOH, I think calling saveGraphicsState inside the overhang draw
> process is not too much of a problem as it happens infrequently enough
> to not be relevant.

Thanks, I'm much happier with this now.

I have no problem with saveGraphicsState being called within functions
directly, I'd just rather avoid adding complexity to commonly used
functions like ns_focus, which I already think are doing too much.

I've got a couple of further notes.

> diff --git a/src/nsterm.m b/src/nsterm.m
> index 4c2a3f287c..051ee511ca 100644
> --- a/src/nsterm.m
> +++ b/src/nsterm.m
> @@ -1078,11 +1078,20 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
>    /* clipping */
>    if (r)
>      {
> -      [[NSGraphicsContext currentContext] saveGraphicsState];
> +      NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
> +      [ctx saveGraphicsState];
>        if (n == 2)
>          NSRectClipList (r, 2);
>        else
>          NSRectClip (*r);
> +#ifdef NS_IMPL_GNUSTEP
> +      DPSrectclip (ctx, NSMinX (*r), NSMinY (*r),
> +		   NSWidth (*r), NSHeight (*r));
> +
> +      if (n == 2)
> +	DPSrectclip (ctx, NSMinX (r[1]), NSMinY (r[1]),
> +		     NSWidth (r[1]), NSHeight (r[1]));
> +#endif
>        gsaved = YES;
>      }
>  }

NSRectClipList creates a union of the passed rectangles and then sets
the clipping rectangle to that, so it contains all of them. That's
useful because NSRectClip uses the intersection of the current
clipping rectangle and the new one.

Does DPSrectclip use the intersection? If so this may not work as
intended and it might be better to union the rectangles ourselves.

> @@ -4195,13 +4091,88 @@ overwriting cursor (usually when cursor on a tab) */
>  
>    /* Draw box if not done already.  */
>    if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
> +    ns_dumpglyphs_box_or_relief (s);
> +
> +  ns_unfocus (s->f);

You unfocus here...

> +
> +  /* Draw surrounding overhangs. */
> +  if (s->prev)
>      {
> -      n = ns_get_glyph_string_clip_rect (s, r);
> -      ns_focus (s->f, r, n);
> -      ns_dumpglyphs_box_or_relief (s);
> -      ns_unfocus (s->f);
> +      struct glyph_string *prev;
> +
> +      for (prev = s->prev; prev; prev = prev->prev)
> +	if (prev->hl != s->hl
> +	    && prev->x + prev->width + prev->right_overhang > s->x)
> +	  {
> +	    /* As prev was drawn while clipped to its own area, we
> +	       must draw the right_overhang part using s->hl now.  */
> +	    enum draw_glyphs_face save = prev->hl;
> +	    struct face *save_face = prev->face;
> +
> +	    prev->face = s->face;
> +	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
> +	    [[NSGraphicsContext currentContext] saveGraphicsState];

... then continue working on the frame here. You either need to focus
again or extend the original focus. Remember you can focus without
setting the clipping, then save and reset the graphics state as
required if you prefer.

-- 
Alan Third





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-28 10:17           ` Alan Third
@ 2021-10-28 11:25             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31 10:22               ` Alan Third
  0 siblings, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-28 11:25 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

[-- Attachment #1: Type: text/plain, Size: 1221 bytes --]

Alan Third <alan@idiocy.org> writes:

> NSRectClipList creates a union of the passed rectangles and then sets
> the clipping rectangle to that, so it contains all of them. That's
> useful because NSRectClip uses the intersection of the current
> clipping rectangle and the new one.
>
> Does DPSrectclip use the intersection? If so this may not work as
> intended and it might be better to union the rectangles ourselves.

It does intersect the rectangle with the current clipping.  I determined
that from a cursory examination of the source code, as the function
appeared in the documentation as documented, but no documentation was
actually written for it.

>> @@ -4195,13 +4091,88 @@ overwriting cursor (usually when cursor on a tab) */
>>  
>>    /* Draw box if not done already.  */
>>    if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
>> +    ns_dumpglyphs_box_or_relief (s);
>> +
>> +  ns_unfocus (s->f);

> You unfocus here, then continue working on the frame here. You either
> need to focus again or extend the original focus. Remember you can
> focus without setting the clipping, then save and reset the graphics
> state as required if you prefer.

Hmm.  Will this work better?  Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-font-display-on-NS-port.patch --]
[-- Type: text/x-patch, Size: 78728 bytes --]

From f33043632baf3e6ae55ff7dbff8bddcbf74b4d0d Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:44:03 +0800
Subject: [PATCH] Improve font display on NS port

* src/nsfns.m (Fx_create_frame): Use "fixed" for the default font on
GNUstep.

* src/nsfont.m (LCD_SMOOTHING_MARGIN, ns_escape_name)
(ns_unescape_name, ns_attribute_fvalue)
(STYLE_REF): Remove unused defines and functions.

(struct ns_glyph_layout, enum lgstring_direction).
(enum gs_font_slant, enum gs_font_weight, enum gs_font_width)
(enum gs_specified, struct gs_font_data): New enumerators and
structures.

(ns_font_descs_match_p)
(ns_done_font_data, ns_get_font_data): New functions.
(ns_glyph_metrics): Stop escaping names.

(ns_spec_to_descriptor): Fix font descriptor creation for symbolic
font spec entires.
(ns_descriptor_to_entity): Create entries with the correct symbolic
styles.

(ns_fallback_entity): Fix fallback entity selection.
(ns_findfonts): Use our own font matcher instead of the broken GNUstep
matcher.

(ns_list_family): Remove obsolete comment.
(nsfont_open): Remove obsolete code, comments, and synthItal logic
which doesn't work on GNUstep.

(nsfont_encode_char): Use a type that can fit NSGlyph
(nsfont_draw): Chose correct font, remove obsolete mouse face logic,
obsolete comments, and switch to using glyph-based drawing instead of
character-based drawing.

(ns_font_shape, nsfont_shape): New functions.

(ns_uni_to_glyphs_1): New function.
(ns_uni_to_glyphs): Return glyphs instead of unicode codepoints.

(ns_glyph_metrics): Use NSGlyphs instead of unicode codepoints and fix
left bearing, right bearing, ascent and descent computation.

(struct nsfont_driver): Add shaping capability.

* src/nsterm.h (struct nsfont_info): Use unsigned int for glyph cache.

* src/nsterm.c (ns_focus): Set DPS clipping on GNUstep.

(ns_compute_glyph_string_overhangs): Fix overhang computation by using
xterm code.
(ns_draw_window_cursor): Simplify cursor drawing.
(ns_maybe_dumpglyphs_background): Test for cursor HL and remove
obsolete mouse face logic.
(ns_dumpglyphs_image)
(ns_dumpglyphs_box_or_relief): Rectify for new cursor logic.
(ns_dumpglyphs_stretch): Rectify for new cursor logic and rely on
ns_draw_glyph_string to set focus.
(ns_draw_glyph_string_foreground): Remove mouse face logic.
(ns_draw_glyph_strings): Implement overhangs, remove obsolete
comment, and always focus before dumping glyphs.
(ns_draw_text_decoration): Add condition for DRAW_CURSOR and simplify
color selection.
(ns_define_frame_cursor): Remove nonsensical code (define_frame_cursor
has nothing to do with the text cursor, aka caret).

* src/xdisp.c (draw_glyphs): Enable code for NS port to fix mouse face
cursor display.

* src/macfont.m (get_cgcolor_from_nscolor): New function.
(macfont_draw): Remove obsolete mouse-face code and enable cursor
display.
---
 src/macfont.m |   36 +-
 src/nsfns.m   |    6 +
 src/nsfont.m  | 1215 +++++++++++++++++++++++++++++++++++--------------
 src/nsterm.h  |    2 +-
 src/nsterm.m  |  417 ++++++++---------
 src/xdisp.c   |    2 -
 6 files changed, 1096 insertions(+), 582 deletions(-)

diff --git a/src/macfont.m b/src/macfont.m
index d86f09f485..df552400e3 100644
--- a/src/macfont.m
+++ b/src/macfont.m
@@ -613,6 +613,21 @@ static void mac_font_get_glyphs_for_variants (CFDataRef, UTF32Char,
   return cgColor;
 }
 
+static CGColorRef
+get_cgcolor_from_nscolor (NSColor *nsColor, struct frame *f)
+{
+  [nsColor set];
+  CGColorSpaceRef colorSpace = [[nsColor colorSpace] CGColorSpace];
+  NSInteger noc = [nsColor numberOfComponents];
+  CGFloat *components = xmalloc (sizeof(CGFloat)*(1+noc));
+  CGColorRef cgColor;
+
+  [nsColor getComponents: components];
+  cgColor = CGColorCreate (colorSpace, components);
+  xfree (components);
+  return cgColor;
+}
+
 #define CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND(context, face, f)        \
   do {                                                                  \
     CGColorRef refcol_ = get_cgcolor (NS_FACE_FOREGROUND (face), f);    \
@@ -2907,14 +2922,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
 
   if (!CGRectIsNull (background_rect))
     {
-      if (s->hl == DRAW_MOUSE_FACE)
+      if (s->hl == DRAW_CURSOR)
         {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-				       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_CURSOR_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
         }
-      CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
       CGContextFillRects (context, &background_rect, 1);
     }
 
@@ -2923,7 +2938,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
       CGAffineTransform atfm;
 
       CGContextScaleCTM (context, 1, -1);
-      CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
+      if (s->hl == DRAW_CURSOR)
+        {
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_BACKGROUND_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
+        }
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
       if (macfont_info->synthetic_italic_p)
         atfm = synthetic_italic_atfm;
       else
diff --git a/src/nsfns.m b/src/nsfns.m
index 797d0ce782..f4d8172246 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -1236,6 +1236,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                          "fontBackend", "FontBackend", RES_TYPE_STRING);
 
   {
+#ifdef NS_IMPL_COCOA
     /* use for default font name */
     id font = [NSFont userFixedPitchFontOfSize: -1.0]; /* default */
     gui_default_parameter (f, parms, Qfontsize,
@@ -1250,6 +1251,11 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                            build_string (fontname),
                            "font", "Font", RES_TYPE_STRING);
     xfree (fontname);
+#else
+    gui_default_parameter (f, parms, Qfont,
+                           build_string ("fixed"),
+                           "font", "Font", RES_TYPE_STRING);
+#endif
   }
   unblock_input ();
 
diff --git a/src/nsfont.m b/src/nsfont.m
index 5a9cdfebc0..b3224629f0 100644
--- a/src/nsfont.m
+++ b/src/nsfont.m
@@ -1,4 +1,4 @@
-/* Font back-end driver for the NeXT/Open/GNUstep and macOS window system.
+/* Font back-end driver for the GNUstep window system.
    See font.h
    Copyright (C) 2006-2021 Free Software Foundation, Inc.
 
@@ -38,47 +38,269 @@
 #include "termchar.h"
 #include "pdumper.h"
 
-/* TODO: Drop once we can assume gnustep-gui 0.17.1.  */
+#import <Foundation/NSException.h>
 #import <AppKit/NSFontDescriptor.h>
+#import <AppKit/NSLayoutManager.h>
+#import <GNUstepGUI/GSLayoutManager.h>
+#import <GNUstepGUI/GSFontInfo.h>
 
 #define NSFONT_TRACE 0
-#define LCD_SMOOTHING_MARGIN 2
 
-/* Font glyph and metrics caching functions, implemented at end.  */
-static void ns_uni_to_glyphs (struct nsfont_info *font_info,
-                              unsigned char block);
-static void ns_glyph_metrics (struct nsfont_info *font_info,
-                              unsigned char block);
+/* Structure used by GS `shape' functions for storing layout
+   information for each glyph.  Borrowed from macfont.h.  */
+struct ns_glyph_layout
+{
+  /* Range of indices of the characters composed into the group of
+     glyphs that share the cursor position with this glyph.  The
+     members `location' and `length' are in UTF-16 indices.  */
+  NSRange comp_range;
 
-#define INVALID_GLYPH 0xFFFF
+  /* UTF-16 index in the source string for the first character
+     associated with this glyph.  */
+  NSUInteger string_index;
 
-/* ==========================================================================
+  /* Horizontal and vertical adjustments of glyph position.  The
+     coordinate space is that of Core Text.  So, the `baseline_delta'
+     value is negative if the glyph should be placed below the
+     baseline.  */
+  CGFloat advance_delta, baseline_delta;
 
-    Utilities
+  /* Typographical width of the glyph.  */
+  CGFloat advance;
 
-   ========================================================================== */
+  /* Glyph ID of the glyph.  */
+  NSGlyph glyph_id;
+};
+
+
+enum lgstring_direction
+  {
+    DIR_R2L = -1, DIR_UNKNOWN = 0, DIR_L2R = 1
+  };
+
+enum gs_font_slant
+  {
+    GS_FONT_SLANT_ITALIC,
+    GS_FONT_SLANT_REVERSE_ITALIC,
+    GS_FONT_SLANT_NORMAL
+  };
+
+enum gs_font_weight
+  {
+    GS_FONT_WEIGHT_LIGHT,
+    GS_FONT_WEIGHT_BOLD,
+    GS_FONT_WEIGHT_NORMAL
+  };
+
+enum gs_font_width
+  {
+    GS_FONT_WIDTH_CONDENSED,
+    GS_FONT_WIDTH_EXPANDED,
+    GS_FONT_WIDTH_NORMAL
+  };
+
+enum gs_specified
+  {
+    GS_SPECIFIED_SLANT = 1,
+    GS_SPECIFIED_WEIGHT = 1 << 1,
+    GS_SPECIFIED_WIDTH = 1 << 2,
+    GS_SPECIFIED_FAMILY = 1 << 3,
+    GS_SPECIFIED_SPACING = 1 << 4
+  };
 
+struct gs_font_data
+{
+  int specified;
+  enum gs_font_slant slant;
+  enum gs_font_weight weight;
+  enum gs_font_width width;
+  bool monospace_p;
+  char *family_name;
+};
 
-/* Replace spaces w/another character so emacs core font parsing routines
-   aren't thrown off.  */
 static void
-ns_escape_name (char *name)
+ns_done_font_data (struct gs_font_data *data)
 {
-  for (; *name; name++)
-    if (*name == ' ')
-      *name = '_';
+  if (data->specified & GS_SPECIFIED_FAMILY)
+    xfree (data->family_name);
 }
 
-
-/* Reconstruct spaces in a font family name passed through emacs.  */
 static void
-ns_unescape_name (char *name)
+ns_get_font_data (NSFontDescriptor *desc, struct gs_font_data *dat)
 {
-  for (; *name; name++)
-    if (*name == '_')
-      *name = ' ';
+  NSNumber *tem;
+  NSFontSymbolicTraits traits = [desc symbolicTraits];
+  NSDictionary *dict = [desc objectForKey: NSFontTraitsAttribute];
+  NSString *family = [desc objectForKey: NSFontFamilyAttribute];
+
+  dat->specified = 0;
+
+  if (family != nil)
+    {
+      dat->specified |= GS_SPECIFIED_FAMILY;
+      dat->family_name = xstrdup ([family cStringUsingEncoding: NSUTF8StringEncoding]);
+    }
+
+  tem = [desc objectForKey: NSFontFixedAdvanceAttribute];
+
+  if ((tem != nil && [tem boolValue] != NO)
+      || (traits & NSFontMonoSpaceTrait))
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = true;
+    }
+  else if (tem != nil && [tem boolValue] == NO)
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = false;
+    }
+
+  if (traits & NSFontBoldTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WEIGHT;
+      dat->weight = GS_FONT_WEIGHT_BOLD;
+    }
+
+  if (traits & NSFontItalicTrait)
+    {
+      dat->specified |= GS_SPECIFIED_SLANT;
+      dat->slant = GS_FONT_SLANT_ITALIC;
+    }
+
+  if (traits & NSFontCondensedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_CONDENSED;
+    }
+  else if (traits & NSFontExpandedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_EXPANDED;
+    }
+
+  if (dict != nil)
+    {
+      tem = [dict objectForKey: NSFontSlantTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_SLANT;
+
+	  dat->slant = [tem floatValue] > 0
+	    ? GS_FONT_SLANT_ITALIC
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_SLANT_REVERSE_ITALIC
+	       : GS_FONT_SLANT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWeightTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WEIGHT;
+
+	  dat->weight = [tem floatValue] > 0
+	    ? GS_FONT_WEIGHT_BOLD
+	    : ([tem floatValue] < -0.4f
+	       ? GS_FONT_WEIGHT_LIGHT
+	       : GS_FONT_WEIGHT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWidthTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WIDTH;
+
+	  dat->width = [tem floatValue] > 0
+	    ? GS_FONT_WIDTH_EXPANDED
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_WIDTH_NORMAL
+	       : GS_FONT_WIDTH_CONDENSED);
+	}
+    }
+}
+
+static bool
+ns_font_descs_match_p (NSFontDescriptor *desc, NSFontDescriptor *target)
+{
+  struct gs_font_data dat;
+  struct gs_font_data t;
+
+  ns_get_font_data (desc, &dat);
+  ns_get_font_data (target, &t);
+
+  if (!(t.specified & GS_SPECIFIED_WIDTH))
+    t.width = GS_FONT_WIDTH_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_WEIGHT))
+    t.weight = GS_FONT_WEIGHT_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_SPACING))
+    t.monospace_p = false;
+  if (!(t.specified & GS_SPECIFIED_SLANT))
+    t.slant = GS_FONT_SLANT_NORMAL;
+
+  if (!(t.specified & GS_SPECIFIED_FAMILY))
+    emacs_abort ();
+
+  bool match_p = true;
+
+  if (dat.specified & GS_SPECIFIED_WIDTH
+      && dat.width != t.width)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_WEIGHT
+      && dat.weight != t.weight)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SPACING
+      && dat.monospace_p != t.monospace_p)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SLANT
+      && dat.monospace_p != t.monospace_p)
+    {
+      if (NSFONT_TRACE)
+	printf ("Matching monospace for %s: %d %d\n",
+		t.family_name, dat.monospace_p,
+		t.monospace_p);
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_FAMILY
+      && strcmp (dat.family_name, t.family_name))
+    match_p = false;
+
+ gout:
+  ns_done_font_data (&dat);
+  ns_done_font_data (&t);
+
+  return match_p;
 }
 
+/* Font glyph and metrics caching functions, implemented at end.  */
+static void ns_uni_to_glyphs (struct nsfont_info *font_info,
+                              unsigned char block);
+static void ns_glyph_metrics (struct nsfont_info *font_info,
+                              unsigned int block);
+
+#define INVALID_GLYPH 0xFFFF
+
+/* ==========================================================================
+
+    Utilities
+
+   ========================================================================== */
+
 
 /* Extract family name from a font spec.  */
 static NSString *
@@ -91,66 +313,116 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
     {
       char *tmp = xlispstrdup (SYMBOL_NAME (tem));
       NSString *family;
-      ns_unescape_name (tmp);
       family = [NSString stringWithUTF8String: tmp];
       xfree (tmp);
       return family;
     }
 }
 
-
-/* Return 0 if attr not set, else value (which might also be 0).
-   On Leopard 0 gets returned even on descriptors where the attribute
-   was never set, so there's no way to distinguish between unspecified
-   and set to not have.  Callers should assume 0 means unspecified.  */
-static float
-ns_attribute_fvalue (NSFontDescriptor *fdesc, NSString *trait)
-{
-    NSDictionary *tdict = [fdesc objectForKey: NSFontTraitsAttribute];
-    NSNumber *val = [tdict objectForKey: trait];
-    return val == nil ? 0.0F : [val floatValue];
-}
-
-
 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
    to NSFont descriptor.  Information under extra only needed for matching.  */
-#define STYLE_REF 100
 static NSFontDescriptor *
 ns_spec_to_descriptor (Lisp_Object font_spec)
 {
     NSFontDescriptor *fdesc;
     NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
-    NSMutableDictionary *tdict = [NSMutableDictionary new];
     NSString *family = ns_get_family (font_spec);
-    float n;
-
-    /* Add each attr in font_spec to fdAttrs.  */
-    n = min (FONT_WEIGHT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWeightTrait];
-    n = min (FONT_SLANT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontSlantTrait];
-    n = min (FONT_WIDTH_NUMERIC (font_spec), 200);
-    if (n > -1 && (n > STYLE_REF + 10 || n < STYLE_REF - 10))
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWidthTrait];
-    if ([tdict count] > 0)
-	[fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+    NSMutableDictionary *tdict = [NSMutableDictionary new];
 
-    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
-               retain] autorelease];
+    Lisp_Object tem;
+
+    tem = FONT_SLANT_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontSlantTrait];
+	else if (EQ (tem, intern ("reverse-italic")) ||
+		 EQ (tem, intern ("reverse-oblique")))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontSlantTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontSlantTrait];
+      }
+
+    tem = FONT_WIDTH_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qcondensed))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontWidthTrait];
+	else if (EQ (tem, Qexpanded))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontWidthTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontWidthTrait];
+      }
+
+    tem = FONT_WEIGHT_SYMBOLIC (font_spec);
+
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qbold))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else if (EQ (tem, Qlight))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		      forKey: NSFontWeightTrait];
+	  }
+      }
+
+    tem = AREF (font_spec, FONT_SPACING_INDEX);
 
     if (family != nil)
       {
-        NSFontDescriptor *fdesc2 = [fdesc fontDescriptorWithFamily: family];
-        fdesc = [[fdesc2 retain] autorelease];
+	[fdAttrs setObject: family
+		    forKey: NSFontFamilyAttribute];
       }
 
-    [fdAttrs release];
+    if (FIXNUMP (tem))
+      {
+	if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:YES]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+	else
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:NO]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+      }
+
+    /* Handle special families such as ``fixed'' or ``Sans Serif''.  */
+
+    if ([family isEqualToString: @"fixed"])
+      {
+	[fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+    else if ([family isEqualToString: @"Sans Serif"])
+      {
+	[fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+
+    [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+
+    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
+               retain] autorelease];
+
     [tdict release];
+    [fdAttrs release];
     return fdesc;
 }
 
@@ -161,61 +433,64 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
                          Lisp_Object extra,
                          const char *style)
 {
-    Lisp_Object font_entity = font_make_entity ();
-    /*   NSString *psName = [desc postscriptName]; */
-    NSString *family = [desc objectForKey: NSFontFamilyAttribute];
-    unsigned int traits = [desc symbolicTraits];
-    char *escapedFamily;
-
-    /* Shouldn't happen, but on Tiger fallback desc gets name but no family.  */
-    if (family == nil)
-      family = [desc objectForKey: NSFontNameAttribute];
-    if (family == nil)
-      family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-
-    escapedFamily = xstrdup ([family UTF8String]);
-    ns_escape_name (escapedFamily);
-
-    ASET (font_entity, FONT_TYPE_INDEX, Qns);
-    ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
-    ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
-    ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
-    ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
-
-    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    traits & NSFontBoldTrait ? Qbold : Qmedium);
-/*    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    make_fixnum (100 + 100
-			* ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    traits & NSFontItalicTrait ? Qitalic : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-                    traits & NSFontCondensedTrait ? Qcondensed :
-                    traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
-
-    ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_SPACING_INDEX,
-	  make_fixnum([desc symbolicTraits] & NSFontMonoSpaceTrait
-	      ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
-
-    ASET (font_entity, FONT_EXTRA_INDEX, extra);
-    ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+  Lisp_Object font_entity = font_make_entity ();
+  struct gs_font_data data;
+  ns_get_font_data (desc, &data);
+
+  ASET (font_entity, FONT_TYPE_INDEX, Qns);
+  ASET (font_entity, FONT_FOUNDRY_INDEX, Qns);
+  if (data.specified & GS_SPECIFIED_FAMILY)
+    ASET (font_entity, FONT_FAMILY_INDEX, intern (data.family_name));
+  ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
+  ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
+
+  if (data.specified & GS_SPECIFIED_WEIGHT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
+		      data.weight == GS_FONT_WEIGHT_BOLD
+		      ? Qbold : (data.weight == GS_FONT_WEIGHT_LIGHT
+				 ? Qlight : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX, Qnormal);
 
-    if (NSFONT_TRACE)
-      {
-	fputs ("created font_entity:\n    ", stderr);
-	debug_print (font_entity);
-      }
+  if (data.specified & GS_SPECIFIED_SLANT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
+		      data.slant == GS_FONT_SLANT_ITALIC
+		      ? Qitalic : (data.slant == GS_FONT_SLANT_REVERSE_ITALIC
+				   ? intern ("reverse-italic") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX, Qnormal);
+
+  if (data.specified & GS_SPECIFIED_WIDTH)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
+		      data.width == GS_FONT_WIDTH_CONDENSED
+		      ? Qcondensed : (data.width == GS_FONT_WIDTH_EXPANDED
+				      ? intern ("expanded") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX, Qnormal);
 
-    xfree (escapedFamily);
-    return font_entity;
+  ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_SPACING_INDEX,
+	make_fixnum ((data.specified & GS_SPECIFIED_WIDTH && data.monospace_p)
+		     ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
+
+  ASET (font_entity, FONT_EXTRA_INDEX, extra);
+  ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+
+  if (NSFONT_TRACE)
+    {
+      fputs ("created font_entity:\n    ", stderr);
+      debug_print (font_entity);
+    }
+
+  ns_done_font_data (&data);
+  return font_entity;
 }
 
 
@@ -223,8 +498,7 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
 static Lisp_Object
 ns_fallback_entity (void)
 {
-  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
-      fontDescriptor], Qnil, NULL);
+  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 1] fontDescriptor], Qnil, NULL);
 }
 
 
@@ -510,21 +784,20 @@ but also for ascii (which causes unnecessary font substitution).  */
     return families;
 }
 
+/* GNUstep font matching is very mediocre (it can't even compare
+   symbolic styles correctly), which is why our own font matching
+   mechanism must be implemented.  */
 
-/* Implementation for list() and match().  List() can return nil, match()
-must return something.  Strategy is to drop family name from attribute
-matching set for match.  */
+/* Implementation for list and match.  */
 static Lisp_Object
 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
 {
     Lisp_Object tem, list = Qnil;
-    NSFontDescriptor *fdesc, *desc;
-    NSMutableSet *fkeys;
-    NSArray *matchingDescs;
-    NSEnumerator *dEnum;
-    NSString *family;
+    NSFontDescriptor *fdesc;
+    NSArray *all_descs;
+    GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
+
     NSSet *cFamilies;
-    BOOL foundItal = NO;
 
     block_input ();
     if (NSFONT_TRACE)
@@ -537,43 +810,22 @@ but also for ascii (which causes unnecessary font substitution).  */
     cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
 
     fdesc = ns_spec_to_descriptor (font_spec);
-    fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
-    if (isMatch)
-	[fkeys removeObject: NSFontFamilyAttribute];
-
-    matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
+    all_descs = [enumerator availableFontDescriptors];
 
-    if (NSFONT_TRACE)
-	NSLog(@"Got desc %@ and found %lu matching fonts from it: ", fdesc,
-	      (unsigned long)[matchingDescs count]);
-
-    for (dEnum = [matchingDescs objectEnumerator]; (desc = [dEnum nextObject]);)
+    for (NSFontDescriptor *desc in all_descs)
       {
 	if (![cFamilies containsObject:
 	         [desc objectForKey: NSFontFamilyAttribute]])
 	    continue;
+	if (!ns_font_descs_match_p (fdesc, desc))
+	  continue;
+
         tem = ns_descriptor_to_entity (desc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
+				       AREF (font_spec, FONT_EXTRA_INDEX),
                                        NULL);
         if (isMatch)
           return tem;
 	list = Fcons (tem, list);
-	if (fabs (ns_attribute_fvalue (desc, NSFontSlantTrait)) > 0.05)
-	    foundItal = YES;
-      }
-
-    /* Add synthItal member if needed.  */
-    family = [fdesc objectForKey: NSFontFamilyAttribute];
-    if (family != nil && !foundItal && !NILP (list))
-      {
-        NSFontDescriptor *s1 = [NSFontDescriptor new];
-        NSFontDescriptor *sDesc
-          = [[s1 fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
-              fontDescriptorWithFamily: family];
-	list = Fcons (ns_descriptor_to_entity (sDesc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
-					 "synthItal"), list);
-        [s1 release];
       }
 
     unblock_input ();
@@ -652,7 +904,6 @@ Properties to be considered are same as for list().  */
                objectEnumerator];
   while ((family = [families nextObject]))
       list = Fcons (intern ([family UTF8String]), list);
-  /* FIXME: escape the name?  */
 
   if (NSFONT_TRACE)
     fprintf (stderr, "nsfont: list families returning %"pD"d entries\n",
@@ -668,18 +919,15 @@ Properties to be considered are same as for list().  */
 static Lisp_Object
 nsfont_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
 {
-  BOOL synthItal;
-  unsigned int traits = 0;
   struct nsfont_info *font_info;
   struct font *font;
   NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
   NSFontManager *fontMgr = [NSFontManager sharedFontManager];
   NSString *family;
   NSFont *nsfont, *sfont;
-  Lisp_Object tem;
   NSRect brect;
   Lisp_Object font_object;
-  int fixLeopardBug;
+  Lisp_Object tem;
 
   block_input ();
 
@@ -692,42 +940,20 @@ Properties to be considered are same as for list().  */
   if (pixel_size <= 0)
     {
       /* try to get it out of frame params */
-        Lisp_Object tem = get_frame_param (f, Qfontsize);
-        pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
+      tem = get_frame_param (f, Qfontsize);
+      pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
     }
 
   tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
-  synthItal = !NILP (tem) && !strncmp ("synthItal", SSDATA (SYMBOL_NAME (tem)),
-                                       9);
   family = ns_get_family (font_entity);
   if (family == nil)
     family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-  /* Should be > 0.23 as some font descriptors (e.g. Terminus) set to that
-     when setting family in ns_spec_to_descriptor().  */
-  if (ns_attribute_fvalue (fontDesc, NSFontWeightTrait) > 0.50F)
-      traits |= NSBoldFontMask;
-  if (ns_attribute_fvalue (fontDesc, NSFontSlantTrait) > 0.05F)
-      traits |= NSItalicFontMask;
-
-  /* see https://web.archive.org/web/20100201175731/http://cocoadev.com/forums/comments.php?DiscussionID=74 */
-  fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
-  nsfont = [fontMgr fontWithFamily: family
-                            traits: traits weight: fixLeopardBug
-			      size: pixel_size];
-  /* if didn't find, try synthetic italic */
-  if (nsfont == nil && synthItal)
-    {
-      nsfont = [fontMgr fontWithFamily: family
-                                traits: traits & ~NSItalicFontMask
-                                weight: fixLeopardBug size: pixel_size];
-    }
+
+  nsfont = [NSFont fontWithDescriptor: fontDesc
+				 size: pixel_size];
 
   if (nsfont == nil)
-    {
-      message_with_string ("*** Warning: font in family `%s' not found",
-                          build_string ([family UTF8String]), 1);
-      nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
-    }
+    nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
 
   if (NSFONT_TRACE)
     NSLog (@"%@\n", nsfont);
@@ -740,7 +966,7 @@ when setting family in ns_spec_to_descriptor().  */
   if (!font)
     {
       unblock_input ();
-      return Qnil; /* FIXME: other terms do, but returning Qnil causes segfault.  */
+      return Qnil;
     }
 
   font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs);
@@ -781,7 +1007,7 @@ when setting family in ns_spec_to_descriptor().  */
     font_info->name = xstrdup (fontName);
     font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
     font_info->ital =
-      synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
+      ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
 
     /* Metrics etc.; some fonts return an unusually large max advance, so we
        only use it for fonts that have wide characters.  */
@@ -808,8 +1034,6 @@ when setting family in ns_spec_to_descriptor().  */
       lrint (brect.size.width - (CGFloat) font_info->width);
 
     /* set up metrics portion of font struct */
-    font->ascent = lrint([sfont ascender]);
-    font->descent = -lrint(floor(adjusted_descender));
     font->space_width = lrint (ns_char_width (sfont, ' '));
     font->max_width = lrint (font_info->max_bounds.width);
     font->min_width = font->space_width;  /* Approximate.  */
@@ -871,7 +1095,7 @@ when setting family in ns_spec_to_descriptor().  */
 {
   struct nsfont_info *font_info = (struct nsfont_info *)font;
   unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
-  unsigned short g;
+  unsigned int g;
 
   if (c > 0xFFFF)
     return FONT_INVALID_CODE;
@@ -934,51 +1158,23 @@ is false when (FROM > 0 || TO < S->nchars).  */
 static int
 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
              bool with_background)
-/* NOTE: focus and clip must be set.  */
 {
-  static unsigned char cbuf[1024];
-  unsigned char *c = cbuf;
-#if GNUSTEP_GUI_MAJOR_VERSION > 0 || GNUSTEP_GUI_MINOR_VERSION > 22
-  static CGFloat advances[1024];
-  CGFloat *adv = advances;
-#else
-  static float advances[1024];
-  float *adv = advances;
-#endif
+  NSGlyph *c = alloca ((to - from) * sizeof *c);
+
   struct face *face;
   NSRect r;
   struct nsfont_info *font;
-  NSColor *col, *bgCol;
-  unsigned *t = s->char2b;
-  int i, len, flags;
+  NSColor *col;
+  int len = to - from;
   char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
 
   block_input ();
 
-  font = (struct nsfont_info *)s->face->font;
+  font = (struct nsfont_info *) s->font;
   if (font == NULL)
     font = (struct nsfont_info *)FRAME_FONT (s->f);
 
-  /* Select face based on input flags.  */
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
-  switch (flags)
-    {
-    case NS_DUMPGLYPH_CURSOR:
-      face = s->face;
-      break;
-    case NS_DUMPGLYPH_MOUSEFACE:
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-      break;
-    default:
-      face = s->face;
-    }
+  face = s->face;
 
   r.origin.x = s->x;
   if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
@@ -987,91 +1183,24 @@ is false when (FROM > 0 || TO < S->nchars).  */
   r.origin.y = s->y;
   r.size.height = FONT_HEIGHT (font);
 
-  /* Convert UTF-16 (?) to UTF-8 and determine advances.  Note if we just ask
-     NS to render the string, it will come out differently from the individual
-     character widths added up because of layout processing.  */
-  {
-    int cwidth, twidth = 0;
-    int hi, lo;
-    /* FIXME: composition: no vertical displacement is considered.  */
-    t += from; /* advance into composition */
-    for (i = from; i < to; i++, t++)
-      {
-        hi = (*t & 0xFF00) >> 8;
-        lo = *t & 0x00FF;
-        if (isComposite)
-          {
-	    if (!s->first_glyph->u.cmp.automatic)
-		cwidth = s->cmp->offsets[i * 2] /* (H offset) */ - twidth;
-	    else
-	      {
-		Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
-		Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
-		if (NILP (LGLYPH_ADJUSTMENT (glyph)))
-		    cwidth = LGLYPH_WIDTH (glyph);
-		else
-		  {
-		    cwidth = LGLYPH_WADJUST (glyph);
-		    *(adv-1) += LGLYPH_XOFF (glyph);
-		  }
-	      }
-          }
-        else
-          {
-            if (!font->metrics[hi]) /* FIXME: why/how can we need this now?  */
-              ns_glyph_metrics (font, hi);
-            cwidth = font->metrics[hi][lo].width;
-          }
-        twidth += cwidth;
-        *adv++ = cwidth;
-        c += CHAR_STRING (*t, c); /* This converts the char to UTF-8.  */
-      }
-    len = adv - advances;
-    r.size.width = twidth;
-    *c = 0;
-  }
+  for (int i = from; i < to; ++i)
+    c[i] = s->char2b[i];
 
   /* Fill background if requested.  */
   if (with_background && !isComposite)
     {
-      NSRect br = r;
-      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
-      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
-
-      if (s->row->full_width_p)
-        {
-          if (br.origin.x <= fibw + 1 + mbox_line_width)
-            {
-              br.size.width += br.origin.x - mbox_line_width;
-              br.origin.x = mbox_line_width;
-            }
-          if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
-                <= fibw+1)
-            br.size.width += fibw;
-        }
-      if (s->face->box == FACE_NO_BOX)
-        {
-          /* Expand unboxed top row over internal border.  */
-          if (br.origin.y <= fibw + 1 + mbox_line_width)
-            {
-              br.size.height += br.origin.y;
-              br.origin.y = 0;
-            }
-        }
-      else
-        {
-          int correction = abs (s->face->box_horizontal_line_width)+1;
-          br.origin.y += correction;
-          br.size.height -= 2*correction;
-          correction = abs (s->face->box_vertical_line_width)+1;
-          br.origin.x += correction;
-          br.size.width -= 2*correction;
-        }
+      NSRect br = NSMakeRect (x, y - FONT_BASE (s->font),
+			      s->width, FONT_HEIGHT (s->font));
 
       if (!s->face->stipple)
-        [(NS_FACE_BACKGROUND (face) != 0
-          ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-          : FRAME_BACKGROUND_COLOR (s->f)) set];
+	{
+	  if (s->hl != DRAW_CURSOR)
+	    [(NS_FACE_BACKGROUND (face) != 0
+	      ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+	      : FRAME_BACKGROUND_COLOR (s->f)) set];
+	  else
+	    [FRAME_CURSOR_COLOR (s->f) set];
+	}
       else
         {
           struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
@@ -1080,43 +1209,32 @@ is false when (FROM > 0 || TO < S->nchars).  */
       NSRectFill (br);
     }
 
-
   /* set up for character rendering */
   r.origin.y = y;
 
-  col = (NS_FACE_FOREGROUND (face) != 0
-         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
-         : FRAME_FOREGROUND_COLOR (s->f));
-
-  bgCol = (flags != NS_DUMPGLYPH_FOREGROUND ? nil
-           : (NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)));
+  if (s->hl == DRAW_CURSOR)
+    col = FRAME_BACKGROUND_COLOR (s->f);
+  else
+    col = (NS_FACE_FOREGROUND (face) != 0
+	   ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
+	   : FRAME_FOREGROUND_COLOR (s->f));
 
   /* render under GNUstep using DPS */
   {
-    NSGraphicsContext *context = GSCurrentContext ();
-
+    NSGraphicsContext *context = [NSGraphicsContext currentContext];
     DPSgsave (context);
-    [font->nsfont set];
-
-    /* do erase if "foreground" mode */
-    if (bgCol != nil)
+    if (s->clip_head)
       {
-        [bgCol set];
-        DPSmoveto (context, r.origin.x, r.origin.y);
-/*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
-        DPSxshow (context, (const char *) cbuf, advances, len);
-        DPSstroke (context);
-        [col set];
-/*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
+	DPSrectclip (context, s->clip_head->x, 0,
+		     FRAME_PIXEL_WIDTH (s->f),
+		     FRAME_PIXEL_HEIGHT (s->f));
       }
+    [font->nsfont set];
 
     [col set];
 
-    /* draw with DPSxshow () */
     DPSmoveto (context, r.origin.x, r.origin.y);
-    DPSxshow (context, (const char *) cbuf, advances, len);
+    GSShowGlyphs (context, c, len);
     DPSstroke (context);
 
     DPSgrestore (context);
@@ -1126,6 +1244,360 @@ is false when (FROM > 0 || TO < S->nchars).  */
   return to-from;
 }
 
+static NSUInteger
+ns_font_shape (NSFont *font, NSString *string,
+	       struct ns_glyph_layout *glyph_layouts, NSUInteger glyph_len,
+	       enum lgstring_direction dir)
+{
+  NSUInteger i;
+  NSUInteger result = 0;
+  NSTextStorage *textStorage;
+  NSLayoutManager *layoutManager;
+  NSTextContainer *textContainer;
+  NSUInteger stringLength;
+  NSPoint spaceLocation;
+  /* numberOfGlyphs can't actually be 0, but this pacifies GCC */
+  NSUInteger used, numberOfGlyphs = 0;
+
+  textStorage = [[NSTextStorage alloc] initWithString:string];
+  layoutManager = [[NSLayoutManager alloc] init];
+  textContainer = [[NSTextContainer alloc] init];
+
+  /* Append a trailing space to measure baseline position.  */
+  [textStorage appendAttributedString:([[[NSAttributedString alloc]
+                                          initWithString:@" "] autorelease])];
+  [textStorage setFont:font];
+  [textContainer setLineFragmentPadding:0];
+
+  [layoutManager addTextContainer:textContainer];
+  [textContainer release];
+  [textStorage addLayoutManager:layoutManager];
+  [layoutManager release];
+
+  if (!(textStorage && layoutManager && textContainer))
+    emacs_abort ();
+
+  stringLength = [string length];
+
+  /* Force layout.  */
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  spaceLocation = [layoutManager locationForGlyphAtIndex:stringLength];
+
+  /* Remove the appended trailing space because otherwise it may
+     generate a wrong result for a right-to-left text.  */
+  [textStorage beginEditing];
+  [textStorage deleteCharactersInRange:(NSMakeRange (stringLength, 1))];
+  [textStorage endEditing];
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  i = 0;
+  while (i < stringLength)
+    {
+      NSRange range;
+      NSFont *fontInTextStorage =
+        [textStorage attribute: NSFontAttributeName
+		       atIndex:i
+                     longestEffectiveRange: &range
+                       inRange: NSMakeRange (0, stringLength)];
+
+      if (!(fontInTextStorage == font
+            || [[fontInTextStorage fontName] isEqualToString:[font fontName]]))
+        break;
+      i = NSMaxRange (range);
+    }
+  if (i < stringLength)
+    /* Make the test `used <= glyph_len' below fail if textStorage
+       contained some fonts other than the specified one.  */
+    used = glyph_len + 1;
+  else
+    {
+      NSRange range = NSMakeRange (0, stringLength);
+
+      range = [layoutManager glyphRangeForCharacterRange:range
+                                    actualCharacterRange:NULL];
+      numberOfGlyphs = NSMaxRange (range);
+      used = numberOfGlyphs;
+      for (i = 0; i < numberOfGlyphs; i++)
+        if ([layoutManager notShownAttributeForGlyphAtIndex:i])
+          used--;
+    }
+
+  if (0 < used && used <= glyph_len)
+    {
+      NSUInteger glyphIndex, prevGlyphIndex;
+      NSUInteger *permutation;
+      NSRange compRange, range;
+      CGFloat totalAdvance;
+
+      glyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      permutation = NULL;
+#define RIGHT_TO_LEFT_P permutation
+
+      /* Fill the `comp_range' member of struct mac_glyph_layout, and
+         setup a permutation for right-to-left text.  */
+      compRange = NSMakeRange (0, 0);
+      for (range = NSMakeRange (0, 0); NSMaxRange (range) < used;
+           range.length++)
+        {
+          struct ns_glyph_layout *gl = glyph_layouts + NSMaxRange (range);
+          NSUInteger characterIndex =
+            [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
+
+          gl->string_index = characterIndex;
+
+          if (characterIndex >= NSMaxRange (compRange))
+            {
+              compRange.location = NSMaxRange (compRange);
+              do
+                {
+                  NSRange characterRange =
+                    [string
+                      rangeOfComposedCharacterSequenceAtIndex:characterIndex];
+
+                  compRange.length =
+                    NSMaxRange (characterRange) - compRange.location;
+                  [layoutManager glyphRangeForCharacterRange:compRange
+                                        actualCharacterRange:&characterRange];
+                  characterIndex = NSMaxRange (characterRange) - 1;
+                }
+              while (characterIndex >= NSMaxRange (compRange));
+
+              if (RIGHT_TO_LEFT_P)
+                for (i = 0; i < range.length; i++)
+                  permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+              range = NSMakeRange (NSMaxRange (range), 0);
+            }
+
+          gl->comp_range.location = compRange.location;
+          gl->comp_range.length = compRange.length;
+
+          while (++glyphIndex < numberOfGlyphs)
+            if (![layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+              break;
+        }
+      if (RIGHT_TO_LEFT_P)
+        for (i = 0; i < range.length; i++)
+          permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+      /* Then fill the remaining members.  */
+      glyphIndex = prevGlyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      if (!RIGHT_TO_LEFT_P)
+        totalAdvance = 0;
+      else
+        {
+          NSUInteger nrects;
+          NSRect *glyphRects =
+            [layoutManager
+              rectArrayForGlyphRange:(NSMakeRange (0, numberOfGlyphs))
+              withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                     inTextContainer:textContainer rectCount:&nrects];
+
+          totalAdvance = NSMaxX (glyphRects[0]);
+        }
+
+      for (i = 0; i < used; i++)
+        {
+          struct ns_glyph_layout *gl;
+          NSPoint location;
+          NSUInteger nextGlyphIndex;
+          NSRange glyphRange;
+          NSRect *glyphRects;
+          NSUInteger nrects;
+
+          if (!RIGHT_TO_LEFT_P)
+            gl = glyph_layouts + i;
+          else
+            {
+              NSUInteger dest = permutation[i];
+
+              gl = glyph_layouts + dest;
+              if (i < dest)
+                {
+                  NSUInteger tmp = gl->string_index;
+
+                  gl->string_index = glyph_layouts[i].string_index;
+                  glyph_layouts[i].string_index = tmp;
+                }
+            }
+          gl->glyph_id = [layoutManager glyphAtIndex: glyphIndex];
+
+          location = [layoutManager locationForGlyphAtIndex:glyphIndex];
+          gl->baseline_delta = spaceLocation.y - location.y;
+
+          for (nextGlyphIndex = glyphIndex + 1; nextGlyphIndex < numberOfGlyphs;
+               nextGlyphIndex++)
+            if (![layoutManager
+                   notShownAttributeForGlyphAtIndex:nextGlyphIndex])
+              break;
+
+          if (!RIGHT_TO_LEFT_P)
+            {
+              CGFloat maxX;
+
+              if (prevGlyphIndex == 0)
+                glyphRange = NSMakeRange (0, nextGlyphIndex);
+              else
+                glyphRange = NSMakeRange (glyphIndex,
+                                          nextGlyphIndex - glyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              maxX = max (NSMaxX (glyphRects[0]), totalAdvance);
+              gl->advance_delta = location.x - totalAdvance;
+              gl->advance = maxX - totalAdvance;
+              totalAdvance = maxX;
+            }
+          else
+            {
+              CGFloat minX;
+
+              if (nextGlyphIndex == numberOfGlyphs)
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          numberOfGlyphs - prevGlyphIndex);
+              else
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          glyphIndex + 1 - prevGlyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              minX = min (NSMinX (glyphRects[0]), totalAdvance);
+              gl->advance = totalAdvance - minX;
+              totalAdvance = minX;
+              gl->advance_delta = location.x - totalAdvance;
+            }
+
+          prevGlyphIndex = glyphIndex + 1;
+          glyphIndex = nextGlyphIndex;
+        }
+
+      if (RIGHT_TO_LEFT_P)
+        xfree (permutation);
+
+#undef RIGHT_TO_LEFT_P
+
+      result = used;
+    }
+  [textStorage release];
+
+  return result;
+}
+
+static Lisp_Object
+nsfont_shape (Lisp_Object lgstring, Lisp_Object direction)
+{
+  struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  struct nsfont_info *font_info = (struct nsfont_info *) font;
+  struct ns_glyph_layout *glyph_layouts;
+  NSFont *nsfont = font_info->nsfont;
+  ptrdiff_t glyph_len, len, i;
+  Lisp_Object tem;
+  unichar *mb_buf;
+  NSUInteger used;
+
+  glyph_len = LGSTRING_GLYPH_LEN (lgstring);
+  for (i = 0; i < glyph_len; ++i)
+    {
+      tem = LGSTRING_GLYPH (lgstring, i);
+
+      if (NILP (tem))
+	break;
+    }
+
+  len = i;
+
+  if (INT_MAX / 2 < len)
+    memory_full (SIZE_MAX);
+
+  block_input ();
+
+  mb_buf = alloca (len * sizeof *mb_buf);
+
+  for (i = 0; i < len; ++i)
+    {
+      uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
+      mb_buf[i] = (unichar) c;
+    }
+
+  NSString *string = [NSString stringWithCharacters: mb_buf
+					     length: len];
+  unblock_input ();
+
+  if (!string)
+    return Qnil;
+
+  block_input ();
+
+  enum lgstring_direction dir = DIR_UNKNOWN;
+
+  if (EQ (direction, QL2R))
+    dir = DIR_L2R;
+  else if (EQ (direction, QR2L))
+    dir = DIR_R2L;
+  glyph_layouts = alloca (sizeof (struct ns_glyph_layout) * glyph_len);
+  used = ns_font_shape (nsfont, string, glyph_layouts, glyph_len, dir);
+
+  for (i = 0; i < used; i++)
+    {
+      Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
+      struct ns_glyph_layout *gl = glyph_layouts + i;
+      EMACS_INT from, to;
+      struct font_metrics metrics;
+
+      if (NILP (lglyph))
+        {
+          lglyph = LGLYPH_NEW ();
+          LGSTRING_SET_GLYPH (lgstring, i, lglyph);
+        }
+
+      from = gl->comp_range.location;
+      LGLYPH_SET_FROM (lglyph, from);
+
+      to = gl->comp_range.location + gl->comp_range.length;
+      LGLYPH_SET_TO (lglyph, to - 1);
+
+      /* LGLYPH_CHAR is used in `describe-char' for checking whether
+         the composition is trivial.  */
+      {
+        UTF32Char c;
+
+        if (mb_buf[gl->string_index] >= 0xD800
+            && mb_buf[gl->string_index] < 0xDC00)
+          c = (((mb_buf[gl->string_index] - 0xD800) << 10)
+               + (mb_buf[gl->string_index + 1] - 0xDC00) + 0x10000);
+        else
+          c = mb_buf[gl->string_index];
+
+        LGLYPH_SET_CHAR (lglyph, c);
+      }
+
+      {
+        unsigned long cc = gl->glyph_id;
+        LGLYPH_SET_CODE (lglyph, cc);
+      }
+
+      nsfont_text_extents (font, &gl->glyph_id, 1, &metrics);
+      LGLYPH_SET_WIDTH (lglyph, metrics.width);
+      LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
+      LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
+      LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
+      LGLYPH_SET_DESCENT (lglyph, metrics.descent);
+    }
+  unblock_input ();
+
+  return make_fixnum (used);
+}
 
 
 /* ==========================================================================
@@ -1134,6 +1606,50 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
    ========================================================================== */
 
+static NSGlyph
+ns_uni_to_glyphs_1 (struct nsfont_info *info, unsigned int c)
+{
+  unichar characters[] = { c };
+  NSString *string =
+    [NSString stringWithCharacters: characters
+			    length: 1];
+  NSDictionary *attributes =
+    [NSDictionary dictionaryWithObjectsAndKeys:
+		    info->nsfont, NSFontAttributeName, nil];
+  NSTextStorage *storage = [[NSTextStorage alloc] initWithString: string
+						      attributes: attributes];
+  NSTextContainer *text_container = [[NSTextContainer alloc] init];
+  NSLayoutManager *manager = [[NSLayoutManager alloc] init];
+
+  [manager addTextContainer: text_container];
+  [text_container release]; /* Retained by manager */
+  [storage addLayoutManager: manager];
+  [manager release]; /* Retained by storage */
+
+  NSFont *font_in_storage = [storage attribute: NSFontAttributeName
+				       atIndex:0
+				effectiveRange: NULL];
+  NSGlyph glyph = FONT_INVALID_CODE;
+
+  if ((font_in_storage == info->nsfont
+       || [[font_in_storage fontName] isEqualToString: [info->nsfont fontName]]))
+    {
+      @try
+	{
+	  glyph = [manager glyphAtIndex: 0];
+	}
+      @catch (NSException *e)
+	{
+	  /* GNUstep bug? */
+	  glyph = 'X';
+	}
+    }
+
+  [storage release];
+
+  return glyph;
+}
+
 /* Find and cache corresponding glyph codes for unicode values in given
    hi-byte block of 256.  */
 static void
@@ -1141,7 +1657,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   unichar *unichars = xmalloc (0x101 * sizeof (unichar));
   unsigned int i, g, idx;
-  unsigned short *glyphs;
+  unsigned int *glyphs;
 
   if (NSFONT_TRACE)
     fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
@@ -1149,7 +1665,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
   block_input ();
 
-  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
+  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned int));
   if (!unichars || !(font_info->glyphs[block]))
     emacs_abort ();
 
@@ -1166,7 +1682,8 @@ is false when (FROM > 0 || TO < S->nchars).  */
     for (i = 0; i < 0x100; i++, glyphs++)
       {
         g = unichars[i];
-        *glyphs = g;
+	NSGlyph glyph = ns_uni_to_glyphs_1 (font_info, g);
+        *glyphs = glyph;
       }
   }
 
@@ -1175,18 +1692,19 @@ is false when (FROM > 0 || TO < S->nchars).  */
 }
 
 
-/* Determine and cache metrics for corresponding glyph codes in given
-   hi-byte block of 256.  */
+/* Determine and cache metrics for glyphs in given hi-byte block of
+   256.  */
 static void
-ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
+ns_glyph_metrics (struct nsfont_info *font_info, unsigned int block)
 {
-  unsigned int i, g;
+  unsigned int i;
+  NSGlyph g;
   unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
   NSFont *sfont;
   struct font_metrics *metrics;
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
+    fprintf (stderr, "%p\tComputing metrics for glyphs in block %u\n",
             font_info, block);
 
   /* not implemented yet (as of startup 0.18), so punt */
@@ -1209,19 +1727,14 @@ is false when (FROM > 0 || TO < S->nchars).  */
       w = max ([sfont advancementForGlyph: g].width, 2.0);
       metrics->width = lrint (w);
 
-      lb = r.origin.x;
-      rb = r.size.width - w;
-      // Add to bearing for LCD smoothing.  We don't know if it is there.
-      if (lb < 0)
-        metrics->lbearing = round (lb - LCD_SMOOTHING_MARGIN);
-      if (font_info->ital)
-        rb += (CGFloat) (0.22F * font_info->height);
-      metrics->rbearing = lrint (w + rb + LCD_SMOOTHING_MARGIN);
-
-      metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
-      /* lrint (hshrink * [sfont ascender] + expand * hd/2); */
-      metrics->ascent = r.size.height - metrics->descent;
-      /* -lrint (hshrink* [sfont descender] - expand * hd/2); */
+      lb = NSMinX (r);
+      rb = NSMaxX (r);
+
+      metrics->rbearing = lrint (rb);
+      metrics->lbearing = lrint (lb);
+
+      metrics->descent = NSMinY (r);
+      metrics->ascent = NSMaxY (r);
     }
   unblock_input ();
 }
@@ -1257,6 +1770,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
   .has_char = nsfont_has_char,
   .encode_char = nsfont_encode_char,
   .text_extents = nsfont_text_extents,
+  .shape = nsfont_shape,
   .draw = nsfont_draw,
   };
 
@@ -1265,7 +1779,6 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   DEFSYM (Qcondensed, "condensed");
   DEFSYM (Qexpanded, "expanded");
-  DEFSYM (Qapple, "apple");
   DEFSYM (Qmedium, "medium");
   DEFVAR_LISP ("ns-reg-to-script", Vns_reg_to_script,
                doc: /* Internal use: maps font registry to Unicode script.  */);
diff --git a/src/nsterm.h b/src/nsterm.h
index c750d1bd99..8175f99664 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -820,7 +820,7 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
   XCharStruct max_bounds;
   /* We compute glyph codes and metrics on-demand in blocks of 256 indexed
      by hibyte, lobyte.  */
-  unsigned short **glyphs; /* map Unicode index to glyph */
+  unsigned int **glyphs; /* map Unicode index to glyph */
   struct font_metrics **metrics;
 };
 #endif
diff --git a/src/nsterm.m b/src/nsterm.m
index 4c2a3f287c..b4d03e668d 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1078,11 +1078,20 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
   /* clipping */
   if (r)
     {
-      [[NSGraphicsContext currentContext] saveGraphicsState];
+      NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+      [ctx saveGraphicsState];
       if (n == 2)
         NSRectClipList (r, 2);
       else
         NSRectClip (*r);
+#ifdef NS_IMPL_GNUSTEP
+      DPSrectclip (ctx, NSMinX (*r), NSMinY (*r),
+		   NSWidth (*r), NSHeight (*r));
+
+      if (n == 2)
+	DPSrectclip (ctx, NSMinX (r[1]), NSMinY (r[1]),
+		     NSWidth (r[1]), NSHeight (r[1]));
+#endif
       gsaved = YES;
     }
 }
@@ -2440,9 +2449,6 @@ Hide the window (X11 semantics)
       EmacsView *view = FRAME_NS_VIEW (f);
       FRAME_POINTER_TYPE (f) = cursor;
       [[view window] invalidateCursorRectsForView: view];
-      /* Redisplay assumes this function also draws the changed frame
-         cursor, but this function doesn't, so do it explicitly.  */
-      gui_update_cursor (f, 1);
     }
 }
 
@@ -2852,31 +2858,31 @@ Hide the window (X11 semantics)
      External (RIF); compute left/right overhang of whole string and set in s
    -------------------------------------------------------------------------- */
 {
-  struct font *font = s->font;
-
-  if (s->char2b)
+  if (s->cmp == NULL
+      && (s->first_glyph->type == CHAR_GLYPH
+	  || s->first_glyph->type == COMPOSITE_GLYPH))
     {
       struct font_metrics metrics;
-      unsigned int codes[2];
-      codes[0] = *(s->char2b);
-      codes[1] = *(s->char2b + s->nchars - 1);
 
-      font->driver->text_extents (font, codes, 2, &metrics);
-      s->left_overhang = -metrics.lbearing;
-      s->right_overhang
-	= metrics.rbearing > metrics.width
-	? metrics.rbearing - metrics.width : 0;
+      if (s->first_glyph->type == CHAR_GLYPH)
+	{
+	  struct font *font = s->font;
+	  font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+	}
+      else
+	{
+	  Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+	  composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+	}
+      s->right_overhang = (metrics.rbearing > metrics.width
+			   ? metrics.rbearing - metrics.width : 0);
+      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
     }
-  else
+  else if (s->cmp)
     {
-      s->left_overhang = 0;
-#ifdef NS_IMPL_GNUSTEP
-      if (EQ (font->driver->type, Qns))
-        s->right_overhang = ((struct nsfont_info *)font)->ital ?
-          FONT_HEIGHT (font) * 0.2 : 0;
-      else
-#endif
-        s->right_overhang = 0;
+      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+      s->left_overhang = - s->cmp->lbearing;
     }
 }
 
@@ -3016,14 +3022,13 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   struct frame *f = WINDOW_XFRAME (w);
   struct glyph *phys_cursor_glyph;
   struct glyph *cursor_glyph;
-  struct face *face;
-  NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
 
   /* If cursor is out of bounds, don't draw garbage.  This can happen
      in mini-buffer windows when switching between echo area glyphs
      and mini-buffer.  */
 
-  NSTRACE ("ns_draw_window_cursor");
+  NSTRACE ("ns_draw_window_cursor (on = %d, cursor_type = %d)",
+	   on_p, cursor_type);
 
   if (!on_p)
     return;
@@ -3039,6 +3044,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
     {
+      NSTRACE_MSG ("No phys cursor glyph was found!");
+
       if (glyph_row->exact_window_width_line_p
           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
         {
@@ -3048,10 +3055,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       return;
     }
 
-  /* We draw the cursor (with NSRectFill), then draw the glyph on top
-     (other terminals do it the other way round).  We must set
-     w->phys_cursor_width to the cursor width.  For bar cursors, that
-     is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
 
   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
@@ -3083,17 +3086,15 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   /* Prevent the cursor from being drawn outside the text area.  */
   r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
 
-  ns_focus (f, &r, 1);
+  NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+  [ctx saveGraphicsState];
+  NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+  DPSrectclip (ctx, r.origin.x, r.origin.y,
+	       r.size.width, r.size.height);
+#endif
 
-  face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
-  if (face && NS_FACE_BACKGROUND (face)
-      == ns_index_color (FRAME_CURSOR_COLOR (f), f))
-    {
-      [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
-      hollow_color = FRAME_CURSOR_COLOR (f);
-    }
-  else
-    [FRAME_CURSOR_COLOR (f) set];
+  [FRAME_CURSOR_COLOR (f) set];
 
   switch (cursor_type)
     {
@@ -3101,13 +3102,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     case NO_CURSOR:
       break;
     case FILLED_BOX_CURSOR:
-      NSRectFill (r);
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
       break;
     case HOLLOW_BOX_CURSOR:
-      NSRectFill (r);
-      [hollow_color set];
-      NSRectFill (NSInsetRect (r, 1, 1));
-      [FRAME_CURSOR_COLOR (f) set];
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_NORMAL_TEXT);
+      [NSBezierPath strokeRect: r];
       break;
     case HBAR_CURSOR:
       NSRectFill (r);
@@ -3123,12 +3122,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       NSRectFill (s);
       break;
     }
-  ns_unfocus (f);
 
-  /* Draw the character under the cursor.  Other terms only draw
-     the character on top of box cursors, so do the same here.  */
-  if (cursor_type == FILLED_BOX_CURSOR || cursor_type == HOLLOW_BOX_CURSOR)
-    draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+  [ctx restoreGraphicsState];
 }
 
 
@@ -3308,16 +3303,18 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   if (s->for_overlaps)
     return;
 
+  if (s->hl == DRAW_CURSOR)
+    [FRAME_BACKGROUND_COLOR (s->f) set];
+  else if (face->underline_defaulted_p)
+    [defaultCol set];
+  else
+    [ns_lookup_indexed_color (face->underline_color, s->f) set];
+
   /* Do underline.  */
   if (face->underline)
     {
       if (s->face->underline == FACE_UNDER_WAVE)
         {
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
-
           ns_draw_underwave (s, width, x);
         }
       else if (s->face->underline == FACE_UNDER_LINE)
@@ -3388,11 +3385,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
           s->underline_position = position;
 
           r = NSMakeRect (x, s->ybase + position, width, thickness);
-
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
           NSRectFill (r);
         }
     }
@@ -3402,11 +3394,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     {
       NSRect r;
       r = NSMakeRect (x, s->y, width, 1);
-
-      if (face->overline_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->overline_color, s->f) set];
       NSRectFill (r);
     }
 
@@ -3429,10 +3416,6 @@ larger if there are taller display elements (e.g., characters
       dy = lrint ((glyph_height - h) / 2);
       r = NSMakeRect (x, glyph_y + dy, width, 1);
 
-      if (face->strike_through_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
       NSRectFill (r);
     }
 }
@@ -3580,17 +3563,7 @@ Function modeled after x_draw_glyph_string_box ().
   struct glyph *last_glyph;
   NSRect r;
   int hthickness, vthickness;
-  struct face *face;
-
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = s->face;
+  struct face *face = s->face;
 
   vthickness = face->box_vertical_line_width;
   hthickness = face->box_horizontal_line_width;
@@ -3664,34 +3637,26 @@ Function modeled after x_draw_glyph_string_box ().
 	  || FONT_TOO_HIGH (s->font)
           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
 	{
-          struct face *face;
-          if (s->hl == DRAW_MOUSE_FACE)
-            {
-              face
-		= FACE_FROM_ID_OR_NULL (s->f,
-					MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-              if (!face)
-                face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-            }
-          else
-            face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+          struct face *face = s->face;
           if (!face->stipple)
-            [(NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)) set];
+	    {
+	      if (s->hl != DRAW_CURSOR)
+		[(NS_FACE_BACKGROUND (face) != 0
+		  ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+		  : FRAME_BACKGROUND_COLOR (s->f)) set];
+	      else
+		[FRAME_CURSOR_COLOR (s->f) set];
+	    }
           else
             {
               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
             }
 
-          if (s->hl != DRAW_CURSOR)
-            {
-              NSRect r = NSMakeRect (s->x, s->y + box_line_width,
-                                    s->background_width,
-                                    s->height-2*box_line_width);
-              NSRectFill (r);
-            }
+	  NSRect r = NSMakeRect (s->x, s->y + box_line_width,
+				 s->background_width,
+				 s->height-2*box_line_width);
+	  NSRectFill (r);
 
 	  s->background_filled_p = 1;
 	}
@@ -3712,7 +3677,7 @@ Function modeled after x_draw_glyph_string_box ().
   int th;
   char raised_p;
   NSRect br;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *tdCol;
 
   NSTRACE ("ns_dumpglyphs_image");
@@ -3733,15 +3698,6 @@ Function modeled after x_draw_glyph_string_box ().
   /* Draw BG: if we need larger area than image itself cleared, do that,
      otherwise, since we composite the image under NS (instead of mucking
      with its background color), we must clear just the image area.  */
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-       face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
 
   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
 
@@ -3812,16 +3768,8 @@ Function modeled after x_draw_glyph_string_box ().
 
   if (s->hl == DRAW_CURSOR)
     {
-    [FRAME_CURSOR_COLOR (s->f) set];
-    if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
+      [FRAME_CURSOR_COLOR (s->f) set];
       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
-    else
-      /* Currently on NS img->mask is always 0.  Since
-         get_window_cursor_type specifies a hollow box cursor when on
-         a non-masked image we never reach this clause.  But we put it
-         in, in anticipation of better support for image masks on
-         NS.  */
-      tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
     }
   else
     {
@@ -3873,66 +3821,35 @@ Function modeled after x_draw_glyph_string_box ().
 static void
 ns_dumpglyphs_stretch (struct glyph_string *s)
 {
-  NSRect r[2];
   NSRect glyphRect;
-  int n;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *fgCol, *bgCol;
 
   if (!s->background_filled_p)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
 
-      if (s->hl == DRAW_MOUSE_FACE)
-        {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-        }
-      else
-        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+      face = s->face;
 
       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
 
-      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
-
-      [bgCol set];
-
-      /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
-         overwriting cursor (usually when cursor on a tab) */
       if (s->hl == DRAW_CURSOR)
-        {
-          CGFloat x, width;
+	{
+	  fgCol = bgCol;
+	  bgCol = FRAME_CURSOR_COLOR (s->f);
+	}
 
-          /* FIXME: This looks like it will only work for left to
-             right languages.  */
-          x = NSMinX (glyphRect);
-          width = s->w->phys_cursor_width;
-          glyphRect.size.width -= width;
-          glyphRect.origin.x += width;
+      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
 
-          NSRectFill (glyphRect);
+      [bgCol set];
 
-          /* Draw overlining, etc. on the cursor. */
-          if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-            ns_draw_text_decoration (s, face, bgCol, width, x);
-          else
-            ns_draw_text_decoration (s, face, fgCol, width, x);
-        }
-      else
-        {
-          NSRectFill (glyphRect);
-        }
+      NSRectFill (glyphRect);
 
       /* Draw overlining, etc. on the stretch glyph (or the part
          of the stretch glyph after the cursor). */
       ns_draw_text_decoration (s, face, fgCol, NSWidth (glyphRect),
                                NSMinX (glyphRect));
 
-      ns_unfocus (s->f);
       s->background_filled_p = 1;
     }
 }
@@ -3941,7 +3858,7 @@ overwriting cursor (usually when cursor on a tab) */
 static void
 ns_draw_glyph_string_foreground (struct glyph_string *s)
 {
-  int x, flags;
+  int x;
   struct font *font = s->font;
 
   /* If first glyph of S has a left box line, start drawing the text
@@ -3952,15 +3869,9 @@ overwriting cursor (usually when cursor on a tab) */
   else
     x = s->x;
 
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
   font->driver->draw
     (s, s->cmp_from, s->nchars, x, s->ybase,
-     (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
-     || flags == NS_DUMPGLYPH_MOUSEFACE);
+     !s->for_overlaps && !s->background_filled_p);
 }
 
 
@@ -4067,9 +3978,9 @@ overwriting cursor (usually when cursor on a tab) */
   struct font *font = s->face->font;
   if (! font) font = FRAME_FONT (s->f);
 
-  NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
+  NSTRACE ("ns_draw_glyph_string (hl = %u)", s->hl);
 
-  if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
+  if (s->next && s->right_overhang && !s->for_overlaps)
     {
       int width;
       struct glyph_string *next;
@@ -4106,14 +4017,21 @@ overwriting cursor (usually when cursor on a tab) */
       box_drawn_p = 1;
     }
 
+  n = ns_get_glyph_string_clip_rect (s, r);
+
+  if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+      && !s->clip_tail
+      && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+	  || (s->next && s->next->hl != s->hl && s->right_overhang)))
+    r[0] = NSIntersectionRect (r[0], NSMakeRect (s->x, s->y, s->width, s->height));
+
+  ns_focus (s->f, r, n);
+
   switch (s->first_glyph->type)
     {
 
     case IMAGE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
       ns_dumpglyphs_image (s, r[0]);
-      ns_unfocus (s->f);
       break;
 
     case XWIDGET_GLYPH:
@@ -4126,57 +4044,36 @@ overwriting cursor (usually when cursor on a tab) */
 
     case CHAR_GLYPH:
     case COMPOSITE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
-      if (s->for_overlaps || (s->cmp_from > 0
-			      && ! s->first_glyph->u.cmp.automatic))
-        s->background_filled_p = 1;
-      else
-        ns_maybe_dumpglyphs_background
-          (s, s->first_glyph->type == COMPOSITE_GLYPH);
-
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
       {
-        BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	if (s->for_overlaps || (isComposite
+				&& (s->cmp_from > 0
+				    && ! s->first_glyph->u.cmp.automatic)))
+	  s->background_filled_p = 1;
+	else
+	  ns_maybe_dumpglyphs_background
+	    (s, s->first_glyph->type == COMPOSITE_GLYPH);
 
-        if (isComposite)
-          ns_draw_composite_glyph_string_foreground (s);
-        else
-          ns_draw_glyph_string_foreground (s);
-      }
+	if (isComposite)
+	  ns_draw_composite_glyph_string_foreground (s);
+	else
+	  ns_draw_glyph_string_foreground (s);
 
-      {
-        NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
-                        ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
-                                                   s->f)
-                        : FRAME_FOREGROUND_COLOR (s->f));
-        [col set];
-
-        /* Draw underline, overline, strike-through. */
-        ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	{
+	  NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
+			  ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
+						     s->f)
+			  : FRAME_FOREGROUND_COLOR (s->f));
+	  [col set];
+
+	  /* Draw underline, overline, strike-through. */
+	  ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	}
       }
 
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
-      ns_unfocus (s->f);
       break;
 
     case GLYPHLESS_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
       if (s->for_overlaps || (s->cmp_from > 0
 			      && ! s->first_glyph->u.cmp.automatic))
         s->background_filled_p = 1;
@@ -4186,7 +4083,6 @@ overwriting cursor (usually when cursor on a tab) */
       /* ... */
       /* Not yet implemented.  */
       /* ... */
-      ns_unfocus (s->f);
       break;
 
     default:
@@ -4195,13 +4091,92 @@ overwriting cursor (usually when cursor on a tab) */
 
   /* Draw box if not done already.  */
   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
+    ns_dumpglyphs_box_or_relief (s);
+
+  ns_unfocus (s->f);
+
+  /* Draw surrounding overhangs. */
+  if (s->prev)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-      ns_dumpglyphs_box_or_relief (s);
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *prev;
+
+      for (prev = s->prev; prev; prev = prev->prev)
+	if (prev->hl != s->hl
+	    && prev->x + prev->width + prev->right_overhang > s->x)
+	  {
+	    /* As prev was drawn while clipped to its own area, we
+	       must draw the right_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = prev->hl;
+	    struct face *save_face = prev->face;
+
+	    prev->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    prev->num_clips = 1;
+	    prev->hl = s->hl;
+	    if (prev->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (prev);
+	    else
+	      ns_draw_composite_glyph_string_foreground (prev);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    prev->hl = save;
+	    prev->face = save_face;
+	    prev->num_clips = 0;
+	  }
       ns_unfocus (s->f);
     }
 
+  if (s->next)
+    {
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *next;
+
+      for (next = s->next; next; next = next->next)
+	if (next->hl != s->hl
+	    && next->x - next->left_overhang < s->x + s->width)
+	  {
+	    /* As next will be drawn while clipped to its own area,
+	       we must draw the left_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = next->hl;
+	    struct face *save_face = next->face;
+
+	    next->hl = s->hl;
+	    next->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    next->num_clips = 1;
+	    if (next->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (next);
+	    else
+	      ns_draw_composite_glyph_string_foreground (next);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    next->hl = save;
+	    next->num_clips = 0;
+	    next->face = save_face;
+	    next->clip_head = next;
+	    next->background_filled_p = 0;
+	  }
+      ns_unfocus (s->f);
+    }
   s->num_clips = 0;
 }
 
diff --git a/src/xdisp.c b/src/xdisp.c
index dffbdb8d1e..0d95e70212 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -29298,7 +29298,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
   for (s = head; s; s = s->next)
     FRAME_RIF (f)->draw_glyph_string (s);
 
-#ifndef HAVE_NS
   /* When focus a sole frame and move horizontally, this clears on_p
      causing a failure to erase prev cursor position. */
   if (area == TEXT_AREA
@@ -29317,7 +29316,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
       notice_overwritten_cursor (w, TEXT_AREA, x0, x1,
 				 row->y, MATRIX_ROW_BOTTOM_Y (row));
     }
-#endif
 
   /* Value is the x-position up to which drawn, relative to AREA of W.
      This doesn't include parts drawn because of overhangs.  */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
       [not found] <CADwFkmnHko5UrsuNBKjTtt1LjgXet37zP+U9DTMv++njRHePmQ@mail.gmail.com>
@ 2021-10-31  2:38 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31  2:56   ` Stefan Kangas
  0 siblings, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-31  2:38 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: Alan Third, 51411

Stefan Kangas <stefan@marxist.se> writes:

> severity 51411 wishlist
> quit

Is it really fair to tag this issue as wishlist?  It fixes two concrete
issues, bad font display on GNUstep being one, and crashes with the
context menu from `context-menu-mode' being another.

Thanks.





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31  2:38 ` bug#51411: NS port cleanups Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-31  2:56   ` Stefan Kangas
  2021-10-31  3:13     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 22+ messages in thread
From: Stefan Kangas @ 2021-10-31  2:56 UTC (permalink / raw)
  To: Po Lu; +Cc: Alan Third, 51411

tags 51411 normal
thanks

Po Lu <luangruo@yahoo.com> writes:

> Stefan Kangas <stefan@marxist.se> writes:
>
>> severity 51411 wishlist
>> quit
>
> Is it really fair to tag this issue as wishlist?  It fixes two concrete
> issues, bad font display on GNUstep being one, and crashes with the
> context menu from `context-menu-mode' being another.

Sorry, I had missed that part.  It sounds to me that this should better
be tagged "normal", so I'm adjusting the tag back to that with this
message.

Thanks for paying attention to the severity tagging.





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31  2:56   ` Stefan Kangas
@ 2021-10-31  3:13     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31  3:59       ` Stefan Kangas
  0 siblings, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-31  3:13 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: Alan Third, 51411

Stefan Kangas <stefan@marxist.se> writes:

> Sorry, I had missed that part.  It sounds to me that this should better
> be tagged "normal", so I'm adjusting the tag back to that with this
> message.
>
> Thanks for paying attention to the severity tagging.

Thanks, but you forgot to reply to the control server.
(Partly my fault for removing it from Cc)





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31  3:13     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-31  3:59       ` Stefan Kangas
  0 siblings, 0 replies; 22+ messages in thread
From: Stefan Kangas @ 2021-10-31  3:59 UTC (permalink / raw)
  To: Po Lu; +Cc: Alan Third, 51411

Po Lu <luangruo@yahoo.com> writes:

> Thanks, but you forgot to reply to the control server.
> (Partly my fault for removing it from Cc)

I actually had it in Bcc, which is best practice, but I said "tags 51411
normal" instead of "severity 51411 normal" so it didn't take.

Should be fixed now.





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-28 11:25             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-31 10:22               ` Alan Third
  2021-10-31 10:34                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 22+ messages in thread
From: Alan Third @ 2021-10-31 10:22 UTC (permalink / raw)
  To: Po Lu; +Cc: 51411

On Thu, Oct 28, 2021 at 07:25:23PM +0800, Po Lu wrote:
> Alan Third <alan@idiocy.org> writes:
> 
> > NSRectClipList creates a union of the passed rectangles and then sets
> > the clipping rectangle to that, so it contains all of them. That's
> > useful because NSRectClip uses the intersection of the current
> > clipping rectangle and the new one.
> >
> > Does DPSrectclip use the intersection? If so this may not work as
> > intended and it might be better to union the rectangles ourselves.
> 
> It does intersect the rectangle with the current clipping.  I determined
> that from a cursory examination of the source code, as the function
> appeared in the documentation as documented, but no documentation was
> actually written for it.

I think what you'll need to do is union the two rectangles and then
clip to that, rather than clipping them both separately. That will
then provide the same clipping as the NSClipRect code does.

> >> @@ -4195,13 +4091,88 @@ overwriting cursor (usually when cursor on a tab) */
> >>  
> >>    /* Draw box if not done already.  */
> >>    if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
> >> +    ns_dumpglyphs_box_or_relief (s);
> >> +
> >> +  ns_unfocus (s->f);
> 
> > You unfocus here, then continue working on the frame here. You either
> > need to focus again or extend the original focus. Remember you can
> > focus without setting the clipping, then save and reset the graphics
> > state as required if you prefer.
> 
> Hmm.  Will this work better?  Thanks.

I must be failing to communicate well, we keep seeming to
misunderstand each other.

You still need to focus, however you don't have to clip when you
focus.

For example you could do something like:

    ns_focus (f, nil, 0);
    [[NSGraphicsContext currentContext] saveGraphicsState];
    NSClipRect (r1);
    // do something
    [[NSGraphicsContext currentContext] restoreGraphicsState]

    [[NSGraphicsContext currentContext] saveGraphicsState];
    NSClipRect (r2);
    // do something else
    [[NSGraphicsContext currentContext] restoreGraphicsState]
    ns_unfocus (f);

The only time you don't need to focus is when you can guarantee the
calling function (or its caller, etc.) has already focused the view.
-- 
Alan Third





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31 10:22               ` Alan Third
@ 2021-10-31 10:34                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31 10:54                   ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31 10:59                   ` Alan Third
  0 siblings, 2 replies; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-31 10:34 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

Alan Third <alan@idiocy.org> writes:

> I think what you'll need to do is union the two rectangles and then
> clip to that, rather than clipping them both separately. That will
> then provide the same clipping as the NSClipRect code does.

I meant to say that DPSrectclip intersects (IOW, behaves just as
NSClipRect does).  Unless that's incorrect, I think what I'm doing right
now should work fine.

> I must be failing to communicate well, we keep seeming to
> misunderstand each other.

It could be my problem as well: my reading comprehension is nowhere near
as good as I would rather it be.  Thanks a lot for tolerating it.

> You still need to focus, however you don't have to clip when you
> focus.

After I unfocus here:

  /* Draw box if not done already.  */
  if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
    ns_dumpglyphs_box_or_relief (s);

-> ns_unfocus (s->f);

I make sure to focus again if an overhang might be drawn again (inside
if (s->prev) and if (s->next)), like so:

  /* Draw surrounding overhangs. */
  if (s->prev)
    {
->    ns_focus (s->f, NULL, 0);
      struct glyph_string *prev;

There is, of course, a matching unfocus.  Is that not adequate, and if
so, could you please explain how?

Thanks for the feedback, I really appreciate it.





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31 10:34                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-31 10:54                   ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31 10:59                   ` Alan Third
  1 sibling, 0 replies; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-31 10:54 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

[-- Attachment #1: Type: text/plain, Size: 485 bytes --]

Po Lu <luangruo@yahoo.com> writes:

> I meant to say that DPSrectclip intersects (IOW, behaves just as
> NSClipRect does).  Unless that's incorrect, I think what I'm doing right
> now should work fine.

Please disregard what I said here, as I just found out my understanding
of NSRectClip has been incorrect.

OTOH, I found a function that should better suit the situation.
GSRectClipList sets both the DPS and NS clipping, so I changed the
function to just use that instead.

Thanks.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-font-display-on-NS-port.patch --]
[-- Type: text/x-patch, Size: 78523 bytes --]

From 5ab36735d30bb475a3bc44b9f52194e236f3372e Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:44:03 +0800
Subject: [PATCH] Improve font display on NS port

* src/nsfns.m (Fx_create_frame): Use "fixed" for the default font on
GNUstep.

* src/nsfont.m (LCD_SMOOTHING_MARGIN, ns_escape_name)
(ns_unescape_name, ns_attribute_fvalue)
(STYLE_REF): Remove unused defines and functions.

(struct ns_glyph_layout, enum lgstring_direction).
(enum gs_font_slant, enum gs_font_weight, enum gs_font_width)
(enum gs_specified, struct gs_font_data): New enumerators and
structures.

(ns_font_descs_match_p)
(ns_done_font_data, ns_get_font_data): New functions.
(ns_glyph_metrics): Stop escaping names.

(ns_spec_to_descriptor): Fix font descriptor creation for symbolic
font spec entires.
(ns_descriptor_to_entity): Create entries with the correct symbolic
styles.

(ns_fallback_entity): Fix fallback entity selection.
(ns_findfonts): Use our own font matcher instead of the broken GNUstep
matcher.

(ns_list_family): Remove obsolete comment.
(nsfont_open): Remove obsolete code, comments, and synthItal logic
which doesn't work on GNUstep.

(nsfont_encode_char): Use a type that can fit NSGlyph
(nsfont_draw): Chose correct font, remove obsolete mouse face logic,
obsolete comments, and switch to using glyph-based drawing instead of
character-based drawing.

(ns_font_shape, nsfont_shape): New functions.

(ns_uni_to_glyphs_1): New function.
(ns_uni_to_glyphs): Return glyphs instead of unicode codepoints.

(ns_glyph_metrics): Use NSGlyphs instead of unicode codepoints and fix
left bearing, right bearing, ascent and descent computation.

(struct nsfont_driver): Add shaping capability.

* src/nsterm.h (struct nsfont_info): Use unsigned int for glyph cache.

* src/nsterm.c (ns_focus): Set DPS clipping on GNUstep.

(ns_compute_glyph_string_overhangs): Fix overhang computation by using
xterm code.
(ns_draw_window_cursor): Simplify cursor drawing.
(ns_maybe_dumpglyphs_background): Test for cursor HL and remove
obsolete mouse face logic.
(ns_dumpglyphs_image)
(ns_dumpglyphs_box_or_relief): Rectify for new cursor logic.
(ns_dumpglyphs_stretch): Rectify for new cursor logic and rely on
ns_draw_glyph_string to set focus.
(ns_draw_glyph_string_foreground): Remove mouse face logic.
(ns_draw_glyph_strings): Implement overhangs, remove obsolete
comment, and always focus before dumping glyphs.
(ns_draw_text_decoration): Add condition for DRAW_CURSOR and simplify
color selection.
(ns_define_frame_cursor): Remove nonsensical code (define_frame_cursor
has nothing to do with the text cursor, aka caret).

* src/xdisp.c (draw_glyphs): Enable code for NS port to fix mouse face
cursor display.

* src/macfont.m (get_cgcolor_from_nscolor): New function.
(macfont_draw): Remove obsolete mouse-face code and enable cursor
display.
---
 src/macfont.m |   36 +-
 src/nsfns.m   |    6 +
 src/nsfont.m  | 1215 +++++++++++++++++++++++++++++++++++--------------
 src/nsterm.h  |    2 +-
 src/nsterm.m  |  413 ++++++++---------
 src/xdisp.c   |    2 -
 6 files changed, 1092 insertions(+), 582 deletions(-)

diff --git a/src/macfont.m b/src/macfont.m
index 78ed5d53f3..1426cae6dc 100644
--- a/src/macfont.m
+++ b/src/macfont.m
@@ -613,6 +613,21 @@ static void mac_font_get_glyphs_for_variants (CFDataRef, UTF32Char,
   return cgColor;
 }
 
+static CGColorRef
+get_cgcolor_from_nscolor (NSColor *nsColor, struct frame *f)
+{
+  [nsColor set];
+  CGColorSpaceRef colorSpace = [[nsColor colorSpace] CGColorSpace];
+  NSInteger noc = [nsColor numberOfComponents];
+  CGFloat *components = xmalloc (sizeof(CGFloat)*(1+noc));
+  CGColorRef cgColor;
+
+  [nsColor getComponents: components];
+  cgColor = CGColorCreate (colorSpace, components);
+  xfree (components);
+  return cgColor;
+}
+
 #define CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND(context, face, f)        \
   do {                                                                  \
     CGColorRef refcol_ = get_cgcolor (NS_FACE_FOREGROUND (face), f);    \
@@ -2911,14 +2926,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
 
   if (!CGRectIsNull (background_rect))
     {
-      if (s->hl == DRAW_MOUSE_FACE)
+      if (s->hl == DRAW_CURSOR)
         {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-				       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_CURSOR_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
         }
-      CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
       CGContextFillRects (context, &background_rect, 1);
     }
 
@@ -2927,7 +2942,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
       CGAffineTransform atfm;
 
       CGContextScaleCTM (context, 1, -1);
-      CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
+      if (s->hl == DRAW_CURSOR)
+        {
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_BACKGROUND_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
+        }
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
       if (macfont_info->synthetic_italic_p)
         atfm = synthetic_italic_atfm;
       else
diff --git a/src/nsfns.m b/src/nsfns.m
index 797d0ce782..f4d8172246 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -1236,6 +1236,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                          "fontBackend", "FontBackend", RES_TYPE_STRING);
 
   {
+#ifdef NS_IMPL_COCOA
     /* use for default font name */
     id font = [NSFont userFixedPitchFontOfSize: -1.0]; /* default */
     gui_default_parameter (f, parms, Qfontsize,
@@ -1250,6 +1251,11 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                            build_string (fontname),
                            "font", "Font", RES_TYPE_STRING);
     xfree (fontname);
+#else
+    gui_default_parameter (f, parms, Qfont,
+                           build_string ("fixed"),
+                           "font", "Font", RES_TYPE_STRING);
+#endif
   }
   unblock_input ();
 
diff --git a/src/nsfont.m b/src/nsfont.m
index 5a9cdfebc0..b3224629f0 100644
--- a/src/nsfont.m
+++ b/src/nsfont.m
@@ -1,4 +1,4 @@
-/* Font back-end driver for the NeXT/Open/GNUstep and macOS window system.
+/* Font back-end driver for the GNUstep window system.
    See font.h
    Copyright (C) 2006-2021 Free Software Foundation, Inc.
 
@@ -38,47 +38,269 @@
 #include "termchar.h"
 #include "pdumper.h"
 
-/* TODO: Drop once we can assume gnustep-gui 0.17.1.  */
+#import <Foundation/NSException.h>
 #import <AppKit/NSFontDescriptor.h>
+#import <AppKit/NSLayoutManager.h>
+#import <GNUstepGUI/GSLayoutManager.h>
+#import <GNUstepGUI/GSFontInfo.h>
 
 #define NSFONT_TRACE 0
-#define LCD_SMOOTHING_MARGIN 2
 
-/* Font glyph and metrics caching functions, implemented at end.  */
-static void ns_uni_to_glyphs (struct nsfont_info *font_info,
-                              unsigned char block);
-static void ns_glyph_metrics (struct nsfont_info *font_info,
-                              unsigned char block);
+/* Structure used by GS `shape' functions for storing layout
+   information for each glyph.  Borrowed from macfont.h.  */
+struct ns_glyph_layout
+{
+  /* Range of indices of the characters composed into the group of
+     glyphs that share the cursor position with this glyph.  The
+     members `location' and `length' are in UTF-16 indices.  */
+  NSRange comp_range;
 
-#define INVALID_GLYPH 0xFFFF
+  /* UTF-16 index in the source string for the first character
+     associated with this glyph.  */
+  NSUInteger string_index;
 
-/* ==========================================================================
+  /* Horizontal and vertical adjustments of glyph position.  The
+     coordinate space is that of Core Text.  So, the `baseline_delta'
+     value is negative if the glyph should be placed below the
+     baseline.  */
+  CGFloat advance_delta, baseline_delta;
 
-    Utilities
+  /* Typographical width of the glyph.  */
+  CGFloat advance;
 
-   ========================================================================== */
+  /* Glyph ID of the glyph.  */
+  NSGlyph glyph_id;
+};
+
+
+enum lgstring_direction
+  {
+    DIR_R2L = -1, DIR_UNKNOWN = 0, DIR_L2R = 1
+  };
+
+enum gs_font_slant
+  {
+    GS_FONT_SLANT_ITALIC,
+    GS_FONT_SLANT_REVERSE_ITALIC,
+    GS_FONT_SLANT_NORMAL
+  };
+
+enum gs_font_weight
+  {
+    GS_FONT_WEIGHT_LIGHT,
+    GS_FONT_WEIGHT_BOLD,
+    GS_FONT_WEIGHT_NORMAL
+  };
+
+enum gs_font_width
+  {
+    GS_FONT_WIDTH_CONDENSED,
+    GS_FONT_WIDTH_EXPANDED,
+    GS_FONT_WIDTH_NORMAL
+  };
+
+enum gs_specified
+  {
+    GS_SPECIFIED_SLANT = 1,
+    GS_SPECIFIED_WEIGHT = 1 << 1,
+    GS_SPECIFIED_WIDTH = 1 << 2,
+    GS_SPECIFIED_FAMILY = 1 << 3,
+    GS_SPECIFIED_SPACING = 1 << 4
+  };
 
+struct gs_font_data
+{
+  int specified;
+  enum gs_font_slant slant;
+  enum gs_font_weight weight;
+  enum gs_font_width width;
+  bool monospace_p;
+  char *family_name;
+};
 
-/* Replace spaces w/another character so emacs core font parsing routines
-   aren't thrown off.  */
 static void
-ns_escape_name (char *name)
+ns_done_font_data (struct gs_font_data *data)
 {
-  for (; *name; name++)
-    if (*name == ' ')
-      *name = '_';
+  if (data->specified & GS_SPECIFIED_FAMILY)
+    xfree (data->family_name);
 }
 
-
-/* Reconstruct spaces in a font family name passed through emacs.  */
 static void
-ns_unescape_name (char *name)
+ns_get_font_data (NSFontDescriptor *desc, struct gs_font_data *dat)
 {
-  for (; *name; name++)
-    if (*name == '_')
-      *name = ' ';
+  NSNumber *tem;
+  NSFontSymbolicTraits traits = [desc symbolicTraits];
+  NSDictionary *dict = [desc objectForKey: NSFontTraitsAttribute];
+  NSString *family = [desc objectForKey: NSFontFamilyAttribute];
+
+  dat->specified = 0;
+
+  if (family != nil)
+    {
+      dat->specified |= GS_SPECIFIED_FAMILY;
+      dat->family_name = xstrdup ([family cStringUsingEncoding: NSUTF8StringEncoding]);
+    }
+
+  tem = [desc objectForKey: NSFontFixedAdvanceAttribute];
+
+  if ((tem != nil && [tem boolValue] != NO)
+      || (traits & NSFontMonoSpaceTrait))
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = true;
+    }
+  else if (tem != nil && [tem boolValue] == NO)
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = false;
+    }
+
+  if (traits & NSFontBoldTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WEIGHT;
+      dat->weight = GS_FONT_WEIGHT_BOLD;
+    }
+
+  if (traits & NSFontItalicTrait)
+    {
+      dat->specified |= GS_SPECIFIED_SLANT;
+      dat->slant = GS_FONT_SLANT_ITALIC;
+    }
+
+  if (traits & NSFontCondensedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_CONDENSED;
+    }
+  else if (traits & NSFontExpandedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_EXPANDED;
+    }
+
+  if (dict != nil)
+    {
+      tem = [dict objectForKey: NSFontSlantTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_SLANT;
+
+	  dat->slant = [tem floatValue] > 0
+	    ? GS_FONT_SLANT_ITALIC
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_SLANT_REVERSE_ITALIC
+	       : GS_FONT_SLANT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWeightTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WEIGHT;
+
+	  dat->weight = [tem floatValue] > 0
+	    ? GS_FONT_WEIGHT_BOLD
+	    : ([tem floatValue] < -0.4f
+	       ? GS_FONT_WEIGHT_LIGHT
+	       : GS_FONT_WEIGHT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWidthTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WIDTH;
+
+	  dat->width = [tem floatValue] > 0
+	    ? GS_FONT_WIDTH_EXPANDED
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_WIDTH_NORMAL
+	       : GS_FONT_WIDTH_CONDENSED);
+	}
+    }
+}
+
+static bool
+ns_font_descs_match_p (NSFontDescriptor *desc, NSFontDescriptor *target)
+{
+  struct gs_font_data dat;
+  struct gs_font_data t;
+
+  ns_get_font_data (desc, &dat);
+  ns_get_font_data (target, &t);
+
+  if (!(t.specified & GS_SPECIFIED_WIDTH))
+    t.width = GS_FONT_WIDTH_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_WEIGHT))
+    t.weight = GS_FONT_WEIGHT_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_SPACING))
+    t.monospace_p = false;
+  if (!(t.specified & GS_SPECIFIED_SLANT))
+    t.slant = GS_FONT_SLANT_NORMAL;
+
+  if (!(t.specified & GS_SPECIFIED_FAMILY))
+    emacs_abort ();
+
+  bool match_p = true;
+
+  if (dat.specified & GS_SPECIFIED_WIDTH
+      && dat.width != t.width)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_WEIGHT
+      && dat.weight != t.weight)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SPACING
+      && dat.monospace_p != t.monospace_p)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SLANT
+      && dat.monospace_p != t.monospace_p)
+    {
+      if (NSFONT_TRACE)
+	printf ("Matching monospace for %s: %d %d\n",
+		t.family_name, dat.monospace_p,
+		t.monospace_p);
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_FAMILY
+      && strcmp (dat.family_name, t.family_name))
+    match_p = false;
+
+ gout:
+  ns_done_font_data (&dat);
+  ns_done_font_data (&t);
+
+  return match_p;
 }
 
+/* Font glyph and metrics caching functions, implemented at end.  */
+static void ns_uni_to_glyphs (struct nsfont_info *font_info,
+                              unsigned char block);
+static void ns_glyph_metrics (struct nsfont_info *font_info,
+                              unsigned int block);
+
+#define INVALID_GLYPH 0xFFFF
+
+/* ==========================================================================
+
+    Utilities
+
+   ========================================================================== */
+
 
 /* Extract family name from a font spec.  */
 static NSString *
@@ -91,66 +313,116 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
     {
       char *tmp = xlispstrdup (SYMBOL_NAME (tem));
       NSString *family;
-      ns_unescape_name (tmp);
       family = [NSString stringWithUTF8String: tmp];
       xfree (tmp);
       return family;
     }
 }
 
-
-/* Return 0 if attr not set, else value (which might also be 0).
-   On Leopard 0 gets returned even on descriptors where the attribute
-   was never set, so there's no way to distinguish between unspecified
-   and set to not have.  Callers should assume 0 means unspecified.  */
-static float
-ns_attribute_fvalue (NSFontDescriptor *fdesc, NSString *trait)
-{
-    NSDictionary *tdict = [fdesc objectForKey: NSFontTraitsAttribute];
-    NSNumber *val = [tdict objectForKey: trait];
-    return val == nil ? 0.0F : [val floatValue];
-}
-
-
 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
    to NSFont descriptor.  Information under extra only needed for matching.  */
-#define STYLE_REF 100
 static NSFontDescriptor *
 ns_spec_to_descriptor (Lisp_Object font_spec)
 {
     NSFontDescriptor *fdesc;
     NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
-    NSMutableDictionary *tdict = [NSMutableDictionary new];
     NSString *family = ns_get_family (font_spec);
-    float n;
-
-    /* Add each attr in font_spec to fdAttrs.  */
-    n = min (FONT_WEIGHT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWeightTrait];
-    n = min (FONT_SLANT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontSlantTrait];
-    n = min (FONT_WIDTH_NUMERIC (font_spec), 200);
-    if (n > -1 && (n > STYLE_REF + 10 || n < STYLE_REF - 10))
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWidthTrait];
-    if ([tdict count] > 0)
-	[fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+    NSMutableDictionary *tdict = [NSMutableDictionary new];
 
-    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
-               retain] autorelease];
+    Lisp_Object tem;
+
+    tem = FONT_SLANT_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontSlantTrait];
+	else if (EQ (tem, intern ("reverse-italic")) ||
+		 EQ (tem, intern ("reverse-oblique")))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontSlantTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontSlantTrait];
+      }
+
+    tem = FONT_WIDTH_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qcondensed))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontWidthTrait];
+	else if (EQ (tem, Qexpanded))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontWidthTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontWidthTrait];
+      }
+
+    tem = FONT_WEIGHT_SYMBOLIC (font_spec);
+
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qbold))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else if (EQ (tem, Qlight))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		      forKey: NSFontWeightTrait];
+	  }
+      }
+
+    tem = AREF (font_spec, FONT_SPACING_INDEX);
 
     if (family != nil)
       {
-        NSFontDescriptor *fdesc2 = [fdesc fontDescriptorWithFamily: family];
-        fdesc = [[fdesc2 retain] autorelease];
+	[fdAttrs setObject: family
+		    forKey: NSFontFamilyAttribute];
       }
 
-    [fdAttrs release];
+    if (FIXNUMP (tem))
+      {
+	if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:YES]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+	else
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:NO]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+      }
+
+    /* Handle special families such as ``fixed'' or ``Sans Serif''.  */
+
+    if ([family isEqualToString: @"fixed"])
+      {
+	[fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+    else if ([family isEqualToString: @"Sans Serif"])
+      {
+	[fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+
+    [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+
+    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
+               retain] autorelease];
+
     [tdict release];
+    [fdAttrs release];
     return fdesc;
 }
 
@@ -161,61 +433,64 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
                          Lisp_Object extra,
                          const char *style)
 {
-    Lisp_Object font_entity = font_make_entity ();
-    /*   NSString *psName = [desc postscriptName]; */
-    NSString *family = [desc objectForKey: NSFontFamilyAttribute];
-    unsigned int traits = [desc symbolicTraits];
-    char *escapedFamily;
-
-    /* Shouldn't happen, but on Tiger fallback desc gets name but no family.  */
-    if (family == nil)
-      family = [desc objectForKey: NSFontNameAttribute];
-    if (family == nil)
-      family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-
-    escapedFamily = xstrdup ([family UTF8String]);
-    ns_escape_name (escapedFamily);
-
-    ASET (font_entity, FONT_TYPE_INDEX, Qns);
-    ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
-    ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
-    ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
-    ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
-
-    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    traits & NSFontBoldTrait ? Qbold : Qmedium);
-/*    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    make_fixnum (100 + 100
-			* ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    traits & NSFontItalicTrait ? Qitalic : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-                    traits & NSFontCondensedTrait ? Qcondensed :
-                    traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
-
-    ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_SPACING_INDEX,
-	  make_fixnum([desc symbolicTraits] & NSFontMonoSpaceTrait
-	      ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
-
-    ASET (font_entity, FONT_EXTRA_INDEX, extra);
-    ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+  Lisp_Object font_entity = font_make_entity ();
+  struct gs_font_data data;
+  ns_get_font_data (desc, &data);
+
+  ASET (font_entity, FONT_TYPE_INDEX, Qns);
+  ASET (font_entity, FONT_FOUNDRY_INDEX, Qns);
+  if (data.specified & GS_SPECIFIED_FAMILY)
+    ASET (font_entity, FONT_FAMILY_INDEX, intern (data.family_name));
+  ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
+  ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
+
+  if (data.specified & GS_SPECIFIED_WEIGHT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
+		      data.weight == GS_FONT_WEIGHT_BOLD
+		      ? Qbold : (data.weight == GS_FONT_WEIGHT_LIGHT
+				 ? Qlight : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX, Qnormal);
 
-    if (NSFONT_TRACE)
-      {
-	fputs ("created font_entity:\n    ", stderr);
-	debug_print (font_entity);
-      }
+  if (data.specified & GS_SPECIFIED_SLANT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
+		      data.slant == GS_FONT_SLANT_ITALIC
+		      ? Qitalic : (data.slant == GS_FONT_SLANT_REVERSE_ITALIC
+				   ? intern ("reverse-italic") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX, Qnormal);
+
+  if (data.specified & GS_SPECIFIED_WIDTH)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
+		      data.width == GS_FONT_WIDTH_CONDENSED
+		      ? Qcondensed : (data.width == GS_FONT_WIDTH_EXPANDED
+				      ? intern ("expanded") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX, Qnormal);
 
-    xfree (escapedFamily);
-    return font_entity;
+  ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_SPACING_INDEX,
+	make_fixnum ((data.specified & GS_SPECIFIED_WIDTH && data.monospace_p)
+		     ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
+
+  ASET (font_entity, FONT_EXTRA_INDEX, extra);
+  ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+
+  if (NSFONT_TRACE)
+    {
+      fputs ("created font_entity:\n    ", stderr);
+      debug_print (font_entity);
+    }
+
+  ns_done_font_data (&data);
+  return font_entity;
 }
 
 
@@ -223,8 +498,7 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
 static Lisp_Object
 ns_fallback_entity (void)
 {
-  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
-      fontDescriptor], Qnil, NULL);
+  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 1] fontDescriptor], Qnil, NULL);
 }
 
 
@@ -510,21 +784,20 @@ but also for ascii (which causes unnecessary font substitution).  */
     return families;
 }
 
+/* GNUstep font matching is very mediocre (it can't even compare
+   symbolic styles correctly), which is why our own font matching
+   mechanism must be implemented.  */
 
-/* Implementation for list() and match().  List() can return nil, match()
-must return something.  Strategy is to drop family name from attribute
-matching set for match.  */
+/* Implementation for list and match.  */
 static Lisp_Object
 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
 {
     Lisp_Object tem, list = Qnil;
-    NSFontDescriptor *fdesc, *desc;
-    NSMutableSet *fkeys;
-    NSArray *matchingDescs;
-    NSEnumerator *dEnum;
-    NSString *family;
+    NSFontDescriptor *fdesc;
+    NSArray *all_descs;
+    GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
+
     NSSet *cFamilies;
-    BOOL foundItal = NO;
 
     block_input ();
     if (NSFONT_TRACE)
@@ -537,43 +810,22 @@ but also for ascii (which causes unnecessary font substitution).  */
     cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
 
     fdesc = ns_spec_to_descriptor (font_spec);
-    fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
-    if (isMatch)
-	[fkeys removeObject: NSFontFamilyAttribute];
-
-    matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
+    all_descs = [enumerator availableFontDescriptors];
 
-    if (NSFONT_TRACE)
-	NSLog(@"Got desc %@ and found %lu matching fonts from it: ", fdesc,
-	      (unsigned long)[matchingDescs count]);
-
-    for (dEnum = [matchingDescs objectEnumerator]; (desc = [dEnum nextObject]);)
+    for (NSFontDescriptor *desc in all_descs)
       {
 	if (![cFamilies containsObject:
 	         [desc objectForKey: NSFontFamilyAttribute]])
 	    continue;
+	if (!ns_font_descs_match_p (fdesc, desc))
+	  continue;
+
         tem = ns_descriptor_to_entity (desc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
+				       AREF (font_spec, FONT_EXTRA_INDEX),
                                        NULL);
         if (isMatch)
           return tem;
 	list = Fcons (tem, list);
-	if (fabs (ns_attribute_fvalue (desc, NSFontSlantTrait)) > 0.05)
-	    foundItal = YES;
-      }
-
-    /* Add synthItal member if needed.  */
-    family = [fdesc objectForKey: NSFontFamilyAttribute];
-    if (family != nil && !foundItal && !NILP (list))
-      {
-        NSFontDescriptor *s1 = [NSFontDescriptor new];
-        NSFontDescriptor *sDesc
-          = [[s1 fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
-              fontDescriptorWithFamily: family];
-	list = Fcons (ns_descriptor_to_entity (sDesc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
-					 "synthItal"), list);
-        [s1 release];
       }
 
     unblock_input ();
@@ -652,7 +904,6 @@ Properties to be considered are same as for list().  */
                objectEnumerator];
   while ((family = [families nextObject]))
       list = Fcons (intern ([family UTF8String]), list);
-  /* FIXME: escape the name?  */
 
   if (NSFONT_TRACE)
     fprintf (stderr, "nsfont: list families returning %"pD"d entries\n",
@@ -668,18 +919,15 @@ Properties to be considered are same as for list().  */
 static Lisp_Object
 nsfont_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
 {
-  BOOL synthItal;
-  unsigned int traits = 0;
   struct nsfont_info *font_info;
   struct font *font;
   NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
   NSFontManager *fontMgr = [NSFontManager sharedFontManager];
   NSString *family;
   NSFont *nsfont, *sfont;
-  Lisp_Object tem;
   NSRect brect;
   Lisp_Object font_object;
-  int fixLeopardBug;
+  Lisp_Object tem;
 
   block_input ();
 
@@ -692,42 +940,20 @@ Properties to be considered are same as for list().  */
   if (pixel_size <= 0)
     {
       /* try to get it out of frame params */
-        Lisp_Object tem = get_frame_param (f, Qfontsize);
-        pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
+      tem = get_frame_param (f, Qfontsize);
+      pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
     }
 
   tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
-  synthItal = !NILP (tem) && !strncmp ("synthItal", SSDATA (SYMBOL_NAME (tem)),
-                                       9);
   family = ns_get_family (font_entity);
   if (family == nil)
     family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-  /* Should be > 0.23 as some font descriptors (e.g. Terminus) set to that
-     when setting family in ns_spec_to_descriptor().  */
-  if (ns_attribute_fvalue (fontDesc, NSFontWeightTrait) > 0.50F)
-      traits |= NSBoldFontMask;
-  if (ns_attribute_fvalue (fontDesc, NSFontSlantTrait) > 0.05F)
-      traits |= NSItalicFontMask;
-
-  /* see https://web.archive.org/web/20100201175731/http://cocoadev.com/forums/comments.php?DiscussionID=74 */
-  fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
-  nsfont = [fontMgr fontWithFamily: family
-                            traits: traits weight: fixLeopardBug
-			      size: pixel_size];
-  /* if didn't find, try synthetic italic */
-  if (nsfont == nil && synthItal)
-    {
-      nsfont = [fontMgr fontWithFamily: family
-                                traits: traits & ~NSItalicFontMask
-                                weight: fixLeopardBug size: pixel_size];
-    }
+
+  nsfont = [NSFont fontWithDescriptor: fontDesc
+				 size: pixel_size];
 
   if (nsfont == nil)
-    {
-      message_with_string ("*** Warning: font in family `%s' not found",
-                          build_string ([family UTF8String]), 1);
-      nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
-    }
+    nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
 
   if (NSFONT_TRACE)
     NSLog (@"%@\n", nsfont);
@@ -740,7 +966,7 @@ when setting family in ns_spec_to_descriptor().  */
   if (!font)
     {
       unblock_input ();
-      return Qnil; /* FIXME: other terms do, but returning Qnil causes segfault.  */
+      return Qnil;
     }
 
   font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs);
@@ -781,7 +1007,7 @@ when setting family in ns_spec_to_descriptor().  */
     font_info->name = xstrdup (fontName);
     font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
     font_info->ital =
-      synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
+      ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
 
     /* Metrics etc.; some fonts return an unusually large max advance, so we
        only use it for fonts that have wide characters.  */
@@ -808,8 +1034,6 @@ when setting family in ns_spec_to_descriptor().  */
       lrint (brect.size.width - (CGFloat) font_info->width);
 
     /* set up metrics portion of font struct */
-    font->ascent = lrint([sfont ascender]);
-    font->descent = -lrint(floor(adjusted_descender));
     font->space_width = lrint (ns_char_width (sfont, ' '));
     font->max_width = lrint (font_info->max_bounds.width);
     font->min_width = font->space_width;  /* Approximate.  */
@@ -871,7 +1095,7 @@ when setting family in ns_spec_to_descriptor().  */
 {
   struct nsfont_info *font_info = (struct nsfont_info *)font;
   unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
-  unsigned short g;
+  unsigned int g;
 
   if (c > 0xFFFF)
     return FONT_INVALID_CODE;
@@ -934,51 +1158,23 @@ is false when (FROM > 0 || TO < S->nchars).  */
 static int
 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
              bool with_background)
-/* NOTE: focus and clip must be set.  */
 {
-  static unsigned char cbuf[1024];
-  unsigned char *c = cbuf;
-#if GNUSTEP_GUI_MAJOR_VERSION > 0 || GNUSTEP_GUI_MINOR_VERSION > 22
-  static CGFloat advances[1024];
-  CGFloat *adv = advances;
-#else
-  static float advances[1024];
-  float *adv = advances;
-#endif
+  NSGlyph *c = alloca ((to - from) * sizeof *c);
+
   struct face *face;
   NSRect r;
   struct nsfont_info *font;
-  NSColor *col, *bgCol;
-  unsigned *t = s->char2b;
-  int i, len, flags;
+  NSColor *col;
+  int len = to - from;
   char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
 
   block_input ();
 
-  font = (struct nsfont_info *)s->face->font;
+  font = (struct nsfont_info *) s->font;
   if (font == NULL)
     font = (struct nsfont_info *)FRAME_FONT (s->f);
 
-  /* Select face based on input flags.  */
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
-  switch (flags)
-    {
-    case NS_DUMPGLYPH_CURSOR:
-      face = s->face;
-      break;
-    case NS_DUMPGLYPH_MOUSEFACE:
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-      break;
-    default:
-      face = s->face;
-    }
+  face = s->face;
 
   r.origin.x = s->x;
   if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
@@ -987,91 +1183,24 @@ is false when (FROM > 0 || TO < S->nchars).  */
   r.origin.y = s->y;
   r.size.height = FONT_HEIGHT (font);
 
-  /* Convert UTF-16 (?) to UTF-8 and determine advances.  Note if we just ask
-     NS to render the string, it will come out differently from the individual
-     character widths added up because of layout processing.  */
-  {
-    int cwidth, twidth = 0;
-    int hi, lo;
-    /* FIXME: composition: no vertical displacement is considered.  */
-    t += from; /* advance into composition */
-    for (i = from; i < to; i++, t++)
-      {
-        hi = (*t & 0xFF00) >> 8;
-        lo = *t & 0x00FF;
-        if (isComposite)
-          {
-	    if (!s->first_glyph->u.cmp.automatic)
-		cwidth = s->cmp->offsets[i * 2] /* (H offset) */ - twidth;
-	    else
-	      {
-		Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
-		Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
-		if (NILP (LGLYPH_ADJUSTMENT (glyph)))
-		    cwidth = LGLYPH_WIDTH (glyph);
-		else
-		  {
-		    cwidth = LGLYPH_WADJUST (glyph);
-		    *(adv-1) += LGLYPH_XOFF (glyph);
-		  }
-	      }
-          }
-        else
-          {
-            if (!font->metrics[hi]) /* FIXME: why/how can we need this now?  */
-              ns_glyph_metrics (font, hi);
-            cwidth = font->metrics[hi][lo].width;
-          }
-        twidth += cwidth;
-        *adv++ = cwidth;
-        c += CHAR_STRING (*t, c); /* This converts the char to UTF-8.  */
-      }
-    len = adv - advances;
-    r.size.width = twidth;
-    *c = 0;
-  }
+  for (int i = from; i < to; ++i)
+    c[i] = s->char2b[i];
 
   /* Fill background if requested.  */
   if (with_background && !isComposite)
     {
-      NSRect br = r;
-      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
-      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
-
-      if (s->row->full_width_p)
-        {
-          if (br.origin.x <= fibw + 1 + mbox_line_width)
-            {
-              br.size.width += br.origin.x - mbox_line_width;
-              br.origin.x = mbox_line_width;
-            }
-          if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
-                <= fibw+1)
-            br.size.width += fibw;
-        }
-      if (s->face->box == FACE_NO_BOX)
-        {
-          /* Expand unboxed top row over internal border.  */
-          if (br.origin.y <= fibw + 1 + mbox_line_width)
-            {
-              br.size.height += br.origin.y;
-              br.origin.y = 0;
-            }
-        }
-      else
-        {
-          int correction = abs (s->face->box_horizontal_line_width)+1;
-          br.origin.y += correction;
-          br.size.height -= 2*correction;
-          correction = abs (s->face->box_vertical_line_width)+1;
-          br.origin.x += correction;
-          br.size.width -= 2*correction;
-        }
+      NSRect br = NSMakeRect (x, y - FONT_BASE (s->font),
+			      s->width, FONT_HEIGHT (s->font));
 
       if (!s->face->stipple)
-        [(NS_FACE_BACKGROUND (face) != 0
-          ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-          : FRAME_BACKGROUND_COLOR (s->f)) set];
+	{
+	  if (s->hl != DRAW_CURSOR)
+	    [(NS_FACE_BACKGROUND (face) != 0
+	      ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+	      : FRAME_BACKGROUND_COLOR (s->f)) set];
+	  else
+	    [FRAME_CURSOR_COLOR (s->f) set];
+	}
       else
         {
           struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
@@ -1080,43 +1209,32 @@ is false when (FROM > 0 || TO < S->nchars).  */
       NSRectFill (br);
     }
 
-
   /* set up for character rendering */
   r.origin.y = y;
 
-  col = (NS_FACE_FOREGROUND (face) != 0
-         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
-         : FRAME_FOREGROUND_COLOR (s->f));
-
-  bgCol = (flags != NS_DUMPGLYPH_FOREGROUND ? nil
-           : (NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)));
+  if (s->hl == DRAW_CURSOR)
+    col = FRAME_BACKGROUND_COLOR (s->f);
+  else
+    col = (NS_FACE_FOREGROUND (face) != 0
+	   ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
+	   : FRAME_FOREGROUND_COLOR (s->f));
 
   /* render under GNUstep using DPS */
   {
-    NSGraphicsContext *context = GSCurrentContext ();
-
+    NSGraphicsContext *context = [NSGraphicsContext currentContext];
     DPSgsave (context);
-    [font->nsfont set];
-
-    /* do erase if "foreground" mode */
-    if (bgCol != nil)
+    if (s->clip_head)
       {
-        [bgCol set];
-        DPSmoveto (context, r.origin.x, r.origin.y);
-/*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
-        DPSxshow (context, (const char *) cbuf, advances, len);
-        DPSstroke (context);
-        [col set];
-/*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
+	DPSrectclip (context, s->clip_head->x, 0,
+		     FRAME_PIXEL_WIDTH (s->f),
+		     FRAME_PIXEL_HEIGHT (s->f));
       }
+    [font->nsfont set];
 
     [col set];
 
-    /* draw with DPSxshow () */
     DPSmoveto (context, r.origin.x, r.origin.y);
-    DPSxshow (context, (const char *) cbuf, advances, len);
+    GSShowGlyphs (context, c, len);
     DPSstroke (context);
 
     DPSgrestore (context);
@@ -1126,6 +1244,360 @@ is false when (FROM > 0 || TO < S->nchars).  */
   return to-from;
 }
 
+static NSUInteger
+ns_font_shape (NSFont *font, NSString *string,
+	       struct ns_glyph_layout *glyph_layouts, NSUInteger glyph_len,
+	       enum lgstring_direction dir)
+{
+  NSUInteger i;
+  NSUInteger result = 0;
+  NSTextStorage *textStorage;
+  NSLayoutManager *layoutManager;
+  NSTextContainer *textContainer;
+  NSUInteger stringLength;
+  NSPoint spaceLocation;
+  /* numberOfGlyphs can't actually be 0, but this pacifies GCC */
+  NSUInteger used, numberOfGlyphs = 0;
+
+  textStorage = [[NSTextStorage alloc] initWithString:string];
+  layoutManager = [[NSLayoutManager alloc] init];
+  textContainer = [[NSTextContainer alloc] init];
+
+  /* Append a trailing space to measure baseline position.  */
+  [textStorage appendAttributedString:([[[NSAttributedString alloc]
+                                          initWithString:@" "] autorelease])];
+  [textStorage setFont:font];
+  [textContainer setLineFragmentPadding:0];
+
+  [layoutManager addTextContainer:textContainer];
+  [textContainer release];
+  [textStorage addLayoutManager:layoutManager];
+  [layoutManager release];
+
+  if (!(textStorage && layoutManager && textContainer))
+    emacs_abort ();
+
+  stringLength = [string length];
+
+  /* Force layout.  */
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  spaceLocation = [layoutManager locationForGlyphAtIndex:stringLength];
+
+  /* Remove the appended trailing space because otherwise it may
+     generate a wrong result for a right-to-left text.  */
+  [textStorage beginEditing];
+  [textStorage deleteCharactersInRange:(NSMakeRange (stringLength, 1))];
+  [textStorage endEditing];
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  i = 0;
+  while (i < stringLength)
+    {
+      NSRange range;
+      NSFont *fontInTextStorage =
+        [textStorage attribute: NSFontAttributeName
+		       atIndex:i
+                     longestEffectiveRange: &range
+                       inRange: NSMakeRange (0, stringLength)];
+
+      if (!(fontInTextStorage == font
+            || [[fontInTextStorage fontName] isEqualToString:[font fontName]]))
+        break;
+      i = NSMaxRange (range);
+    }
+  if (i < stringLength)
+    /* Make the test `used <= glyph_len' below fail if textStorage
+       contained some fonts other than the specified one.  */
+    used = glyph_len + 1;
+  else
+    {
+      NSRange range = NSMakeRange (0, stringLength);
+
+      range = [layoutManager glyphRangeForCharacterRange:range
+                                    actualCharacterRange:NULL];
+      numberOfGlyphs = NSMaxRange (range);
+      used = numberOfGlyphs;
+      for (i = 0; i < numberOfGlyphs; i++)
+        if ([layoutManager notShownAttributeForGlyphAtIndex:i])
+          used--;
+    }
+
+  if (0 < used && used <= glyph_len)
+    {
+      NSUInteger glyphIndex, prevGlyphIndex;
+      NSUInteger *permutation;
+      NSRange compRange, range;
+      CGFloat totalAdvance;
+
+      glyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      permutation = NULL;
+#define RIGHT_TO_LEFT_P permutation
+
+      /* Fill the `comp_range' member of struct mac_glyph_layout, and
+         setup a permutation for right-to-left text.  */
+      compRange = NSMakeRange (0, 0);
+      for (range = NSMakeRange (0, 0); NSMaxRange (range) < used;
+           range.length++)
+        {
+          struct ns_glyph_layout *gl = glyph_layouts + NSMaxRange (range);
+          NSUInteger characterIndex =
+            [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
+
+          gl->string_index = characterIndex;
+
+          if (characterIndex >= NSMaxRange (compRange))
+            {
+              compRange.location = NSMaxRange (compRange);
+              do
+                {
+                  NSRange characterRange =
+                    [string
+                      rangeOfComposedCharacterSequenceAtIndex:characterIndex];
+
+                  compRange.length =
+                    NSMaxRange (characterRange) - compRange.location;
+                  [layoutManager glyphRangeForCharacterRange:compRange
+                                        actualCharacterRange:&characterRange];
+                  characterIndex = NSMaxRange (characterRange) - 1;
+                }
+              while (characterIndex >= NSMaxRange (compRange));
+
+              if (RIGHT_TO_LEFT_P)
+                for (i = 0; i < range.length; i++)
+                  permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+              range = NSMakeRange (NSMaxRange (range), 0);
+            }
+
+          gl->comp_range.location = compRange.location;
+          gl->comp_range.length = compRange.length;
+
+          while (++glyphIndex < numberOfGlyphs)
+            if (![layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+              break;
+        }
+      if (RIGHT_TO_LEFT_P)
+        for (i = 0; i < range.length; i++)
+          permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+      /* Then fill the remaining members.  */
+      glyphIndex = prevGlyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      if (!RIGHT_TO_LEFT_P)
+        totalAdvance = 0;
+      else
+        {
+          NSUInteger nrects;
+          NSRect *glyphRects =
+            [layoutManager
+              rectArrayForGlyphRange:(NSMakeRange (0, numberOfGlyphs))
+              withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                     inTextContainer:textContainer rectCount:&nrects];
+
+          totalAdvance = NSMaxX (glyphRects[0]);
+        }
+
+      for (i = 0; i < used; i++)
+        {
+          struct ns_glyph_layout *gl;
+          NSPoint location;
+          NSUInteger nextGlyphIndex;
+          NSRange glyphRange;
+          NSRect *glyphRects;
+          NSUInteger nrects;
+
+          if (!RIGHT_TO_LEFT_P)
+            gl = glyph_layouts + i;
+          else
+            {
+              NSUInteger dest = permutation[i];
+
+              gl = glyph_layouts + dest;
+              if (i < dest)
+                {
+                  NSUInteger tmp = gl->string_index;
+
+                  gl->string_index = glyph_layouts[i].string_index;
+                  glyph_layouts[i].string_index = tmp;
+                }
+            }
+          gl->glyph_id = [layoutManager glyphAtIndex: glyphIndex];
+
+          location = [layoutManager locationForGlyphAtIndex:glyphIndex];
+          gl->baseline_delta = spaceLocation.y - location.y;
+
+          for (nextGlyphIndex = glyphIndex + 1; nextGlyphIndex < numberOfGlyphs;
+               nextGlyphIndex++)
+            if (![layoutManager
+                   notShownAttributeForGlyphAtIndex:nextGlyphIndex])
+              break;
+
+          if (!RIGHT_TO_LEFT_P)
+            {
+              CGFloat maxX;
+
+              if (prevGlyphIndex == 0)
+                glyphRange = NSMakeRange (0, nextGlyphIndex);
+              else
+                glyphRange = NSMakeRange (glyphIndex,
+                                          nextGlyphIndex - glyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              maxX = max (NSMaxX (glyphRects[0]), totalAdvance);
+              gl->advance_delta = location.x - totalAdvance;
+              gl->advance = maxX - totalAdvance;
+              totalAdvance = maxX;
+            }
+          else
+            {
+              CGFloat minX;
+
+              if (nextGlyphIndex == numberOfGlyphs)
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          numberOfGlyphs - prevGlyphIndex);
+              else
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          glyphIndex + 1 - prevGlyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              minX = min (NSMinX (glyphRects[0]), totalAdvance);
+              gl->advance = totalAdvance - minX;
+              totalAdvance = minX;
+              gl->advance_delta = location.x - totalAdvance;
+            }
+
+          prevGlyphIndex = glyphIndex + 1;
+          glyphIndex = nextGlyphIndex;
+        }
+
+      if (RIGHT_TO_LEFT_P)
+        xfree (permutation);
+
+#undef RIGHT_TO_LEFT_P
+
+      result = used;
+    }
+  [textStorage release];
+
+  return result;
+}
+
+static Lisp_Object
+nsfont_shape (Lisp_Object lgstring, Lisp_Object direction)
+{
+  struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  struct nsfont_info *font_info = (struct nsfont_info *) font;
+  struct ns_glyph_layout *glyph_layouts;
+  NSFont *nsfont = font_info->nsfont;
+  ptrdiff_t glyph_len, len, i;
+  Lisp_Object tem;
+  unichar *mb_buf;
+  NSUInteger used;
+
+  glyph_len = LGSTRING_GLYPH_LEN (lgstring);
+  for (i = 0; i < glyph_len; ++i)
+    {
+      tem = LGSTRING_GLYPH (lgstring, i);
+
+      if (NILP (tem))
+	break;
+    }
+
+  len = i;
+
+  if (INT_MAX / 2 < len)
+    memory_full (SIZE_MAX);
+
+  block_input ();
+
+  mb_buf = alloca (len * sizeof *mb_buf);
+
+  for (i = 0; i < len; ++i)
+    {
+      uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
+      mb_buf[i] = (unichar) c;
+    }
+
+  NSString *string = [NSString stringWithCharacters: mb_buf
+					     length: len];
+  unblock_input ();
+
+  if (!string)
+    return Qnil;
+
+  block_input ();
+
+  enum lgstring_direction dir = DIR_UNKNOWN;
+
+  if (EQ (direction, QL2R))
+    dir = DIR_L2R;
+  else if (EQ (direction, QR2L))
+    dir = DIR_R2L;
+  glyph_layouts = alloca (sizeof (struct ns_glyph_layout) * glyph_len);
+  used = ns_font_shape (nsfont, string, glyph_layouts, glyph_len, dir);
+
+  for (i = 0; i < used; i++)
+    {
+      Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
+      struct ns_glyph_layout *gl = glyph_layouts + i;
+      EMACS_INT from, to;
+      struct font_metrics metrics;
+
+      if (NILP (lglyph))
+        {
+          lglyph = LGLYPH_NEW ();
+          LGSTRING_SET_GLYPH (lgstring, i, lglyph);
+        }
+
+      from = gl->comp_range.location;
+      LGLYPH_SET_FROM (lglyph, from);
+
+      to = gl->comp_range.location + gl->comp_range.length;
+      LGLYPH_SET_TO (lglyph, to - 1);
+
+      /* LGLYPH_CHAR is used in `describe-char' for checking whether
+         the composition is trivial.  */
+      {
+        UTF32Char c;
+
+        if (mb_buf[gl->string_index] >= 0xD800
+            && mb_buf[gl->string_index] < 0xDC00)
+          c = (((mb_buf[gl->string_index] - 0xD800) << 10)
+               + (mb_buf[gl->string_index + 1] - 0xDC00) + 0x10000);
+        else
+          c = mb_buf[gl->string_index];
+
+        LGLYPH_SET_CHAR (lglyph, c);
+      }
+
+      {
+        unsigned long cc = gl->glyph_id;
+        LGLYPH_SET_CODE (lglyph, cc);
+      }
+
+      nsfont_text_extents (font, &gl->glyph_id, 1, &metrics);
+      LGLYPH_SET_WIDTH (lglyph, metrics.width);
+      LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
+      LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
+      LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
+      LGLYPH_SET_DESCENT (lglyph, metrics.descent);
+    }
+  unblock_input ();
+
+  return make_fixnum (used);
+}
 
 
 /* ==========================================================================
@@ -1134,6 +1606,50 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
    ========================================================================== */
 
+static NSGlyph
+ns_uni_to_glyphs_1 (struct nsfont_info *info, unsigned int c)
+{
+  unichar characters[] = { c };
+  NSString *string =
+    [NSString stringWithCharacters: characters
+			    length: 1];
+  NSDictionary *attributes =
+    [NSDictionary dictionaryWithObjectsAndKeys:
+		    info->nsfont, NSFontAttributeName, nil];
+  NSTextStorage *storage = [[NSTextStorage alloc] initWithString: string
+						      attributes: attributes];
+  NSTextContainer *text_container = [[NSTextContainer alloc] init];
+  NSLayoutManager *manager = [[NSLayoutManager alloc] init];
+
+  [manager addTextContainer: text_container];
+  [text_container release]; /* Retained by manager */
+  [storage addLayoutManager: manager];
+  [manager release]; /* Retained by storage */
+
+  NSFont *font_in_storage = [storage attribute: NSFontAttributeName
+				       atIndex:0
+				effectiveRange: NULL];
+  NSGlyph glyph = FONT_INVALID_CODE;
+
+  if ((font_in_storage == info->nsfont
+       || [[font_in_storage fontName] isEqualToString: [info->nsfont fontName]]))
+    {
+      @try
+	{
+	  glyph = [manager glyphAtIndex: 0];
+	}
+      @catch (NSException *e)
+	{
+	  /* GNUstep bug? */
+	  glyph = 'X';
+	}
+    }
+
+  [storage release];
+
+  return glyph;
+}
+
 /* Find and cache corresponding glyph codes for unicode values in given
    hi-byte block of 256.  */
 static void
@@ -1141,7 +1657,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   unichar *unichars = xmalloc (0x101 * sizeof (unichar));
   unsigned int i, g, idx;
-  unsigned short *glyphs;
+  unsigned int *glyphs;
 
   if (NSFONT_TRACE)
     fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
@@ -1149,7 +1665,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
   block_input ();
 
-  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
+  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned int));
   if (!unichars || !(font_info->glyphs[block]))
     emacs_abort ();
 
@@ -1166,7 +1682,8 @@ is false when (FROM > 0 || TO < S->nchars).  */
     for (i = 0; i < 0x100; i++, glyphs++)
       {
         g = unichars[i];
-        *glyphs = g;
+	NSGlyph glyph = ns_uni_to_glyphs_1 (font_info, g);
+        *glyphs = glyph;
       }
   }
 
@@ -1175,18 +1692,19 @@ is false when (FROM > 0 || TO < S->nchars).  */
 }
 
 
-/* Determine and cache metrics for corresponding glyph codes in given
-   hi-byte block of 256.  */
+/* Determine and cache metrics for glyphs in given hi-byte block of
+   256.  */
 static void
-ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
+ns_glyph_metrics (struct nsfont_info *font_info, unsigned int block)
 {
-  unsigned int i, g;
+  unsigned int i;
+  NSGlyph g;
   unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
   NSFont *sfont;
   struct font_metrics *metrics;
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
+    fprintf (stderr, "%p\tComputing metrics for glyphs in block %u\n",
             font_info, block);
 
   /* not implemented yet (as of startup 0.18), so punt */
@@ -1209,19 +1727,14 @@ is false when (FROM > 0 || TO < S->nchars).  */
       w = max ([sfont advancementForGlyph: g].width, 2.0);
       metrics->width = lrint (w);
 
-      lb = r.origin.x;
-      rb = r.size.width - w;
-      // Add to bearing for LCD smoothing.  We don't know if it is there.
-      if (lb < 0)
-        metrics->lbearing = round (lb - LCD_SMOOTHING_MARGIN);
-      if (font_info->ital)
-        rb += (CGFloat) (0.22F * font_info->height);
-      metrics->rbearing = lrint (w + rb + LCD_SMOOTHING_MARGIN);
-
-      metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
-      /* lrint (hshrink * [sfont ascender] + expand * hd/2); */
-      metrics->ascent = r.size.height - metrics->descent;
-      /* -lrint (hshrink* [sfont descender] - expand * hd/2); */
+      lb = NSMinX (r);
+      rb = NSMaxX (r);
+
+      metrics->rbearing = lrint (rb);
+      metrics->lbearing = lrint (lb);
+
+      metrics->descent = NSMinY (r);
+      metrics->ascent = NSMaxY (r);
     }
   unblock_input ();
 }
@@ -1257,6 +1770,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
   .has_char = nsfont_has_char,
   .encode_char = nsfont_encode_char,
   .text_extents = nsfont_text_extents,
+  .shape = nsfont_shape,
   .draw = nsfont_draw,
   };
 
@@ -1265,7 +1779,6 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   DEFSYM (Qcondensed, "condensed");
   DEFSYM (Qexpanded, "expanded");
-  DEFSYM (Qapple, "apple");
   DEFSYM (Qmedium, "medium");
   DEFVAR_LISP ("ns-reg-to-script", Vns_reg_to_script,
                doc: /* Internal use: maps font registry to Unicode script.  */);
diff --git a/src/nsterm.h b/src/nsterm.h
index c750d1bd99..8175f99664 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -820,7 +820,7 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
   XCharStruct max_bounds;
   /* We compute glyph codes and metrics on-demand in blocks of 256 indexed
      by hibyte, lobyte.  */
-  unsigned short **glyphs; /* map Unicode index to glyph */
+  unsigned int **glyphs; /* map Unicode index to glyph */
   struct font_metrics **metrics;
 };
 #endif
diff --git a/src/nsterm.m b/src/nsterm.m
index 4c2a3f287c..9b036430ed 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1078,11 +1078,16 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
   /* clipping */
   if (r)
     {
-      [[NSGraphicsContext currentContext] saveGraphicsState];
+      NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+      [ctx saveGraphicsState];
+#ifdef NS_IMPL_COCOA
       if (n == 2)
         NSRectClipList (r, 2);
       else
         NSRectClip (*r);
+#else
+      GSRectClipList (ctx, r, n);
+#endif
       gsaved = YES;
     }
 }
@@ -2440,9 +2445,6 @@ Hide the window (X11 semantics)
       EmacsView *view = FRAME_NS_VIEW (f);
       FRAME_POINTER_TYPE (f) = cursor;
       [[view window] invalidateCursorRectsForView: view];
-      /* Redisplay assumes this function also draws the changed frame
-         cursor, but this function doesn't, so do it explicitly.  */
-      gui_update_cursor (f, 1);
     }
 }
 
@@ -2852,31 +2854,31 @@ Hide the window (X11 semantics)
      External (RIF); compute left/right overhang of whole string and set in s
    -------------------------------------------------------------------------- */
 {
-  struct font *font = s->font;
-
-  if (s->char2b)
+  if (s->cmp == NULL
+      && (s->first_glyph->type == CHAR_GLYPH
+	  || s->first_glyph->type == COMPOSITE_GLYPH))
     {
       struct font_metrics metrics;
-      unsigned int codes[2];
-      codes[0] = *(s->char2b);
-      codes[1] = *(s->char2b + s->nchars - 1);
 
-      font->driver->text_extents (font, codes, 2, &metrics);
-      s->left_overhang = -metrics.lbearing;
-      s->right_overhang
-	= metrics.rbearing > metrics.width
-	? metrics.rbearing - metrics.width : 0;
+      if (s->first_glyph->type == CHAR_GLYPH)
+	{
+	  struct font *font = s->font;
+	  font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+	}
+      else
+	{
+	  Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+	  composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+	}
+      s->right_overhang = (metrics.rbearing > metrics.width
+			   ? metrics.rbearing - metrics.width : 0);
+      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
     }
-  else
+  else if (s->cmp)
     {
-      s->left_overhang = 0;
-#ifdef NS_IMPL_GNUSTEP
-      if (EQ (font->driver->type, Qns))
-        s->right_overhang = ((struct nsfont_info *)font)->ital ?
-          FONT_HEIGHT (font) * 0.2 : 0;
-      else
-#endif
-        s->right_overhang = 0;
+      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+      s->left_overhang = - s->cmp->lbearing;
     }
 }
 
@@ -3016,14 +3018,13 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   struct frame *f = WINDOW_XFRAME (w);
   struct glyph *phys_cursor_glyph;
   struct glyph *cursor_glyph;
-  struct face *face;
-  NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
 
   /* If cursor is out of bounds, don't draw garbage.  This can happen
      in mini-buffer windows when switching between echo area glyphs
      and mini-buffer.  */
 
-  NSTRACE ("ns_draw_window_cursor");
+  NSTRACE ("ns_draw_window_cursor (on = %d, cursor_type = %d)",
+	   on_p, cursor_type);
 
   if (!on_p)
     return;
@@ -3039,6 +3040,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
     {
+      NSTRACE_MSG ("No phys cursor glyph was found!");
+
       if (glyph_row->exact_window_width_line_p
           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
         {
@@ -3048,10 +3051,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       return;
     }
 
-  /* We draw the cursor (with NSRectFill), then draw the glyph on top
-     (other terminals do it the other way round).  We must set
-     w->phys_cursor_width to the cursor width.  For bar cursors, that
-     is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
 
   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
@@ -3083,17 +3082,15 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   /* Prevent the cursor from being drawn outside the text area.  */
   r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
 
-  ns_focus (f, &r, 1);
+  NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+  [ctx saveGraphicsState];
+#ifdef NS_IMPL_GNUSTEP
+  GSRectClipList (ctx, &r, 1);
+#else
+  NSRectClip (r);
+#endif
 
-  face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
-  if (face && NS_FACE_BACKGROUND (face)
-      == ns_index_color (FRAME_CURSOR_COLOR (f), f))
-    {
-      [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
-      hollow_color = FRAME_CURSOR_COLOR (f);
-    }
-  else
-    [FRAME_CURSOR_COLOR (f) set];
+  [FRAME_CURSOR_COLOR (f) set];
 
   switch (cursor_type)
     {
@@ -3101,13 +3098,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     case NO_CURSOR:
       break;
     case FILLED_BOX_CURSOR:
-      NSRectFill (r);
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
       break;
     case HOLLOW_BOX_CURSOR:
-      NSRectFill (r);
-      [hollow_color set];
-      NSRectFill (NSInsetRect (r, 1, 1));
-      [FRAME_CURSOR_COLOR (f) set];
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_NORMAL_TEXT);
+      [NSBezierPath strokeRect: r];
       break;
     case HBAR_CURSOR:
       NSRectFill (r);
@@ -3123,12 +3118,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       NSRectFill (s);
       break;
     }
-  ns_unfocus (f);
 
-  /* Draw the character under the cursor.  Other terms only draw
-     the character on top of box cursors, so do the same here.  */
-  if (cursor_type == FILLED_BOX_CURSOR || cursor_type == HOLLOW_BOX_CURSOR)
-    draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+  [ctx restoreGraphicsState];
 }
 
 
@@ -3308,16 +3299,18 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   if (s->for_overlaps)
     return;
 
+  if (s->hl == DRAW_CURSOR)
+    [FRAME_BACKGROUND_COLOR (s->f) set];
+  else if (face->underline_defaulted_p)
+    [defaultCol set];
+  else
+    [ns_lookup_indexed_color (face->underline_color, s->f) set];
+
   /* Do underline.  */
   if (face->underline)
     {
       if (s->face->underline == FACE_UNDER_WAVE)
         {
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
-
           ns_draw_underwave (s, width, x);
         }
       else if (s->face->underline == FACE_UNDER_LINE)
@@ -3388,11 +3381,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
           s->underline_position = position;
 
           r = NSMakeRect (x, s->ybase + position, width, thickness);
-
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
           NSRectFill (r);
         }
     }
@@ -3402,11 +3390,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     {
       NSRect r;
       r = NSMakeRect (x, s->y, width, 1);
-
-      if (face->overline_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->overline_color, s->f) set];
       NSRectFill (r);
     }
 
@@ -3429,10 +3412,6 @@ larger if there are taller display elements (e.g., characters
       dy = lrint ((glyph_height - h) / 2);
       r = NSMakeRect (x, glyph_y + dy, width, 1);
 
-      if (face->strike_through_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
       NSRectFill (r);
     }
 }
@@ -3580,17 +3559,7 @@ Function modeled after x_draw_glyph_string_box ().
   struct glyph *last_glyph;
   NSRect r;
   int hthickness, vthickness;
-  struct face *face;
-
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = s->face;
+  struct face *face = s->face;
 
   vthickness = face->box_vertical_line_width;
   hthickness = face->box_horizontal_line_width;
@@ -3664,34 +3633,26 @@ Function modeled after x_draw_glyph_string_box ().
 	  || FONT_TOO_HIGH (s->font)
           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
 	{
-          struct face *face;
-          if (s->hl == DRAW_MOUSE_FACE)
-            {
-              face
-		= FACE_FROM_ID_OR_NULL (s->f,
-					MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-              if (!face)
-                face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-            }
-          else
-            face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+          struct face *face = s->face;
           if (!face->stipple)
-            [(NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)) set];
+	    {
+	      if (s->hl != DRAW_CURSOR)
+		[(NS_FACE_BACKGROUND (face) != 0
+		  ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+		  : FRAME_BACKGROUND_COLOR (s->f)) set];
+	      else
+		[FRAME_CURSOR_COLOR (s->f) set];
+	    }
           else
             {
               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
             }
 
-          if (s->hl != DRAW_CURSOR)
-            {
-              NSRect r = NSMakeRect (s->x, s->y + box_line_width,
-                                    s->background_width,
-                                    s->height-2*box_line_width);
-              NSRectFill (r);
-            }
+	  NSRect r = NSMakeRect (s->x, s->y + box_line_width,
+				 s->background_width,
+				 s->height-2*box_line_width);
+	  NSRectFill (r);
 
 	  s->background_filled_p = 1;
 	}
@@ -3712,7 +3673,7 @@ Function modeled after x_draw_glyph_string_box ().
   int th;
   char raised_p;
   NSRect br;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *tdCol;
 
   NSTRACE ("ns_dumpglyphs_image");
@@ -3733,15 +3694,6 @@ Function modeled after x_draw_glyph_string_box ().
   /* Draw BG: if we need larger area than image itself cleared, do that,
      otherwise, since we composite the image under NS (instead of mucking
      with its background color), we must clear just the image area.  */
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-       face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
 
   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
 
@@ -3812,16 +3764,8 @@ Function modeled after x_draw_glyph_string_box ().
 
   if (s->hl == DRAW_CURSOR)
     {
-    [FRAME_CURSOR_COLOR (s->f) set];
-    if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
+      [FRAME_CURSOR_COLOR (s->f) set];
       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
-    else
-      /* Currently on NS img->mask is always 0.  Since
-         get_window_cursor_type specifies a hollow box cursor when on
-         a non-masked image we never reach this clause.  But we put it
-         in, in anticipation of better support for image masks on
-         NS.  */
-      tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
     }
   else
     {
@@ -3873,66 +3817,35 @@ Function modeled after x_draw_glyph_string_box ().
 static void
 ns_dumpglyphs_stretch (struct glyph_string *s)
 {
-  NSRect r[2];
   NSRect glyphRect;
-  int n;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *fgCol, *bgCol;
 
   if (!s->background_filled_p)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
 
-      if (s->hl == DRAW_MOUSE_FACE)
-        {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-        }
-      else
-        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+      face = s->face;
 
       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
 
-      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
-
-      [bgCol set];
-
-      /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
-         overwriting cursor (usually when cursor on a tab) */
       if (s->hl == DRAW_CURSOR)
-        {
-          CGFloat x, width;
+	{
+	  fgCol = bgCol;
+	  bgCol = FRAME_CURSOR_COLOR (s->f);
+	}
 
-          /* FIXME: This looks like it will only work for left to
-             right languages.  */
-          x = NSMinX (glyphRect);
-          width = s->w->phys_cursor_width;
-          glyphRect.size.width -= width;
-          glyphRect.origin.x += width;
+      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
 
-          NSRectFill (glyphRect);
+      [bgCol set];
 
-          /* Draw overlining, etc. on the cursor. */
-          if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-            ns_draw_text_decoration (s, face, bgCol, width, x);
-          else
-            ns_draw_text_decoration (s, face, fgCol, width, x);
-        }
-      else
-        {
-          NSRectFill (glyphRect);
-        }
+      NSRectFill (glyphRect);
 
       /* Draw overlining, etc. on the stretch glyph (or the part
          of the stretch glyph after the cursor). */
       ns_draw_text_decoration (s, face, fgCol, NSWidth (glyphRect),
                                NSMinX (glyphRect));
 
-      ns_unfocus (s->f);
       s->background_filled_p = 1;
     }
 }
@@ -3941,7 +3854,7 @@ overwriting cursor (usually when cursor on a tab) */
 static void
 ns_draw_glyph_string_foreground (struct glyph_string *s)
 {
-  int x, flags;
+  int x;
   struct font *font = s->font;
 
   /* If first glyph of S has a left box line, start drawing the text
@@ -3952,15 +3865,9 @@ overwriting cursor (usually when cursor on a tab) */
   else
     x = s->x;
 
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
   font->driver->draw
     (s, s->cmp_from, s->nchars, x, s->ybase,
-     (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
-     || flags == NS_DUMPGLYPH_MOUSEFACE);
+     !s->for_overlaps && !s->background_filled_p);
 }
 
 
@@ -4067,9 +3974,9 @@ overwriting cursor (usually when cursor on a tab) */
   struct font *font = s->face->font;
   if (! font) font = FRAME_FONT (s->f);
 
-  NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
+  NSTRACE ("ns_draw_glyph_string (hl = %u)", s->hl);
 
-  if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
+  if (s->next && s->right_overhang && !s->for_overlaps)
     {
       int width;
       struct glyph_string *next;
@@ -4106,14 +4013,21 @@ overwriting cursor (usually when cursor on a tab) */
       box_drawn_p = 1;
     }
 
+  n = ns_get_glyph_string_clip_rect (s, r);
+
+  if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+      && !s->clip_tail
+      && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+	  || (s->next && s->next->hl != s->hl && s->right_overhang)))
+    r[0] = NSIntersectionRect (r[0], NSMakeRect (s->x, s->y, s->width, s->height));
+
+  ns_focus (s->f, r, n);
+
   switch (s->first_glyph->type)
     {
 
     case IMAGE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
       ns_dumpglyphs_image (s, r[0]);
-      ns_unfocus (s->f);
       break;
 
     case XWIDGET_GLYPH:
@@ -4126,57 +4040,36 @@ overwriting cursor (usually when cursor on a tab) */
 
     case CHAR_GLYPH:
     case COMPOSITE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
-      if (s->for_overlaps || (s->cmp_from > 0
-			      && ! s->first_glyph->u.cmp.automatic))
-        s->background_filled_p = 1;
-      else
-        ns_maybe_dumpglyphs_background
-          (s, s->first_glyph->type == COMPOSITE_GLYPH);
-
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
       {
-        BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	if (s->for_overlaps || (isComposite
+				&& (s->cmp_from > 0
+				    && ! s->first_glyph->u.cmp.automatic)))
+	  s->background_filled_p = 1;
+	else
+	  ns_maybe_dumpglyphs_background
+	    (s, s->first_glyph->type == COMPOSITE_GLYPH);
 
-        if (isComposite)
-          ns_draw_composite_glyph_string_foreground (s);
-        else
-          ns_draw_glyph_string_foreground (s);
-      }
+	if (isComposite)
+	  ns_draw_composite_glyph_string_foreground (s);
+	else
+	  ns_draw_glyph_string_foreground (s);
 
-      {
-        NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
-                        ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
-                                                   s->f)
-                        : FRAME_FOREGROUND_COLOR (s->f));
-        [col set];
-
-        /* Draw underline, overline, strike-through. */
-        ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	{
+	  NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
+			  ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
+						     s->f)
+			  : FRAME_FOREGROUND_COLOR (s->f));
+	  [col set];
+
+	  /* Draw underline, overline, strike-through. */
+	  ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	}
       }
 
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
-      ns_unfocus (s->f);
       break;
 
     case GLYPHLESS_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
       if (s->for_overlaps || (s->cmp_from > 0
 			      && ! s->first_glyph->u.cmp.automatic))
         s->background_filled_p = 1;
@@ -4186,7 +4079,6 @@ overwriting cursor (usually when cursor on a tab) */
       /* ... */
       /* Not yet implemented.  */
       /* ... */
-      ns_unfocus (s->f);
       break;
 
     default:
@@ -4195,13 +4087,92 @@ overwriting cursor (usually when cursor on a tab) */
 
   /* Draw box if not done already.  */
   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
+    ns_dumpglyphs_box_or_relief (s);
+
+  ns_unfocus (s->f);
+
+  /* Draw surrounding overhangs. */
+  if (s->prev)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-      ns_dumpglyphs_box_or_relief (s);
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *prev;
+
+      for (prev = s->prev; prev; prev = prev->prev)
+	if (prev->hl != s->hl
+	    && prev->x + prev->width + prev->right_overhang > s->x)
+	  {
+	    /* As prev was drawn while clipped to its own area, we
+	       must draw the right_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = prev->hl;
+	    struct face *save_face = prev->face;
+
+	    prev->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    prev->num_clips = 1;
+	    prev->hl = s->hl;
+	    if (prev->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (prev);
+	    else
+	      ns_draw_composite_glyph_string_foreground (prev);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    prev->hl = save;
+	    prev->face = save_face;
+	    prev->num_clips = 0;
+	  }
       ns_unfocus (s->f);
     }
 
+  if (s->next)
+    {
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *next;
+
+      for (next = s->next; next; next = next->next)
+	if (next->hl != s->hl
+	    && next->x - next->left_overhang < s->x + s->width)
+	  {
+	    /* As next will be drawn while clipped to its own area,
+	       we must draw the left_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = next->hl;
+	    struct face *save_face = next->face;
+
+	    next->hl = s->hl;
+	    next->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    next->num_clips = 1;
+	    if (next->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (next);
+	    else
+	      ns_draw_composite_glyph_string_foreground (next);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    next->hl = save;
+	    next->num_clips = 0;
+	    next->face = save_face;
+	    next->clip_head = next;
+	    next->background_filled_p = 0;
+	  }
+      ns_unfocus (s->f);
+    }
   s->num_clips = 0;
 }
 
diff --git a/src/xdisp.c b/src/xdisp.c
index dffbdb8d1e..0d95e70212 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -29298,7 +29298,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
   for (s = head; s; s = s->next)
     FRAME_RIF (f)->draw_glyph_string (s);
 
-#ifndef HAVE_NS
   /* When focus a sole frame and move horizontally, this clears on_p
      causing a failure to erase prev cursor position. */
   if (area == TEXT_AREA
@@ -29317,7 +29316,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
       notice_overwritten_cursor (w, TEXT_AREA, x0, x1,
 				 row->y, MATRIX_ROW_BOTTOM_Y (row));
     }
-#endif
 
   /* Value is the x-position up to which drawn, relative to AREA of W.
      This doesn't include parts drawn because of overhangs.  */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31 10:34                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31 10:54                   ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-31 10:59                   ` Alan Third
  2021-10-31 11:20                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31 11:26                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 2 replies; 22+ messages in thread
From: Alan Third @ 2021-10-31 10:59 UTC (permalink / raw)
  To: Po Lu; +Cc: 51411

On Sun, Oct 31, 2021 at 06:34:15PM +0800, Po Lu wrote:
> Alan Third <alan@idiocy.org> writes:
> 
> > I think what you'll need to do is union the two rectangles and then
> > clip to that, rather than clipping them both separately. That will
> > then provide the same clipping as the NSClipRect code does.
> 
> I meant to say that DPSrectclip intersects (IOW, behaves just as
> NSClipRect does).  Unless that's incorrect, I think what I'm doing right
> now should work fine.

When there are two rectangles we use NSRectClipList, which behaves a
little differently. It uses the union of the two rectangles to
intersect with the existing clipping. So you'll have to do something
like

    u = NSUnionRect (r[0], r[1]);
    DPSrectclip (ctx, NSMinX (u), NSMinY (u),
		   NSWidth (u), NSHeight (u));

> > I must be failing to communicate well, we keep seeming to
> > misunderstand each other.
> 
> It could be my problem as well: my reading comprehension is nowhere near
> as good as I would rather it be.  Thanks a lot for tolerating it.

It turns out in this case I'm getting confused over what's changed in
each patch! Entirely my fault.

> > You still need to focus, however you don't have to clip when you
> > focus.
> 
> After I unfocus here:
> 
>   /* Draw box if not done already.  */
>   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
>     ns_dumpglyphs_box_or_relief (s);
> 
> -> ns_unfocus (s->f);
> 
> I make sure to focus again if an overhang might be drawn again (inside
> if (s->prev) and if (s->next)), like so:
> 
>   /* Draw surrounding overhangs. */
>   if (s->prev)
>     {
> ->    ns_focus (s->f, NULL, 0);
>       struct glyph_string *prev;
> 
> There is, of course, a matching unfocus.  Is that not adequate, and if
> so, could you please explain how?

Sorry, I've got confused. What you're doing here now is good.

I noticed that you removed all calls to ns_focus in
ns_draw_window_cursor and thought you'd removed all calls to ns_focus,
but now I see that's an older change.

Is that a good idea? I think ns_draw_window_cursor is sometimes called
without matching ns_update_begin/end calls, so it needs to focus, or am I
misunderstanding the flow?

-- 
Alan Third





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31 10:59                   ` Alan Third
@ 2021-10-31 11:20                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31 11:26                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 0 replies; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-31 11:20 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

Alan Third <alan@idiocy.org> writes:

> When there are two rectangles we use NSRectClipList, which behaves a
> little differently. It uses the union of the two rectangles to
> intersect with the existing clipping. So you'll have to do something
> like
>
>     u = NSUnionRect (r[0], r[1]);
>     DPSrectclip (ctx, NSMinX (u), NSMinY (u),
> 		   NSWidth (u), NSHeight (u));

Yes, thanks, please see my other mail.  Thanks!





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31 10:59                   ` Alan Third
  2021-10-31 11:20                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-31 11:26                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-10-31 12:55                       ` Alan Third
  1 sibling, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-31 11:26 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

Alan Third <alan@idiocy.org> writes:

> I noticed that you removed all calls to ns_focus in
> ns_draw_window_cursor and thought you'd removed all calls to ns_focus,
> but now I see that's an older change.
>
> Is that a good idea? I think ns_draw_window_cursor is sometimes called
> without matching ns_update_begin/end calls, so it needs to focus, or am I
> misunderstanding the flow?

I don't know if ns_draw_window_cursor is called outside
ns_update_begin/end, but if that's the case then I'm not sure how, for
instance, double buffering would work on the X port.

Thanks.





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31 11:26                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-10-31 12:55                       ` Alan Third
  2021-10-31 13:12                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 22+ messages in thread
From: Alan Third @ 2021-10-31 12:55 UTC (permalink / raw)
  To: Po Lu; +Cc: 51411

On Sun, Oct 31, 2021 at 07:26:05PM +0800, Po Lu wrote:
> Alan Third <alan@idiocy.org> writes:
> 
> > I noticed that you removed all calls to ns_focus in
> > ns_draw_window_cursor and thought you'd removed all calls to ns_focus,
> > but now I see that's an older change.
> >
> > Is that a good idea? I think ns_draw_window_cursor is sometimes called
> > without matching ns_update_begin/end calls, so it needs to focus, or am I
> > misunderstanding the flow?
> 
> I don't know if ns_draw_window_cursor is called outside
> ns_update_begin/end, but if that's the case then I'm not sure how, for
> instance, double buffering would work on the X port.

Some drawing functions are definitely used outside
ns_update_begin/end, at least in the NS port.

Could you please put ns_focus calls back into ns_draw_window_cursor,
just in case they are needed, and we can get these changes put into
master and then maybe look at whether we need all these ns_focus calls
as a separate task (it would certainly be nice if we could get rid of
a few of them!).
-- 
Alan Third





^ permalink raw reply	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31 12:55                       ` Alan Third
@ 2021-10-31 13:12                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-05  7:44                           ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-10-31 13:12 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

[-- Attachment #1: Type: text/plain, Size: 651 bytes --]

Alan Third <alan@idiocy.org> writes:

> Some drawing functions are definitely used outside
> ns_update_begin/end, at least in the NS port.
>
> Could you please put ns_focus calls back into ns_draw_window_cursor,
> just in case they are needed, and we can get these changes put into
> master and then maybe look at whether we need all these ns_focus calls
> as a separate task (it would certainly be nice if we could get rid of
> a few of them!).

Thanks, here's the updated patch (along with the other patch for the
improvements not related to text display).  I will be sure to take a
look at the rest of the ns_focus calls later, if I get the time.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-font-display-on-NS-port.patch --]
[-- Type: text/x-patch, Size: 78574 bytes --]

From 63b5b710b88d9cf3092a8fda33966c97704767d2 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:44:03 +0800
Subject: [PATCH 1/2] Improve font display on NS port

* src/nsfns.m (Fx_create_frame): Use "fixed" for the default font on
GNUstep.

* src/nsfont.m (LCD_SMOOTHING_MARGIN, ns_escape_name)
(ns_unescape_name, ns_attribute_fvalue)
(STYLE_REF): Remove unused defines and functions.

(struct ns_glyph_layout, enum lgstring_direction).
(enum gs_font_slant, enum gs_font_weight, enum gs_font_width)
(enum gs_specified, struct gs_font_data): New enumerators and
structures.

(ns_font_descs_match_p)
(ns_done_font_data, ns_get_font_data): New functions.
(ns_glyph_metrics): Stop escaping names.

(ns_spec_to_descriptor): Fix font descriptor creation for symbolic
font spec entires.
(ns_descriptor_to_entity): Create entries with the correct symbolic
styles.

(ns_fallback_entity): Fix fallback entity selection.
(ns_findfonts): Use our own font matcher instead of the broken GNUstep
matcher.

(ns_list_family): Remove obsolete comment.
(nsfont_open): Remove obsolete code, comments, and synthItal logic
which doesn't work on GNUstep.

(nsfont_encode_char): Use a type that can fit NSGlyph
(nsfont_draw): Chose correct font, remove obsolete mouse face logic,
obsolete comments, and switch to using glyph-based drawing instead of
character-based drawing.

(ns_font_shape, nsfont_shape): New functions.

(ns_uni_to_glyphs_1): New function.
(ns_uni_to_glyphs): Return glyphs instead of unicode codepoints.

(ns_glyph_metrics): Use NSGlyphs instead of unicode codepoints and fix
left bearing, right bearing, ascent and descent computation.

(struct nsfont_driver): Add shaping capability.

* src/nsterm.h (struct nsfont_info): Use unsigned int for glyph cache.

* src/nsterm.c (ns_focus): Set DPS clipping on GNUstep.

(ns_compute_glyph_string_overhangs): Fix overhang computation by using
xterm code.
(ns_draw_window_cursor): Simplify cursor drawing.
(ns_maybe_dumpglyphs_background): Test for cursor HL and remove
obsolete mouse face logic.
(ns_dumpglyphs_image)
(ns_dumpglyphs_box_or_relief): Rectify for new cursor logic.
(ns_dumpglyphs_stretch): Rectify for new cursor logic and rely on
ns_draw_glyph_string to set focus.
(ns_draw_glyph_string_foreground): Remove mouse face logic.
(ns_draw_glyph_strings): Implement overhangs, remove obsolete
comment, and always focus before dumping glyphs.
(ns_draw_text_decoration): Add condition for DRAW_CURSOR and simplify
color selection.
(ns_define_frame_cursor): Remove nonsensical code (define_frame_cursor
has nothing to do with the text cursor, aka caret).

* src/xdisp.c (draw_glyphs): Enable code for NS port to fix mouse face
cursor display.

* src/macfont.m (get_cgcolor_from_nscolor): New function.
(macfont_draw): Remove obsolete mouse-face code and enable cursor
display.
---
 src/macfont.m |   36 +-
 src/nsfns.m   |    6 +
 src/nsfont.m  | 1215 +++++++++++++++++++++++++++++++++++--------------
 src/nsterm.h  |    2 +-
 src/nsterm.m  |  416 ++++++++---------
 src/xdisp.c   |    2 -
 6 files changed, 1095 insertions(+), 582 deletions(-)

diff --git a/src/macfont.m b/src/macfont.m
index 78ed5d53f3..1426cae6dc 100644
--- a/src/macfont.m
+++ b/src/macfont.m
@@ -613,6 +613,21 @@ static void mac_font_get_glyphs_for_variants (CFDataRef, UTF32Char,
   return cgColor;
 }
 
+static CGColorRef
+get_cgcolor_from_nscolor (NSColor *nsColor, struct frame *f)
+{
+  [nsColor set];
+  CGColorSpaceRef colorSpace = [[nsColor colorSpace] CGColorSpace];
+  NSInteger noc = [nsColor numberOfComponents];
+  CGFloat *components = xmalloc (sizeof(CGFloat)*(1+noc));
+  CGColorRef cgColor;
+
+  [nsColor getComponents: components];
+  cgColor = CGColorCreate (colorSpace, components);
+  xfree (components);
+  return cgColor;
+}
+
 #define CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND(context, face, f)        \
   do {                                                                  \
     CGColorRef refcol_ = get_cgcolor (NS_FACE_FOREGROUND (face), f);    \
@@ -2911,14 +2926,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
 
   if (!CGRectIsNull (background_rect))
     {
-      if (s->hl == DRAW_MOUSE_FACE)
+      if (s->hl == DRAW_CURSOR)
         {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-				       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_CURSOR_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
         }
-      CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
       CGContextFillRects (context, &background_rect, 1);
     }
 
@@ -2927,7 +2942,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
       CGAffineTransform atfm;
 
       CGContextScaleCTM (context, 1, -1);
-      CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
+      if (s->hl == DRAW_CURSOR)
+        {
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_BACKGROUND_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
+        }
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
       if (macfont_info->synthetic_italic_p)
         atfm = synthetic_italic_atfm;
       else
diff --git a/src/nsfns.m b/src/nsfns.m
index 797d0ce782..f4d8172246 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -1236,6 +1236,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                          "fontBackend", "FontBackend", RES_TYPE_STRING);
 
   {
+#ifdef NS_IMPL_COCOA
     /* use for default font name */
     id font = [NSFont userFixedPitchFontOfSize: -1.0]; /* default */
     gui_default_parameter (f, parms, Qfontsize,
@@ -1250,6 +1251,11 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                            build_string (fontname),
                            "font", "Font", RES_TYPE_STRING);
     xfree (fontname);
+#else
+    gui_default_parameter (f, parms, Qfont,
+                           build_string ("fixed"),
+                           "font", "Font", RES_TYPE_STRING);
+#endif
   }
   unblock_input ();
 
diff --git a/src/nsfont.m b/src/nsfont.m
index 5a9cdfebc0..b3224629f0 100644
--- a/src/nsfont.m
+++ b/src/nsfont.m
@@ -1,4 +1,4 @@
-/* Font back-end driver for the NeXT/Open/GNUstep and macOS window system.
+/* Font back-end driver for the GNUstep window system.
    See font.h
    Copyright (C) 2006-2021 Free Software Foundation, Inc.
 
@@ -38,47 +38,269 @@
 #include "termchar.h"
 #include "pdumper.h"
 
-/* TODO: Drop once we can assume gnustep-gui 0.17.1.  */
+#import <Foundation/NSException.h>
 #import <AppKit/NSFontDescriptor.h>
+#import <AppKit/NSLayoutManager.h>
+#import <GNUstepGUI/GSLayoutManager.h>
+#import <GNUstepGUI/GSFontInfo.h>
 
 #define NSFONT_TRACE 0
-#define LCD_SMOOTHING_MARGIN 2
 
-/* Font glyph and metrics caching functions, implemented at end.  */
-static void ns_uni_to_glyphs (struct nsfont_info *font_info,
-                              unsigned char block);
-static void ns_glyph_metrics (struct nsfont_info *font_info,
-                              unsigned char block);
+/* Structure used by GS `shape' functions for storing layout
+   information for each glyph.  Borrowed from macfont.h.  */
+struct ns_glyph_layout
+{
+  /* Range of indices of the characters composed into the group of
+     glyphs that share the cursor position with this glyph.  The
+     members `location' and `length' are in UTF-16 indices.  */
+  NSRange comp_range;
 
-#define INVALID_GLYPH 0xFFFF
+  /* UTF-16 index in the source string for the first character
+     associated with this glyph.  */
+  NSUInteger string_index;
 
-/* ==========================================================================
+  /* Horizontal and vertical adjustments of glyph position.  The
+     coordinate space is that of Core Text.  So, the `baseline_delta'
+     value is negative if the glyph should be placed below the
+     baseline.  */
+  CGFloat advance_delta, baseline_delta;
 
-    Utilities
+  /* Typographical width of the glyph.  */
+  CGFloat advance;
 
-   ========================================================================== */
+  /* Glyph ID of the glyph.  */
+  NSGlyph glyph_id;
+};
+
+
+enum lgstring_direction
+  {
+    DIR_R2L = -1, DIR_UNKNOWN = 0, DIR_L2R = 1
+  };
+
+enum gs_font_slant
+  {
+    GS_FONT_SLANT_ITALIC,
+    GS_FONT_SLANT_REVERSE_ITALIC,
+    GS_FONT_SLANT_NORMAL
+  };
+
+enum gs_font_weight
+  {
+    GS_FONT_WEIGHT_LIGHT,
+    GS_FONT_WEIGHT_BOLD,
+    GS_FONT_WEIGHT_NORMAL
+  };
+
+enum gs_font_width
+  {
+    GS_FONT_WIDTH_CONDENSED,
+    GS_FONT_WIDTH_EXPANDED,
+    GS_FONT_WIDTH_NORMAL
+  };
+
+enum gs_specified
+  {
+    GS_SPECIFIED_SLANT = 1,
+    GS_SPECIFIED_WEIGHT = 1 << 1,
+    GS_SPECIFIED_WIDTH = 1 << 2,
+    GS_SPECIFIED_FAMILY = 1 << 3,
+    GS_SPECIFIED_SPACING = 1 << 4
+  };
 
+struct gs_font_data
+{
+  int specified;
+  enum gs_font_slant slant;
+  enum gs_font_weight weight;
+  enum gs_font_width width;
+  bool monospace_p;
+  char *family_name;
+};
 
-/* Replace spaces w/another character so emacs core font parsing routines
-   aren't thrown off.  */
 static void
-ns_escape_name (char *name)
+ns_done_font_data (struct gs_font_data *data)
 {
-  for (; *name; name++)
-    if (*name == ' ')
-      *name = '_';
+  if (data->specified & GS_SPECIFIED_FAMILY)
+    xfree (data->family_name);
 }
 
-
-/* Reconstruct spaces in a font family name passed through emacs.  */
 static void
-ns_unescape_name (char *name)
+ns_get_font_data (NSFontDescriptor *desc, struct gs_font_data *dat)
 {
-  for (; *name; name++)
-    if (*name == '_')
-      *name = ' ';
+  NSNumber *tem;
+  NSFontSymbolicTraits traits = [desc symbolicTraits];
+  NSDictionary *dict = [desc objectForKey: NSFontTraitsAttribute];
+  NSString *family = [desc objectForKey: NSFontFamilyAttribute];
+
+  dat->specified = 0;
+
+  if (family != nil)
+    {
+      dat->specified |= GS_SPECIFIED_FAMILY;
+      dat->family_name = xstrdup ([family cStringUsingEncoding: NSUTF8StringEncoding]);
+    }
+
+  tem = [desc objectForKey: NSFontFixedAdvanceAttribute];
+
+  if ((tem != nil && [tem boolValue] != NO)
+      || (traits & NSFontMonoSpaceTrait))
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = true;
+    }
+  else if (tem != nil && [tem boolValue] == NO)
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = false;
+    }
+
+  if (traits & NSFontBoldTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WEIGHT;
+      dat->weight = GS_FONT_WEIGHT_BOLD;
+    }
+
+  if (traits & NSFontItalicTrait)
+    {
+      dat->specified |= GS_SPECIFIED_SLANT;
+      dat->slant = GS_FONT_SLANT_ITALIC;
+    }
+
+  if (traits & NSFontCondensedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_CONDENSED;
+    }
+  else if (traits & NSFontExpandedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_EXPANDED;
+    }
+
+  if (dict != nil)
+    {
+      tem = [dict objectForKey: NSFontSlantTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_SLANT;
+
+	  dat->slant = [tem floatValue] > 0
+	    ? GS_FONT_SLANT_ITALIC
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_SLANT_REVERSE_ITALIC
+	       : GS_FONT_SLANT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWeightTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WEIGHT;
+
+	  dat->weight = [tem floatValue] > 0
+	    ? GS_FONT_WEIGHT_BOLD
+	    : ([tem floatValue] < -0.4f
+	       ? GS_FONT_WEIGHT_LIGHT
+	       : GS_FONT_WEIGHT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWidthTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WIDTH;
+
+	  dat->width = [tem floatValue] > 0
+	    ? GS_FONT_WIDTH_EXPANDED
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_WIDTH_NORMAL
+	       : GS_FONT_WIDTH_CONDENSED);
+	}
+    }
+}
+
+static bool
+ns_font_descs_match_p (NSFontDescriptor *desc, NSFontDescriptor *target)
+{
+  struct gs_font_data dat;
+  struct gs_font_data t;
+
+  ns_get_font_data (desc, &dat);
+  ns_get_font_data (target, &t);
+
+  if (!(t.specified & GS_SPECIFIED_WIDTH))
+    t.width = GS_FONT_WIDTH_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_WEIGHT))
+    t.weight = GS_FONT_WEIGHT_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_SPACING))
+    t.monospace_p = false;
+  if (!(t.specified & GS_SPECIFIED_SLANT))
+    t.slant = GS_FONT_SLANT_NORMAL;
+
+  if (!(t.specified & GS_SPECIFIED_FAMILY))
+    emacs_abort ();
+
+  bool match_p = true;
+
+  if (dat.specified & GS_SPECIFIED_WIDTH
+      && dat.width != t.width)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_WEIGHT
+      && dat.weight != t.weight)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SPACING
+      && dat.monospace_p != t.monospace_p)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SLANT
+      && dat.monospace_p != t.monospace_p)
+    {
+      if (NSFONT_TRACE)
+	printf ("Matching monospace for %s: %d %d\n",
+		t.family_name, dat.monospace_p,
+		t.monospace_p);
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_FAMILY
+      && strcmp (dat.family_name, t.family_name))
+    match_p = false;
+
+ gout:
+  ns_done_font_data (&dat);
+  ns_done_font_data (&t);
+
+  return match_p;
 }
 
+/* Font glyph and metrics caching functions, implemented at end.  */
+static void ns_uni_to_glyphs (struct nsfont_info *font_info,
+                              unsigned char block);
+static void ns_glyph_metrics (struct nsfont_info *font_info,
+                              unsigned int block);
+
+#define INVALID_GLYPH 0xFFFF
+
+/* ==========================================================================
+
+    Utilities
+
+   ========================================================================== */
+
 
 /* Extract family name from a font spec.  */
 static NSString *
@@ -91,66 +313,116 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
     {
       char *tmp = xlispstrdup (SYMBOL_NAME (tem));
       NSString *family;
-      ns_unescape_name (tmp);
       family = [NSString stringWithUTF8String: tmp];
       xfree (tmp);
       return family;
     }
 }
 
-
-/* Return 0 if attr not set, else value (which might also be 0).
-   On Leopard 0 gets returned even on descriptors where the attribute
-   was never set, so there's no way to distinguish between unspecified
-   and set to not have.  Callers should assume 0 means unspecified.  */
-static float
-ns_attribute_fvalue (NSFontDescriptor *fdesc, NSString *trait)
-{
-    NSDictionary *tdict = [fdesc objectForKey: NSFontTraitsAttribute];
-    NSNumber *val = [tdict objectForKey: trait];
-    return val == nil ? 0.0F : [val floatValue];
-}
-
-
 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
    to NSFont descriptor.  Information under extra only needed for matching.  */
-#define STYLE_REF 100
 static NSFontDescriptor *
 ns_spec_to_descriptor (Lisp_Object font_spec)
 {
     NSFontDescriptor *fdesc;
     NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
-    NSMutableDictionary *tdict = [NSMutableDictionary new];
     NSString *family = ns_get_family (font_spec);
-    float n;
-
-    /* Add each attr in font_spec to fdAttrs.  */
-    n = min (FONT_WEIGHT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWeightTrait];
-    n = min (FONT_SLANT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontSlantTrait];
-    n = min (FONT_WIDTH_NUMERIC (font_spec), 200);
-    if (n > -1 && (n > STYLE_REF + 10 || n < STYLE_REF - 10))
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWidthTrait];
-    if ([tdict count] > 0)
-	[fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+    NSMutableDictionary *tdict = [NSMutableDictionary new];
 
-    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
-               retain] autorelease];
+    Lisp_Object tem;
+
+    tem = FONT_SLANT_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontSlantTrait];
+	else if (EQ (tem, intern ("reverse-italic")) ||
+		 EQ (tem, intern ("reverse-oblique")))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontSlantTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontSlantTrait];
+      }
+
+    tem = FONT_WIDTH_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qcondensed))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontWidthTrait];
+	else if (EQ (tem, Qexpanded))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontWidthTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontWidthTrait];
+      }
+
+    tem = FONT_WEIGHT_SYMBOLIC (font_spec);
+
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qbold))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else if (EQ (tem, Qlight))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		      forKey: NSFontWeightTrait];
+	  }
+      }
+
+    tem = AREF (font_spec, FONT_SPACING_INDEX);
 
     if (family != nil)
       {
-        NSFontDescriptor *fdesc2 = [fdesc fontDescriptorWithFamily: family];
-        fdesc = [[fdesc2 retain] autorelease];
+	[fdAttrs setObject: family
+		    forKey: NSFontFamilyAttribute];
       }
 
-    [fdAttrs release];
+    if (FIXNUMP (tem))
+      {
+	if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:YES]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+	else
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:NO]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+      }
+
+    /* Handle special families such as ``fixed'' or ``Sans Serif''.  */
+
+    if ([family isEqualToString: @"fixed"])
+      {
+	[fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+    else if ([family isEqualToString: @"Sans Serif"])
+      {
+	[fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+
+    [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+
+    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
+               retain] autorelease];
+
     [tdict release];
+    [fdAttrs release];
     return fdesc;
 }
 
@@ -161,61 +433,64 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
                          Lisp_Object extra,
                          const char *style)
 {
-    Lisp_Object font_entity = font_make_entity ();
-    /*   NSString *psName = [desc postscriptName]; */
-    NSString *family = [desc objectForKey: NSFontFamilyAttribute];
-    unsigned int traits = [desc symbolicTraits];
-    char *escapedFamily;
-
-    /* Shouldn't happen, but on Tiger fallback desc gets name but no family.  */
-    if (family == nil)
-      family = [desc objectForKey: NSFontNameAttribute];
-    if (family == nil)
-      family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-
-    escapedFamily = xstrdup ([family UTF8String]);
-    ns_escape_name (escapedFamily);
-
-    ASET (font_entity, FONT_TYPE_INDEX, Qns);
-    ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
-    ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
-    ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
-    ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
-
-    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    traits & NSFontBoldTrait ? Qbold : Qmedium);
-/*    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    make_fixnum (100 + 100
-			* ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    traits & NSFontItalicTrait ? Qitalic : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-                    traits & NSFontCondensedTrait ? Qcondensed :
-                    traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
-
-    ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_SPACING_INDEX,
-	  make_fixnum([desc symbolicTraits] & NSFontMonoSpaceTrait
-	      ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
-
-    ASET (font_entity, FONT_EXTRA_INDEX, extra);
-    ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+  Lisp_Object font_entity = font_make_entity ();
+  struct gs_font_data data;
+  ns_get_font_data (desc, &data);
+
+  ASET (font_entity, FONT_TYPE_INDEX, Qns);
+  ASET (font_entity, FONT_FOUNDRY_INDEX, Qns);
+  if (data.specified & GS_SPECIFIED_FAMILY)
+    ASET (font_entity, FONT_FAMILY_INDEX, intern (data.family_name));
+  ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
+  ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
+
+  if (data.specified & GS_SPECIFIED_WEIGHT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
+		      data.weight == GS_FONT_WEIGHT_BOLD
+		      ? Qbold : (data.weight == GS_FONT_WEIGHT_LIGHT
+				 ? Qlight : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX, Qnormal);
 
-    if (NSFONT_TRACE)
-      {
-	fputs ("created font_entity:\n    ", stderr);
-	debug_print (font_entity);
-      }
+  if (data.specified & GS_SPECIFIED_SLANT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
+		      data.slant == GS_FONT_SLANT_ITALIC
+		      ? Qitalic : (data.slant == GS_FONT_SLANT_REVERSE_ITALIC
+				   ? intern ("reverse-italic") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX, Qnormal);
+
+  if (data.specified & GS_SPECIFIED_WIDTH)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
+		      data.width == GS_FONT_WIDTH_CONDENSED
+		      ? Qcondensed : (data.width == GS_FONT_WIDTH_EXPANDED
+				      ? intern ("expanded") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX, Qnormal);
 
-    xfree (escapedFamily);
-    return font_entity;
+  ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_SPACING_INDEX,
+	make_fixnum ((data.specified & GS_SPECIFIED_WIDTH && data.monospace_p)
+		     ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
+
+  ASET (font_entity, FONT_EXTRA_INDEX, extra);
+  ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+
+  if (NSFONT_TRACE)
+    {
+      fputs ("created font_entity:\n    ", stderr);
+      debug_print (font_entity);
+    }
+
+  ns_done_font_data (&data);
+  return font_entity;
 }
 
 
@@ -223,8 +498,7 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
 static Lisp_Object
 ns_fallback_entity (void)
 {
-  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
-      fontDescriptor], Qnil, NULL);
+  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 1] fontDescriptor], Qnil, NULL);
 }
 
 
@@ -510,21 +784,20 @@ but also for ascii (which causes unnecessary font substitution).  */
     return families;
 }
 
+/* GNUstep font matching is very mediocre (it can't even compare
+   symbolic styles correctly), which is why our own font matching
+   mechanism must be implemented.  */
 
-/* Implementation for list() and match().  List() can return nil, match()
-must return something.  Strategy is to drop family name from attribute
-matching set for match.  */
+/* Implementation for list and match.  */
 static Lisp_Object
 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
 {
     Lisp_Object tem, list = Qnil;
-    NSFontDescriptor *fdesc, *desc;
-    NSMutableSet *fkeys;
-    NSArray *matchingDescs;
-    NSEnumerator *dEnum;
-    NSString *family;
+    NSFontDescriptor *fdesc;
+    NSArray *all_descs;
+    GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
+
     NSSet *cFamilies;
-    BOOL foundItal = NO;
 
     block_input ();
     if (NSFONT_TRACE)
@@ -537,43 +810,22 @@ but also for ascii (which causes unnecessary font substitution).  */
     cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
 
     fdesc = ns_spec_to_descriptor (font_spec);
-    fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
-    if (isMatch)
-	[fkeys removeObject: NSFontFamilyAttribute];
-
-    matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
+    all_descs = [enumerator availableFontDescriptors];
 
-    if (NSFONT_TRACE)
-	NSLog(@"Got desc %@ and found %lu matching fonts from it: ", fdesc,
-	      (unsigned long)[matchingDescs count]);
-
-    for (dEnum = [matchingDescs objectEnumerator]; (desc = [dEnum nextObject]);)
+    for (NSFontDescriptor *desc in all_descs)
       {
 	if (![cFamilies containsObject:
 	         [desc objectForKey: NSFontFamilyAttribute]])
 	    continue;
+	if (!ns_font_descs_match_p (fdesc, desc))
+	  continue;
+
         tem = ns_descriptor_to_entity (desc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
+				       AREF (font_spec, FONT_EXTRA_INDEX),
                                        NULL);
         if (isMatch)
           return tem;
 	list = Fcons (tem, list);
-	if (fabs (ns_attribute_fvalue (desc, NSFontSlantTrait)) > 0.05)
-	    foundItal = YES;
-      }
-
-    /* Add synthItal member if needed.  */
-    family = [fdesc objectForKey: NSFontFamilyAttribute];
-    if (family != nil && !foundItal && !NILP (list))
-      {
-        NSFontDescriptor *s1 = [NSFontDescriptor new];
-        NSFontDescriptor *sDesc
-          = [[s1 fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
-              fontDescriptorWithFamily: family];
-	list = Fcons (ns_descriptor_to_entity (sDesc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
-					 "synthItal"), list);
-        [s1 release];
       }
 
     unblock_input ();
@@ -652,7 +904,6 @@ Properties to be considered are same as for list().  */
                objectEnumerator];
   while ((family = [families nextObject]))
       list = Fcons (intern ([family UTF8String]), list);
-  /* FIXME: escape the name?  */
 
   if (NSFONT_TRACE)
     fprintf (stderr, "nsfont: list families returning %"pD"d entries\n",
@@ -668,18 +919,15 @@ Properties to be considered are same as for list().  */
 static Lisp_Object
 nsfont_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
 {
-  BOOL synthItal;
-  unsigned int traits = 0;
   struct nsfont_info *font_info;
   struct font *font;
   NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
   NSFontManager *fontMgr = [NSFontManager sharedFontManager];
   NSString *family;
   NSFont *nsfont, *sfont;
-  Lisp_Object tem;
   NSRect brect;
   Lisp_Object font_object;
-  int fixLeopardBug;
+  Lisp_Object tem;
 
   block_input ();
 
@@ -692,42 +940,20 @@ Properties to be considered are same as for list().  */
   if (pixel_size <= 0)
     {
       /* try to get it out of frame params */
-        Lisp_Object tem = get_frame_param (f, Qfontsize);
-        pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
+      tem = get_frame_param (f, Qfontsize);
+      pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
     }
 
   tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
-  synthItal = !NILP (tem) && !strncmp ("synthItal", SSDATA (SYMBOL_NAME (tem)),
-                                       9);
   family = ns_get_family (font_entity);
   if (family == nil)
     family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-  /* Should be > 0.23 as some font descriptors (e.g. Terminus) set to that
-     when setting family in ns_spec_to_descriptor().  */
-  if (ns_attribute_fvalue (fontDesc, NSFontWeightTrait) > 0.50F)
-      traits |= NSBoldFontMask;
-  if (ns_attribute_fvalue (fontDesc, NSFontSlantTrait) > 0.05F)
-      traits |= NSItalicFontMask;
-
-  /* see https://web.archive.org/web/20100201175731/http://cocoadev.com/forums/comments.php?DiscussionID=74 */
-  fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
-  nsfont = [fontMgr fontWithFamily: family
-                            traits: traits weight: fixLeopardBug
-			      size: pixel_size];
-  /* if didn't find, try synthetic italic */
-  if (nsfont == nil && synthItal)
-    {
-      nsfont = [fontMgr fontWithFamily: family
-                                traits: traits & ~NSItalicFontMask
-                                weight: fixLeopardBug size: pixel_size];
-    }
+
+  nsfont = [NSFont fontWithDescriptor: fontDesc
+				 size: pixel_size];
 
   if (nsfont == nil)
-    {
-      message_with_string ("*** Warning: font in family `%s' not found",
-                          build_string ([family UTF8String]), 1);
-      nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
-    }
+    nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
 
   if (NSFONT_TRACE)
     NSLog (@"%@\n", nsfont);
@@ -740,7 +966,7 @@ when setting family in ns_spec_to_descriptor().  */
   if (!font)
     {
       unblock_input ();
-      return Qnil; /* FIXME: other terms do, but returning Qnil causes segfault.  */
+      return Qnil;
     }
 
   font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs);
@@ -781,7 +1007,7 @@ when setting family in ns_spec_to_descriptor().  */
     font_info->name = xstrdup (fontName);
     font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
     font_info->ital =
-      synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
+      ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
 
     /* Metrics etc.; some fonts return an unusually large max advance, so we
        only use it for fonts that have wide characters.  */
@@ -808,8 +1034,6 @@ when setting family in ns_spec_to_descriptor().  */
       lrint (brect.size.width - (CGFloat) font_info->width);
 
     /* set up metrics portion of font struct */
-    font->ascent = lrint([sfont ascender]);
-    font->descent = -lrint(floor(adjusted_descender));
     font->space_width = lrint (ns_char_width (sfont, ' '));
     font->max_width = lrint (font_info->max_bounds.width);
     font->min_width = font->space_width;  /* Approximate.  */
@@ -871,7 +1095,7 @@ when setting family in ns_spec_to_descriptor().  */
 {
   struct nsfont_info *font_info = (struct nsfont_info *)font;
   unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
-  unsigned short g;
+  unsigned int g;
 
   if (c > 0xFFFF)
     return FONT_INVALID_CODE;
@@ -934,51 +1158,23 @@ is false when (FROM > 0 || TO < S->nchars).  */
 static int
 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
              bool with_background)
-/* NOTE: focus and clip must be set.  */
 {
-  static unsigned char cbuf[1024];
-  unsigned char *c = cbuf;
-#if GNUSTEP_GUI_MAJOR_VERSION > 0 || GNUSTEP_GUI_MINOR_VERSION > 22
-  static CGFloat advances[1024];
-  CGFloat *adv = advances;
-#else
-  static float advances[1024];
-  float *adv = advances;
-#endif
+  NSGlyph *c = alloca ((to - from) * sizeof *c);
+
   struct face *face;
   NSRect r;
   struct nsfont_info *font;
-  NSColor *col, *bgCol;
-  unsigned *t = s->char2b;
-  int i, len, flags;
+  NSColor *col;
+  int len = to - from;
   char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
 
   block_input ();
 
-  font = (struct nsfont_info *)s->face->font;
+  font = (struct nsfont_info *) s->font;
   if (font == NULL)
     font = (struct nsfont_info *)FRAME_FONT (s->f);
 
-  /* Select face based on input flags.  */
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
-  switch (flags)
-    {
-    case NS_DUMPGLYPH_CURSOR:
-      face = s->face;
-      break;
-    case NS_DUMPGLYPH_MOUSEFACE:
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-      break;
-    default:
-      face = s->face;
-    }
+  face = s->face;
 
   r.origin.x = s->x;
   if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
@@ -987,91 +1183,24 @@ is false when (FROM > 0 || TO < S->nchars).  */
   r.origin.y = s->y;
   r.size.height = FONT_HEIGHT (font);
 
-  /* Convert UTF-16 (?) to UTF-8 and determine advances.  Note if we just ask
-     NS to render the string, it will come out differently from the individual
-     character widths added up because of layout processing.  */
-  {
-    int cwidth, twidth = 0;
-    int hi, lo;
-    /* FIXME: composition: no vertical displacement is considered.  */
-    t += from; /* advance into composition */
-    for (i = from; i < to; i++, t++)
-      {
-        hi = (*t & 0xFF00) >> 8;
-        lo = *t & 0x00FF;
-        if (isComposite)
-          {
-	    if (!s->first_glyph->u.cmp.automatic)
-		cwidth = s->cmp->offsets[i * 2] /* (H offset) */ - twidth;
-	    else
-	      {
-		Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
-		Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
-		if (NILP (LGLYPH_ADJUSTMENT (glyph)))
-		    cwidth = LGLYPH_WIDTH (glyph);
-		else
-		  {
-		    cwidth = LGLYPH_WADJUST (glyph);
-		    *(adv-1) += LGLYPH_XOFF (glyph);
-		  }
-	      }
-          }
-        else
-          {
-            if (!font->metrics[hi]) /* FIXME: why/how can we need this now?  */
-              ns_glyph_metrics (font, hi);
-            cwidth = font->metrics[hi][lo].width;
-          }
-        twidth += cwidth;
-        *adv++ = cwidth;
-        c += CHAR_STRING (*t, c); /* This converts the char to UTF-8.  */
-      }
-    len = adv - advances;
-    r.size.width = twidth;
-    *c = 0;
-  }
+  for (int i = from; i < to; ++i)
+    c[i] = s->char2b[i];
 
   /* Fill background if requested.  */
   if (with_background && !isComposite)
     {
-      NSRect br = r;
-      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
-      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
-
-      if (s->row->full_width_p)
-        {
-          if (br.origin.x <= fibw + 1 + mbox_line_width)
-            {
-              br.size.width += br.origin.x - mbox_line_width;
-              br.origin.x = mbox_line_width;
-            }
-          if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
-                <= fibw+1)
-            br.size.width += fibw;
-        }
-      if (s->face->box == FACE_NO_BOX)
-        {
-          /* Expand unboxed top row over internal border.  */
-          if (br.origin.y <= fibw + 1 + mbox_line_width)
-            {
-              br.size.height += br.origin.y;
-              br.origin.y = 0;
-            }
-        }
-      else
-        {
-          int correction = abs (s->face->box_horizontal_line_width)+1;
-          br.origin.y += correction;
-          br.size.height -= 2*correction;
-          correction = abs (s->face->box_vertical_line_width)+1;
-          br.origin.x += correction;
-          br.size.width -= 2*correction;
-        }
+      NSRect br = NSMakeRect (x, y - FONT_BASE (s->font),
+			      s->width, FONT_HEIGHT (s->font));
 
       if (!s->face->stipple)
-        [(NS_FACE_BACKGROUND (face) != 0
-          ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-          : FRAME_BACKGROUND_COLOR (s->f)) set];
+	{
+	  if (s->hl != DRAW_CURSOR)
+	    [(NS_FACE_BACKGROUND (face) != 0
+	      ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+	      : FRAME_BACKGROUND_COLOR (s->f)) set];
+	  else
+	    [FRAME_CURSOR_COLOR (s->f) set];
+	}
       else
         {
           struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
@@ -1080,43 +1209,32 @@ is false when (FROM > 0 || TO < S->nchars).  */
       NSRectFill (br);
     }
 
-
   /* set up for character rendering */
   r.origin.y = y;
 
-  col = (NS_FACE_FOREGROUND (face) != 0
-         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
-         : FRAME_FOREGROUND_COLOR (s->f));
-
-  bgCol = (flags != NS_DUMPGLYPH_FOREGROUND ? nil
-           : (NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)));
+  if (s->hl == DRAW_CURSOR)
+    col = FRAME_BACKGROUND_COLOR (s->f);
+  else
+    col = (NS_FACE_FOREGROUND (face) != 0
+	   ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
+	   : FRAME_FOREGROUND_COLOR (s->f));
 
   /* render under GNUstep using DPS */
   {
-    NSGraphicsContext *context = GSCurrentContext ();
-
+    NSGraphicsContext *context = [NSGraphicsContext currentContext];
     DPSgsave (context);
-    [font->nsfont set];
-
-    /* do erase if "foreground" mode */
-    if (bgCol != nil)
+    if (s->clip_head)
       {
-        [bgCol set];
-        DPSmoveto (context, r.origin.x, r.origin.y);
-/*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
-        DPSxshow (context, (const char *) cbuf, advances, len);
-        DPSstroke (context);
-        [col set];
-/*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
+	DPSrectclip (context, s->clip_head->x, 0,
+		     FRAME_PIXEL_WIDTH (s->f),
+		     FRAME_PIXEL_HEIGHT (s->f));
       }
+    [font->nsfont set];
 
     [col set];
 
-    /* draw with DPSxshow () */
     DPSmoveto (context, r.origin.x, r.origin.y);
-    DPSxshow (context, (const char *) cbuf, advances, len);
+    GSShowGlyphs (context, c, len);
     DPSstroke (context);
 
     DPSgrestore (context);
@@ -1126,6 +1244,360 @@ is false when (FROM > 0 || TO < S->nchars).  */
   return to-from;
 }
 
+static NSUInteger
+ns_font_shape (NSFont *font, NSString *string,
+	       struct ns_glyph_layout *glyph_layouts, NSUInteger glyph_len,
+	       enum lgstring_direction dir)
+{
+  NSUInteger i;
+  NSUInteger result = 0;
+  NSTextStorage *textStorage;
+  NSLayoutManager *layoutManager;
+  NSTextContainer *textContainer;
+  NSUInteger stringLength;
+  NSPoint spaceLocation;
+  /* numberOfGlyphs can't actually be 0, but this pacifies GCC */
+  NSUInteger used, numberOfGlyphs = 0;
+
+  textStorage = [[NSTextStorage alloc] initWithString:string];
+  layoutManager = [[NSLayoutManager alloc] init];
+  textContainer = [[NSTextContainer alloc] init];
+
+  /* Append a trailing space to measure baseline position.  */
+  [textStorage appendAttributedString:([[[NSAttributedString alloc]
+                                          initWithString:@" "] autorelease])];
+  [textStorage setFont:font];
+  [textContainer setLineFragmentPadding:0];
+
+  [layoutManager addTextContainer:textContainer];
+  [textContainer release];
+  [textStorage addLayoutManager:layoutManager];
+  [layoutManager release];
+
+  if (!(textStorage && layoutManager && textContainer))
+    emacs_abort ();
+
+  stringLength = [string length];
+
+  /* Force layout.  */
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  spaceLocation = [layoutManager locationForGlyphAtIndex:stringLength];
+
+  /* Remove the appended trailing space because otherwise it may
+     generate a wrong result for a right-to-left text.  */
+  [textStorage beginEditing];
+  [textStorage deleteCharactersInRange:(NSMakeRange (stringLength, 1))];
+  [textStorage endEditing];
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  i = 0;
+  while (i < stringLength)
+    {
+      NSRange range;
+      NSFont *fontInTextStorage =
+        [textStorage attribute: NSFontAttributeName
+		       atIndex:i
+                     longestEffectiveRange: &range
+                       inRange: NSMakeRange (0, stringLength)];
+
+      if (!(fontInTextStorage == font
+            || [[fontInTextStorage fontName] isEqualToString:[font fontName]]))
+        break;
+      i = NSMaxRange (range);
+    }
+  if (i < stringLength)
+    /* Make the test `used <= glyph_len' below fail if textStorage
+       contained some fonts other than the specified one.  */
+    used = glyph_len + 1;
+  else
+    {
+      NSRange range = NSMakeRange (0, stringLength);
+
+      range = [layoutManager glyphRangeForCharacterRange:range
+                                    actualCharacterRange:NULL];
+      numberOfGlyphs = NSMaxRange (range);
+      used = numberOfGlyphs;
+      for (i = 0; i < numberOfGlyphs; i++)
+        if ([layoutManager notShownAttributeForGlyphAtIndex:i])
+          used--;
+    }
+
+  if (0 < used && used <= glyph_len)
+    {
+      NSUInteger glyphIndex, prevGlyphIndex;
+      NSUInteger *permutation;
+      NSRange compRange, range;
+      CGFloat totalAdvance;
+
+      glyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      permutation = NULL;
+#define RIGHT_TO_LEFT_P permutation
+
+      /* Fill the `comp_range' member of struct mac_glyph_layout, and
+         setup a permutation for right-to-left text.  */
+      compRange = NSMakeRange (0, 0);
+      for (range = NSMakeRange (0, 0); NSMaxRange (range) < used;
+           range.length++)
+        {
+          struct ns_glyph_layout *gl = glyph_layouts + NSMaxRange (range);
+          NSUInteger characterIndex =
+            [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
+
+          gl->string_index = characterIndex;
+
+          if (characterIndex >= NSMaxRange (compRange))
+            {
+              compRange.location = NSMaxRange (compRange);
+              do
+                {
+                  NSRange characterRange =
+                    [string
+                      rangeOfComposedCharacterSequenceAtIndex:characterIndex];
+
+                  compRange.length =
+                    NSMaxRange (characterRange) - compRange.location;
+                  [layoutManager glyphRangeForCharacterRange:compRange
+                                        actualCharacterRange:&characterRange];
+                  characterIndex = NSMaxRange (characterRange) - 1;
+                }
+              while (characterIndex >= NSMaxRange (compRange));
+
+              if (RIGHT_TO_LEFT_P)
+                for (i = 0; i < range.length; i++)
+                  permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+              range = NSMakeRange (NSMaxRange (range), 0);
+            }
+
+          gl->comp_range.location = compRange.location;
+          gl->comp_range.length = compRange.length;
+
+          while (++glyphIndex < numberOfGlyphs)
+            if (![layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+              break;
+        }
+      if (RIGHT_TO_LEFT_P)
+        for (i = 0; i < range.length; i++)
+          permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+      /* Then fill the remaining members.  */
+      glyphIndex = prevGlyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      if (!RIGHT_TO_LEFT_P)
+        totalAdvance = 0;
+      else
+        {
+          NSUInteger nrects;
+          NSRect *glyphRects =
+            [layoutManager
+              rectArrayForGlyphRange:(NSMakeRange (0, numberOfGlyphs))
+              withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                     inTextContainer:textContainer rectCount:&nrects];
+
+          totalAdvance = NSMaxX (glyphRects[0]);
+        }
+
+      for (i = 0; i < used; i++)
+        {
+          struct ns_glyph_layout *gl;
+          NSPoint location;
+          NSUInteger nextGlyphIndex;
+          NSRange glyphRange;
+          NSRect *glyphRects;
+          NSUInteger nrects;
+
+          if (!RIGHT_TO_LEFT_P)
+            gl = glyph_layouts + i;
+          else
+            {
+              NSUInteger dest = permutation[i];
+
+              gl = glyph_layouts + dest;
+              if (i < dest)
+                {
+                  NSUInteger tmp = gl->string_index;
+
+                  gl->string_index = glyph_layouts[i].string_index;
+                  glyph_layouts[i].string_index = tmp;
+                }
+            }
+          gl->glyph_id = [layoutManager glyphAtIndex: glyphIndex];
+
+          location = [layoutManager locationForGlyphAtIndex:glyphIndex];
+          gl->baseline_delta = spaceLocation.y - location.y;
+
+          for (nextGlyphIndex = glyphIndex + 1; nextGlyphIndex < numberOfGlyphs;
+               nextGlyphIndex++)
+            if (![layoutManager
+                   notShownAttributeForGlyphAtIndex:nextGlyphIndex])
+              break;
+
+          if (!RIGHT_TO_LEFT_P)
+            {
+              CGFloat maxX;
+
+              if (prevGlyphIndex == 0)
+                glyphRange = NSMakeRange (0, nextGlyphIndex);
+              else
+                glyphRange = NSMakeRange (glyphIndex,
+                                          nextGlyphIndex - glyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              maxX = max (NSMaxX (glyphRects[0]), totalAdvance);
+              gl->advance_delta = location.x - totalAdvance;
+              gl->advance = maxX - totalAdvance;
+              totalAdvance = maxX;
+            }
+          else
+            {
+              CGFloat minX;
+
+              if (nextGlyphIndex == numberOfGlyphs)
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          numberOfGlyphs - prevGlyphIndex);
+              else
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          glyphIndex + 1 - prevGlyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              minX = min (NSMinX (glyphRects[0]), totalAdvance);
+              gl->advance = totalAdvance - minX;
+              totalAdvance = minX;
+              gl->advance_delta = location.x - totalAdvance;
+            }
+
+          prevGlyphIndex = glyphIndex + 1;
+          glyphIndex = nextGlyphIndex;
+        }
+
+      if (RIGHT_TO_LEFT_P)
+        xfree (permutation);
+
+#undef RIGHT_TO_LEFT_P
+
+      result = used;
+    }
+  [textStorage release];
+
+  return result;
+}
+
+static Lisp_Object
+nsfont_shape (Lisp_Object lgstring, Lisp_Object direction)
+{
+  struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  struct nsfont_info *font_info = (struct nsfont_info *) font;
+  struct ns_glyph_layout *glyph_layouts;
+  NSFont *nsfont = font_info->nsfont;
+  ptrdiff_t glyph_len, len, i;
+  Lisp_Object tem;
+  unichar *mb_buf;
+  NSUInteger used;
+
+  glyph_len = LGSTRING_GLYPH_LEN (lgstring);
+  for (i = 0; i < glyph_len; ++i)
+    {
+      tem = LGSTRING_GLYPH (lgstring, i);
+
+      if (NILP (tem))
+	break;
+    }
+
+  len = i;
+
+  if (INT_MAX / 2 < len)
+    memory_full (SIZE_MAX);
+
+  block_input ();
+
+  mb_buf = alloca (len * sizeof *mb_buf);
+
+  for (i = 0; i < len; ++i)
+    {
+      uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
+      mb_buf[i] = (unichar) c;
+    }
+
+  NSString *string = [NSString stringWithCharacters: mb_buf
+					     length: len];
+  unblock_input ();
+
+  if (!string)
+    return Qnil;
+
+  block_input ();
+
+  enum lgstring_direction dir = DIR_UNKNOWN;
+
+  if (EQ (direction, QL2R))
+    dir = DIR_L2R;
+  else if (EQ (direction, QR2L))
+    dir = DIR_R2L;
+  glyph_layouts = alloca (sizeof (struct ns_glyph_layout) * glyph_len);
+  used = ns_font_shape (nsfont, string, glyph_layouts, glyph_len, dir);
+
+  for (i = 0; i < used; i++)
+    {
+      Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
+      struct ns_glyph_layout *gl = glyph_layouts + i;
+      EMACS_INT from, to;
+      struct font_metrics metrics;
+
+      if (NILP (lglyph))
+        {
+          lglyph = LGLYPH_NEW ();
+          LGSTRING_SET_GLYPH (lgstring, i, lglyph);
+        }
+
+      from = gl->comp_range.location;
+      LGLYPH_SET_FROM (lglyph, from);
+
+      to = gl->comp_range.location + gl->comp_range.length;
+      LGLYPH_SET_TO (lglyph, to - 1);
+
+      /* LGLYPH_CHAR is used in `describe-char' for checking whether
+         the composition is trivial.  */
+      {
+        UTF32Char c;
+
+        if (mb_buf[gl->string_index] >= 0xD800
+            && mb_buf[gl->string_index] < 0xDC00)
+          c = (((mb_buf[gl->string_index] - 0xD800) << 10)
+               + (mb_buf[gl->string_index + 1] - 0xDC00) + 0x10000);
+        else
+          c = mb_buf[gl->string_index];
+
+        LGLYPH_SET_CHAR (lglyph, c);
+      }
+
+      {
+        unsigned long cc = gl->glyph_id;
+        LGLYPH_SET_CODE (lglyph, cc);
+      }
+
+      nsfont_text_extents (font, &gl->glyph_id, 1, &metrics);
+      LGLYPH_SET_WIDTH (lglyph, metrics.width);
+      LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
+      LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
+      LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
+      LGLYPH_SET_DESCENT (lglyph, metrics.descent);
+    }
+  unblock_input ();
+
+  return make_fixnum (used);
+}
 
 
 /* ==========================================================================
@@ -1134,6 +1606,50 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
    ========================================================================== */
 
+static NSGlyph
+ns_uni_to_glyphs_1 (struct nsfont_info *info, unsigned int c)
+{
+  unichar characters[] = { c };
+  NSString *string =
+    [NSString stringWithCharacters: characters
+			    length: 1];
+  NSDictionary *attributes =
+    [NSDictionary dictionaryWithObjectsAndKeys:
+		    info->nsfont, NSFontAttributeName, nil];
+  NSTextStorage *storage = [[NSTextStorage alloc] initWithString: string
+						      attributes: attributes];
+  NSTextContainer *text_container = [[NSTextContainer alloc] init];
+  NSLayoutManager *manager = [[NSLayoutManager alloc] init];
+
+  [manager addTextContainer: text_container];
+  [text_container release]; /* Retained by manager */
+  [storage addLayoutManager: manager];
+  [manager release]; /* Retained by storage */
+
+  NSFont *font_in_storage = [storage attribute: NSFontAttributeName
+				       atIndex:0
+				effectiveRange: NULL];
+  NSGlyph glyph = FONT_INVALID_CODE;
+
+  if ((font_in_storage == info->nsfont
+       || [[font_in_storage fontName] isEqualToString: [info->nsfont fontName]]))
+    {
+      @try
+	{
+	  glyph = [manager glyphAtIndex: 0];
+	}
+      @catch (NSException *e)
+	{
+	  /* GNUstep bug? */
+	  glyph = 'X';
+	}
+    }
+
+  [storage release];
+
+  return glyph;
+}
+
 /* Find and cache corresponding glyph codes for unicode values in given
    hi-byte block of 256.  */
 static void
@@ -1141,7 +1657,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   unichar *unichars = xmalloc (0x101 * sizeof (unichar));
   unsigned int i, g, idx;
-  unsigned short *glyphs;
+  unsigned int *glyphs;
 
   if (NSFONT_TRACE)
     fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
@@ -1149,7 +1665,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
   block_input ();
 
-  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
+  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned int));
   if (!unichars || !(font_info->glyphs[block]))
     emacs_abort ();
 
@@ -1166,7 +1682,8 @@ is false when (FROM > 0 || TO < S->nchars).  */
     for (i = 0; i < 0x100; i++, glyphs++)
       {
         g = unichars[i];
-        *glyphs = g;
+	NSGlyph glyph = ns_uni_to_glyphs_1 (font_info, g);
+        *glyphs = glyph;
       }
   }
 
@@ -1175,18 +1692,19 @@ is false when (FROM > 0 || TO < S->nchars).  */
 }
 
 
-/* Determine and cache metrics for corresponding glyph codes in given
-   hi-byte block of 256.  */
+/* Determine and cache metrics for glyphs in given hi-byte block of
+   256.  */
 static void
-ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
+ns_glyph_metrics (struct nsfont_info *font_info, unsigned int block)
 {
-  unsigned int i, g;
+  unsigned int i;
+  NSGlyph g;
   unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
   NSFont *sfont;
   struct font_metrics *metrics;
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
+    fprintf (stderr, "%p\tComputing metrics for glyphs in block %u\n",
             font_info, block);
 
   /* not implemented yet (as of startup 0.18), so punt */
@@ -1209,19 +1727,14 @@ is false when (FROM > 0 || TO < S->nchars).  */
       w = max ([sfont advancementForGlyph: g].width, 2.0);
       metrics->width = lrint (w);
 
-      lb = r.origin.x;
-      rb = r.size.width - w;
-      // Add to bearing for LCD smoothing.  We don't know if it is there.
-      if (lb < 0)
-        metrics->lbearing = round (lb - LCD_SMOOTHING_MARGIN);
-      if (font_info->ital)
-        rb += (CGFloat) (0.22F * font_info->height);
-      metrics->rbearing = lrint (w + rb + LCD_SMOOTHING_MARGIN);
-
-      metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
-      /* lrint (hshrink * [sfont ascender] + expand * hd/2); */
-      metrics->ascent = r.size.height - metrics->descent;
-      /* -lrint (hshrink* [sfont descender] - expand * hd/2); */
+      lb = NSMinX (r);
+      rb = NSMaxX (r);
+
+      metrics->rbearing = lrint (rb);
+      metrics->lbearing = lrint (lb);
+
+      metrics->descent = NSMinY (r);
+      metrics->ascent = NSMaxY (r);
     }
   unblock_input ();
 }
@@ -1257,6 +1770,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
   .has_char = nsfont_has_char,
   .encode_char = nsfont_encode_char,
   .text_extents = nsfont_text_extents,
+  .shape = nsfont_shape,
   .draw = nsfont_draw,
   };
 
@@ -1265,7 +1779,6 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   DEFSYM (Qcondensed, "condensed");
   DEFSYM (Qexpanded, "expanded");
-  DEFSYM (Qapple, "apple");
   DEFSYM (Qmedium, "medium");
   DEFVAR_LISP ("ns-reg-to-script", Vns_reg_to_script,
                doc: /* Internal use: maps font registry to Unicode script.  */);
diff --git a/src/nsterm.h b/src/nsterm.h
index 4bbcf43973..944dbd727c 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -820,7 +820,7 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
   XCharStruct max_bounds;
   /* We compute glyph codes and metrics on-demand in blocks of 256 indexed
      by hibyte, lobyte.  */
-  unsigned short **glyphs; /* map Unicode index to glyph */
+  unsigned int **glyphs; /* map Unicode index to glyph */
   struct font_metrics **metrics;
 };
 #endif
diff --git a/src/nsterm.m b/src/nsterm.m
index e8848b39b7..37580c39ef 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1077,11 +1077,16 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
   /* clipping */
   if (r)
     {
-      [[NSGraphicsContext currentContext] saveGraphicsState];
+      NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+      [ctx saveGraphicsState];
+#ifdef NS_IMPL_COCOA
       if (n == 2)
         NSRectClipList (r, 2);
       else
         NSRectClip (*r);
+#else
+      GSRectClipList (ctx, r, n);
+#endif
       gsaved = YES;
     }
 }
@@ -2433,9 +2438,6 @@ Hide the window (X11 semantics)
       EmacsView *view = FRAME_NS_VIEW (f);
       FRAME_POINTER_TYPE (f) = cursor;
       [[view window] invalidateCursorRectsForView: view];
-      /* Redisplay assumes this function also draws the changed frame
-         cursor, but this function doesn't, so do it explicitly.  */
-      gui_update_cursor (f, 1);
     }
 }
 
@@ -2847,31 +2849,31 @@ Hide the window (X11 semantics)
      External (RIF); compute left/right overhang of whole string and set in s
    -------------------------------------------------------------------------- */
 {
-  struct font *font = s->font;
-
-  if (s->char2b)
+  if (s->cmp == NULL
+      && (s->first_glyph->type == CHAR_GLYPH
+	  || s->first_glyph->type == COMPOSITE_GLYPH))
     {
       struct font_metrics metrics;
-      unsigned int codes[2];
-      codes[0] = *(s->char2b);
-      codes[1] = *(s->char2b + s->nchars - 1);
 
-      font->driver->text_extents (font, codes, 2, &metrics);
-      s->left_overhang = -metrics.lbearing;
-      s->right_overhang
-	= metrics.rbearing > metrics.width
-	? metrics.rbearing - metrics.width : 0;
+      if (s->first_glyph->type == CHAR_GLYPH)
+	{
+	  struct font *font = s->font;
+	  font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+	}
+      else
+	{
+	  Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+	  composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+	}
+      s->right_overhang = (metrics.rbearing > metrics.width
+			   ? metrics.rbearing - metrics.width : 0);
+      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
     }
-  else
+  else if (s->cmp)
     {
-      s->left_overhang = 0;
-#ifdef NS_IMPL_GNUSTEP
-      if (EQ (font->driver->type, Qns))
-        s->right_overhang = ((struct nsfont_info *)font)->ital ?
-          FONT_HEIGHT (font) * 0.2 : 0;
-      else
-#endif
-        s->right_overhang = 0;
+      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+      s->left_overhang = - s->cmp->lbearing;
     }
 }
 
@@ -3011,14 +3013,13 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   struct frame *f = WINDOW_XFRAME (w);
   struct glyph *phys_cursor_glyph;
   struct glyph *cursor_glyph;
-  struct face *face;
-  NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
 
   /* If cursor is out of bounds, don't draw garbage.  This can happen
      in mini-buffer windows when switching between echo area glyphs
      and mini-buffer.  */
 
-  NSTRACE ("ns_draw_window_cursor");
+  NSTRACE ("ns_draw_window_cursor (on = %d, cursor_type = %d)",
+	   on_p, cursor_type);
 
   if (!on_p)
     return;
@@ -3034,6 +3035,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
     {
+      NSTRACE_MSG ("No phys cursor glyph was found!");
+
       if (glyph_row->exact_window_width_line_p
           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
         {
@@ -3043,10 +3046,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       return;
     }
 
-  /* We draw the cursor (with NSRectFill), then draw the glyph on top
-     (other terminals do it the other way round).  We must set
-     w->phys_cursor_width to the cursor width.  For bar cursors, that
-     is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
 
   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
@@ -3078,17 +3077,17 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   /* Prevent the cursor from being drawn outside the text area.  */
   r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
 
-  ns_focus (f, &r, 1);
+  ns_focus (f, NULL, 0);
 
-  face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
-  if (face && NS_FACE_BACKGROUND (face)
-      == ns_index_color (FRAME_CURSOR_COLOR (f), f))
-    {
-      [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
-      hollow_color = FRAME_CURSOR_COLOR (f);
-    }
-  else
-    [FRAME_CURSOR_COLOR (f) set];
+  NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+  [ctx saveGraphicsState];
+#ifdef NS_IMPL_GNUSTEP
+  GSRectClipList (ctx, &r, 1);
+#else
+  NSRectClip (r);
+#endif
+
+  [FRAME_CURSOR_COLOR (f) set];
 
   switch (cursor_type)
     {
@@ -3096,13 +3095,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     case NO_CURSOR:
       break;
     case FILLED_BOX_CURSOR:
-      NSRectFill (r);
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
       break;
     case HOLLOW_BOX_CURSOR:
-      NSRectFill (r);
-      [hollow_color set];
-      NSRectFill (NSInsetRect (r, 1, 1));
-      [FRAME_CURSOR_COLOR (f) set];
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_NORMAL_TEXT);
+      [NSBezierPath strokeRect: r];
       break;
     case HBAR_CURSOR:
       NSRectFill (r);
@@ -3118,12 +3115,9 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       NSRectFill (s);
       break;
     }
-  ns_unfocus (f);
 
-  /* Draw the character under the cursor.  Other terms only draw
-     the character on top of box cursors, so do the same here.  */
-  if (cursor_type == FILLED_BOX_CURSOR || cursor_type == HOLLOW_BOX_CURSOR)
-    draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+  [ctx restoreGraphicsState];
+  ns_unfocus (f);
 }
 
 
@@ -3303,16 +3297,18 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   if (s->for_overlaps)
     return;
 
+  if (s->hl == DRAW_CURSOR)
+    [FRAME_BACKGROUND_COLOR (s->f) set];
+  else if (face->underline_defaulted_p)
+    [defaultCol set];
+  else
+    [ns_lookup_indexed_color (face->underline_color, s->f) set];
+
   /* Do underline.  */
   if (face->underline)
     {
       if (s->face->underline == FACE_UNDER_WAVE)
         {
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
-
           ns_draw_underwave (s, width, x);
         }
       else if (s->face->underline == FACE_UNDER_LINE)
@@ -3383,11 +3379,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
           s->underline_position = position;
 
           r = NSMakeRect (x, s->ybase + position, width, thickness);
-
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
           NSRectFill (r);
         }
     }
@@ -3397,11 +3388,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     {
       NSRect r;
       r = NSMakeRect (x, s->y, width, 1);
-
-      if (face->overline_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->overline_color, s->f) set];
       NSRectFill (r);
     }
 
@@ -3424,10 +3410,6 @@ larger if there are taller display elements (e.g., characters
       dy = lrint ((glyph_height - h) / 2);
       r = NSMakeRect (x, glyph_y + dy, width, 1);
 
-      if (face->strike_through_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
       NSRectFill (r);
     }
 }
@@ -3575,17 +3557,7 @@ Function modeled after x_draw_glyph_string_box ().
   struct glyph *last_glyph;
   NSRect r;
   int hthickness, vthickness;
-  struct face *face;
-
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = s->face;
+  struct face *face = s->face;
 
   vthickness = face->box_vertical_line_width;
   hthickness = face->box_horizontal_line_width;
@@ -3659,34 +3631,26 @@ Function modeled after x_draw_glyph_string_box ().
 	  || FONT_TOO_HIGH (s->font)
           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
 	{
-          struct face *face;
-          if (s->hl == DRAW_MOUSE_FACE)
-            {
-              face
-		= FACE_FROM_ID_OR_NULL (s->f,
-					MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-              if (!face)
-                face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-            }
-          else
-            face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+          struct face *face = s->face;
           if (!face->stipple)
-            [(NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)) set];
+	    {
+	      if (s->hl != DRAW_CURSOR)
+		[(NS_FACE_BACKGROUND (face) != 0
+		  ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+		  : FRAME_BACKGROUND_COLOR (s->f)) set];
+	      else
+		[FRAME_CURSOR_COLOR (s->f) set];
+	    }
           else
             {
               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
             }
 
-          if (s->hl != DRAW_CURSOR)
-            {
-              NSRect r = NSMakeRect (s->x, s->y + box_line_width,
-                                    s->background_width,
-                                    s->height-2*box_line_width);
-              NSRectFill (r);
-            }
+	  NSRect r = NSMakeRect (s->x, s->y + box_line_width,
+				 s->background_width,
+				 s->height-2*box_line_width);
+	  NSRectFill (r);
 
 	  s->background_filled_p = 1;
 	}
@@ -3707,7 +3671,7 @@ Function modeled after x_draw_glyph_string_box ().
   int th;
   char raised_p;
   NSRect br;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *tdCol;
 
   NSTRACE ("ns_dumpglyphs_image");
@@ -3728,15 +3692,6 @@ Function modeled after x_draw_glyph_string_box ().
   /* Draw BG: if we need larger area than image itself cleared, do that,
      otherwise, since we composite the image under NS (instead of mucking
      with its background color), we must clear just the image area.  */
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-       face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
 
   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
 
@@ -3807,16 +3762,8 @@ Function modeled after x_draw_glyph_string_box ().
 
   if (s->hl == DRAW_CURSOR)
     {
-    [FRAME_CURSOR_COLOR (s->f) set];
-    if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
+      [FRAME_CURSOR_COLOR (s->f) set];
       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
-    else
-      /* Currently on NS img->mask is always 0.  Since
-         get_window_cursor_type specifies a hollow box cursor when on
-         a non-masked image we never reach this clause.  But we put it
-         in, in anticipation of better support for image masks on
-         NS.  */
-      tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
     }
   else
     {
@@ -3868,66 +3815,35 @@ Function modeled after x_draw_glyph_string_box ().
 static void
 ns_dumpglyphs_stretch (struct glyph_string *s)
 {
-  NSRect r[2];
   NSRect glyphRect;
-  int n;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *fgCol, *bgCol;
 
   if (!s->background_filled_p)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
 
-      if (s->hl == DRAW_MOUSE_FACE)
-        {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-        }
-      else
-        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+      face = s->face;
 
       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
 
-      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
-
-      [bgCol set];
-
-      /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
-         overwriting cursor (usually when cursor on a tab) */
       if (s->hl == DRAW_CURSOR)
-        {
-          CGFloat x, width;
+	{
+	  fgCol = bgCol;
+	  bgCol = FRAME_CURSOR_COLOR (s->f);
+	}
 
-          /* FIXME: This looks like it will only work for left to
-             right languages.  */
-          x = NSMinX (glyphRect);
-          width = s->w->phys_cursor_width;
-          glyphRect.size.width -= width;
-          glyphRect.origin.x += width;
+      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
 
-          NSRectFill (glyphRect);
+      [bgCol set];
 
-          /* Draw overlining, etc. on the cursor. */
-          if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-            ns_draw_text_decoration (s, face, bgCol, width, x);
-          else
-            ns_draw_text_decoration (s, face, fgCol, width, x);
-        }
-      else
-        {
-          NSRectFill (glyphRect);
-        }
+      NSRectFill (glyphRect);
 
       /* Draw overlining, etc. on the stretch glyph (or the part
          of the stretch glyph after the cursor). */
       ns_draw_text_decoration (s, face, fgCol, NSWidth (glyphRect),
                                NSMinX (glyphRect));
 
-      ns_unfocus (s->f);
       s->background_filled_p = 1;
     }
 }
@@ -3936,7 +3852,7 @@ overwriting cursor (usually when cursor on a tab) */
 static void
 ns_draw_glyph_string_foreground (struct glyph_string *s)
 {
-  int x, flags;
+  int x;
   struct font *font = s->font;
 
   /* If first glyph of S has a left box line, start drawing the text
@@ -3947,15 +3863,9 @@ overwriting cursor (usually when cursor on a tab) */
   else
     x = s->x;
 
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
   font->driver->draw
     (s, s->cmp_from, s->nchars, x, s->ybase,
-     (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
-     || flags == NS_DUMPGLYPH_MOUSEFACE);
+     !s->for_overlaps && !s->background_filled_p);
 }
 
 
@@ -4062,9 +3972,9 @@ overwriting cursor (usually when cursor on a tab) */
   struct font *font = s->face->font;
   if (! font) font = FRAME_FONT (s->f);
 
-  NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
+  NSTRACE ("ns_draw_glyph_string (hl = %u)", s->hl);
 
-  if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
+  if (s->next && s->right_overhang && !s->for_overlaps)
     {
       int width;
       struct glyph_string *next;
@@ -4101,14 +4011,21 @@ overwriting cursor (usually when cursor on a tab) */
       box_drawn_p = 1;
     }
 
+  n = ns_get_glyph_string_clip_rect (s, r);
+
+  if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+      && !s->clip_tail
+      && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+	  || (s->next && s->next->hl != s->hl && s->right_overhang)))
+    r[0] = NSIntersectionRect (r[0], NSMakeRect (s->x, s->y, s->width, s->height));
+
+  ns_focus (s->f, r, n);
+
   switch (s->first_glyph->type)
     {
 
     case IMAGE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
       ns_dumpglyphs_image (s, r[0]);
-      ns_unfocus (s->f);
       break;
 
     case XWIDGET_GLYPH:
@@ -4121,57 +4038,36 @@ overwriting cursor (usually when cursor on a tab) */
 
     case CHAR_GLYPH:
     case COMPOSITE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
-      if (s->for_overlaps || (s->cmp_from > 0
-			      && ! s->first_glyph->u.cmp.automatic))
-        s->background_filled_p = 1;
-      else
-        ns_maybe_dumpglyphs_background
-          (s, s->first_glyph->type == COMPOSITE_GLYPH);
-
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
       {
-        BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	if (s->for_overlaps || (isComposite
+				&& (s->cmp_from > 0
+				    && ! s->first_glyph->u.cmp.automatic)))
+	  s->background_filled_p = 1;
+	else
+	  ns_maybe_dumpglyphs_background
+	    (s, s->first_glyph->type == COMPOSITE_GLYPH);
 
-        if (isComposite)
-          ns_draw_composite_glyph_string_foreground (s);
-        else
-          ns_draw_glyph_string_foreground (s);
-      }
+	if (isComposite)
+	  ns_draw_composite_glyph_string_foreground (s);
+	else
+	  ns_draw_glyph_string_foreground (s);
 
-      {
-        NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
-                        ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
-                                                   s->f)
-                        : FRAME_FOREGROUND_COLOR (s->f));
-        [col set];
-
-        /* Draw underline, overline, strike-through. */
-        ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	{
+	  NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
+			  ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
+						     s->f)
+			  : FRAME_FOREGROUND_COLOR (s->f));
+	  [col set];
+
+	  /* Draw underline, overline, strike-through. */
+	  ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	}
       }
 
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
-      ns_unfocus (s->f);
       break;
 
     case GLYPHLESS_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
       if (s->for_overlaps || (s->cmp_from > 0
 			      && ! s->first_glyph->u.cmp.automatic))
         s->background_filled_p = 1;
@@ -4181,7 +4077,6 @@ overwriting cursor (usually when cursor on a tab) */
       /* ... */
       /* Not yet implemented.  */
       /* ... */
-      ns_unfocus (s->f);
       break;
 
     default:
@@ -4190,13 +4085,92 @@ overwriting cursor (usually when cursor on a tab) */
 
   /* Draw box if not done already.  */
   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
+    ns_dumpglyphs_box_or_relief (s);
+
+  ns_unfocus (s->f);
+
+  /* Draw surrounding overhangs. */
+  if (s->prev)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-      ns_dumpglyphs_box_or_relief (s);
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *prev;
+
+      for (prev = s->prev; prev; prev = prev->prev)
+	if (prev->hl != s->hl
+	    && prev->x + prev->width + prev->right_overhang > s->x)
+	  {
+	    /* As prev was drawn while clipped to its own area, we
+	       must draw the right_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = prev->hl;
+	    struct face *save_face = prev->face;
+
+	    prev->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    prev->num_clips = 1;
+	    prev->hl = s->hl;
+	    if (prev->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (prev);
+	    else
+	      ns_draw_composite_glyph_string_foreground (prev);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    prev->hl = save;
+	    prev->face = save_face;
+	    prev->num_clips = 0;
+	  }
       ns_unfocus (s->f);
     }
 
+  if (s->next)
+    {
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *next;
+
+      for (next = s->next; next; next = next->next)
+	if (next->hl != s->hl
+	    && next->x - next->left_overhang < s->x + s->width)
+	  {
+	    /* As next will be drawn while clipped to its own area,
+	       we must draw the left_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = next->hl;
+	    struct face *save_face = next->face;
+
+	    next->hl = s->hl;
+	    next->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    next->num_clips = 1;
+	    if (next->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (next);
+	    else
+	      ns_draw_composite_glyph_string_foreground (next);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    next->hl = save;
+	    next->num_clips = 0;
+	    next->face = save_face;
+	    next->clip_head = next;
+	    next->background_filled_p = 0;
+	  }
+      ns_unfocus (s->f);
+    }
   s->num_clips = 0;
 }
 
diff --git a/src/xdisp.c b/src/xdisp.c
index aa01db210b..67c6c74567 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -29300,7 +29300,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
   for (s = head; s; s = s->next)
     FRAME_RIF (f)->draw_glyph_string (s);
 
-#ifndef HAVE_NS
   /* When focus a sole frame and move horizontally, this clears on_p
      causing a failure to erase prev cursor position. */
   if (area == TEXT_AREA
@@ -29319,7 +29318,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
       notice_overwritten_cursor (w, TEXT_AREA, x0, x1,
 				 row->y, MATRIX_ROW_BOTTOM_Y (row));
     }
-#endif
 
   /* Value is the x-position up to which drawn, relative to AREA of W.
      This doesn't include parts drawn because of overhangs.  */
-- 
2.31.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-General-improvements-to-NS-port.patch --]
[-- Type: text/x-patch, Size: 12123 bytes --]

From 7266c4c407f618716f5289057de8c8555d0a81aa Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:51:31 +0800
Subject: [PATCH 2/2] General improvements to NS port

* src/dispextern.h: Remove some !HAVE_NS conditionals around grab
related code.
* src/frame.c (gui_mouse_grabbed, gui_redo_mouse_highlight): Remove
!HAVE_NS conditionals around code.

* src/nsmenu.m (ns_update_menubar): Prevent recursive calls and enable
shallow updates on GNUstep.
(menuNeedsUpdate): Prevent recursive calls.
(ns_menu_show): Fix mysterious GC-related bug.
(update_frame_tool_bar_1): Work around mysterious toolbar sizing bug
on GNUstep.

* src/nsterm.h (struct ns_output): New field for tracking toolbar
visibility changes.

* src/nsterm.m (frame_set_mouse_pixel_position): Implement for
GNUstep.
(ns_redraw_scroll_bars): Enable for GNUstep.
(ns_clear_frame): Redraw scrollbars on GNUstep.
(ns_update_window_end): New function.
(ns_redisplay_interface): Add ns_update_window_end on GNUstep.
(- keyDown): Remove debug code that doesn't work on GNUstep.
(- mouseDown): Enable grab tracking on NS port.
(- resizeWithOldSuperviewSize): Fix build with NSTRACE.

* src/xdisp.c (note_tab_bar_highlight): Enable some code for NS port.
---
 src/dispextern.h |  2 --
 src/frame.c      |  4 ---
 src/nsmenu.m     | 76 +++++++++++++++++++++++++++++++++++++++---------
 src/nsterm.h     |  6 ++++
 src/nsterm.m     | 42 +++++++++++++++++++++-----
 src/xdisp.c      |  2 --
 6 files changed, 102 insertions(+), 30 deletions(-)

diff --git a/src/dispextern.h b/src/dispextern.h
index e03e21fddc..5b28fe7666 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3722,10 +3722,8 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask)			      \
                                           const char *, const char *,
                                           enum resource_types);
 
-#ifndef HAVE_NS /* These both used on W32 and X only.  */
 extern bool gui_mouse_grabbed (Display_Info *);
 extern void gui_redo_mouse_highlight (Display_Info *);
-#endif /* HAVE_NS */
 
 #endif /* HAVE_WINDOW_SYSTEM */
 
diff --git a/src/frame.c b/src/frame.c
index 2b1cb452ef..79a7c89e0d 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -5028,8 +5028,6 @@ gui_set_no_special_glyphs (struct frame *f, Lisp_Object new_value, Lisp_Object o
 }
 
 
-#ifndef HAVE_NS
-
 /* Non-zero if mouse is grabbed on DPYINFO
    and we know the frame where it is.  */
 
@@ -5054,8 +5052,6 @@ gui_redo_mouse_highlight (Display_Info *dpyinfo)
 			  dpyinfo->last_mouse_motion_y);
 }
 
-#endif /* HAVE_NS */
-
 /* Subroutines of creating an X frame.  */
 
 /* Make sure that Vx_resource_name is set to a reasonable value.
diff --git a/src/nsmenu.m b/src/nsmenu.m
index 05b89c2f56..b93d3a79bd 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -101,6 +101,15 @@
 static void
 ns_update_menubar (struct frame *f, bool deep_p)
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+
+  if (inside)
+    return;
+
+  inside++;
+#endif
+
   BOOL needsSet = NO;
   id menu = [NSApp mainMenu];
   bool owfi;
@@ -120,7 +129,12 @@
   NSTRACE ("ns_update_menubar");
 
   if (f != SELECTED_FRAME () || FRAME_EXTERNAL_MENU_BAR (f) == 0)
+    {
+#ifdef NS_IMPL_GNUSTEP
+      inside--;
+#endif
       return;
+    }
   XSETFRAME (Vmenu_updating_frame, f);
 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
 
@@ -144,10 +158,6 @@
   t = -(1000*tb.time+tb.millitm);
 #endif
 
-#ifdef NS_IMPL_GNUSTEP
-  deep_p = 1; /* See comment in menuNeedsUpdate.  */
-#endif
-
   if (deep_p)
     {
       /* Make a widget-value tree representing the entire menu trees.  */
@@ -275,6 +285,9 @@
 	  free_menubar_widget_value_tree (first_wv);
 	  discard_menu_items ();
 	  unbind_to (specpdl_count, Qnil);
+#ifdef NS_IMPL_GNUSTEP
+	  inside--;
+#endif
 	  return;
 	}
 
@@ -408,6 +421,10 @@
   if (needsSet)
     [NSApp setMainMenu: menu];
 
+#ifdef NS_IMPL_GNUSTEP
+  inside--;
+#endif
+
   unblock_input ();
 
 }
@@ -490,17 +507,34 @@ - (instancetype)initWithTitle: (NSString *)title
    call to ns_update_menubar.  */
 - (void)menuNeedsUpdate: (NSMenu *)menu
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+#endif
+
   if (!FRAME_LIVE_P (SELECTED_FRAME ()))
     return;
 
-#ifdef NS_IMPL_COCOA
-/* TODO: GNUstep calls this method when the menu is still being built
-   which results in a recursive stack overflow.  One possible solution
-   is to use menuWillOpen instead, but the Apple docs explicitly warn
-   against changing the contents of the menu in it.  I don't know what
-   the right thing to do for GNUstep is.  */
+#ifdef NS_IMPL_GNUSTEP
+  /* GNUstep calls this method when the menu is still being built
+     which results in a recursive stack overflow, which this variable
+     prevents.  */
+
+  if (!inside)
+    ++inside;
+  else
+    return;
+#endif
+
   if (needsUpdate)
-    ns_update_menubar (SELECTED_FRAME (), true);
+    {
+#ifdef NS_IMPL_GNUSTEP
+      needsUpdate = NO;
+#endif
+      ns_update_menubar (SELECTED_FRAME (), true);
+    }
+
+#ifdef NS_IMPL_GNUSTEP
+  --inside;
 #endif
 }
 
@@ -827,6 +861,9 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   p.x = x; p.y = y;
 
+  /* Don't GC due to a mysterious bug.  */
+  inhibit_garbage_collection ();
+
   /* now parse stage 2 as in ns_update_menubar */
   wv = make_widget_value ("contextmenu", NULL, true, Qnil);
   wv->button_type = BUTTON_TYPE_NONE;
@@ -998,15 +1035,17 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   pmenu = [[EmacsMenu alloc] initWithTitle:
                    NILP (title) ? @"" : [NSString stringWithLispString: title]];
+  /* On GNUstep, this call makes menu_items nil for whatever reason
+     when displaying a context menu from `context-menu-mode'.  */
+  Lisp_Object items = menu_items;
   [pmenu fillWithWidgetValue: first_wv->contents];
+  menu_items = items;
   free_menubar_widget_value_tree (first_wv);
-  unbind_to (specpdl_count, Qnil);
-
   popup_activated_flag = 1;
   tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
   popup_activated_flag = 0;
   [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
-
+  unbind_to (specpdl_count, Qnil);
   unblock_input ();
   return tem;
 }
@@ -1057,6 +1096,15 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
   [toolbar clearActive];
 #else
   [toolbar clearAll];
+  /* It takes at least 3 such adjustments to fix an issue where the
+     tool bar is 2x too tall when a frame's tool bar is first shown.
+     This is ugly, but I have no other solution for this problem.  */
+  if (FRAME_OUTPUT_DATA (f)->tool_bar_adjusted < 3)
+    {
+      [toolbar setVisible: NO];
+      FRAME_OUTPUT_DATA (f)->tool_bar_adjusted++;
+      [toolbar setVisible: YES];
+    }
 #endif
 
   /* Update EmacsToolbar as in GtkUtils, build items list.  */
diff --git a/src/nsterm.h b/src/nsterm.h
index 944dbd727c..8175f99664 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -978,6 +978,12 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
 
   /* Non-zero if we are doing an animation, e.g. toggling the tool bar.  */
   int in_animation;
+
+#ifdef NS_IMPL_GNUSTEP
+  /* Zero if this is the first time a toolbar has been updated on this
+     frame. */
+  int tool_bar_adjusted;
+#endif
 };
 
 /* This dummy declaration needed to support TTYs.  */
diff --git a/src/nsterm.m b/src/nsterm.m
index 37580c39ef..469f867579 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -65,6 +65,7 @@ Updated by Christian Limpach (chris@nice.ch)
 
 #ifdef NS_IMPL_GNUSTEP
 #include "process.h"
+#import <GNUstepGUI/GSDisplayServer.h>
 #endif
 
 #ifdef NS_IMPL_COCOA
@@ -2254,13 +2255,19 @@ Hide the window (X11 semantics)
 {
   NSTRACE ("frame_set_mouse_pixel_position");
 
-  /* FIXME: what about GNUstep?  */
 #ifdef NS_IMPL_COCOA
   CGPoint mouse_pos =
     CGPointMake(f->left_pos + pix_x,
                 f->top_pos + pix_y +
                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
   CGWarpMouseCursorPosition (mouse_pos);
+#else
+  GSDisplayServer *server = GSServerForWindow ([FRAME_NS_VIEW (f) window]);
+  [server setMouseLocation: NSMakePoint (f->left_pos + pix_x,
+					 f->top_pos + pix_y
+					 + FRAME_NS_TITLEBAR_HEIGHT(f)
+					 + FRAME_TOOLBAR_HEIGHT(f))
+		  onScreen: [[[FRAME_NS_VIEW (f) window] screen] screenNumber]];
 #endif
 }
 
@@ -2573,8 +2580,7 @@ Hide the window (X11 semantics)
    ========================================================================== */
 
 
-#if 0
-/* FIXME: Remove this function. */
+#ifdef NS_IMPL_GNUSTEP
 static void
 ns_redraw_scroll_bars (struct frame *f)
 {
@@ -2619,10 +2625,9 @@ Hide the window (X11 semantics)
   NSRectFill (r);
   ns_unfocus (f);
 
-  /* as of 2006/11 or so this is now needed */
-  /* FIXME: I don't see any reason for this and removing it makes no
-     difference here.  Do we need it for GNUstep?  */
-  //ns_redraw_scroll_bars (f);
+#ifdef NS_IMPL_GNUSTEP
+  ns_redraw_scroll_bars (f);
+#endif
   unblock_input ();
 }
 
@@ -4920,6 +4925,17 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
 {
 }
 
+#ifdef NS_IMPL_GNUSTEP
+static void
+ns_update_window_end (struct window *w, bool cursor_on_p,
+		      bool mouse_face_overwritten_p)
+{
+  NSTRACE ("ns_update_window_end (cursor_on_p = %d)", cursor_on_p);
+
+  ns_redraw_scroll_bars (WINDOW_XFRAME (w));
+}
+#endif
+
 /* This and next define (many of the) public functions in this file.  */
 /* gui_* are generic versions in xdisp.c that we, and other terms, get away
          with using despite presence in the "system dependent" redisplay
@@ -4936,7 +4952,11 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
   ns_scroll_run,
   ns_after_update_window_line,
   NULL, /* update_window_begin */
+#ifndef NS_IMPL_GNUSTEP
   NULL, /* update_window_end   */
+#else
+  ns_update_window_end,
+#endif
   0, /* flush_display */
   gui_clear_window_mouse_face,
   gui_get_glyph_overhangs,
@@ -6164,9 +6184,11 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
       Lisp_Object kind = fnKeysym ? QCfunction : QCordinary;
       emacs_event->modifiers = EV_MODIFIERS2 (flags, kind);
 
+#ifndef NS_IMPL_GNUSTEP
       if (NS_KEYLOG)
         fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
                  code, fnKeysym, flags, emacs_event->modifiers);
+#endif
 
       /* If it was a function key or had control-like modifiers, pass
          it directly to Emacs.  */
@@ -6679,6 +6701,11 @@ - (void)mouseDown: (NSEvent *)theEvent
       emacs_event->code = EV_BUTTON (theEvent);
       emacs_event->modifiers = EV_MODIFIERS (theEvent)
                              | EV_UDMODIFIERS (theEvent);
+
+      if (emacs_event->modifiers & down_modifier)
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed |= 1 << EV_BUTTON (theEvent);
+      else
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed &= ~(1 << EV_BUTTON (theEvent));
     }
 
   XSETINT (emacs_event->x, lrint (p.x));
@@ -6979,7 +7006,6 @@ - (void)resizeWithOldSuperviewSize: (NSSize)oldSize
   height = (int)NSHeight (frame);
 
   NSTRACE_SIZE ("New size", NSMakeSize (width, height));
-  NSTRACE_SIZE ("Original size", size);
 
   /* Reset the frame size to match the bounds of the superview (the
      NSWindow's contentView).  We need to do this as sometimes the
diff --git a/src/xdisp.c b/src/xdisp.c
index 67c6c74567..0d95e70212 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13894,7 +13894,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   clear_mouse_face (hlinfo);
 
   bool mouse_down_p = false;
-#ifndef HAVE_NS
   /* Mouse is down, but on different tab-bar item?  Or alternatively,
      the mouse might've been pressed somewhere we don't know about,
      and then have moved onto the tab bar.  In this case,
@@ -13907,7 +13906,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   if (mouse_down_p && f->last_tab_bar_item != prop_idx
       && f->last_tab_bar_item != -1)
     return;
-#endif
   draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED;
 
   /* If tab-bar item is not enabled, don't highlight it.  */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-10-31 13:12                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-05  7:44                           ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-06  0:20                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-05  7:44 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

[-- Attachment #1: Type: text/plain, Size: 495 bytes --]

Po Lu <luangruo@yahoo.com> writes:

> Thanks, here's the updated patch (along with the other patch for the
> improvements not related to text display).  I will be sure to take a
> look at the rest of the ns_focus calls later, if I get the time.

As of 48af19c1 those patches don't apply anymore.  Here's a version that
does.  (I kept my version of the overhang computation code instead of
Daniel's, because the xterm code computes overhangs correctly, and
bug#51105 doesn't apply to it AFAIU.)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-font-display-on-NS-port.patch --]
[-- Type: text/x-patch, Size: 79058 bytes --]

From 12ff5156b755a3654404328171045f13bcaec901 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:44:03 +0800
Subject: [PATCH 1/2] Improve font display on NS port

* src/nsfns.m (Fx_create_frame): Use "fixed" for the default font on
GNUstep.

* src/nsfont.m (LCD_SMOOTHING_MARGIN, ns_escape_name)
(ns_unescape_name, ns_attribute_fvalue)
(STYLE_REF): Remove unused defines and functions.

(struct ns_glyph_layout, enum lgstring_direction).
(enum gs_font_slant, enum gs_font_weight, enum gs_font_width)
(enum gs_specified, struct gs_font_data): New enumerators and
structures.

(ns_font_descs_match_p)
(ns_done_font_data, ns_get_font_data): New functions.
(ns_glyph_metrics): Stop escaping names.

(ns_spec_to_descriptor): Fix font descriptor creation for symbolic
font spec entires.
(ns_descriptor_to_entity): Create entries with the correct symbolic
styles.

(ns_fallback_entity): Fix fallback entity selection.
(ns_findfonts): Use our own font matcher instead of the broken GNUstep
matcher.

(ns_list_family): Remove obsolete comment.
(nsfont_open): Remove obsolete code, comments, and synthItal logic
which doesn't work on GNUstep.

(nsfont_encode_char): Use a type that can fit NSGlyph
(nsfont_draw): Chose correct font, remove obsolete mouse face logic,
obsolete comments, and switch to using glyph-based drawing instead of
character-based drawing.

(ns_font_shape, nsfont_shape): New functions.

(ns_uni_to_glyphs_1): New function.
(ns_uni_to_glyphs): Return glyphs instead of unicode codepoints.

(ns_glyph_metrics): Use NSGlyphs instead of unicode codepoints and fix
left bearing, right bearing, ascent and descent computation.

(struct nsfont_driver): Add shaping capability.

* src/nsterm.h (struct nsfont_info): Use unsigned int for glyph cache.

* src/nsterm.c (ns_focus): Set DPS clipping on GNUstep.

(ns_compute_glyph_string_overhangs): Fix overhang computation by using
xterm code.
(ns_draw_window_cursor): Simplify cursor drawing.
(ns_maybe_dumpglyphs_background): Test for cursor HL and remove
obsolete mouse face logic.
(ns_dumpglyphs_image)
(ns_dumpglyphs_box_or_relief): Rectify for new cursor logic.
(ns_dumpglyphs_stretch): Rectify for new cursor logic and rely on
ns_draw_glyph_string to set focus.
(ns_draw_glyph_string_foreground): Remove mouse face logic.
(ns_draw_glyph_strings): Implement overhangs, remove obsolete
comment, and always focus before dumping glyphs.
(ns_draw_text_decoration): Add condition for DRAW_CURSOR and simplify
color selection.
(ns_define_frame_cursor): Remove nonsensical code (define_frame_cursor
has nothing to do with the text cursor, aka caret).

* src/xdisp.c (draw_glyphs): Enable code for NS port to fix mouse face
cursor display.

* src/macfont.m (get_cgcolor_from_nscolor): New function.
(macfont_draw): Remove obsolete mouse-face code and enable cursor
display.
---
 src/macfont.m |   36 +-
 src/nsfns.m   |    6 +
 src/nsfont.m  | 1215 +++++++++++++++++++++++++++++++++++--------------
 src/nsterm.h  |    2 +-
 src/nsterm.m  |  423 ++++++++---------
 src/xdisp.c   |    2 -
 6 files changed, 1095 insertions(+), 589 deletions(-)

diff --git a/src/macfont.m b/src/macfont.m
index 78ed5d53f3..1426cae6dc 100644
--- a/src/macfont.m
+++ b/src/macfont.m
@@ -613,6 +613,21 @@ static void mac_font_get_glyphs_for_variants (CFDataRef, UTF32Char,
   return cgColor;
 }
 
+static CGColorRef
+get_cgcolor_from_nscolor (NSColor *nsColor, struct frame *f)
+{
+  [nsColor set];
+  CGColorSpaceRef colorSpace = [[nsColor colorSpace] CGColorSpace];
+  NSInteger noc = [nsColor numberOfComponents];
+  CGFloat *components = xmalloc (sizeof(CGFloat)*(1+noc));
+  CGColorRef cgColor;
+
+  [nsColor getComponents: components];
+  cgColor = CGColorCreate (colorSpace, components);
+  xfree (components);
+  return cgColor;
+}
+
 #define CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND(context, face, f)        \
   do {                                                                  \
     CGColorRef refcol_ = get_cgcolor (NS_FACE_FOREGROUND (face), f);    \
@@ -2911,14 +2926,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
 
   if (!CGRectIsNull (background_rect))
     {
-      if (s->hl == DRAW_MOUSE_FACE)
+      if (s->hl == DRAW_CURSOR)
         {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-				       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_CURSOR_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
         }
-      CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
       CGContextFillRects (context, &background_rect, 1);
     }
 
@@ -2927,7 +2942,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
       CGAffineTransform atfm;
 
       CGContextScaleCTM (context, 1, -1);
-      CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
+      if (s->hl == DRAW_CURSOR)
+        {
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_BACKGROUND_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
+        }
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
       if (macfont_info->synthetic_italic_p)
         atfm = synthetic_italic_atfm;
       else
diff --git a/src/nsfns.m b/src/nsfns.m
index 797d0ce782..f4d8172246 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -1236,6 +1236,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                          "fontBackend", "FontBackend", RES_TYPE_STRING);
 
   {
+#ifdef NS_IMPL_COCOA
     /* use for default font name */
     id font = [NSFont userFixedPitchFontOfSize: -1.0]; /* default */
     gui_default_parameter (f, parms, Qfontsize,
@@ -1250,6 +1251,11 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                            build_string (fontname),
                            "font", "Font", RES_TYPE_STRING);
     xfree (fontname);
+#else
+    gui_default_parameter (f, parms, Qfont,
+                           build_string ("fixed"),
+                           "font", "Font", RES_TYPE_STRING);
+#endif
   }
   unblock_input ();
 
diff --git a/src/nsfont.m b/src/nsfont.m
index 5a9cdfebc0..b3224629f0 100644
--- a/src/nsfont.m
+++ b/src/nsfont.m
@@ -1,4 +1,4 @@
-/* Font back-end driver for the NeXT/Open/GNUstep and macOS window system.
+/* Font back-end driver for the GNUstep window system.
    See font.h
    Copyright (C) 2006-2021 Free Software Foundation, Inc.
 
@@ -38,47 +38,269 @@
 #include "termchar.h"
 #include "pdumper.h"
 
-/* TODO: Drop once we can assume gnustep-gui 0.17.1.  */
+#import <Foundation/NSException.h>
 #import <AppKit/NSFontDescriptor.h>
+#import <AppKit/NSLayoutManager.h>
+#import <GNUstepGUI/GSLayoutManager.h>
+#import <GNUstepGUI/GSFontInfo.h>
 
 #define NSFONT_TRACE 0
-#define LCD_SMOOTHING_MARGIN 2
 
-/* Font glyph and metrics caching functions, implemented at end.  */
-static void ns_uni_to_glyphs (struct nsfont_info *font_info,
-                              unsigned char block);
-static void ns_glyph_metrics (struct nsfont_info *font_info,
-                              unsigned char block);
+/* Structure used by GS `shape' functions for storing layout
+   information for each glyph.  Borrowed from macfont.h.  */
+struct ns_glyph_layout
+{
+  /* Range of indices of the characters composed into the group of
+     glyphs that share the cursor position with this glyph.  The
+     members `location' and `length' are in UTF-16 indices.  */
+  NSRange comp_range;
 
-#define INVALID_GLYPH 0xFFFF
+  /* UTF-16 index in the source string for the first character
+     associated with this glyph.  */
+  NSUInteger string_index;
 
-/* ==========================================================================
+  /* Horizontal and vertical adjustments of glyph position.  The
+     coordinate space is that of Core Text.  So, the `baseline_delta'
+     value is negative if the glyph should be placed below the
+     baseline.  */
+  CGFloat advance_delta, baseline_delta;
 
-    Utilities
+  /* Typographical width of the glyph.  */
+  CGFloat advance;
 
-   ========================================================================== */
+  /* Glyph ID of the glyph.  */
+  NSGlyph glyph_id;
+};
+
+
+enum lgstring_direction
+  {
+    DIR_R2L = -1, DIR_UNKNOWN = 0, DIR_L2R = 1
+  };
+
+enum gs_font_slant
+  {
+    GS_FONT_SLANT_ITALIC,
+    GS_FONT_SLANT_REVERSE_ITALIC,
+    GS_FONT_SLANT_NORMAL
+  };
+
+enum gs_font_weight
+  {
+    GS_FONT_WEIGHT_LIGHT,
+    GS_FONT_WEIGHT_BOLD,
+    GS_FONT_WEIGHT_NORMAL
+  };
+
+enum gs_font_width
+  {
+    GS_FONT_WIDTH_CONDENSED,
+    GS_FONT_WIDTH_EXPANDED,
+    GS_FONT_WIDTH_NORMAL
+  };
+
+enum gs_specified
+  {
+    GS_SPECIFIED_SLANT = 1,
+    GS_SPECIFIED_WEIGHT = 1 << 1,
+    GS_SPECIFIED_WIDTH = 1 << 2,
+    GS_SPECIFIED_FAMILY = 1 << 3,
+    GS_SPECIFIED_SPACING = 1 << 4
+  };
 
+struct gs_font_data
+{
+  int specified;
+  enum gs_font_slant slant;
+  enum gs_font_weight weight;
+  enum gs_font_width width;
+  bool monospace_p;
+  char *family_name;
+};
 
-/* Replace spaces w/another character so emacs core font parsing routines
-   aren't thrown off.  */
 static void
-ns_escape_name (char *name)
+ns_done_font_data (struct gs_font_data *data)
 {
-  for (; *name; name++)
-    if (*name == ' ')
-      *name = '_';
+  if (data->specified & GS_SPECIFIED_FAMILY)
+    xfree (data->family_name);
 }
 
-
-/* Reconstruct spaces in a font family name passed through emacs.  */
 static void
-ns_unescape_name (char *name)
+ns_get_font_data (NSFontDescriptor *desc, struct gs_font_data *dat)
 {
-  for (; *name; name++)
-    if (*name == '_')
-      *name = ' ';
+  NSNumber *tem;
+  NSFontSymbolicTraits traits = [desc symbolicTraits];
+  NSDictionary *dict = [desc objectForKey: NSFontTraitsAttribute];
+  NSString *family = [desc objectForKey: NSFontFamilyAttribute];
+
+  dat->specified = 0;
+
+  if (family != nil)
+    {
+      dat->specified |= GS_SPECIFIED_FAMILY;
+      dat->family_name = xstrdup ([family cStringUsingEncoding: NSUTF8StringEncoding]);
+    }
+
+  tem = [desc objectForKey: NSFontFixedAdvanceAttribute];
+
+  if ((tem != nil && [tem boolValue] != NO)
+      || (traits & NSFontMonoSpaceTrait))
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = true;
+    }
+  else if (tem != nil && [tem boolValue] == NO)
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = false;
+    }
+
+  if (traits & NSFontBoldTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WEIGHT;
+      dat->weight = GS_FONT_WEIGHT_BOLD;
+    }
+
+  if (traits & NSFontItalicTrait)
+    {
+      dat->specified |= GS_SPECIFIED_SLANT;
+      dat->slant = GS_FONT_SLANT_ITALIC;
+    }
+
+  if (traits & NSFontCondensedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_CONDENSED;
+    }
+  else if (traits & NSFontExpandedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_EXPANDED;
+    }
+
+  if (dict != nil)
+    {
+      tem = [dict objectForKey: NSFontSlantTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_SLANT;
+
+	  dat->slant = [tem floatValue] > 0
+	    ? GS_FONT_SLANT_ITALIC
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_SLANT_REVERSE_ITALIC
+	       : GS_FONT_SLANT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWeightTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WEIGHT;
+
+	  dat->weight = [tem floatValue] > 0
+	    ? GS_FONT_WEIGHT_BOLD
+	    : ([tem floatValue] < -0.4f
+	       ? GS_FONT_WEIGHT_LIGHT
+	       : GS_FONT_WEIGHT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWidthTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WIDTH;
+
+	  dat->width = [tem floatValue] > 0
+	    ? GS_FONT_WIDTH_EXPANDED
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_WIDTH_NORMAL
+	       : GS_FONT_WIDTH_CONDENSED);
+	}
+    }
+}
+
+static bool
+ns_font_descs_match_p (NSFontDescriptor *desc, NSFontDescriptor *target)
+{
+  struct gs_font_data dat;
+  struct gs_font_data t;
+
+  ns_get_font_data (desc, &dat);
+  ns_get_font_data (target, &t);
+
+  if (!(t.specified & GS_SPECIFIED_WIDTH))
+    t.width = GS_FONT_WIDTH_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_WEIGHT))
+    t.weight = GS_FONT_WEIGHT_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_SPACING))
+    t.monospace_p = false;
+  if (!(t.specified & GS_SPECIFIED_SLANT))
+    t.slant = GS_FONT_SLANT_NORMAL;
+
+  if (!(t.specified & GS_SPECIFIED_FAMILY))
+    emacs_abort ();
+
+  bool match_p = true;
+
+  if (dat.specified & GS_SPECIFIED_WIDTH
+      && dat.width != t.width)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_WEIGHT
+      && dat.weight != t.weight)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SPACING
+      && dat.monospace_p != t.monospace_p)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SLANT
+      && dat.monospace_p != t.monospace_p)
+    {
+      if (NSFONT_TRACE)
+	printf ("Matching monospace for %s: %d %d\n",
+		t.family_name, dat.monospace_p,
+		t.monospace_p);
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_FAMILY
+      && strcmp (dat.family_name, t.family_name))
+    match_p = false;
+
+ gout:
+  ns_done_font_data (&dat);
+  ns_done_font_data (&t);
+
+  return match_p;
 }
 
+/* Font glyph and metrics caching functions, implemented at end.  */
+static void ns_uni_to_glyphs (struct nsfont_info *font_info,
+                              unsigned char block);
+static void ns_glyph_metrics (struct nsfont_info *font_info,
+                              unsigned int block);
+
+#define INVALID_GLYPH 0xFFFF
+
+/* ==========================================================================
+
+    Utilities
+
+   ========================================================================== */
+
 
 /* Extract family name from a font spec.  */
 static NSString *
@@ -91,66 +313,116 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
     {
       char *tmp = xlispstrdup (SYMBOL_NAME (tem));
       NSString *family;
-      ns_unescape_name (tmp);
       family = [NSString stringWithUTF8String: tmp];
       xfree (tmp);
       return family;
     }
 }
 
-
-/* Return 0 if attr not set, else value (which might also be 0).
-   On Leopard 0 gets returned even on descriptors where the attribute
-   was never set, so there's no way to distinguish between unspecified
-   and set to not have.  Callers should assume 0 means unspecified.  */
-static float
-ns_attribute_fvalue (NSFontDescriptor *fdesc, NSString *trait)
-{
-    NSDictionary *tdict = [fdesc objectForKey: NSFontTraitsAttribute];
-    NSNumber *val = [tdict objectForKey: trait];
-    return val == nil ? 0.0F : [val floatValue];
-}
-
-
 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
    to NSFont descriptor.  Information under extra only needed for matching.  */
-#define STYLE_REF 100
 static NSFontDescriptor *
 ns_spec_to_descriptor (Lisp_Object font_spec)
 {
     NSFontDescriptor *fdesc;
     NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
-    NSMutableDictionary *tdict = [NSMutableDictionary new];
     NSString *family = ns_get_family (font_spec);
-    float n;
-
-    /* Add each attr in font_spec to fdAttrs.  */
-    n = min (FONT_WEIGHT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWeightTrait];
-    n = min (FONT_SLANT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontSlantTrait];
-    n = min (FONT_WIDTH_NUMERIC (font_spec), 200);
-    if (n > -1 && (n > STYLE_REF + 10 || n < STYLE_REF - 10))
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWidthTrait];
-    if ([tdict count] > 0)
-	[fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+    NSMutableDictionary *tdict = [NSMutableDictionary new];
 
-    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
-               retain] autorelease];
+    Lisp_Object tem;
+
+    tem = FONT_SLANT_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontSlantTrait];
+	else if (EQ (tem, intern ("reverse-italic")) ||
+		 EQ (tem, intern ("reverse-oblique")))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontSlantTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontSlantTrait];
+      }
+
+    tem = FONT_WIDTH_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qcondensed))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontWidthTrait];
+	else if (EQ (tem, Qexpanded))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontWidthTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontWidthTrait];
+      }
+
+    tem = FONT_WEIGHT_SYMBOLIC (font_spec);
+
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qbold))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else if (EQ (tem, Qlight))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		      forKey: NSFontWeightTrait];
+	  }
+      }
+
+    tem = AREF (font_spec, FONT_SPACING_INDEX);
 
     if (family != nil)
       {
-        NSFontDescriptor *fdesc2 = [fdesc fontDescriptorWithFamily: family];
-        fdesc = [[fdesc2 retain] autorelease];
+	[fdAttrs setObject: family
+		    forKey: NSFontFamilyAttribute];
       }
 
-    [fdAttrs release];
+    if (FIXNUMP (tem))
+      {
+	if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:YES]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+	else
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:NO]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+      }
+
+    /* Handle special families such as ``fixed'' or ``Sans Serif''.  */
+
+    if ([family isEqualToString: @"fixed"])
+      {
+	[fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+    else if ([family isEqualToString: @"Sans Serif"])
+      {
+	[fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+
+    [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+
+    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
+               retain] autorelease];
+
     [tdict release];
+    [fdAttrs release];
     return fdesc;
 }
 
@@ -161,61 +433,64 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
                          Lisp_Object extra,
                          const char *style)
 {
-    Lisp_Object font_entity = font_make_entity ();
-    /*   NSString *psName = [desc postscriptName]; */
-    NSString *family = [desc objectForKey: NSFontFamilyAttribute];
-    unsigned int traits = [desc symbolicTraits];
-    char *escapedFamily;
-
-    /* Shouldn't happen, but on Tiger fallback desc gets name but no family.  */
-    if (family == nil)
-      family = [desc objectForKey: NSFontNameAttribute];
-    if (family == nil)
-      family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-
-    escapedFamily = xstrdup ([family UTF8String]);
-    ns_escape_name (escapedFamily);
-
-    ASET (font_entity, FONT_TYPE_INDEX, Qns);
-    ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
-    ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
-    ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
-    ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
-
-    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    traits & NSFontBoldTrait ? Qbold : Qmedium);
-/*    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    make_fixnum (100 + 100
-			* ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    traits & NSFontItalicTrait ? Qitalic : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-                    traits & NSFontCondensedTrait ? Qcondensed :
-                    traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
-
-    ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_SPACING_INDEX,
-	  make_fixnum([desc symbolicTraits] & NSFontMonoSpaceTrait
-	      ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
-
-    ASET (font_entity, FONT_EXTRA_INDEX, extra);
-    ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+  Lisp_Object font_entity = font_make_entity ();
+  struct gs_font_data data;
+  ns_get_font_data (desc, &data);
+
+  ASET (font_entity, FONT_TYPE_INDEX, Qns);
+  ASET (font_entity, FONT_FOUNDRY_INDEX, Qns);
+  if (data.specified & GS_SPECIFIED_FAMILY)
+    ASET (font_entity, FONT_FAMILY_INDEX, intern (data.family_name));
+  ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
+  ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
+
+  if (data.specified & GS_SPECIFIED_WEIGHT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
+		      data.weight == GS_FONT_WEIGHT_BOLD
+		      ? Qbold : (data.weight == GS_FONT_WEIGHT_LIGHT
+				 ? Qlight : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX, Qnormal);
 
-    if (NSFONT_TRACE)
-      {
-	fputs ("created font_entity:\n    ", stderr);
-	debug_print (font_entity);
-      }
+  if (data.specified & GS_SPECIFIED_SLANT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
+		      data.slant == GS_FONT_SLANT_ITALIC
+		      ? Qitalic : (data.slant == GS_FONT_SLANT_REVERSE_ITALIC
+				   ? intern ("reverse-italic") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX, Qnormal);
+
+  if (data.specified & GS_SPECIFIED_WIDTH)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
+		      data.width == GS_FONT_WIDTH_CONDENSED
+		      ? Qcondensed : (data.width == GS_FONT_WIDTH_EXPANDED
+				      ? intern ("expanded") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX, Qnormal);
 
-    xfree (escapedFamily);
-    return font_entity;
+  ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_SPACING_INDEX,
+	make_fixnum ((data.specified & GS_SPECIFIED_WIDTH && data.monospace_p)
+		     ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
+
+  ASET (font_entity, FONT_EXTRA_INDEX, extra);
+  ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+
+  if (NSFONT_TRACE)
+    {
+      fputs ("created font_entity:\n    ", stderr);
+      debug_print (font_entity);
+    }
+
+  ns_done_font_data (&data);
+  return font_entity;
 }
 
 
@@ -223,8 +498,7 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
 static Lisp_Object
 ns_fallback_entity (void)
 {
-  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
-      fontDescriptor], Qnil, NULL);
+  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 1] fontDescriptor], Qnil, NULL);
 }
 
 
@@ -510,21 +784,20 @@ but also for ascii (which causes unnecessary font substitution).  */
     return families;
 }
 
+/* GNUstep font matching is very mediocre (it can't even compare
+   symbolic styles correctly), which is why our own font matching
+   mechanism must be implemented.  */
 
-/* Implementation for list() and match().  List() can return nil, match()
-must return something.  Strategy is to drop family name from attribute
-matching set for match.  */
+/* Implementation for list and match.  */
 static Lisp_Object
 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
 {
     Lisp_Object tem, list = Qnil;
-    NSFontDescriptor *fdesc, *desc;
-    NSMutableSet *fkeys;
-    NSArray *matchingDescs;
-    NSEnumerator *dEnum;
-    NSString *family;
+    NSFontDescriptor *fdesc;
+    NSArray *all_descs;
+    GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
+
     NSSet *cFamilies;
-    BOOL foundItal = NO;
 
     block_input ();
     if (NSFONT_TRACE)
@@ -537,43 +810,22 @@ but also for ascii (which causes unnecessary font substitution).  */
     cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
 
     fdesc = ns_spec_to_descriptor (font_spec);
-    fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
-    if (isMatch)
-	[fkeys removeObject: NSFontFamilyAttribute];
-
-    matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
+    all_descs = [enumerator availableFontDescriptors];
 
-    if (NSFONT_TRACE)
-	NSLog(@"Got desc %@ and found %lu matching fonts from it: ", fdesc,
-	      (unsigned long)[matchingDescs count]);
-
-    for (dEnum = [matchingDescs objectEnumerator]; (desc = [dEnum nextObject]);)
+    for (NSFontDescriptor *desc in all_descs)
       {
 	if (![cFamilies containsObject:
 	         [desc objectForKey: NSFontFamilyAttribute]])
 	    continue;
+	if (!ns_font_descs_match_p (fdesc, desc))
+	  continue;
+
         tem = ns_descriptor_to_entity (desc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
+				       AREF (font_spec, FONT_EXTRA_INDEX),
                                        NULL);
         if (isMatch)
           return tem;
 	list = Fcons (tem, list);
-	if (fabs (ns_attribute_fvalue (desc, NSFontSlantTrait)) > 0.05)
-	    foundItal = YES;
-      }
-
-    /* Add synthItal member if needed.  */
-    family = [fdesc objectForKey: NSFontFamilyAttribute];
-    if (family != nil && !foundItal && !NILP (list))
-      {
-        NSFontDescriptor *s1 = [NSFontDescriptor new];
-        NSFontDescriptor *sDesc
-          = [[s1 fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
-              fontDescriptorWithFamily: family];
-	list = Fcons (ns_descriptor_to_entity (sDesc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
-					 "synthItal"), list);
-        [s1 release];
       }
 
     unblock_input ();
@@ -652,7 +904,6 @@ Properties to be considered are same as for list().  */
                objectEnumerator];
   while ((family = [families nextObject]))
       list = Fcons (intern ([family UTF8String]), list);
-  /* FIXME: escape the name?  */
 
   if (NSFONT_TRACE)
     fprintf (stderr, "nsfont: list families returning %"pD"d entries\n",
@@ -668,18 +919,15 @@ Properties to be considered are same as for list().  */
 static Lisp_Object
 nsfont_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
 {
-  BOOL synthItal;
-  unsigned int traits = 0;
   struct nsfont_info *font_info;
   struct font *font;
   NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
   NSFontManager *fontMgr = [NSFontManager sharedFontManager];
   NSString *family;
   NSFont *nsfont, *sfont;
-  Lisp_Object tem;
   NSRect brect;
   Lisp_Object font_object;
-  int fixLeopardBug;
+  Lisp_Object tem;
 
   block_input ();
 
@@ -692,42 +940,20 @@ Properties to be considered are same as for list().  */
   if (pixel_size <= 0)
     {
       /* try to get it out of frame params */
-        Lisp_Object tem = get_frame_param (f, Qfontsize);
-        pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
+      tem = get_frame_param (f, Qfontsize);
+      pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
     }
 
   tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
-  synthItal = !NILP (tem) && !strncmp ("synthItal", SSDATA (SYMBOL_NAME (tem)),
-                                       9);
   family = ns_get_family (font_entity);
   if (family == nil)
     family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-  /* Should be > 0.23 as some font descriptors (e.g. Terminus) set to that
-     when setting family in ns_spec_to_descriptor().  */
-  if (ns_attribute_fvalue (fontDesc, NSFontWeightTrait) > 0.50F)
-      traits |= NSBoldFontMask;
-  if (ns_attribute_fvalue (fontDesc, NSFontSlantTrait) > 0.05F)
-      traits |= NSItalicFontMask;
-
-  /* see https://web.archive.org/web/20100201175731/http://cocoadev.com/forums/comments.php?DiscussionID=74 */
-  fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
-  nsfont = [fontMgr fontWithFamily: family
-                            traits: traits weight: fixLeopardBug
-			      size: pixel_size];
-  /* if didn't find, try synthetic italic */
-  if (nsfont == nil && synthItal)
-    {
-      nsfont = [fontMgr fontWithFamily: family
-                                traits: traits & ~NSItalicFontMask
-                                weight: fixLeopardBug size: pixel_size];
-    }
+
+  nsfont = [NSFont fontWithDescriptor: fontDesc
+				 size: pixel_size];
 
   if (nsfont == nil)
-    {
-      message_with_string ("*** Warning: font in family `%s' not found",
-                          build_string ([family UTF8String]), 1);
-      nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
-    }
+    nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
 
   if (NSFONT_TRACE)
     NSLog (@"%@\n", nsfont);
@@ -740,7 +966,7 @@ when setting family in ns_spec_to_descriptor().  */
   if (!font)
     {
       unblock_input ();
-      return Qnil; /* FIXME: other terms do, but returning Qnil causes segfault.  */
+      return Qnil;
     }
 
   font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs);
@@ -781,7 +1007,7 @@ when setting family in ns_spec_to_descriptor().  */
     font_info->name = xstrdup (fontName);
     font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
     font_info->ital =
-      synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
+      ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
 
     /* Metrics etc.; some fonts return an unusually large max advance, so we
        only use it for fonts that have wide characters.  */
@@ -808,8 +1034,6 @@ when setting family in ns_spec_to_descriptor().  */
       lrint (brect.size.width - (CGFloat) font_info->width);
 
     /* set up metrics portion of font struct */
-    font->ascent = lrint([sfont ascender]);
-    font->descent = -lrint(floor(adjusted_descender));
     font->space_width = lrint (ns_char_width (sfont, ' '));
     font->max_width = lrint (font_info->max_bounds.width);
     font->min_width = font->space_width;  /* Approximate.  */
@@ -871,7 +1095,7 @@ when setting family in ns_spec_to_descriptor().  */
 {
   struct nsfont_info *font_info = (struct nsfont_info *)font;
   unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
-  unsigned short g;
+  unsigned int g;
 
   if (c > 0xFFFF)
     return FONT_INVALID_CODE;
@@ -934,51 +1158,23 @@ is false when (FROM > 0 || TO < S->nchars).  */
 static int
 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
              bool with_background)
-/* NOTE: focus and clip must be set.  */
 {
-  static unsigned char cbuf[1024];
-  unsigned char *c = cbuf;
-#if GNUSTEP_GUI_MAJOR_VERSION > 0 || GNUSTEP_GUI_MINOR_VERSION > 22
-  static CGFloat advances[1024];
-  CGFloat *adv = advances;
-#else
-  static float advances[1024];
-  float *adv = advances;
-#endif
+  NSGlyph *c = alloca ((to - from) * sizeof *c);
+
   struct face *face;
   NSRect r;
   struct nsfont_info *font;
-  NSColor *col, *bgCol;
-  unsigned *t = s->char2b;
-  int i, len, flags;
+  NSColor *col;
+  int len = to - from;
   char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
 
   block_input ();
 
-  font = (struct nsfont_info *)s->face->font;
+  font = (struct nsfont_info *) s->font;
   if (font == NULL)
     font = (struct nsfont_info *)FRAME_FONT (s->f);
 
-  /* Select face based on input flags.  */
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
-  switch (flags)
-    {
-    case NS_DUMPGLYPH_CURSOR:
-      face = s->face;
-      break;
-    case NS_DUMPGLYPH_MOUSEFACE:
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-      break;
-    default:
-      face = s->face;
-    }
+  face = s->face;
 
   r.origin.x = s->x;
   if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
@@ -987,91 +1183,24 @@ is false when (FROM > 0 || TO < S->nchars).  */
   r.origin.y = s->y;
   r.size.height = FONT_HEIGHT (font);
 
-  /* Convert UTF-16 (?) to UTF-8 and determine advances.  Note if we just ask
-     NS to render the string, it will come out differently from the individual
-     character widths added up because of layout processing.  */
-  {
-    int cwidth, twidth = 0;
-    int hi, lo;
-    /* FIXME: composition: no vertical displacement is considered.  */
-    t += from; /* advance into composition */
-    for (i = from; i < to; i++, t++)
-      {
-        hi = (*t & 0xFF00) >> 8;
-        lo = *t & 0x00FF;
-        if (isComposite)
-          {
-	    if (!s->first_glyph->u.cmp.automatic)
-		cwidth = s->cmp->offsets[i * 2] /* (H offset) */ - twidth;
-	    else
-	      {
-		Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
-		Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
-		if (NILP (LGLYPH_ADJUSTMENT (glyph)))
-		    cwidth = LGLYPH_WIDTH (glyph);
-		else
-		  {
-		    cwidth = LGLYPH_WADJUST (glyph);
-		    *(adv-1) += LGLYPH_XOFF (glyph);
-		  }
-	      }
-          }
-        else
-          {
-            if (!font->metrics[hi]) /* FIXME: why/how can we need this now?  */
-              ns_glyph_metrics (font, hi);
-            cwidth = font->metrics[hi][lo].width;
-          }
-        twidth += cwidth;
-        *adv++ = cwidth;
-        c += CHAR_STRING (*t, c); /* This converts the char to UTF-8.  */
-      }
-    len = adv - advances;
-    r.size.width = twidth;
-    *c = 0;
-  }
+  for (int i = from; i < to; ++i)
+    c[i] = s->char2b[i];
 
   /* Fill background if requested.  */
   if (with_background && !isComposite)
     {
-      NSRect br = r;
-      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
-      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
-
-      if (s->row->full_width_p)
-        {
-          if (br.origin.x <= fibw + 1 + mbox_line_width)
-            {
-              br.size.width += br.origin.x - mbox_line_width;
-              br.origin.x = mbox_line_width;
-            }
-          if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
-                <= fibw+1)
-            br.size.width += fibw;
-        }
-      if (s->face->box == FACE_NO_BOX)
-        {
-          /* Expand unboxed top row over internal border.  */
-          if (br.origin.y <= fibw + 1 + mbox_line_width)
-            {
-              br.size.height += br.origin.y;
-              br.origin.y = 0;
-            }
-        }
-      else
-        {
-          int correction = abs (s->face->box_horizontal_line_width)+1;
-          br.origin.y += correction;
-          br.size.height -= 2*correction;
-          correction = abs (s->face->box_vertical_line_width)+1;
-          br.origin.x += correction;
-          br.size.width -= 2*correction;
-        }
+      NSRect br = NSMakeRect (x, y - FONT_BASE (s->font),
+			      s->width, FONT_HEIGHT (s->font));
 
       if (!s->face->stipple)
-        [(NS_FACE_BACKGROUND (face) != 0
-          ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-          : FRAME_BACKGROUND_COLOR (s->f)) set];
+	{
+	  if (s->hl != DRAW_CURSOR)
+	    [(NS_FACE_BACKGROUND (face) != 0
+	      ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+	      : FRAME_BACKGROUND_COLOR (s->f)) set];
+	  else
+	    [FRAME_CURSOR_COLOR (s->f) set];
+	}
       else
         {
           struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
@@ -1080,43 +1209,32 @@ is false when (FROM > 0 || TO < S->nchars).  */
       NSRectFill (br);
     }
 
-
   /* set up for character rendering */
   r.origin.y = y;
 
-  col = (NS_FACE_FOREGROUND (face) != 0
-         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
-         : FRAME_FOREGROUND_COLOR (s->f));
-
-  bgCol = (flags != NS_DUMPGLYPH_FOREGROUND ? nil
-           : (NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)));
+  if (s->hl == DRAW_CURSOR)
+    col = FRAME_BACKGROUND_COLOR (s->f);
+  else
+    col = (NS_FACE_FOREGROUND (face) != 0
+	   ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
+	   : FRAME_FOREGROUND_COLOR (s->f));
 
   /* render under GNUstep using DPS */
   {
-    NSGraphicsContext *context = GSCurrentContext ();
-
+    NSGraphicsContext *context = [NSGraphicsContext currentContext];
     DPSgsave (context);
-    [font->nsfont set];
-
-    /* do erase if "foreground" mode */
-    if (bgCol != nil)
+    if (s->clip_head)
       {
-        [bgCol set];
-        DPSmoveto (context, r.origin.x, r.origin.y);
-/*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
-        DPSxshow (context, (const char *) cbuf, advances, len);
-        DPSstroke (context);
-        [col set];
-/*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
+	DPSrectclip (context, s->clip_head->x, 0,
+		     FRAME_PIXEL_WIDTH (s->f),
+		     FRAME_PIXEL_HEIGHT (s->f));
       }
+    [font->nsfont set];
 
     [col set];
 
-    /* draw with DPSxshow () */
     DPSmoveto (context, r.origin.x, r.origin.y);
-    DPSxshow (context, (const char *) cbuf, advances, len);
+    GSShowGlyphs (context, c, len);
     DPSstroke (context);
 
     DPSgrestore (context);
@@ -1126,6 +1244,360 @@ is false when (FROM > 0 || TO < S->nchars).  */
   return to-from;
 }
 
+static NSUInteger
+ns_font_shape (NSFont *font, NSString *string,
+	       struct ns_glyph_layout *glyph_layouts, NSUInteger glyph_len,
+	       enum lgstring_direction dir)
+{
+  NSUInteger i;
+  NSUInteger result = 0;
+  NSTextStorage *textStorage;
+  NSLayoutManager *layoutManager;
+  NSTextContainer *textContainer;
+  NSUInteger stringLength;
+  NSPoint spaceLocation;
+  /* numberOfGlyphs can't actually be 0, but this pacifies GCC */
+  NSUInteger used, numberOfGlyphs = 0;
+
+  textStorage = [[NSTextStorage alloc] initWithString:string];
+  layoutManager = [[NSLayoutManager alloc] init];
+  textContainer = [[NSTextContainer alloc] init];
+
+  /* Append a trailing space to measure baseline position.  */
+  [textStorage appendAttributedString:([[[NSAttributedString alloc]
+                                          initWithString:@" "] autorelease])];
+  [textStorage setFont:font];
+  [textContainer setLineFragmentPadding:0];
+
+  [layoutManager addTextContainer:textContainer];
+  [textContainer release];
+  [textStorage addLayoutManager:layoutManager];
+  [layoutManager release];
+
+  if (!(textStorage && layoutManager && textContainer))
+    emacs_abort ();
+
+  stringLength = [string length];
+
+  /* Force layout.  */
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  spaceLocation = [layoutManager locationForGlyphAtIndex:stringLength];
+
+  /* Remove the appended trailing space because otherwise it may
+     generate a wrong result for a right-to-left text.  */
+  [textStorage beginEditing];
+  [textStorage deleteCharactersInRange:(NSMakeRange (stringLength, 1))];
+  [textStorage endEditing];
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  i = 0;
+  while (i < stringLength)
+    {
+      NSRange range;
+      NSFont *fontInTextStorage =
+        [textStorage attribute: NSFontAttributeName
+		       atIndex:i
+                     longestEffectiveRange: &range
+                       inRange: NSMakeRange (0, stringLength)];
+
+      if (!(fontInTextStorage == font
+            || [[fontInTextStorage fontName] isEqualToString:[font fontName]]))
+        break;
+      i = NSMaxRange (range);
+    }
+  if (i < stringLength)
+    /* Make the test `used <= glyph_len' below fail if textStorage
+       contained some fonts other than the specified one.  */
+    used = glyph_len + 1;
+  else
+    {
+      NSRange range = NSMakeRange (0, stringLength);
+
+      range = [layoutManager glyphRangeForCharacterRange:range
+                                    actualCharacterRange:NULL];
+      numberOfGlyphs = NSMaxRange (range);
+      used = numberOfGlyphs;
+      for (i = 0; i < numberOfGlyphs; i++)
+        if ([layoutManager notShownAttributeForGlyphAtIndex:i])
+          used--;
+    }
+
+  if (0 < used && used <= glyph_len)
+    {
+      NSUInteger glyphIndex, prevGlyphIndex;
+      NSUInteger *permutation;
+      NSRange compRange, range;
+      CGFloat totalAdvance;
+
+      glyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      permutation = NULL;
+#define RIGHT_TO_LEFT_P permutation
+
+      /* Fill the `comp_range' member of struct mac_glyph_layout, and
+         setup a permutation for right-to-left text.  */
+      compRange = NSMakeRange (0, 0);
+      for (range = NSMakeRange (0, 0); NSMaxRange (range) < used;
+           range.length++)
+        {
+          struct ns_glyph_layout *gl = glyph_layouts + NSMaxRange (range);
+          NSUInteger characterIndex =
+            [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
+
+          gl->string_index = characterIndex;
+
+          if (characterIndex >= NSMaxRange (compRange))
+            {
+              compRange.location = NSMaxRange (compRange);
+              do
+                {
+                  NSRange characterRange =
+                    [string
+                      rangeOfComposedCharacterSequenceAtIndex:characterIndex];
+
+                  compRange.length =
+                    NSMaxRange (characterRange) - compRange.location;
+                  [layoutManager glyphRangeForCharacterRange:compRange
+                                        actualCharacterRange:&characterRange];
+                  characterIndex = NSMaxRange (characterRange) - 1;
+                }
+              while (characterIndex >= NSMaxRange (compRange));
+
+              if (RIGHT_TO_LEFT_P)
+                for (i = 0; i < range.length; i++)
+                  permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+              range = NSMakeRange (NSMaxRange (range), 0);
+            }
+
+          gl->comp_range.location = compRange.location;
+          gl->comp_range.length = compRange.length;
+
+          while (++glyphIndex < numberOfGlyphs)
+            if (![layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+              break;
+        }
+      if (RIGHT_TO_LEFT_P)
+        for (i = 0; i < range.length; i++)
+          permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+      /* Then fill the remaining members.  */
+      glyphIndex = prevGlyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      if (!RIGHT_TO_LEFT_P)
+        totalAdvance = 0;
+      else
+        {
+          NSUInteger nrects;
+          NSRect *glyphRects =
+            [layoutManager
+              rectArrayForGlyphRange:(NSMakeRange (0, numberOfGlyphs))
+              withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                     inTextContainer:textContainer rectCount:&nrects];
+
+          totalAdvance = NSMaxX (glyphRects[0]);
+        }
+
+      for (i = 0; i < used; i++)
+        {
+          struct ns_glyph_layout *gl;
+          NSPoint location;
+          NSUInteger nextGlyphIndex;
+          NSRange glyphRange;
+          NSRect *glyphRects;
+          NSUInteger nrects;
+
+          if (!RIGHT_TO_LEFT_P)
+            gl = glyph_layouts + i;
+          else
+            {
+              NSUInteger dest = permutation[i];
+
+              gl = glyph_layouts + dest;
+              if (i < dest)
+                {
+                  NSUInteger tmp = gl->string_index;
+
+                  gl->string_index = glyph_layouts[i].string_index;
+                  glyph_layouts[i].string_index = tmp;
+                }
+            }
+          gl->glyph_id = [layoutManager glyphAtIndex: glyphIndex];
+
+          location = [layoutManager locationForGlyphAtIndex:glyphIndex];
+          gl->baseline_delta = spaceLocation.y - location.y;
+
+          for (nextGlyphIndex = glyphIndex + 1; nextGlyphIndex < numberOfGlyphs;
+               nextGlyphIndex++)
+            if (![layoutManager
+                   notShownAttributeForGlyphAtIndex:nextGlyphIndex])
+              break;
+
+          if (!RIGHT_TO_LEFT_P)
+            {
+              CGFloat maxX;
+
+              if (prevGlyphIndex == 0)
+                glyphRange = NSMakeRange (0, nextGlyphIndex);
+              else
+                glyphRange = NSMakeRange (glyphIndex,
+                                          nextGlyphIndex - glyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              maxX = max (NSMaxX (glyphRects[0]), totalAdvance);
+              gl->advance_delta = location.x - totalAdvance;
+              gl->advance = maxX - totalAdvance;
+              totalAdvance = maxX;
+            }
+          else
+            {
+              CGFloat minX;
+
+              if (nextGlyphIndex == numberOfGlyphs)
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          numberOfGlyphs - prevGlyphIndex);
+              else
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          glyphIndex + 1 - prevGlyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              minX = min (NSMinX (glyphRects[0]), totalAdvance);
+              gl->advance = totalAdvance - minX;
+              totalAdvance = minX;
+              gl->advance_delta = location.x - totalAdvance;
+            }
+
+          prevGlyphIndex = glyphIndex + 1;
+          glyphIndex = nextGlyphIndex;
+        }
+
+      if (RIGHT_TO_LEFT_P)
+        xfree (permutation);
+
+#undef RIGHT_TO_LEFT_P
+
+      result = used;
+    }
+  [textStorage release];
+
+  return result;
+}
+
+static Lisp_Object
+nsfont_shape (Lisp_Object lgstring, Lisp_Object direction)
+{
+  struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  struct nsfont_info *font_info = (struct nsfont_info *) font;
+  struct ns_glyph_layout *glyph_layouts;
+  NSFont *nsfont = font_info->nsfont;
+  ptrdiff_t glyph_len, len, i;
+  Lisp_Object tem;
+  unichar *mb_buf;
+  NSUInteger used;
+
+  glyph_len = LGSTRING_GLYPH_LEN (lgstring);
+  for (i = 0; i < glyph_len; ++i)
+    {
+      tem = LGSTRING_GLYPH (lgstring, i);
+
+      if (NILP (tem))
+	break;
+    }
+
+  len = i;
+
+  if (INT_MAX / 2 < len)
+    memory_full (SIZE_MAX);
+
+  block_input ();
+
+  mb_buf = alloca (len * sizeof *mb_buf);
+
+  for (i = 0; i < len; ++i)
+    {
+      uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
+      mb_buf[i] = (unichar) c;
+    }
+
+  NSString *string = [NSString stringWithCharacters: mb_buf
+					     length: len];
+  unblock_input ();
+
+  if (!string)
+    return Qnil;
+
+  block_input ();
+
+  enum lgstring_direction dir = DIR_UNKNOWN;
+
+  if (EQ (direction, QL2R))
+    dir = DIR_L2R;
+  else if (EQ (direction, QR2L))
+    dir = DIR_R2L;
+  glyph_layouts = alloca (sizeof (struct ns_glyph_layout) * glyph_len);
+  used = ns_font_shape (nsfont, string, glyph_layouts, glyph_len, dir);
+
+  for (i = 0; i < used; i++)
+    {
+      Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
+      struct ns_glyph_layout *gl = glyph_layouts + i;
+      EMACS_INT from, to;
+      struct font_metrics metrics;
+
+      if (NILP (lglyph))
+        {
+          lglyph = LGLYPH_NEW ();
+          LGSTRING_SET_GLYPH (lgstring, i, lglyph);
+        }
+
+      from = gl->comp_range.location;
+      LGLYPH_SET_FROM (lglyph, from);
+
+      to = gl->comp_range.location + gl->comp_range.length;
+      LGLYPH_SET_TO (lglyph, to - 1);
+
+      /* LGLYPH_CHAR is used in `describe-char' for checking whether
+         the composition is trivial.  */
+      {
+        UTF32Char c;
+
+        if (mb_buf[gl->string_index] >= 0xD800
+            && mb_buf[gl->string_index] < 0xDC00)
+          c = (((mb_buf[gl->string_index] - 0xD800) << 10)
+               + (mb_buf[gl->string_index + 1] - 0xDC00) + 0x10000);
+        else
+          c = mb_buf[gl->string_index];
+
+        LGLYPH_SET_CHAR (lglyph, c);
+      }
+
+      {
+        unsigned long cc = gl->glyph_id;
+        LGLYPH_SET_CODE (lglyph, cc);
+      }
+
+      nsfont_text_extents (font, &gl->glyph_id, 1, &metrics);
+      LGLYPH_SET_WIDTH (lglyph, metrics.width);
+      LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
+      LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
+      LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
+      LGLYPH_SET_DESCENT (lglyph, metrics.descent);
+    }
+  unblock_input ();
+
+  return make_fixnum (used);
+}
 
 
 /* ==========================================================================
@@ -1134,6 +1606,50 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
    ========================================================================== */
 
+static NSGlyph
+ns_uni_to_glyphs_1 (struct nsfont_info *info, unsigned int c)
+{
+  unichar characters[] = { c };
+  NSString *string =
+    [NSString stringWithCharacters: characters
+			    length: 1];
+  NSDictionary *attributes =
+    [NSDictionary dictionaryWithObjectsAndKeys:
+		    info->nsfont, NSFontAttributeName, nil];
+  NSTextStorage *storage = [[NSTextStorage alloc] initWithString: string
+						      attributes: attributes];
+  NSTextContainer *text_container = [[NSTextContainer alloc] init];
+  NSLayoutManager *manager = [[NSLayoutManager alloc] init];
+
+  [manager addTextContainer: text_container];
+  [text_container release]; /* Retained by manager */
+  [storage addLayoutManager: manager];
+  [manager release]; /* Retained by storage */
+
+  NSFont *font_in_storage = [storage attribute: NSFontAttributeName
+				       atIndex:0
+				effectiveRange: NULL];
+  NSGlyph glyph = FONT_INVALID_CODE;
+
+  if ((font_in_storage == info->nsfont
+       || [[font_in_storage fontName] isEqualToString: [info->nsfont fontName]]))
+    {
+      @try
+	{
+	  glyph = [manager glyphAtIndex: 0];
+	}
+      @catch (NSException *e)
+	{
+	  /* GNUstep bug? */
+	  glyph = 'X';
+	}
+    }
+
+  [storage release];
+
+  return glyph;
+}
+
 /* Find and cache corresponding glyph codes for unicode values in given
    hi-byte block of 256.  */
 static void
@@ -1141,7 +1657,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   unichar *unichars = xmalloc (0x101 * sizeof (unichar));
   unsigned int i, g, idx;
-  unsigned short *glyphs;
+  unsigned int *glyphs;
 
   if (NSFONT_TRACE)
     fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
@@ -1149,7 +1665,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
   block_input ();
 
-  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
+  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned int));
   if (!unichars || !(font_info->glyphs[block]))
     emacs_abort ();
 
@@ -1166,7 +1682,8 @@ is false when (FROM > 0 || TO < S->nchars).  */
     for (i = 0; i < 0x100; i++, glyphs++)
       {
         g = unichars[i];
-        *glyphs = g;
+	NSGlyph glyph = ns_uni_to_glyphs_1 (font_info, g);
+        *glyphs = glyph;
       }
   }
 
@@ -1175,18 +1692,19 @@ is false when (FROM > 0 || TO < S->nchars).  */
 }
 
 
-/* Determine and cache metrics for corresponding glyph codes in given
-   hi-byte block of 256.  */
+/* Determine and cache metrics for glyphs in given hi-byte block of
+   256.  */
 static void
-ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
+ns_glyph_metrics (struct nsfont_info *font_info, unsigned int block)
 {
-  unsigned int i, g;
+  unsigned int i;
+  NSGlyph g;
   unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
   NSFont *sfont;
   struct font_metrics *metrics;
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
+    fprintf (stderr, "%p\tComputing metrics for glyphs in block %u\n",
             font_info, block);
 
   /* not implemented yet (as of startup 0.18), so punt */
@@ -1209,19 +1727,14 @@ is false when (FROM > 0 || TO < S->nchars).  */
       w = max ([sfont advancementForGlyph: g].width, 2.0);
       metrics->width = lrint (w);
 
-      lb = r.origin.x;
-      rb = r.size.width - w;
-      // Add to bearing for LCD smoothing.  We don't know if it is there.
-      if (lb < 0)
-        metrics->lbearing = round (lb - LCD_SMOOTHING_MARGIN);
-      if (font_info->ital)
-        rb += (CGFloat) (0.22F * font_info->height);
-      metrics->rbearing = lrint (w + rb + LCD_SMOOTHING_MARGIN);
-
-      metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
-      /* lrint (hshrink * [sfont ascender] + expand * hd/2); */
-      metrics->ascent = r.size.height - metrics->descent;
-      /* -lrint (hshrink* [sfont descender] - expand * hd/2); */
+      lb = NSMinX (r);
+      rb = NSMaxX (r);
+
+      metrics->rbearing = lrint (rb);
+      metrics->lbearing = lrint (lb);
+
+      metrics->descent = NSMinY (r);
+      metrics->ascent = NSMaxY (r);
     }
   unblock_input ();
 }
@@ -1257,6 +1770,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
   .has_char = nsfont_has_char,
   .encode_char = nsfont_encode_char,
   .text_extents = nsfont_text_extents,
+  .shape = nsfont_shape,
   .draw = nsfont_draw,
   };
 
@@ -1265,7 +1779,6 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   DEFSYM (Qcondensed, "condensed");
   DEFSYM (Qexpanded, "expanded");
-  DEFSYM (Qapple, "apple");
   DEFSYM (Qmedium, "medium");
   DEFVAR_LISP ("ns-reg-to-script", Vns_reg_to_script,
                doc: /* Internal use: maps font registry to Unicode script.  */);
diff --git a/src/nsterm.h b/src/nsterm.h
index 4bbcf43973..944dbd727c 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -820,7 +820,7 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
   XCharStruct max_bounds;
   /* We compute glyph codes and metrics on-demand in blocks of 256 indexed
      by hibyte, lobyte.  */
-  unsigned short **glyphs; /* map Unicode index to glyph */
+  unsigned int **glyphs; /* map Unicode index to glyph */
   struct font_metrics **metrics;
 };
 #endif
diff --git a/src/nsterm.m b/src/nsterm.m
index 641f3f548b..4c915dac5d 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1079,11 +1079,16 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
   /* clipping */
   if (r)
     {
-      [[NSGraphicsContext currentContext] saveGraphicsState];
+      NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+      [ctx saveGraphicsState];
+#ifdef NS_IMPL_COCOA
       if (n == 2)
         NSRectClipList (r, 2);
       else
         NSRectClip (*r);
+#else
+      GSRectClipList (ctx, r, n);
+#endif
       gsaved = YES;
     }
 }
@@ -2435,9 +2440,6 @@ Hide the window (X11 semantics)
       EmacsView *view = FRAME_NS_VIEW (f);
       FRAME_POINTER_TYPE (f) = cursor;
       [[view window] invalidateCursorRectsForView: view];
-      /* Redisplay assumes this function also draws the changed frame
-         cursor, but this function doesn't, so do it explicitly.  */
-      gui_update_cursor (f, 1);
     }
 }
 
@@ -2849,38 +2851,31 @@ Hide the window (X11 semantics)
      External (RIF); compute left/right overhang of whole string and set in s
    -------------------------------------------------------------------------- */
 {
-  if (s->char2b)
+  if (s->cmp == NULL
+      && (s->first_glyph->type == CHAR_GLYPH
+	  || s->first_glyph->type == COMPOSITE_GLYPH))
     {
       struct font_metrics metrics;
-      if (s->first_glyph->type == CHAR_GLYPH && !s->font_not_found_p)
-        {
-          struct font *font = s->font;
-          font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
-          s->left_overhang = -metrics.lbearing;
-          s->right_overhang
-            = metrics.rbearing > metrics.width
-            ? metrics.rbearing - metrics.width : 0;
-        }
-      else if (s->first_glyph->type == COMPOSITE_GLYPH)
-        {
-          Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
 
-          composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
-          s->right_overhang = (metrics.rbearing > metrics.width
-                               ? metrics.rbearing - metrics.width : 0);
-          s->left_overhang = metrics.lbearing < 0 ? -metrics.lbearing : 0;
-        }
+      if (s->first_glyph->type == CHAR_GLYPH)
+	{
+	  struct font *font = s->font;
+	  font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+	}
+      else
+	{
+	  Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+	  composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+	}
+      s->right_overhang = (metrics.rbearing > metrics.width
+			   ? metrics.rbearing - metrics.width : 0);
+      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
     }
-  else
+  else if (s->cmp)
     {
-      s->left_overhang = 0;
-#ifdef NS_IMPL_GNUSTEP
-      if (EQ (font->driver->type, Qns))
-        s->right_overhang = ((struct nsfont_info *)font)->ital ?
-          FONT_HEIGHT (font) * 0.2 : 0;
-      else
-#endif
-        s->right_overhang = 0;
+      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+      s->left_overhang = - s->cmp->lbearing;
     }
 }
 
@@ -3020,14 +3015,13 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   struct frame *f = WINDOW_XFRAME (w);
   struct glyph *phys_cursor_glyph;
   struct glyph *cursor_glyph;
-  struct face *face;
-  NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
 
   /* If cursor is out of bounds, don't draw garbage.  This can happen
      in mini-buffer windows when switching between echo area glyphs
      and mini-buffer.  */
 
-  NSTRACE ("ns_draw_window_cursor");
+  NSTRACE ("ns_draw_window_cursor (on = %d, cursor_type = %d)",
+	   on_p, cursor_type);
 
   if (!on_p)
     return;
@@ -3043,6 +3037,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
     {
+      NSTRACE_MSG ("No phys cursor glyph was found!");
+
       if (glyph_row->exact_window_width_line_p
           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
         {
@@ -3052,10 +3048,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       return;
     }
 
-  /* We draw the cursor (with NSRectFill), then draw the glyph on top
-     (other terminals do it the other way round).  We must set
-     w->phys_cursor_width to the cursor width.  For bar cursors, that
-     is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
 
   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
@@ -3087,17 +3079,17 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   /* Prevent the cursor from being drawn outside the text area.  */
   r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
 
-  ns_focus (f, &r, 1);
+  ns_focus (f, NULL, 0);
 
-  face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
-  if (face && NS_FACE_BACKGROUND (face)
-      == ns_index_color (FRAME_CURSOR_COLOR (f), f))
-    {
-      [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
-      hollow_color = FRAME_CURSOR_COLOR (f);
-    }
-  else
-    [FRAME_CURSOR_COLOR (f) set];
+  NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+  [ctx saveGraphicsState];
+#ifdef NS_IMPL_GNUSTEP
+  GSRectClipList (ctx, &r, 1);
+#else
+  NSRectClip (r);
+#endif
+
+  [FRAME_CURSOR_COLOR (f) set];
 
   switch (cursor_type)
     {
@@ -3105,13 +3097,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     case NO_CURSOR:
       break;
     case FILLED_BOX_CURSOR:
-      NSRectFill (r);
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
       break;
     case HOLLOW_BOX_CURSOR:
-      NSRectFill (r);
-      [hollow_color set];
-      NSRectFill (NSInsetRect (r, 1, 1));
-      [FRAME_CURSOR_COLOR (f) set];
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_NORMAL_TEXT);
+      [NSBezierPath strokeRect: r];
       break;
     case HBAR_CURSOR:
       NSRectFill (r);
@@ -3127,12 +3117,9 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       NSRectFill (s);
       break;
     }
-  ns_unfocus (f);
 
-  /* Draw the character under the cursor.  Other terms only draw
-     the character on top of box cursors, so do the same here.  */
-  if (cursor_type == FILLED_BOX_CURSOR || cursor_type == HOLLOW_BOX_CURSOR)
-    draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+  [ctx restoreGraphicsState];
+  ns_unfocus (f);
 }
 
 
@@ -3312,16 +3299,18 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   if (s->for_overlaps)
     return;
 
+  if (s->hl == DRAW_CURSOR)
+    [FRAME_BACKGROUND_COLOR (s->f) set];
+  else if (face->underline_defaulted_p)
+    [defaultCol set];
+  else
+    [ns_lookup_indexed_color (face->underline_color, s->f) set];
+
   /* Do underline.  */
   if (face->underline)
     {
       if (s->face->underline == FACE_UNDER_WAVE)
         {
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
-
           ns_draw_underwave (s, width, x);
         }
       else if (s->face->underline == FACE_UNDER_LINE)
@@ -3392,11 +3381,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
           s->underline_position = position;
 
           r = NSMakeRect (x, s->ybase + position, width, thickness);
-
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
           NSRectFill (r);
         }
     }
@@ -3406,11 +3390,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     {
       NSRect r;
       r = NSMakeRect (x, s->y, width, 1);
-
-      if (face->overline_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->overline_color, s->f) set];
       NSRectFill (r);
     }
 
@@ -3433,10 +3412,6 @@ larger if there are taller display elements (e.g., characters
       dy = lrint ((glyph_height - h) / 2);
       r = NSMakeRect (x, glyph_y + dy, width, 1);
 
-      if (face->strike_through_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
       NSRectFill (r);
     }
 }
@@ -3584,17 +3559,7 @@ Function modeled after x_draw_glyph_string_box ().
   struct glyph *last_glyph;
   NSRect r;
   int hthickness, vthickness;
-  struct face *face;
-
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = s->face;
+  struct face *face = s->face;
 
   vthickness = face->box_vertical_line_width;
   hthickness = face->box_horizontal_line_width;
@@ -3668,34 +3633,26 @@ Function modeled after x_draw_glyph_string_box ().
 	  || FONT_TOO_HIGH (s->font)
           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
 	{
-          struct face *face;
-          if (s->hl == DRAW_MOUSE_FACE)
-            {
-              face
-		= FACE_FROM_ID_OR_NULL (s->f,
-					MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-              if (!face)
-                face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-            }
-          else
-            face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+          struct face *face = s->face;
           if (!face->stipple)
-            [(NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)) set];
+	    {
+	      if (s->hl != DRAW_CURSOR)
+		[(NS_FACE_BACKGROUND (face) != 0
+		  ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+		  : FRAME_BACKGROUND_COLOR (s->f)) set];
+	      else
+		[FRAME_CURSOR_COLOR (s->f) set];
+	    }
           else
             {
               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
             }
 
-          if (s->hl != DRAW_CURSOR)
-            {
-              NSRect r = NSMakeRect (s->x, s->y + box_line_width,
-                                    s->background_width,
-                                    s->height-2*box_line_width);
-              NSRectFill (r);
-            }
+	  NSRect r = NSMakeRect (s->x, s->y + box_line_width,
+				 s->background_width,
+				 s->height-2*box_line_width);
+	  NSRectFill (r);
 
 	  s->background_filled_p = 1;
 	}
@@ -3716,7 +3673,7 @@ Function modeled after x_draw_glyph_string_box ().
   int th;
   char raised_p;
   NSRect br;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *tdCol;
 
   NSTRACE ("ns_dumpglyphs_image");
@@ -3737,15 +3694,6 @@ Function modeled after x_draw_glyph_string_box ().
   /* Draw BG: if we need larger area than image itself cleared, do that,
      otherwise, since we composite the image under NS (instead of mucking
      with its background color), we must clear just the image area.  */
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-       face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
 
   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
 
@@ -3816,16 +3764,8 @@ Function modeled after x_draw_glyph_string_box ().
 
   if (s->hl == DRAW_CURSOR)
     {
-    [FRAME_CURSOR_COLOR (s->f) set];
-    if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
+      [FRAME_CURSOR_COLOR (s->f) set];
       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
-    else
-      /* Currently on NS img->mask is always 0.  Since
-         get_window_cursor_type specifies a hollow box cursor when on
-         a non-masked image we never reach this clause.  But we put it
-         in, in anticipation of better support for image masks on
-         NS.  */
-      tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
     }
   else
     {
@@ -3877,66 +3817,35 @@ Function modeled after x_draw_glyph_string_box ().
 static void
 ns_dumpglyphs_stretch (struct glyph_string *s)
 {
-  NSRect r[2];
   NSRect glyphRect;
-  int n;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *fgCol, *bgCol;
 
   if (!s->background_filled_p)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
 
-      if (s->hl == DRAW_MOUSE_FACE)
-        {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-        }
-      else
-        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+      face = s->face;
 
       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
 
-      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
-
-      [bgCol set];
-
-      /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
-         overwriting cursor (usually when cursor on a tab) */
       if (s->hl == DRAW_CURSOR)
-        {
-          CGFloat x, width;
+	{
+	  fgCol = bgCol;
+	  bgCol = FRAME_CURSOR_COLOR (s->f);
+	}
 
-          /* FIXME: This looks like it will only work for left to
-             right languages.  */
-          x = NSMinX (glyphRect);
-          width = s->w->phys_cursor_width;
-          glyphRect.size.width -= width;
-          glyphRect.origin.x += width;
+      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
 
-          NSRectFill (glyphRect);
+      [bgCol set];
 
-          /* Draw overlining, etc. on the cursor. */
-          if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-            ns_draw_text_decoration (s, face, bgCol, width, x);
-          else
-            ns_draw_text_decoration (s, face, fgCol, width, x);
-        }
-      else
-        {
-          NSRectFill (glyphRect);
-        }
+      NSRectFill (glyphRect);
 
       /* Draw overlining, etc. on the stretch glyph (or the part
          of the stretch glyph after the cursor). */
       ns_draw_text_decoration (s, face, fgCol, NSWidth (glyphRect),
                                NSMinX (glyphRect));
 
-      ns_unfocus (s->f);
       s->background_filled_p = 1;
     }
 }
@@ -3945,7 +3854,7 @@ overwriting cursor (usually when cursor on a tab) */
 static void
 ns_draw_glyph_string_foreground (struct glyph_string *s)
 {
-  int x, flags;
+  int x;
   struct font *font = s->font;
 
   /* If first glyph of S has a left box line, start drawing the text
@@ -3956,15 +3865,9 @@ overwriting cursor (usually when cursor on a tab) */
   else
     x = s->x;
 
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
   font->driver->draw
     (s, s->cmp_from, s->nchars, x, s->ybase,
-     (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
-     || flags == NS_DUMPGLYPH_MOUSEFACE);
+     !s->for_overlaps && !s->background_filled_p);
 }
 
 
@@ -4071,9 +3974,9 @@ overwriting cursor (usually when cursor on a tab) */
   struct font *font = s->face->font;
   if (! font) font = FRAME_FONT (s->f);
 
-  NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
+  NSTRACE ("ns_draw_glyph_string (hl = %u)", s->hl);
 
-  if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
+  if (s->next && s->right_overhang && !s->for_overlaps)
     {
       int width;
       struct glyph_string *next;
@@ -4110,14 +4013,21 @@ overwriting cursor (usually when cursor on a tab) */
       box_drawn_p = 1;
     }
 
+  n = ns_get_glyph_string_clip_rect (s, r);
+
+  if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+      && !s->clip_tail
+      && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+	  || (s->next && s->next->hl != s->hl && s->right_overhang)))
+    r[0] = NSIntersectionRect (r[0], NSMakeRect (s->x, s->y, s->width, s->height));
+
+  ns_focus (s->f, r, n);
+
   switch (s->first_glyph->type)
     {
 
     case IMAGE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
       ns_dumpglyphs_image (s, r[0]);
-      ns_unfocus (s->f);
       break;
 
     case XWIDGET_GLYPH:
@@ -4130,57 +4040,36 @@ overwriting cursor (usually when cursor on a tab) */
 
     case CHAR_GLYPH:
     case COMPOSITE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
-      if (s->for_overlaps || (s->cmp_from > 0
-			      && ! s->first_glyph->u.cmp.automatic))
-        s->background_filled_p = 1;
-      else
-        ns_maybe_dumpglyphs_background
-          (s, s->first_glyph->type == COMPOSITE_GLYPH);
-
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
       {
-        BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	if (s->for_overlaps || (isComposite
+				&& (s->cmp_from > 0
+				    && ! s->first_glyph->u.cmp.automatic)))
+	  s->background_filled_p = 1;
+	else
+	  ns_maybe_dumpglyphs_background
+	    (s, s->first_glyph->type == COMPOSITE_GLYPH);
 
-        if (isComposite)
-          ns_draw_composite_glyph_string_foreground (s);
-        else
-          ns_draw_glyph_string_foreground (s);
-      }
+	if (isComposite)
+	  ns_draw_composite_glyph_string_foreground (s);
+	else
+	  ns_draw_glyph_string_foreground (s);
 
-      {
-        NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
-                        ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
-                                                   s->f)
-                        : FRAME_FOREGROUND_COLOR (s->f));
-        [col set];
-
-        /* Draw underline, overline, strike-through. */
-        ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	{
+	  NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
+			  ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
+						     s->f)
+			  : FRAME_FOREGROUND_COLOR (s->f));
+	  [col set];
+
+	  /* Draw underline, overline, strike-through. */
+	  ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	}
       }
 
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
-      ns_unfocus (s->f);
       break;
 
     case GLYPHLESS_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
       if (s->for_overlaps || (s->cmp_from > 0
 			      && ! s->first_glyph->u.cmp.automatic))
         s->background_filled_p = 1;
@@ -4190,7 +4079,6 @@ overwriting cursor (usually when cursor on a tab) */
       /* ... */
       /* Not yet implemented.  */
       /* ... */
-      ns_unfocus (s->f);
       break;
 
     default:
@@ -4199,13 +4087,92 @@ overwriting cursor (usually when cursor on a tab) */
 
   /* Draw box if not done already.  */
   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
+    ns_dumpglyphs_box_or_relief (s);
+
+  ns_unfocus (s->f);
+
+  /* Draw surrounding overhangs. */
+  if (s->prev)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-      ns_dumpglyphs_box_or_relief (s);
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *prev;
+
+      for (prev = s->prev; prev; prev = prev->prev)
+	if (prev->hl != s->hl
+	    && prev->x + prev->width + prev->right_overhang > s->x)
+	  {
+	    /* As prev was drawn while clipped to its own area, we
+	       must draw the right_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = prev->hl;
+	    struct face *save_face = prev->face;
+
+	    prev->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    prev->num_clips = 1;
+	    prev->hl = s->hl;
+	    if (prev->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (prev);
+	    else
+	      ns_draw_composite_glyph_string_foreground (prev);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    prev->hl = save;
+	    prev->face = save_face;
+	    prev->num_clips = 0;
+	  }
       ns_unfocus (s->f);
     }
 
+  if (s->next)
+    {
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *next;
+
+      for (next = s->next; next; next = next->next)
+	if (next->hl != s->hl
+	    && next->x - next->left_overhang < s->x + s->width)
+	  {
+	    /* As next will be drawn while clipped to its own area,
+	       we must draw the left_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = next->hl;
+	    struct face *save_face = next->face;
+
+	    next->hl = s->hl;
+	    next->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    next->num_clips = 1;
+	    if (next->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (next);
+	    else
+	      ns_draw_composite_glyph_string_foreground (next);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    next->hl = save;
+	    next->num_clips = 0;
+	    next->face = save_face;
+	    next->clip_head = next;
+	    next->background_filled_p = 0;
+	  }
+      ns_unfocus (s->f);
+    }
   s->num_clips = 0;
 }
 
diff --git a/src/xdisp.c b/src/xdisp.c
index 646beed6f0..9814efd63c 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -29306,7 +29306,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
   for (s = head; s; s = s->next)
     FRAME_RIF (f)->draw_glyph_string (s);
 
-#ifndef HAVE_NS
   /* When focus a sole frame and move horizontally, this clears on_p
      causing a failure to erase prev cursor position. */
   if (area == TEXT_AREA
@@ -29325,7 +29324,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
       notice_overwritten_cursor (w, TEXT_AREA, x0, x1,
 				 row->y, MATRIX_ROW_BOTTOM_Y (row));
     }
-#endif
 
   /* Value is the x-position up to which drawn, relative to AREA of W.
      This doesn't include parts drawn because of overhangs.  */
-- 
2.31.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-General-improvements-to-NS-port.patch --]
[-- Type: text/x-patch, Size: 12123 bytes --]

From 27747e0e1b7836aa346572fbac3796ef2c5121c2 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:51:31 +0800
Subject: [PATCH 2/2] General improvements to NS port

* src/dispextern.h: Remove some !HAVE_NS conditionals around grab
related code.
* src/frame.c (gui_mouse_grabbed, gui_redo_mouse_highlight): Remove
!HAVE_NS conditionals around code.

* src/nsmenu.m (ns_update_menubar): Prevent recursive calls and enable
shallow updates on GNUstep.
(menuNeedsUpdate): Prevent recursive calls.
(ns_menu_show): Fix mysterious GC-related bug.
(update_frame_tool_bar_1): Work around mysterious toolbar sizing bug
on GNUstep.

* src/nsterm.h (struct ns_output): New field for tracking toolbar
visibility changes.

* src/nsterm.m (frame_set_mouse_pixel_position): Implement for
GNUstep.
(ns_redraw_scroll_bars): Enable for GNUstep.
(ns_clear_frame): Redraw scrollbars on GNUstep.
(ns_update_window_end): New function.
(ns_redisplay_interface): Add ns_update_window_end on GNUstep.
(- keyDown): Remove debug code that doesn't work on GNUstep.
(- mouseDown): Enable grab tracking on NS port.
(- resizeWithOldSuperviewSize): Fix build with NSTRACE.

* src/xdisp.c (note_tab_bar_highlight): Enable some code for NS port.
---
 src/dispextern.h |  2 --
 src/frame.c      |  4 ---
 src/nsmenu.m     | 76 +++++++++++++++++++++++++++++++++++++++---------
 src/nsterm.h     |  6 ++++
 src/nsterm.m     | 42 +++++++++++++++++++++-----
 src/xdisp.c      |  2 --
 6 files changed, 102 insertions(+), 30 deletions(-)

diff --git a/src/dispextern.h b/src/dispextern.h
index e03e21fddc..5b28fe7666 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3722,10 +3722,8 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask)			      \
                                           const char *, const char *,
                                           enum resource_types);
 
-#ifndef HAVE_NS /* These both used on W32 and X only.  */
 extern bool gui_mouse_grabbed (Display_Info *);
 extern void gui_redo_mouse_highlight (Display_Info *);
-#endif /* HAVE_NS */
 
 #endif /* HAVE_WINDOW_SYSTEM */
 
diff --git a/src/frame.c b/src/frame.c
index 2b1cb452ef..79a7c89e0d 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -5028,8 +5028,6 @@ gui_set_no_special_glyphs (struct frame *f, Lisp_Object new_value, Lisp_Object o
 }
 
 
-#ifndef HAVE_NS
-
 /* Non-zero if mouse is grabbed on DPYINFO
    and we know the frame where it is.  */
 
@@ -5054,8 +5052,6 @@ gui_redo_mouse_highlight (Display_Info *dpyinfo)
 			  dpyinfo->last_mouse_motion_y);
 }
 
-#endif /* HAVE_NS */
-
 /* Subroutines of creating an X frame.  */
 
 /* Make sure that Vx_resource_name is set to a reasonable value.
diff --git a/src/nsmenu.m b/src/nsmenu.m
index 05b89c2f56..b93d3a79bd 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -101,6 +101,15 @@
 static void
 ns_update_menubar (struct frame *f, bool deep_p)
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+
+  if (inside)
+    return;
+
+  inside++;
+#endif
+
   BOOL needsSet = NO;
   id menu = [NSApp mainMenu];
   bool owfi;
@@ -120,7 +129,12 @@
   NSTRACE ("ns_update_menubar");
 
   if (f != SELECTED_FRAME () || FRAME_EXTERNAL_MENU_BAR (f) == 0)
+    {
+#ifdef NS_IMPL_GNUSTEP
+      inside--;
+#endif
       return;
+    }
   XSETFRAME (Vmenu_updating_frame, f);
 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
 
@@ -144,10 +158,6 @@
   t = -(1000*tb.time+tb.millitm);
 #endif
 
-#ifdef NS_IMPL_GNUSTEP
-  deep_p = 1; /* See comment in menuNeedsUpdate.  */
-#endif
-
   if (deep_p)
     {
       /* Make a widget-value tree representing the entire menu trees.  */
@@ -275,6 +285,9 @@
 	  free_menubar_widget_value_tree (first_wv);
 	  discard_menu_items ();
 	  unbind_to (specpdl_count, Qnil);
+#ifdef NS_IMPL_GNUSTEP
+	  inside--;
+#endif
 	  return;
 	}
 
@@ -408,6 +421,10 @@
   if (needsSet)
     [NSApp setMainMenu: menu];
 
+#ifdef NS_IMPL_GNUSTEP
+  inside--;
+#endif
+
   unblock_input ();
 
 }
@@ -490,17 +507,34 @@ - (instancetype)initWithTitle: (NSString *)title
    call to ns_update_menubar.  */
 - (void)menuNeedsUpdate: (NSMenu *)menu
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+#endif
+
   if (!FRAME_LIVE_P (SELECTED_FRAME ()))
     return;
 
-#ifdef NS_IMPL_COCOA
-/* TODO: GNUstep calls this method when the menu is still being built
-   which results in a recursive stack overflow.  One possible solution
-   is to use menuWillOpen instead, but the Apple docs explicitly warn
-   against changing the contents of the menu in it.  I don't know what
-   the right thing to do for GNUstep is.  */
+#ifdef NS_IMPL_GNUSTEP
+  /* GNUstep calls this method when the menu is still being built
+     which results in a recursive stack overflow, which this variable
+     prevents.  */
+
+  if (!inside)
+    ++inside;
+  else
+    return;
+#endif
+
   if (needsUpdate)
-    ns_update_menubar (SELECTED_FRAME (), true);
+    {
+#ifdef NS_IMPL_GNUSTEP
+      needsUpdate = NO;
+#endif
+      ns_update_menubar (SELECTED_FRAME (), true);
+    }
+
+#ifdef NS_IMPL_GNUSTEP
+  --inside;
 #endif
 }
 
@@ -827,6 +861,9 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   p.x = x; p.y = y;
 
+  /* Don't GC due to a mysterious bug.  */
+  inhibit_garbage_collection ();
+
   /* now parse stage 2 as in ns_update_menubar */
   wv = make_widget_value ("contextmenu", NULL, true, Qnil);
   wv->button_type = BUTTON_TYPE_NONE;
@@ -998,15 +1035,17 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   pmenu = [[EmacsMenu alloc] initWithTitle:
                    NILP (title) ? @"" : [NSString stringWithLispString: title]];
+  /* On GNUstep, this call makes menu_items nil for whatever reason
+     when displaying a context menu from `context-menu-mode'.  */
+  Lisp_Object items = menu_items;
   [pmenu fillWithWidgetValue: first_wv->contents];
+  menu_items = items;
   free_menubar_widget_value_tree (first_wv);
-  unbind_to (specpdl_count, Qnil);
-
   popup_activated_flag = 1;
   tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
   popup_activated_flag = 0;
   [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
-
+  unbind_to (specpdl_count, Qnil);
   unblock_input ();
   return tem;
 }
@@ -1057,6 +1096,15 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
   [toolbar clearActive];
 #else
   [toolbar clearAll];
+  /* It takes at least 3 such adjustments to fix an issue where the
+     tool bar is 2x too tall when a frame's tool bar is first shown.
+     This is ugly, but I have no other solution for this problem.  */
+  if (FRAME_OUTPUT_DATA (f)->tool_bar_adjusted < 3)
+    {
+      [toolbar setVisible: NO];
+      FRAME_OUTPUT_DATA (f)->tool_bar_adjusted++;
+      [toolbar setVisible: YES];
+    }
 #endif
 
   /* Update EmacsToolbar as in GtkUtils, build items list.  */
diff --git a/src/nsterm.h b/src/nsterm.h
index 944dbd727c..8175f99664 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -978,6 +978,12 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
 
   /* Non-zero if we are doing an animation, e.g. toggling the tool bar.  */
   int in_animation;
+
+#ifdef NS_IMPL_GNUSTEP
+  /* Zero if this is the first time a toolbar has been updated on this
+     frame. */
+  int tool_bar_adjusted;
+#endif
 };
 
 /* This dummy declaration needed to support TTYs.  */
diff --git a/src/nsterm.m b/src/nsterm.m
index 4c915dac5d..a63883cd08 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -65,6 +65,7 @@ Updated by Christian Limpach (chris@nice.ch)
 
 #ifdef NS_IMPL_GNUSTEP
 #include "process.h"
+#import <GNUstepGUI/GSDisplayServer.h>
 #endif
 
 #ifdef NS_IMPL_COCOA
@@ -2256,13 +2257,19 @@ Hide the window (X11 semantics)
 {
   NSTRACE ("frame_set_mouse_pixel_position");
 
-  /* FIXME: what about GNUstep?  */
 #ifdef NS_IMPL_COCOA
   CGPoint mouse_pos =
     CGPointMake(f->left_pos + pix_x,
                 f->top_pos + pix_y +
                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
   CGWarpMouseCursorPosition (mouse_pos);
+#else
+  GSDisplayServer *server = GSServerForWindow ([FRAME_NS_VIEW (f) window]);
+  [server setMouseLocation: NSMakePoint (f->left_pos + pix_x,
+					 f->top_pos + pix_y
+					 + FRAME_NS_TITLEBAR_HEIGHT(f)
+					 + FRAME_TOOLBAR_HEIGHT(f))
+		  onScreen: [[[FRAME_NS_VIEW (f) window] screen] screenNumber]];
 #endif
 }
 
@@ -2575,8 +2582,7 @@ Hide the window (X11 semantics)
    ========================================================================== */
 
 
-#if 0
-/* FIXME: Remove this function. */
+#ifdef NS_IMPL_GNUSTEP
 static void
 ns_redraw_scroll_bars (struct frame *f)
 {
@@ -2621,10 +2627,9 @@ Hide the window (X11 semantics)
   NSRectFill (r);
   ns_unfocus (f);
 
-  /* as of 2006/11 or so this is now needed */
-  /* FIXME: I don't see any reason for this and removing it makes no
-     difference here.  Do we need it for GNUstep?  */
-  //ns_redraw_scroll_bars (f);
+#ifdef NS_IMPL_GNUSTEP
+  ns_redraw_scroll_bars (f);
+#endif
   unblock_input ();
 }
 
@@ -4922,6 +4927,17 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
 {
 }
 
+#ifdef NS_IMPL_GNUSTEP
+static void
+ns_update_window_end (struct window *w, bool cursor_on_p,
+		      bool mouse_face_overwritten_p)
+{
+  NSTRACE ("ns_update_window_end (cursor_on_p = %d)", cursor_on_p);
+
+  ns_redraw_scroll_bars (WINDOW_XFRAME (w));
+}
+#endif
+
 /* This and next define (many of the) public functions in this file.  */
 /* gui_* are generic versions in xdisp.c that we, and other terms, get away
          with using despite presence in the "system dependent" redisplay
@@ -4938,7 +4954,11 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
   ns_scroll_run,
   ns_after_update_window_line,
   NULL, /* update_window_begin */
+#ifndef NS_IMPL_GNUSTEP
   NULL, /* update_window_end   */
+#else
+  ns_update_window_end,
+#endif
   0, /* flush_display */
   gui_clear_window_mouse_face,
   gui_get_glyph_overhangs,
@@ -6166,9 +6186,11 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
       Lisp_Object kind = fnKeysym ? QCfunction : QCordinary;
       emacs_event->modifiers = EV_MODIFIERS2 (flags, kind);
 
+#ifndef NS_IMPL_GNUSTEP
       if (NS_KEYLOG)
         fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
                  code, fnKeysym, flags, emacs_event->modifiers);
+#endif
 
       /* If it was a function key or had control-like modifiers, pass
          it directly to Emacs.  */
@@ -6681,6 +6703,11 @@ - (void)mouseDown: (NSEvent *)theEvent
       emacs_event->code = EV_BUTTON (theEvent);
       emacs_event->modifiers = EV_MODIFIERS (theEvent)
                              | EV_UDMODIFIERS (theEvent);
+
+      if (emacs_event->modifiers & down_modifier)
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed |= 1 << EV_BUTTON (theEvent);
+      else
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed &= ~(1 << EV_BUTTON (theEvent));
     }
 
   XSETINT (emacs_event->x, lrint (p.x));
@@ -6981,7 +7008,6 @@ - (void)resizeWithOldSuperviewSize: (NSSize)oldSize
   height = (int)NSHeight (frame);
 
   NSTRACE_SIZE ("New size", NSMakeSize (width, height));
-  NSTRACE_SIZE ("Original size", size);
 
   /* Reset the frame size to match the bounds of the superview (the
      NSWindow's contentView).  We need to do this as sometimes the
diff --git a/src/xdisp.c b/src/xdisp.c
index 9814efd63c..86c4e704d5 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13894,7 +13894,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   clear_mouse_face (hlinfo);
 
   bool mouse_down_p = false;
-#ifndef HAVE_NS
   /* Mouse is down, but on different tab-bar item?  Or alternatively,
      the mouse might've been pressed somewhere we don't know about,
      and then have moved onto the tab bar.  In this case,
@@ -13907,7 +13906,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   if (mouse_down_p && f->last_tab_bar_item != prop_idx
       && f->last_tab_bar_item != -1)
     return;
-#endif
   draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED;
 
   /* If tab-bar item is not enabled, don't highlight it.  */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-11-05  7:44                           ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-06  0:20                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2021-11-06 13:02                               ` Alan Third
  0 siblings, 1 reply; 22+ messages in thread
From: Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-11-06  0:20 UTC (permalink / raw)
  To: Alan Third; +Cc: 51411

[-- Attachment #1: Type: text/plain, Size: 337 bytes --]

Po Lu <luangruo@yahoo.com> writes:

> As of 48af19c1 those patches don't apply anymore.  Here's a version that
> does.  (I kept my version of the overhang computation code instead of
> Daniel's, because the xterm code computes overhangs correctly, and
> bug#51105 doesn't apply to it AFAIU.)

And it doesn't apply, again.  This should:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-font-display-on-NS-port.patch --]
[-- Type: text/x-patch, Size: 79052 bytes --]

From 075d3d8dc020c5991c4a758d66c58dfd04f70964 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:44:03 +0800
Subject: [PATCH 1/2] Improve font display on NS port

* src/nsfns.m (Fx_create_frame): Use "fixed" for the default font on
GNUstep.

* src/nsfont.m (LCD_SMOOTHING_MARGIN, ns_escape_name)
(ns_unescape_name, ns_attribute_fvalue)
(STYLE_REF): Remove unused defines and functions.

(struct ns_glyph_layout, enum lgstring_direction).
(enum gs_font_slant, enum gs_font_weight, enum gs_font_width)
(enum gs_specified, struct gs_font_data): New enumerators and
structures.

(ns_font_descs_match_p)
(ns_done_font_data, ns_get_font_data): New functions.
(ns_glyph_metrics): Stop escaping names.

(ns_spec_to_descriptor): Fix font descriptor creation for symbolic
font spec entires.
(ns_descriptor_to_entity): Create entries with the correct symbolic
styles.

(ns_fallback_entity): Fix fallback entity selection.
(ns_findfonts): Use our own font matcher instead of the broken GNUstep
matcher.

(ns_list_family): Remove obsolete comment.
(nsfont_open): Remove obsolete code, comments, and synthItal logic
which doesn't work on GNUstep.

(nsfont_encode_char): Use a type that can fit NSGlyph
(nsfont_draw): Chose correct font, remove obsolete mouse face logic,
obsolete comments, and switch to using glyph-based drawing instead of
character-based drawing.

(ns_font_shape, nsfont_shape): New functions.

(ns_uni_to_glyphs_1): New function.
(ns_uni_to_glyphs): Return glyphs instead of unicode codepoints.

(ns_glyph_metrics): Use NSGlyphs instead of unicode codepoints and fix
left bearing, right bearing, ascent and descent computation.

(struct nsfont_driver): Add shaping capability.

* src/nsterm.h (struct nsfont_info): Use unsigned int for glyph cache.

* src/nsterm.c (ns_focus): Set DPS clipping on GNUstep.

(ns_compute_glyph_string_overhangs): Fix overhang computation by using
xterm code.
(ns_draw_window_cursor): Simplify cursor drawing.
(ns_maybe_dumpglyphs_background): Test for cursor HL and remove
obsolete mouse face logic.
(ns_dumpglyphs_image)
(ns_dumpglyphs_box_or_relief): Rectify for new cursor logic.
(ns_dumpglyphs_stretch): Rectify for new cursor logic and rely on
ns_draw_glyph_string to set focus.
(ns_draw_glyph_string_foreground): Remove mouse face logic.
(ns_draw_glyph_strings): Implement overhangs, remove obsolete
comment, and always focus before dumping glyphs.
(ns_draw_text_decoration): Add condition for DRAW_CURSOR and simplify
color selection.
(ns_define_frame_cursor): Remove nonsensical code (define_frame_cursor
has nothing to do with the text cursor, aka caret).

* src/xdisp.c (draw_glyphs): Enable code for NS port to fix mouse face
cursor display.

* src/macfont.m (get_cgcolor_from_nscolor): New function.
(macfont_draw): Remove obsolete mouse-face code and enable cursor
display.
---
 src/macfont.m |   36 +-
 src/nsfns.m   |    6 +
 src/nsfont.m  | 1215 +++++++++++++++++++++++++++++++++++--------------
 src/nsterm.h  |    2 +-
 src/nsterm.m  |  424 ++++++++---------
 src/xdisp.c   |    2 -
 6 files changed, 1095 insertions(+), 590 deletions(-)

diff --git a/src/macfont.m b/src/macfont.m
index 78ed5d53f3..1426cae6dc 100644
--- a/src/macfont.m
+++ b/src/macfont.m
@@ -613,6 +613,21 @@ static void mac_font_get_glyphs_for_variants (CFDataRef, UTF32Char,
   return cgColor;
 }
 
+static CGColorRef
+get_cgcolor_from_nscolor (NSColor *nsColor, struct frame *f)
+{
+  [nsColor set];
+  CGColorSpaceRef colorSpace = [[nsColor colorSpace] CGColorSpace];
+  NSInteger noc = [nsColor numberOfComponents];
+  CGFloat *components = xmalloc (sizeof(CGFloat)*(1+noc));
+  CGColorRef cgColor;
+
+  [nsColor getComponents: components];
+  cgColor = CGColorCreate (colorSpace, components);
+  xfree (components);
+  return cgColor;
+}
+
 #define CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND(context, face, f)        \
   do {                                                                  \
     CGColorRef refcol_ = get_cgcolor (NS_FACE_FOREGROUND (face), f);    \
@@ -2911,14 +2926,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
 
   if (!CGRectIsNull (background_rect))
     {
-      if (s->hl == DRAW_MOUSE_FACE)
+      if (s->hl == DRAW_CURSOR)
         {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-				       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_CURSOR_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
         }
-      CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_BACKGROUND (context, face, f);
       CGContextFillRects (context, &background_rect, 1);
     }
 
@@ -2927,7 +2942,14 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no
       CGAffineTransform atfm;
 
       CGContextScaleCTM (context, 1, -1);
-      CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
+      if (s->hl == DRAW_CURSOR)
+        {
+	  CGColorRef *colorref = get_cgcolor_from_nscolor (FRAME_BACKGROUND_COLOR (f), f);
+	  CGContextSetFillColorWithColor (context, colorref);
+	  CGColorRelease (colorref);
+        }
+      else
+	CG_SET_FILL_COLOR_WITH_FACE_FOREGROUND (context, face, s->f);
       if (macfont_info->synthetic_italic_p)
         atfm = synthetic_italic_atfm;
       else
diff --git a/src/nsfns.m b/src/nsfns.m
index 797d0ce782..f4d8172246 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -1236,6 +1236,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                          "fontBackend", "FontBackend", RES_TYPE_STRING);
 
   {
+#ifdef NS_IMPL_COCOA
     /* use for default font name */
     id font = [NSFont userFixedPitchFontOfSize: -1.0]; /* default */
     gui_default_parameter (f, parms, Qfontsize,
@@ -1250,6 +1251,11 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side.
                            build_string (fontname),
                            "font", "Font", RES_TYPE_STRING);
     xfree (fontname);
+#else
+    gui_default_parameter (f, parms, Qfont,
+                           build_string ("fixed"),
+                           "font", "Font", RES_TYPE_STRING);
+#endif
   }
   unblock_input ();
 
diff --git a/src/nsfont.m b/src/nsfont.m
index 5a9cdfebc0..b3224629f0 100644
--- a/src/nsfont.m
+++ b/src/nsfont.m
@@ -1,4 +1,4 @@
-/* Font back-end driver for the NeXT/Open/GNUstep and macOS window system.
+/* Font back-end driver for the GNUstep window system.
    See font.h
    Copyright (C) 2006-2021 Free Software Foundation, Inc.
 
@@ -38,47 +38,269 @@
 #include "termchar.h"
 #include "pdumper.h"
 
-/* TODO: Drop once we can assume gnustep-gui 0.17.1.  */
+#import <Foundation/NSException.h>
 #import <AppKit/NSFontDescriptor.h>
+#import <AppKit/NSLayoutManager.h>
+#import <GNUstepGUI/GSLayoutManager.h>
+#import <GNUstepGUI/GSFontInfo.h>
 
 #define NSFONT_TRACE 0
-#define LCD_SMOOTHING_MARGIN 2
 
-/* Font glyph and metrics caching functions, implemented at end.  */
-static void ns_uni_to_glyphs (struct nsfont_info *font_info,
-                              unsigned char block);
-static void ns_glyph_metrics (struct nsfont_info *font_info,
-                              unsigned char block);
+/* Structure used by GS `shape' functions for storing layout
+   information for each glyph.  Borrowed from macfont.h.  */
+struct ns_glyph_layout
+{
+  /* Range of indices of the characters composed into the group of
+     glyphs that share the cursor position with this glyph.  The
+     members `location' and `length' are in UTF-16 indices.  */
+  NSRange comp_range;
 
-#define INVALID_GLYPH 0xFFFF
+  /* UTF-16 index in the source string for the first character
+     associated with this glyph.  */
+  NSUInteger string_index;
 
-/* ==========================================================================
+  /* Horizontal and vertical adjustments of glyph position.  The
+     coordinate space is that of Core Text.  So, the `baseline_delta'
+     value is negative if the glyph should be placed below the
+     baseline.  */
+  CGFloat advance_delta, baseline_delta;
 
-    Utilities
+  /* Typographical width of the glyph.  */
+  CGFloat advance;
 
-   ========================================================================== */
+  /* Glyph ID of the glyph.  */
+  NSGlyph glyph_id;
+};
+
+
+enum lgstring_direction
+  {
+    DIR_R2L = -1, DIR_UNKNOWN = 0, DIR_L2R = 1
+  };
+
+enum gs_font_slant
+  {
+    GS_FONT_SLANT_ITALIC,
+    GS_FONT_SLANT_REVERSE_ITALIC,
+    GS_FONT_SLANT_NORMAL
+  };
+
+enum gs_font_weight
+  {
+    GS_FONT_WEIGHT_LIGHT,
+    GS_FONT_WEIGHT_BOLD,
+    GS_FONT_WEIGHT_NORMAL
+  };
+
+enum gs_font_width
+  {
+    GS_FONT_WIDTH_CONDENSED,
+    GS_FONT_WIDTH_EXPANDED,
+    GS_FONT_WIDTH_NORMAL
+  };
+
+enum gs_specified
+  {
+    GS_SPECIFIED_SLANT = 1,
+    GS_SPECIFIED_WEIGHT = 1 << 1,
+    GS_SPECIFIED_WIDTH = 1 << 2,
+    GS_SPECIFIED_FAMILY = 1 << 3,
+    GS_SPECIFIED_SPACING = 1 << 4
+  };
 
+struct gs_font_data
+{
+  int specified;
+  enum gs_font_slant slant;
+  enum gs_font_weight weight;
+  enum gs_font_width width;
+  bool monospace_p;
+  char *family_name;
+};
 
-/* Replace spaces w/another character so emacs core font parsing routines
-   aren't thrown off.  */
 static void
-ns_escape_name (char *name)
+ns_done_font_data (struct gs_font_data *data)
 {
-  for (; *name; name++)
-    if (*name == ' ')
-      *name = '_';
+  if (data->specified & GS_SPECIFIED_FAMILY)
+    xfree (data->family_name);
 }
 
-
-/* Reconstruct spaces in a font family name passed through emacs.  */
 static void
-ns_unescape_name (char *name)
+ns_get_font_data (NSFontDescriptor *desc, struct gs_font_data *dat)
 {
-  for (; *name; name++)
-    if (*name == '_')
-      *name = ' ';
+  NSNumber *tem;
+  NSFontSymbolicTraits traits = [desc symbolicTraits];
+  NSDictionary *dict = [desc objectForKey: NSFontTraitsAttribute];
+  NSString *family = [desc objectForKey: NSFontFamilyAttribute];
+
+  dat->specified = 0;
+
+  if (family != nil)
+    {
+      dat->specified |= GS_SPECIFIED_FAMILY;
+      dat->family_name = xstrdup ([family cStringUsingEncoding: NSUTF8StringEncoding]);
+    }
+
+  tem = [desc objectForKey: NSFontFixedAdvanceAttribute];
+
+  if ((tem != nil && [tem boolValue] != NO)
+      || (traits & NSFontMonoSpaceTrait))
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = true;
+    }
+  else if (tem != nil && [tem boolValue] == NO)
+    {
+      dat->specified |= GS_SPECIFIED_SPACING;
+      dat->monospace_p = false;
+    }
+
+  if (traits & NSFontBoldTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WEIGHT;
+      dat->weight = GS_FONT_WEIGHT_BOLD;
+    }
+
+  if (traits & NSFontItalicTrait)
+    {
+      dat->specified |= GS_SPECIFIED_SLANT;
+      dat->slant = GS_FONT_SLANT_ITALIC;
+    }
+
+  if (traits & NSFontCondensedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_CONDENSED;
+    }
+  else if (traits & NSFontExpandedTrait)
+    {
+      dat->specified |= GS_SPECIFIED_WIDTH;
+      dat->width = GS_FONT_WIDTH_EXPANDED;
+    }
+
+  if (dict != nil)
+    {
+      tem = [dict objectForKey: NSFontSlantTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_SLANT;
+
+	  dat->slant = [tem floatValue] > 0
+	    ? GS_FONT_SLANT_ITALIC
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_SLANT_REVERSE_ITALIC
+	       : GS_FONT_SLANT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWeightTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WEIGHT;
+
+	  dat->weight = [tem floatValue] > 0
+	    ? GS_FONT_WEIGHT_BOLD
+	    : ([tem floatValue] < -0.4f
+	       ? GS_FONT_WEIGHT_LIGHT
+	       : GS_FONT_WEIGHT_NORMAL);
+	}
+
+      tem = [dict objectForKey: NSFontWidthTrait];
+
+      if (tem != nil)
+	{
+	  dat->specified |= GS_SPECIFIED_WIDTH;
+
+	  dat->width = [tem floatValue] > 0
+	    ? GS_FONT_WIDTH_EXPANDED
+	    : ([tem floatValue] < 0
+	       ? GS_FONT_WIDTH_NORMAL
+	       : GS_FONT_WIDTH_CONDENSED);
+	}
+    }
+}
+
+static bool
+ns_font_descs_match_p (NSFontDescriptor *desc, NSFontDescriptor *target)
+{
+  struct gs_font_data dat;
+  struct gs_font_data t;
+
+  ns_get_font_data (desc, &dat);
+  ns_get_font_data (target, &t);
+
+  if (!(t.specified & GS_SPECIFIED_WIDTH))
+    t.width = GS_FONT_WIDTH_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_WEIGHT))
+    t.weight = GS_FONT_WEIGHT_NORMAL;
+  if (!(t.specified & GS_SPECIFIED_SPACING))
+    t.monospace_p = false;
+  if (!(t.specified & GS_SPECIFIED_SLANT))
+    t.slant = GS_FONT_SLANT_NORMAL;
+
+  if (!(t.specified & GS_SPECIFIED_FAMILY))
+    emacs_abort ();
+
+  bool match_p = true;
+
+  if (dat.specified & GS_SPECIFIED_WIDTH
+      && dat.width != t.width)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_WEIGHT
+      && dat.weight != t.weight)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SPACING
+      && dat.monospace_p != t.monospace_p)
+    {
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_SLANT
+      && dat.monospace_p != t.monospace_p)
+    {
+      if (NSFONT_TRACE)
+	printf ("Matching monospace for %s: %d %d\n",
+		t.family_name, dat.monospace_p,
+		t.monospace_p);
+      match_p = false;
+      goto gout;
+    }
+
+  if (dat.specified & GS_SPECIFIED_FAMILY
+      && strcmp (dat.family_name, t.family_name))
+    match_p = false;
+
+ gout:
+  ns_done_font_data (&dat);
+  ns_done_font_data (&t);
+
+  return match_p;
 }
 
+/* Font glyph and metrics caching functions, implemented at end.  */
+static void ns_uni_to_glyphs (struct nsfont_info *font_info,
+                              unsigned char block);
+static void ns_glyph_metrics (struct nsfont_info *font_info,
+                              unsigned int block);
+
+#define INVALID_GLYPH 0xFFFF
+
+/* ==========================================================================
+
+    Utilities
+
+   ========================================================================== */
+
 
 /* Extract family name from a font spec.  */
 static NSString *
@@ -91,66 +313,116 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
     {
       char *tmp = xlispstrdup (SYMBOL_NAME (tem));
       NSString *family;
-      ns_unescape_name (tmp);
       family = [NSString stringWithUTF8String: tmp];
       xfree (tmp);
       return family;
     }
 }
 
-
-/* Return 0 if attr not set, else value (which might also be 0).
-   On Leopard 0 gets returned even on descriptors where the attribute
-   was never set, so there's no way to distinguish between unspecified
-   and set to not have.  Callers should assume 0 means unspecified.  */
-static float
-ns_attribute_fvalue (NSFontDescriptor *fdesc, NSString *trait)
-{
-    NSDictionary *tdict = [fdesc objectForKey: NSFontTraitsAttribute];
-    NSNumber *val = [tdict objectForKey: trait];
-    return val == nil ? 0.0F : [val floatValue];
-}
-
-
 /* Converts FONT_WEIGHT, FONT_SLANT, FONT_WIDTH, plus family and script/lang
    to NSFont descriptor.  Information under extra only needed for matching.  */
-#define STYLE_REF 100
 static NSFontDescriptor *
 ns_spec_to_descriptor (Lisp_Object font_spec)
 {
     NSFontDescriptor *fdesc;
     NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
-    NSMutableDictionary *tdict = [NSMutableDictionary new];
     NSString *family = ns_get_family (font_spec);
-    float n;
-
-    /* Add each attr in font_spec to fdAttrs.  */
-    n = min (FONT_WEIGHT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWeightTrait];
-    n = min (FONT_SLANT_NUMERIC (font_spec), 200);
-    if (n != -1 && n != STYLE_REF)
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontSlantTrait];
-    n = min (FONT_WIDTH_NUMERIC (font_spec), 200);
-    if (n > -1 && (n > STYLE_REF + 10 || n < STYLE_REF - 10))
-	[tdict setObject: [NSNumber numberWithFloat: (n - 100.0F) / 100.0F]
-		  forKey: NSFontWidthTrait];
-    if ([tdict count] > 0)
-	[fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+    NSMutableDictionary *tdict = [NSMutableDictionary new];
 
-    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
-               retain] autorelease];
+    Lisp_Object tem;
+
+    tem = FONT_SLANT_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontSlantTrait];
+	else if (EQ (tem, intern ("reverse-italic")) ||
+		 EQ (tem, intern ("reverse-oblique")))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontSlantTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontSlantTrait];
+      }
+
+    tem = FONT_WIDTH_SYMBOLIC (font_spec);
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qcondensed))
+	  [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		    forKey: NSFontWidthTrait];
+	else if (EQ (tem, Qexpanded))
+	  [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		    forKey: NSFontWidthTrait];
+	else
+	  [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		    forKey: NSFontWidthTrait];
+      }
+
+    tem = FONT_WEIGHT_SYMBOLIC (font_spec);
+
+    if (!NILP (tem))
+      {
+	if (EQ (tem, Qbold))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else if (EQ (tem, Qlight))
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: -1.0]
+		      forKey: NSFontWeightTrait];
+	  }
+	else
+	  {
+	    [tdict setObject: [NSNumber numberWithFloat: 0.0]
+		      forKey: NSFontWeightTrait];
+	  }
+      }
+
+    tem = AREF (font_spec, FONT_SPACING_INDEX);
 
     if (family != nil)
       {
-        NSFontDescriptor *fdesc2 = [fdesc fontDescriptorWithFamily: family];
-        fdesc = [[fdesc2 retain] autorelease];
+	[fdAttrs setObject: family
+		    forKey: NSFontFamilyAttribute];
       }
 
-    [fdAttrs release];
+    if (FIXNUMP (tem))
+      {
+	if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:YES]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+	else
+	  {
+	    [fdAttrs setObject: [NSNumber numberWithBool:NO]
+			forKey: NSFontFixedAdvanceAttribute];
+	  }
+      }
+
+    /* Handle special families such as ``fixed'' or ``Sans Serif''.  */
+
+    if ([family isEqualToString: @"fixed"])
+      {
+	[fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+    else if ([family isEqualToString: @"Sans Serif"])
+      {
+	[fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
+		    forKey: NSFontFamilyAttribute];
+      }
+
+    [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+
+    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
+               retain] autorelease];
+
     [tdict release];
+    [fdAttrs release];
     return fdesc;
 }
 
@@ -161,61 +433,64 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
                          Lisp_Object extra,
                          const char *style)
 {
-    Lisp_Object font_entity = font_make_entity ();
-    /*   NSString *psName = [desc postscriptName]; */
-    NSString *family = [desc objectForKey: NSFontFamilyAttribute];
-    unsigned int traits = [desc symbolicTraits];
-    char *escapedFamily;
-
-    /* Shouldn't happen, but on Tiger fallback desc gets name but no family.  */
-    if (family == nil)
-      family = [desc objectForKey: NSFontNameAttribute];
-    if (family == nil)
-      family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-
-    escapedFamily = xstrdup ([family UTF8String]);
-    ns_escape_name (escapedFamily);
-
-    ASET (font_entity, FONT_TYPE_INDEX, Qns);
-    ASET (font_entity, FONT_FOUNDRY_INDEX, Qapple);
-    ASET (font_entity, FONT_FAMILY_INDEX, intern (escapedFamily));
-    ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
-    ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
-
-    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    traits & NSFontBoldTrait ? Qbold : Qmedium);
-/*    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
-		    make_fixnum (100 + 100
-			* ns_attribute_fvalue (desc, NSFontWeightTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    traits & NSFontItalicTrait ? Qitalic : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontSlantTrait)));*/
-    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-                    traits & NSFontCondensedTrait ? Qcondensed :
-                    traits & NSFontExpandedTrait ? Qexpanded : Qnormal);
-/*    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
-		    make_fixnum (100 + 100
-			 * ns_attribute_fvalue (desc, NSFontWidthTrait)));*/
-
-    ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
-    ASET (font_entity, FONT_SPACING_INDEX,
-	  make_fixnum([desc symbolicTraits] & NSFontMonoSpaceTrait
-	      ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
-
-    ASET (font_entity, FONT_EXTRA_INDEX, extra);
-    ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+  Lisp_Object font_entity = font_make_entity ();
+  struct gs_font_data data;
+  ns_get_font_data (desc, &data);
+
+  ASET (font_entity, FONT_TYPE_INDEX, Qns);
+  ASET (font_entity, FONT_FOUNDRY_INDEX, Qns);
+  if (data.specified & GS_SPECIFIED_FAMILY)
+    ASET (font_entity, FONT_FAMILY_INDEX, intern (data.family_name));
+  ASET (font_entity, FONT_ADSTYLE_INDEX, style ? intern (style) : Qnil);
+  ASET (font_entity, FONT_REGISTRY_INDEX, Qiso10646_1);
+
+  if (data.specified & GS_SPECIFIED_WEIGHT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX,
+		      data.weight == GS_FONT_WEIGHT_BOLD
+		      ? Qbold : (data.weight == GS_FONT_WEIGHT_LIGHT
+				 ? Qlight : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WEIGHT_INDEX, Qnormal);
 
-    if (NSFONT_TRACE)
-      {
-	fputs ("created font_entity:\n    ", stderr);
-	debug_print (font_entity);
-      }
+  if (data.specified & GS_SPECIFIED_SLANT)
+    {
+      FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX,
+		      data.slant == GS_FONT_SLANT_ITALIC
+		      ? Qitalic : (data.slant == GS_FONT_SLANT_REVERSE_ITALIC
+				   ? intern ("reverse-italic") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_SLANT_INDEX, Qnormal);
+
+  if (data.specified & GS_SPECIFIED_WIDTH)
+    {
+      FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX,
+		      data.width == GS_FONT_WIDTH_CONDENSED
+		      ? Qcondensed : (data.width == GS_FONT_WIDTH_EXPANDED
+				      ? intern ("expanded") : Qnormal));
+    }
+  else
+    FONT_SET_STYLE (font_entity, FONT_WIDTH_INDEX, Qnormal);
 
-    xfree (escapedFamily);
-    return font_entity;
+  ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (font_entity, FONT_SPACING_INDEX,
+	make_fixnum ((data.specified & GS_SPECIFIED_WIDTH && data.monospace_p)
+		     ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
+
+  ASET (font_entity, FONT_EXTRA_INDEX, extra);
+  ASET (font_entity, FONT_OBJLIST_INDEX, Qnil);
+
+  if (NSFONT_TRACE)
+    {
+      fputs ("created font_entity:\n    ", stderr);
+      debug_print (font_entity);
+    }
+
+  ns_done_font_data (&data);
+  return font_entity;
 }
 
 
@@ -223,8 +498,7 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
 static Lisp_Object
 ns_fallback_entity (void)
 {
-  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 0]
-      fontDescriptor], Qnil, NULL);
+  return ns_descriptor_to_entity ([[NSFont userFixedPitchFontOfSize: 1] fontDescriptor], Qnil, NULL);
 }
 
 
@@ -510,21 +784,20 @@ but also for ascii (which causes unnecessary font substitution).  */
     return families;
 }
 
+/* GNUstep font matching is very mediocre (it can't even compare
+   symbolic styles correctly), which is why our own font matching
+   mechanism must be implemented.  */
 
-/* Implementation for list() and match().  List() can return nil, match()
-must return something.  Strategy is to drop family name from attribute
-matching set for match.  */
+/* Implementation for list and match.  */
 static Lisp_Object
 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
 {
     Lisp_Object tem, list = Qnil;
-    NSFontDescriptor *fdesc, *desc;
-    NSMutableSet *fkeys;
-    NSArray *matchingDescs;
-    NSEnumerator *dEnum;
-    NSString *family;
+    NSFontDescriptor *fdesc;
+    NSArray *all_descs;
+    GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
+
     NSSet *cFamilies;
-    BOOL foundItal = NO;
 
     block_input ();
     if (NSFONT_TRACE)
@@ -537,43 +810,22 @@ but also for ascii (which causes unnecessary font substitution).  */
     cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
 
     fdesc = ns_spec_to_descriptor (font_spec);
-    fkeys = [NSMutableSet setWithArray: [[fdesc fontAttributes] allKeys]];
-    if (isMatch)
-	[fkeys removeObject: NSFontFamilyAttribute];
-
-    matchingDescs = [fdesc matchingFontDescriptorsWithMandatoryKeys: fkeys];
+    all_descs = [enumerator availableFontDescriptors];
 
-    if (NSFONT_TRACE)
-	NSLog(@"Got desc %@ and found %lu matching fonts from it: ", fdesc,
-	      (unsigned long)[matchingDescs count]);
-
-    for (dEnum = [matchingDescs objectEnumerator]; (desc = [dEnum nextObject]);)
+    for (NSFontDescriptor *desc in all_descs)
       {
 	if (![cFamilies containsObject:
 	         [desc objectForKey: NSFontFamilyAttribute]])
 	    continue;
+	if (!ns_font_descs_match_p (fdesc, desc))
+	  continue;
+
         tem = ns_descriptor_to_entity (desc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
+				       AREF (font_spec, FONT_EXTRA_INDEX),
                                        NULL);
         if (isMatch)
           return tem;
 	list = Fcons (tem, list);
-	if (fabs (ns_attribute_fvalue (desc, NSFontSlantTrait)) > 0.05)
-	    foundItal = YES;
-      }
-
-    /* Add synthItal member if needed.  */
-    family = [fdesc objectForKey: NSFontFamilyAttribute];
-    if (family != nil && !foundItal && !NILP (list))
-      {
-        NSFontDescriptor *s1 = [NSFontDescriptor new];
-        NSFontDescriptor *sDesc
-          = [[s1 fontDescriptorWithSymbolicTraits: NSFontItalicTrait]
-              fontDescriptorWithFamily: family];
-	list = Fcons (ns_descriptor_to_entity (sDesc,
-					 AREF (font_spec, FONT_EXTRA_INDEX),
-					 "synthItal"), list);
-        [s1 release];
       }
 
     unblock_input ();
@@ -652,7 +904,6 @@ Properties to be considered are same as for list().  */
                objectEnumerator];
   while ((family = [families nextObject]))
       list = Fcons (intern ([family UTF8String]), list);
-  /* FIXME: escape the name?  */
 
   if (NSFONT_TRACE)
     fprintf (stderr, "nsfont: list families returning %"pD"d entries\n",
@@ -668,18 +919,15 @@ Properties to be considered are same as for list().  */
 static Lisp_Object
 nsfont_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
 {
-  BOOL synthItal;
-  unsigned int traits = 0;
   struct nsfont_info *font_info;
   struct font *font;
   NSFontDescriptor *fontDesc = ns_spec_to_descriptor (font_entity);
   NSFontManager *fontMgr = [NSFontManager sharedFontManager];
   NSString *family;
   NSFont *nsfont, *sfont;
-  Lisp_Object tem;
   NSRect brect;
   Lisp_Object font_object;
-  int fixLeopardBug;
+  Lisp_Object tem;
 
   block_input ();
 
@@ -692,42 +940,20 @@ Properties to be considered are same as for list().  */
   if (pixel_size <= 0)
     {
       /* try to get it out of frame params */
-        Lisp_Object tem = get_frame_param (f, Qfontsize);
-        pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
+      tem = get_frame_param (f, Qfontsize);
+      pixel_size = NILP (tem) ? 0 : XFIXNAT (tem);
     }
 
   tem = AREF (font_entity, FONT_ADSTYLE_INDEX);
-  synthItal = !NILP (tem) && !strncmp ("synthItal", SSDATA (SYMBOL_NAME (tem)),
-                                       9);
   family = ns_get_family (font_entity);
   if (family == nil)
     family = [[NSFont userFixedPitchFontOfSize: 0] familyName];
-  /* Should be > 0.23 as some font descriptors (e.g. Terminus) set to that
-     when setting family in ns_spec_to_descriptor().  */
-  if (ns_attribute_fvalue (fontDesc, NSFontWeightTrait) > 0.50F)
-      traits |= NSBoldFontMask;
-  if (ns_attribute_fvalue (fontDesc, NSFontSlantTrait) > 0.05F)
-      traits |= NSItalicFontMask;
-
-  /* see https://web.archive.org/web/20100201175731/http://cocoadev.com/forums/comments.php?DiscussionID=74 */
-  fixLeopardBug = traits & NSBoldFontMask ? 10 : 5;
-  nsfont = [fontMgr fontWithFamily: family
-                            traits: traits weight: fixLeopardBug
-			      size: pixel_size];
-  /* if didn't find, try synthetic italic */
-  if (nsfont == nil && synthItal)
-    {
-      nsfont = [fontMgr fontWithFamily: family
-                                traits: traits & ~NSItalicFontMask
-                                weight: fixLeopardBug size: pixel_size];
-    }
+
+  nsfont = [NSFont fontWithDescriptor: fontDesc
+				 size: pixel_size];
 
   if (nsfont == nil)
-    {
-      message_with_string ("*** Warning: font in family `%s' not found",
-                          build_string ([family UTF8String]), 1);
-      nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
-    }
+    nsfont = [NSFont userFixedPitchFontOfSize: pixel_size];
 
   if (NSFONT_TRACE)
     NSLog (@"%@\n", nsfont);
@@ -740,7 +966,7 @@ when setting family in ns_spec_to_descriptor().  */
   if (!font)
     {
       unblock_input ();
-      return Qnil; /* FIXME: other terms do, but returning Qnil causes segfault.  */
+      return Qnil;
     }
 
   font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs);
@@ -781,7 +1007,7 @@ when setting family in ns_spec_to_descriptor().  */
     font_info->name = xstrdup (fontName);
     font_info->bold = [fontMgr traitsOfFont: nsfont] & NSBoldFontMask;
     font_info->ital =
-      synthItal || ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
+      ([fontMgr traitsOfFont: nsfont] & NSItalicFontMask);
 
     /* Metrics etc.; some fonts return an unusually large max advance, so we
        only use it for fonts that have wide characters.  */
@@ -808,8 +1034,6 @@ when setting family in ns_spec_to_descriptor().  */
       lrint (brect.size.width - (CGFloat) font_info->width);
 
     /* set up metrics portion of font struct */
-    font->ascent = lrint([sfont ascender]);
-    font->descent = -lrint(floor(adjusted_descender));
     font->space_width = lrint (ns_char_width (sfont, ' '));
     font->max_width = lrint (font_info->max_bounds.width);
     font->min_width = font->space_width;  /* Approximate.  */
@@ -871,7 +1095,7 @@ when setting family in ns_spec_to_descriptor().  */
 {
   struct nsfont_info *font_info = (struct nsfont_info *)font;
   unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
-  unsigned short g;
+  unsigned int g;
 
   if (c > 0xFFFF)
     return FONT_INVALID_CODE;
@@ -934,51 +1158,23 @@ is false when (FROM > 0 || TO < S->nchars).  */
 static int
 nsfont_draw (struct glyph_string *s, int from, int to, int x, int y,
              bool with_background)
-/* NOTE: focus and clip must be set.  */
 {
-  static unsigned char cbuf[1024];
-  unsigned char *c = cbuf;
-#if GNUSTEP_GUI_MAJOR_VERSION > 0 || GNUSTEP_GUI_MINOR_VERSION > 22
-  static CGFloat advances[1024];
-  CGFloat *adv = advances;
-#else
-  static float advances[1024];
-  float *adv = advances;
-#endif
+  NSGlyph *c = alloca ((to - from) * sizeof *c);
+
   struct face *face;
   NSRect r;
   struct nsfont_info *font;
-  NSColor *col, *bgCol;
-  unsigned *t = s->char2b;
-  int i, len, flags;
+  NSColor *col;
+  int len = to - from;
   char isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
 
   block_input ();
 
-  font = (struct nsfont_info *)s->face->font;
+  font = (struct nsfont_info *) s->font;
   if (font == NULL)
     font = (struct nsfont_info *)FRAME_FONT (s->f);
 
-  /* Select face based on input flags.  */
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
-  switch (flags)
-    {
-    case NS_DUMPGLYPH_CURSOR:
-      face = s->face;
-      break;
-    case NS_DUMPGLYPH_MOUSEFACE:
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-      break;
-    default:
-      face = s->face;
-    }
+  face = s->face;
 
   r.origin.x = s->x;
   if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
@@ -987,91 +1183,24 @@ is false when (FROM > 0 || TO < S->nchars).  */
   r.origin.y = s->y;
   r.size.height = FONT_HEIGHT (font);
 
-  /* Convert UTF-16 (?) to UTF-8 and determine advances.  Note if we just ask
-     NS to render the string, it will come out differently from the individual
-     character widths added up because of layout processing.  */
-  {
-    int cwidth, twidth = 0;
-    int hi, lo;
-    /* FIXME: composition: no vertical displacement is considered.  */
-    t += from; /* advance into composition */
-    for (i = from; i < to; i++, t++)
-      {
-        hi = (*t & 0xFF00) >> 8;
-        lo = *t & 0x00FF;
-        if (isComposite)
-          {
-	    if (!s->first_glyph->u.cmp.automatic)
-		cwidth = s->cmp->offsets[i * 2] /* (H offset) */ - twidth;
-	    else
-	      {
-		Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
-		Lisp_Object glyph = LGSTRING_GLYPH (gstring, i);
-		if (NILP (LGLYPH_ADJUSTMENT (glyph)))
-		    cwidth = LGLYPH_WIDTH (glyph);
-		else
-		  {
-		    cwidth = LGLYPH_WADJUST (glyph);
-		    *(adv-1) += LGLYPH_XOFF (glyph);
-		  }
-	      }
-          }
-        else
-          {
-            if (!font->metrics[hi]) /* FIXME: why/how can we need this now?  */
-              ns_glyph_metrics (font, hi);
-            cwidth = font->metrics[hi][lo].width;
-          }
-        twidth += cwidth;
-        *adv++ = cwidth;
-        c += CHAR_STRING (*t, c); /* This converts the char to UTF-8.  */
-      }
-    len = adv - advances;
-    r.size.width = twidth;
-    *c = 0;
-  }
+  for (int i = from; i < to; ++i)
+    c[i] = s->char2b[i];
 
   /* Fill background if requested.  */
   if (with_background && !isComposite)
     {
-      NSRect br = r;
-      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
-      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
-
-      if (s->row->full_width_p)
-        {
-          if (br.origin.x <= fibw + 1 + mbox_line_width)
-            {
-              br.size.width += br.origin.x - mbox_line_width;
-              br.origin.x = mbox_line_width;
-            }
-          if (FRAME_PIXEL_WIDTH (s->f) - (br.origin.x + br.size.width)
-                <= fibw+1)
-            br.size.width += fibw;
-        }
-      if (s->face->box == FACE_NO_BOX)
-        {
-          /* Expand unboxed top row over internal border.  */
-          if (br.origin.y <= fibw + 1 + mbox_line_width)
-            {
-              br.size.height += br.origin.y;
-              br.origin.y = 0;
-            }
-        }
-      else
-        {
-          int correction = abs (s->face->box_horizontal_line_width)+1;
-          br.origin.y += correction;
-          br.size.height -= 2*correction;
-          correction = abs (s->face->box_vertical_line_width)+1;
-          br.origin.x += correction;
-          br.size.width -= 2*correction;
-        }
+      NSRect br = NSMakeRect (x, y - FONT_BASE (s->font),
+			      s->width, FONT_HEIGHT (s->font));
 
       if (!s->face->stipple)
-        [(NS_FACE_BACKGROUND (face) != 0
-          ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-          : FRAME_BACKGROUND_COLOR (s->f)) set];
+	{
+	  if (s->hl != DRAW_CURSOR)
+	    [(NS_FACE_BACKGROUND (face) != 0
+	      ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+	      : FRAME_BACKGROUND_COLOR (s->f)) set];
+	  else
+	    [FRAME_CURSOR_COLOR (s->f) set];
+	}
       else
         {
           struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
@@ -1080,43 +1209,32 @@ is false when (FROM > 0 || TO < S->nchars).  */
       NSRectFill (br);
     }
 
-
   /* set up for character rendering */
   r.origin.y = y;
 
-  col = (NS_FACE_FOREGROUND (face) != 0
-         ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
-         : FRAME_FOREGROUND_COLOR (s->f));
-
-  bgCol = (flags != NS_DUMPGLYPH_FOREGROUND ? nil
-           : (NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)));
+  if (s->hl == DRAW_CURSOR)
+    col = FRAME_BACKGROUND_COLOR (s->f);
+  else
+    col = (NS_FACE_FOREGROUND (face) != 0
+	   ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f)
+	   : FRAME_FOREGROUND_COLOR (s->f));
 
   /* render under GNUstep using DPS */
   {
-    NSGraphicsContext *context = GSCurrentContext ();
-
+    NSGraphicsContext *context = [NSGraphicsContext currentContext];
     DPSgsave (context);
-    [font->nsfont set];
-
-    /* do erase if "foreground" mode */
-    if (bgCol != nil)
+    if (s->clip_head)
       {
-        [bgCol set];
-        DPSmoveto (context, r.origin.x, r.origin.y);
-/*[context GSSetTextDrawingMode: GSTextFillStroke]; /// not implemented yet */
-        DPSxshow (context, (const char *) cbuf, advances, len);
-        DPSstroke (context);
-        [col set];
-/*[context GSSetTextDrawingMode: GSTextFill]; /// not implemented yet */
+	DPSrectclip (context, s->clip_head->x, 0,
+		     FRAME_PIXEL_WIDTH (s->f),
+		     FRAME_PIXEL_HEIGHT (s->f));
       }
+    [font->nsfont set];
 
     [col set];
 
-    /* draw with DPSxshow () */
     DPSmoveto (context, r.origin.x, r.origin.y);
-    DPSxshow (context, (const char *) cbuf, advances, len);
+    GSShowGlyphs (context, c, len);
     DPSstroke (context);
 
     DPSgrestore (context);
@@ -1126,6 +1244,360 @@ is false when (FROM > 0 || TO < S->nchars).  */
   return to-from;
 }
 
+static NSUInteger
+ns_font_shape (NSFont *font, NSString *string,
+	       struct ns_glyph_layout *glyph_layouts, NSUInteger glyph_len,
+	       enum lgstring_direction dir)
+{
+  NSUInteger i;
+  NSUInteger result = 0;
+  NSTextStorage *textStorage;
+  NSLayoutManager *layoutManager;
+  NSTextContainer *textContainer;
+  NSUInteger stringLength;
+  NSPoint spaceLocation;
+  /* numberOfGlyphs can't actually be 0, but this pacifies GCC */
+  NSUInteger used, numberOfGlyphs = 0;
+
+  textStorage = [[NSTextStorage alloc] initWithString:string];
+  layoutManager = [[NSLayoutManager alloc] init];
+  textContainer = [[NSTextContainer alloc] init];
+
+  /* Append a trailing space to measure baseline position.  */
+  [textStorage appendAttributedString:([[[NSAttributedString alloc]
+                                          initWithString:@" "] autorelease])];
+  [textStorage setFont:font];
+  [textContainer setLineFragmentPadding:0];
+
+  [layoutManager addTextContainer:textContainer];
+  [textContainer release];
+  [textStorage addLayoutManager:layoutManager];
+  [layoutManager release];
+
+  if (!(textStorage && layoutManager && textContainer))
+    emacs_abort ();
+
+  stringLength = [string length];
+
+  /* Force layout.  */
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  spaceLocation = [layoutManager locationForGlyphAtIndex:stringLength];
+
+  /* Remove the appended trailing space because otherwise it may
+     generate a wrong result for a right-to-left text.  */
+  [textStorage beginEditing];
+  [textStorage deleteCharactersInRange:(NSMakeRange (stringLength, 1))];
+  [textStorage endEditing];
+  (void) [layoutManager glyphRangeForTextContainer:textContainer];
+
+  i = 0;
+  while (i < stringLength)
+    {
+      NSRange range;
+      NSFont *fontInTextStorage =
+        [textStorage attribute: NSFontAttributeName
+		       atIndex:i
+                     longestEffectiveRange: &range
+                       inRange: NSMakeRange (0, stringLength)];
+
+      if (!(fontInTextStorage == font
+            || [[fontInTextStorage fontName] isEqualToString:[font fontName]]))
+        break;
+      i = NSMaxRange (range);
+    }
+  if (i < stringLength)
+    /* Make the test `used <= glyph_len' below fail if textStorage
+       contained some fonts other than the specified one.  */
+    used = glyph_len + 1;
+  else
+    {
+      NSRange range = NSMakeRange (0, stringLength);
+
+      range = [layoutManager glyphRangeForCharacterRange:range
+                                    actualCharacterRange:NULL];
+      numberOfGlyphs = NSMaxRange (range);
+      used = numberOfGlyphs;
+      for (i = 0; i < numberOfGlyphs; i++)
+        if ([layoutManager notShownAttributeForGlyphAtIndex:i])
+          used--;
+    }
+
+  if (0 < used && used <= glyph_len)
+    {
+      NSUInteger glyphIndex, prevGlyphIndex;
+      NSUInteger *permutation;
+      NSRange compRange, range;
+      CGFloat totalAdvance;
+
+      glyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      permutation = NULL;
+#define RIGHT_TO_LEFT_P permutation
+
+      /* Fill the `comp_range' member of struct mac_glyph_layout, and
+         setup a permutation for right-to-left text.  */
+      compRange = NSMakeRange (0, 0);
+      for (range = NSMakeRange (0, 0); NSMaxRange (range) < used;
+           range.length++)
+        {
+          struct ns_glyph_layout *gl = glyph_layouts + NSMaxRange (range);
+          NSUInteger characterIndex =
+            [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
+
+          gl->string_index = characterIndex;
+
+          if (characterIndex >= NSMaxRange (compRange))
+            {
+              compRange.location = NSMaxRange (compRange);
+              do
+                {
+                  NSRange characterRange =
+                    [string
+                      rangeOfComposedCharacterSequenceAtIndex:characterIndex];
+
+                  compRange.length =
+                    NSMaxRange (characterRange) - compRange.location;
+                  [layoutManager glyphRangeForCharacterRange:compRange
+                                        actualCharacterRange:&characterRange];
+                  characterIndex = NSMaxRange (characterRange) - 1;
+                }
+              while (characterIndex >= NSMaxRange (compRange));
+
+              if (RIGHT_TO_LEFT_P)
+                for (i = 0; i < range.length; i++)
+                  permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+              range = NSMakeRange (NSMaxRange (range), 0);
+            }
+
+          gl->comp_range.location = compRange.location;
+          gl->comp_range.length = compRange.length;
+
+          while (++glyphIndex < numberOfGlyphs)
+            if (![layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+              break;
+        }
+      if (RIGHT_TO_LEFT_P)
+        for (i = 0; i < range.length; i++)
+          permutation[range.location + i] = NSMaxRange (range) - i - 1;
+
+      /* Then fill the remaining members.  */
+      glyphIndex = prevGlyphIndex = 0;
+      while ([layoutManager notShownAttributeForGlyphAtIndex:glyphIndex])
+        glyphIndex++;
+
+      if (!RIGHT_TO_LEFT_P)
+        totalAdvance = 0;
+      else
+        {
+          NSUInteger nrects;
+          NSRect *glyphRects =
+            [layoutManager
+              rectArrayForGlyphRange:(NSMakeRange (0, numberOfGlyphs))
+              withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                     inTextContainer:textContainer rectCount:&nrects];
+
+          totalAdvance = NSMaxX (glyphRects[0]);
+        }
+
+      for (i = 0; i < used; i++)
+        {
+          struct ns_glyph_layout *gl;
+          NSPoint location;
+          NSUInteger nextGlyphIndex;
+          NSRange glyphRange;
+          NSRect *glyphRects;
+          NSUInteger nrects;
+
+          if (!RIGHT_TO_LEFT_P)
+            gl = glyph_layouts + i;
+          else
+            {
+              NSUInteger dest = permutation[i];
+
+              gl = glyph_layouts + dest;
+              if (i < dest)
+                {
+                  NSUInteger tmp = gl->string_index;
+
+                  gl->string_index = glyph_layouts[i].string_index;
+                  glyph_layouts[i].string_index = tmp;
+                }
+            }
+          gl->glyph_id = [layoutManager glyphAtIndex: glyphIndex];
+
+          location = [layoutManager locationForGlyphAtIndex:glyphIndex];
+          gl->baseline_delta = spaceLocation.y - location.y;
+
+          for (nextGlyphIndex = glyphIndex + 1; nextGlyphIndex < numberOfGlyphs;
+               nextGlyphIndex++)
+            if (![layoutManager
+                   notShownAttributeForGlyphAtIndex:nextGlyphIndex])
+              break;
+
+          if (!RIGHT_TO_LEFT_P)
+            {
+              CGFloat maxX;
+
+              if (prevGlyphIndex == 0)
+                glyphRange = NSMakeRange (0, nextGlyphIndex);
+              else
+                glyphRange = NSMakeRange (glyphIndex,
+                                          nextGlyphIndex - glyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              maxX = max (NSMaxX (glyphRects[0]), totalAdvance);
+              gl->advance_delta = location.x - totalAdvance;
+              gl->advance = maxX - totalAdvance;
+              totalAdvance = maxX;
+            }
+          else
+            {
+              CGFloat minX;
+
+              if (nextGlyphIndex == numberOfGlyphs)
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          numberOfGlyphs - prevGlyphIndex);
+              else
+                glyphRange = NSMakeRange (prevGlyphIndex,
+                                          glyphIndex + 1 - prevGlyphIndex);
+              glyphRects =
+                [layoutManager
+                  rectArrayForGlyphRange:glyphRange
+                  withinSelectedGlyphRange:(NSMakeRange (NSNotFound, 0))
+                         inTextContainer:textContainer rectCount:&nrects];
+              minX = min (NSMinX (glyphRects[0]), totalAdvance);
+              gl->advance = totalAdvance - minX;
+              totalAdvance = minX;
+              gl->advance_delta = location.x - totalAdvance;
+            }
+
+          prevGlyphIndex = glyphIndex + 1;
+          glyphIndex = nextGlyphIndex;
+        }
+
+      if (RIGHT_TO_LEFT_P)
+        xfree (permutation);
+
+#undef RIGHT_TO_LEFT_P
+
+      result = used;
+    }
+  [textStorage release];
+
+  return result;
+}
+
+static Lisp_Object
+nsfont_shape (Lisp_Object lgstring, Lisp_Object direction)
+{
+  struct font *font = CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  struct nsfont_info *font_info = (struct nsfont_info *) font;
+  struct ns_glyph_layout *glyph_layouts;
+  NSFont *nsfont = font_info->nsfont;
+  ptrdiff_t glyph_len, len, i;
+  Lisp_Object tem;
+  unichar *mb_buf;
+  NSUInteger used;
+
+  glyph_len = LGSTRING_GLYPH_LEN (lgstring);
+  for (i = 0; i < glyph_len; ++i)
+    {
+      tem = LGSTRING_GLYPH (lgstring, i);
+
+      if (NILP (tem))
+	break;
+    }
+
+  len = i;
+
+  if (INT_MAX / 2 < len)
+    memory_full (SIZE_MAX);
+
+  block_input ();
+
+  mb_buf = alloca (len * sizeof *mb_buf);
+
+  for (i = 0; i < len; ++i)
+    {
+      uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
+      mb_buf[i] = (unichar) c;
+    }
+
+  NSString *string = [NSString stringWithCharacters: mb_buf
+					     length: len];
+  unblock_input ();
+
+  if (!string)
+    return Qnil;
+
+  block_input ();
+
+  enum lgstring_direction dir = DIR_UNKNOWN;
+
+  if (EQ (direction, QL2R))
+    dir = DIR_L2R;
+  else if (EQ (direction, QR2L))
+    dir = DIR_R2L;
+  glyph_layouts = alloca (sizeof (struct ns_glyph_layout) * glyph_len);
+  used = ns_font_shape (nsfont, string, glyph_layouts, glyph_len, dir);
+
+  for (i = 0; i < used; i++)
+    {
+      Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, i);
+      struct ns_glyph_layout *gl = glyph_layouts + i;
+      EMACS_INT from, to;
+      struct font_metrics metrics;
+
+      if (NILP (lglyph))
+        {
+          lglyph = LGLYPH_NEW ();
+          LGSTRING_SET_GLYPH (lgstring, i, lglyph);
+        }
+
+      from = gl->comp_range.location;
+      LGLYPH_SET_FROM (lglyph, from);
+
+      to = gl->comp_range.location + gl->comp_range.length;
+      LGLYPH_SET_TO (lglyph, to - 1);
+
+      /* LGLYPH_CHAR is used in `describe-char' for checking whether
+         the composition is trivial.  */
+      {
+        UTF32Char c;
+
+        if (mb_buf[gl->string_index] >= 0xD800
+            && mb_buf[gl->string_index] < 0xDC00)
+          c = (((mb_buf[gl->string_index] - 0xD800) << 10)
+               + (mb_buf[gl->string_index + 1] - 0xDC00) + 0x10000);
+        else
+          c = mb_buf[gl->string_index];
+
+        LGLYPH_SET_CHAR (lglyph, c);
+      }
+
+      {
+        unsigned long cc = gl->glyph_id;
+        LGLYPH_SET_CODE (lglyph, cc);
+      }
+
+      nsfont_text_extents (font, &gl->glyph_id, 1, &metrics);
+      LGLYPH_SET_WIDTH (lglyph, metrics.width);
+      LGLYPH_SET_LBEARING (lglyph, metrics.lbearing);
+      LGLYPH_SET_RBEARING (lglyph, metrics.rbearing);
+      LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
+      LGLYPH_SET_DESCENT (lglyph, metrics.descent);
+    }
+  unblock_input ();
+
+  return make_fixnum (used);
+}
 
 
 /* ==========================================================================
@@ -1134,6 +1606,50 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
    ========================================================================== */
 
+static NSGlyph
+ns_uni_to_glyphs_1 (struct nsfont_info *info, unsigned int c)
+{
+  unichar characters[] = { c };
+  NSString *string =
+    [NSString stringWithCharacters: characters
+			    length: 1];
+  NSDictionary *attributes =
+    [NSDictionary dictionaryWithObjectsAndKeys:
+		    info->nsfont, NSFontAttributeName, nil];
+  NSTextStorage *storage = [[NSTextStorage alloc] initWithString: string
+						      attributes: attributes];
+  NSTextContainer *text_container = [[NSTextContainer alloc] init];
+  NSLayoutManager *manager = [[NSLayoutManager alloc] init];
+
+  [manager addTextContainer: text_container];
+  [text_container release]; /* Retained by manager */
+  [storage addLayoutManager: manager];
+  [manager release]; /* Retained by storage */
+
+  NSFont *font_in_storage = [storage attribute: NSFontAttributeName
+				       atIndex:0
+				effectiveRange: NULL];
+  NSGlyph glyph = FONT_INVALID_CODE;
+
+  if ((font_in_storage == info->nsfont
+       || [[font_in_storage fontName] isEqualToString: [info->nsfont fontName]]))
+    {
+      @try
+	{
+	  glyph = [manager glyphAtIndex: 0];
+	}
+      @catch (NSException *e)
+	{
+	  /* GNUstep bug? */
+	  glyph = 'X';
+	}
+    }
+
+  [storage release];
+
+  return glyph;
+}
+
 /* Find and cache corresponding glyph codes for unicode values in given
    hi-byte block of 256.  */
 static void
@@ -1141,7 +1657,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   unichar *unichars = xmalloc (0x101 * sizeof (unichar));
   unsigned int i, g, idx;
-  unsigned short *glyphs;
+  unsigned int *glyphs;
 
   if (NSFONT_TRACE)
     fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
@@ -1149,7 +1665,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
 
   block_input ();
 
-  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned short));
+  font_info->glyphs[block] = xmalloc (0x100 * sizeof (unsigned int));
   if (!unichars || !(font_info->glyphs[block]))
     emacs_abort ();
 
@@ -1166,7 +1682,8 @@ is false when (FROM > 0 || TO < S->nchars).  */
     for (i = 0; i < 0x100; i++, glyphs++)
       {
         g = unichars[i];
-        *glyphs = g;
+	NSGlyph glyph = ns_uni_to_glyphs_1 (font_info, g);
+        *glyphs = glyph;
       }
   }
 
@@ -1175,18 +1692,19 @@ is false when (FROM > 0 || TO < S->nchars).  */
 }
 
 
-/* Determine and cache metrics for corresponding glyph codes in given
-   hi-byte block of 256.  */
+/* Determine and cache metrics for glyphs in given hi-byte block of
+   256.  */
 static void
-ns_glyph_metrics (struct nsfont_info *font_info, unsigned char block)
+ns_glyph_metrics (struct nsfont_info *font_info, unsigned int block)
 {
-  unsigned int i, g;
+  unsigned int i;
+  NSGlyph g;
   unsigned int numGlyphs = [font_info->nsfont numberOfGlyphs];
   NSFont *sfont;
   struct font_metrics *metrics;
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
+    fprintf (stderr, "%p\tComputing metrics for glyphs in block %u\n",
             font_info, block);
 
   /* not implemented yet (as of startup 0.18), so punt */
@@ -1209,19 +1727,14 @@ is false when (FROM > 0 || TO < S->nchars).  */
       w = max ([sfont advancementForGlyph: g].width, 2.0);
       metrics->width = lrint (w);
 
-      lb = r.origin.x;
-      rb = r.size.width - w;
-      // Add to bearing for LCD smoothing.  We don't know if it is there.
-      if (lb < 0)
-        metrics->lbearing = round (lb - LCD_SMOOTHING_MARGIN);
-      if (font_info->ital)
-        rb += (CGFloat) (0.22F * font_info->height);
-      metrics->rbearing = lrint (w + rb + LCD_SMOOTHING_MARGIN);
-
-      metrics->descent = r.origin.y < 0 ? -r.origin.y : 0;
-      /* lrint (hshrink * [sfont ascender] + expand * hd/2); */
-      metrics->ascent = r.size.height - metrics->descent;
-      /* -lrint (hshrink* [sfont descender] - expand * hd/2); */
+      lb = NSMinX (r);
+      rb = NSMaxX (r);
+
+      metrics->rbearing = lrint (rb);
+      metrics->lbearing = lrint (lb);
+
+      metrics->descent = NSMinY (r);
+      metrics->ascent = NSMaxY (r);
     }
   unblock_input ();
 }
@@ -1257,6 +1770,7 @@ is false when (FROM > 0 || TO < S->nchars).  */
   .has_char = nsfont_has_char,
   .encode_char = nsfont_encode_char,
   .text_extents = nsfont_text_extents,
+  .shape = nsfont_shape,
   .draw = nsfont_draw,
   };
 
@@ -1265,7 +1779,6 @@ is false when (FROM > 0 || TO < S->nchars).  */
 {
   DEFSYM (Qcondensed, "condensed");
   DEFSYM (Qexpanded, "expanded");
-  DEFSYM (Qapple, "apple");
   DEFSYM (Qmedium, "medium");
   DEFVAR_LISP ("ns-reg-to-script", Vns_reg_to_script,
                doc: /* Internal use: maps font registry to Unicode script.  */);
diff --git a/src/nsterm.h b/src/nsterm.h
index 4bbcf43973..944dbd727c 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -820,7 +820,7 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
   XCharStruct max_bounds;
   /* We compute glyph codes and metrics on-demand in blocks of 256 indexed
      by hibyte, lobyte.  */
-  unsigned short **glyphs; /* map Unicode index to glyph */
+  unsigned int **glyphs; /* map Unicode index to glyph */
   struct font_metrics **metrics;
 };
 #endif
diff --git a/src/nsterm.m b/src/nsterm.m
index 418931eeee..4c915dac5d 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1079,11 +1079,16 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
   /* clipping */
   if (r)
     {
-      [[NSGraphicsContext currentContext] saveGraphicsState];
+      NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+      [ctx saveGraphicsState];
+#ifdef NS_IMPL_COCOA
       if (n == 2)
         NSRectClipList (r, 2);
       else
         NSRectClip (*r);
+#else
+      GSRectClipList (ctx, r, n);
+#endif
       gsaved = YES;
     }
 }
@@ -2435,9 +2440,6 @@ Hide the window (X11 semantics)
       EmacsView *view = FRAME_NS_VIEW (f);
       FRAME_POINTER_TYPE (f) = cursor;
       [[view window] invalidateCursorRectsForView: view];
-      /* Redisplay assumes this function also draws the changed frame
-         cursor, but this function doesn't, so do it explicitly.  */
-      gui_update_cursor (f, 1);
     }
 }
 
@@ -2849,39 +2851,31 @@ Hide the window (X11 semantics)
      External (RIF); compute left/right overhang of whole string and set in s
    -------------------------------------------------------------------------- */
 {
-  struct font *font = s->font;
-
-  if (s->char2b)
+  if (s->cmp == NULL
+      && (s->first_glyph->type == CHAR_GLYPH
+	  || s->first_glyph->type == COMPOSITE_GLYPH))
     {
       struct font_metrics metrics;
-      if (s->first_glyph->type == CHAR_GLYPH && !s->font_not_found_p)
-        {
-          font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
-          s->left_overhang = -metrics.lbearing;
-          s->right_overhang
-            = metrics.rbearing > metrics.width
-            ? metrics.rbearing - metrics.width : 0;
-        }
-      else if (s->first_glyph->type == COMPOSITE_GLYPH)
-        {
-          Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
 
-          composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
-          s->right_overhang = (metrics.rbearing > metrics.width
-                               ? metrics.rbearing - metrics.width : 0);
-          s->left_overhang = metrics.lbearing < 0 ? -metrics.lbearing : 0;
-        }
+      if (s->first_glyph->type == CHAR_GLYPH)
+	{
+	  struct font *font = s->font;
+	  font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+	}
+      else
+	{
+	  Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+	  composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+	}
+      s->right_overhang = (metrics.rbearing > metrics.width
+			   ? metrics.rbearing - metrics.width : 0);
+      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
     }
-  else
+  else if (s->cmp)
     {
-      s->left_overhang = 0;
-#ifdef NS_IMPL_GNUSTEP
-      if (EQ (font->driver->type, Qns))
-        s->right_overhang = ((struct nsfont_info *)font)->ital ?
-          FONT_HEIGHT (font) * 0.2 : 0;
-      else
-#endif
-        s->right_overhang = 0;
+      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+      s->left_overhang = - s->cmp->lbearing;
     }
 }
 
@@ -3021,14 +3015,13 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   struct frame *f = WINDOW_XFRAME (w);
   struct glyph *phys_cursor_glyph;
   struct glyph *cursor_glyph;
-  struct face *face;
-  NSColor *hollow_color = FRAME_BACKGROUND_COLOR (f);
 
   /* If cursor is out of bounds, don't draw garbage.  This can happen
      in mini-buffer windows when switching between echo area glyphs
      and mini-buffer.  */
 
-  NSTRACE ("ns_draw_window_cursor");
+  NSTRACE ("ns_draw_window_cursor (on = %d, cursor_type = %d)",
+	   on_p, cursor_type);
 
   if (!on_p)
     return;
@@ -3044,6 +3037,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   if ((phys_cursor_glyph = get_phys_cursor_glyph (w)) == NULL)
     {
+      NSTRACE_MSG ("No phys cursor glyph was found!");
+
       if (glyph_row->exact_window_width_line_p
           && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
         {
@@ -3053,10 +3048,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       return;
     }
 
-  /* We draw the cursor (with NSRectFill), then draw the glyph on top
-     (other terminals do it the other way round).  We must set
-     w->phys_cursor_width to the cursor width.  For bar cursors, that
-     is CURSOR_WIDTH; for box cursors, it is the glyph width.  */
   get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
 
   /* The above get_phys_cursor_geometry call set w->phys_cursor_width
@@ -3088,17 +3079,17 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   /* Prevent the cursor from being drawn outside the text area.  */
   r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
 
-  ns_focus (f, &r, 1);
+  ns_focus (f, NULL, 0);
 
-  face = FACE_FROM_ID_OR_NULL (f, phys_cursor_glyph->face_id);
-  if (face && NS_FACE_BACKGROUND (face)
-      == ns_index_color (FRAME_CURSOR_COLOR (f), f))
-    {
-      [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
-      hollow_color = FRAME_CURSOR_COLOR (f);
-    }
-  else
-    [FRAME_CURSOR_COLOR (f) set];
+  NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
+  [ctx saveGraphicsState];
+#ifdef NS_IMPL_GNUSTEP
+  GSRectClipList (ctx, &r, 1);
+#else
+  NSRectClip (r);
+#endif
+
+  [FRAME_CURSOR_COLOR (f) set];
 
   switch (cursor_type)
     {
@@ -3106,13 +3097,11 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     case NO_CURSOR:
       break;
     case FILLED_BOX_CURSOR:
-      NSRectFill (r);
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
       break;
     case HOLLOW_BOX_CURSOR:
-      NSRectFill (r);
-      [hollow_color set];
-      NSRectFill (NSInsetRect (r, 1, 1));
-      [FRAME_CURSOR_COLOR (f) set];
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_NORMAL_TEXT);
+      [NSBezierPath strokeRect: r];
       break;
     case HBAR_CURSOR:
       NSRectFill (r);
@@ -3128,12 +3117,9 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       NSRectFill (s);
       break;
     }
-  ns_unfocus (f);
 
-  /* Draw the character under the cursor.  Other terms only draw
-     the character on top of box cursors, so do the same here.  */
-  if (cursor_type == FILLED_BOX_CURSOR || cursor_type == HOLLOW_BOX_CURSOR)
-    draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+  [ctx restoreGraphicsState];
+  ns_unfocus (f);
 }
 
 
@@ -3313,16 +3299,18 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   if (s->for_overlaps)
     return;
 
+  if (s->hl == DRAW_CURSOR)
+    [FRAME_BACKGROUND_COLOR (s->f) set];
+  else if (face->underline_defaulted_p)
+    [defaultCol set];
+  else
+    [ns_lookup_indexed_color (face->underline_color, s->f) set];
+
   /* Do underline.  */
   if (face->underline)
     {
       if (s->face->underline == FACE_UNDER_WAVE)
         {
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
-
           ns_draw_underwave (s, width, x);
         }
       else if (s->face->underline == FACE_UNDER_LINE)
@@ -3393,11 +3381,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
           s->underline_position = position;
 
           r = NSMakeRect (x, s->ybase + position, width, thickness);
-
-          if (face->underline_defaulted_p)
-            [defaultCol set];
-          else
-            [ns_lookup_indexed_color (face->underline_color, s->f) set];
           NSRectFill (r);
         }
     }
@@ -3407,11 +3390,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
     {
       NSRect r;
       r = NSMakeRect (x, s->y, width, 1);
-
-      if (face->overline_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->overline_color, s->f) set];
       NSRectFill (r);
     }
 
@@ -3434,10 +3412,6 @@ larger if there are taller display elements (e.g., characters
       dy = lrint ((glyph_height - h) / 2);
       r = NSMakeRect (x, glyph_y + dy, width, 1);
 
-      if (face->strike_through_color_defaulted_p)
-        [defaultCol set];
-      else
-        [ns_lookup_indexed_color (face->strike_through_color, s->f) set];
       NSRectFill (r);
     }
 }
@@ -3585,17 +3559,7 @@ Function modeled after x_draw_glyph_string_box ().
   struct glyph *last_glyph;
   NSRect r;
   int hthickness, vthickness;
-  struct face *face;
-
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-        face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = s->face;
+  struct face *face = s->face;
 
   vthickness = face->box_vertical_line_width;
   hthickness = face->box_horizontal_line_width;
@@ -3669,34 +3633,26 @@ Function modeled after x_draw_glyph_string_box ().
 	  || FONT_TOO_HIGH (s->font)
           || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
 	{
-          struct face *face;
-          if (s->hl == DRAW_MOUSE_FACE)
-            {
-              face
-		= FACE_FROM_ID_OR_NULL (s->f,
-					MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-              if (!face)
-                face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-            }
-          else
-            face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+          struct face *face = s->face;
           if (!face->stipple)
-            [(NS_FACE_BACKGROUND (face) != 0
-              ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
-              : FRAME_BACKGROUND_COLOR (s->f)) set];
+	    {
+	      if (s->hl != DRAW_CURSOR)
+		[(NS_FACE_BACKGROUND (face) != 0
+		  ? ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f)
+		  : FRAME_BACKGROUND_COLOR (s->f)) set];
+	      else
+		[FRAME_CURSOR_COLOR (s->f) set];
+	    }
           else
             {
               struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f);
               [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set];
             }
 
-          if (s->hl != DRAW_CURSOR)
-            {
-              NSRect r = NSMakeRect (s->x, s->y + box_line_width,
-                                    s->background_width,
-                                    s->height-2*box_line_width);
-              NSRectFill (r);
-            }
+	  NSRect r = NSMakeRect (s->x, s->y + box_line_width,
+				 s->background_width,
+				 s->height-2*box_line_width);
+	  NSRectFill (r);
 
 	  s->background_filled_p = 1;
 	}
@@ -3717,7 +3673,7 @@ Function modeled after x_draw_glyph_string_box ().
   int th;
   char raised_p;
   NSRect br;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *tdCol;
 
   NSTRACE ("ns_dumpglyphs_image");
@@ -3738,15 +3694,6 @@ Function modeled after x_draw_glyph_string_box ().
   /* Draw BG: if we need larger area than image itself cleared, do that,
      otherwise, since we composite the image under NS (instead of mucking
      with its background color), we must clear just the image area.  */
-  if (s->hl == DRAW_MOUSE_FACE)
-    {
-      face = FACE_FROM_ID_OR_NULL (s->f,
-				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-      if (!face)
-       face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-    }
-  else
-    face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
 
   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
 
@@ -3817,16 +3764,8 @@ Function modeled after x_draw_glyph_string_box ().
 
   if (s->hl == DRAW_CURSOR)
     {
-    [FRAME_CURSOR_COLOR (s->f) set];
-    if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
+      [FRAME_CURSOR_COLOR (s->f) set];
       tdCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
-    else
-      /* Currently on NS img->mask is always 0.  Since
-         get_window_cursor_type specifies a hollow box cursor when on
-         a non-masked image we never reach this clause.  But we put it
-         in, in anticipation of better support for image masks on
-         NS.  */
-      tdCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
     }
   else
     {
@@ -3878,66 +3817,35 @@ Function modeled after x_draw_glyph_string_box ().
 static void
 ns_dumpglyphs_stretch (struct glyph_string *s)
 {
-  NSRect r[2];
   NSRect glyphRect;
-  int n;
-  struct face *face;
+  struct face *face = s->face;
   NSColor *fgCol, *bgCol;
 
   if (!s->background_filled_p)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
 
-      if (s->hl == DRAW_MOUSE_FACE)
-        {
-          face = FACE_FROM_ID_OR_NULL (s->f,
-                                       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
-          if (!face)
-            face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
-        }
-      else
-        face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+      face = s->face;
 
       bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
       fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
 
-      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
-
-      [bgCol set];
-
-      /* NOTE: under NS this is NOT used to draw cursors, but we must avoid
-         overwriting cursor (usually when cursor on a tab) */
       if (s->hl == DRAW_CURSOR)
-        {
-          CGFloat x, width;
+	{
+	  fgCol = bgCol;
+	  bgCol = FRAME_CURSOR_COLOR (s->f);
+	}
 
-          /* FIXME: This looks like it will only work for left to
-             right languages.  */
-          x = NSMinX (glyphRect);
-          width = s->w->phys_cursor_width;
-          glyphRect.size.width -= width;
-          glyphRect.origin.x += width;
+      glyphRect = NSMakeRect (s->x, s->y, s->background_width, s->height);
 
-          NSRectFill (glyphRect);
+      [bgCol set];
 
-          /* Draw overlining, etc. on the cursor. */
-          if (s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-            ns_draw_text_decoration (s, face, bgCol, width, x);
-          else
-            ns_draw_text_decoration (s, face, fgCol, width, x);
-        }
-      else
-        {
-          NSRectFill (glyphRect);
-        }
+      NSRectFill (glyphRect);
 
       /* Draw overlining, etc. on the stretch glyph (or the part
          of the stretch glyph after the cursor). */
       ns_draw_text_decoration (s, face, fgCol, NSWidth (glyphRect),
                                NSMinX (glyphRect));
 
-      ns_unfocus (s->f);
       s->background_filled_p = 1;
     }
 }
@@ -3946,7 +3854,7 @@ overwriting cursor (usually when cursor on a tab) */
 static void
 ns_draw_glyph_string_foreground (struct glyph_string *s)
 {
-  int x, flags;
+  int x;
   struct font *font = s->font;
 
   /* If first glyph of S has a left box line, start drawing the text
@@ -3957,15 +3865,9 @@ overwriting cursor (usually when cursor on a tab) */
   else
     x = s->x;
 
-  flags = s->hl == DRAW_CURSOR ? NS_DUMPGLYPH_CURSOR :
-    (s->hl == DRAW_MOUSE_FACE ? NS_DUMPGLYPH_MOUSEFACE :
-     (s->for_overlaps ? NS_DUMPGLYPH_FOREGROUND :
-      NS_DUMPGLYPH_NORMAL));
-
   font->driver->draw
     (s, s->cmp_from, s->nchars, x, s->ybase,
-     (flags == NS_DUMPGLYPH_NORMAL && !s->background_filled_p)
-     || flags == NS_DUMPGLYPH_MOUSEFACE);
+     !s->for_overlaps && !s->background_filled_p);
 }
 
 
@@ -4072,9 +3974,9 @@ overwriting cursor (usually when cursor on a tab) */
   struct font *font = s->face->font;
   if (! font) font = FRAME_FONT (s->f);
 
-  NSTRACE_WHEN (NSTRACE_GROUP_GLYPHS, "ns_draw_glyph_string");
+  NSTRACE ("ns_draw_glyph_string (hl = %u)", s->hl);
 
-  if (s->next && s->right_overhang && !s->for_overlaps/*&&s->hl!=DRAW_CURSOR*/)
+  if (s->next && s->right_overhang && !s->for_overlaps)
     {
       int width;
       struct glyph_string *next;
@@ -4111,14 +4013,21 @@ overwriting cursor (usually when cursor on a tab) */
       box_drawn_p = 1;
     }
 
+  n = ns_get_glyph_string_clip_rect (s, r);
+
+  if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+      && !s->clip_tail
+      && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+	  || (s->next && s->next->hl != s->hl && s->right_overhang)))
+    r[0] = NSIntersectionRect (r[0], NSMakeRect (s->x, s->y, s->width, s->height));
+
+  ns_focus (s->f, r, n);
+
   switch (s->first_glyph->type)
     {
 
     case IMAGE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
       ns_dumpglyphs_image (s, r[0]);
-      ns_unfocus (s->f);
       break;
 
     case XWIDGET_GLYPH:
@@ -4131,57 +4040,36 @@ overwriting cursor (usually when cursor on a tab) */
 
     case CHAR_GLYPH:
     case COMPOSITE_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
-      if (s->for_overlaps || (s->cmp_from > 0
-			      && ! s->first_glyph->u.cmp.automatic))
-        s->background_filled_p = 1;
-      else
-        ns_maybe_dumpglyphs_background
-          (s, s->first_glyph->type == COMPOSITE_GLYPH);
-
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
       {
-        BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
+	if (s->for_overlaps || (isComposite
+				&& (s->cmp_from > 0
+				    && ! s->first_glyph->u.cmp.automatic)))
+	  s->background_filled_p = 1;
+	else
+	  ns_maybe_dumpglyphs_background
+	    (s, s->first_glyph->type == COMPOSITE_GLYPH);
 
-        if (isComposite)
-          ns_draw_composite_glyph_string_foreground (s);
-        else
-          ns_draw_glyph_string_foreground (s);
-      }
+	if (isComposite)
+	  ns_draw_composite_glyph_string_foreground (s);
+	else
+	  ns_draw_glyph_string_foreground (s);
 
-      {
-        NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
-                        ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
-                                                   s->f)
-                        : FRAME_FOREGROUND_COLOR (s->f));
-        [col set];
-
-        /* Draw underline, overline, strike-through. */
-        ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	{
+	  NSColor *col = (NS_FACE_FOREGROUND (s->face) != 0
+			  ? ns_lookup_indexed_color (NS_FACE_FOREGROUND (s->face),
+						     s->f)
+			  : FRAME_FOREGROUND_COLOR (s->f));
+	  [col set];
+
+	  /* Draw underline, overline, strike-through. */
+	  ns_draw_text_decoration (s, s->face, col, s->width, s->x);
+	}
       }
 
-      if (s->hl == DRAW_CURSOR && s->w->phys_cursor_type == FILLED_BOX_CURSOR)
-        {
-          unsigned long tmp = NS_FACE_BACKGROUND (s->face);
-          NS_FACE_BACKGROUND (s->face) = NS_FACE_FOREGROUND (s->face);
-          NS_FACE_FOREGROUND (s->face) = tmp;
-        }
-
-      ns_unfocus (s->f);
       break;
 
     case GLYPHLESS_GLYPH:
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-
       if (s->for_overlaps || (s->cmp_from > 0
 			      && ! s->first_glyph->u.cmp.automatic))
         s->background_filled_p = 1;
@@ -4191,7 +4079,6 @@ overwriting cursor (usually when cursor on a tab) */
       /* ... */
       /* Not yet implemented.  */
       /* ... */
-      ns_unfocus (s->f);
       break;
 
     default:
@@ -4200,13 +4087,92 @@ overwriting cursor (usually when cursor on a tab) */
 
   /* Draw box if not done already.  */
   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
+    ns_dumpglyphs_box_or_relief (s);
+
+  ns_unfocus (s->f);
+
+  /* Draw surrounding overhangs. */
+  if (s->prev)
     {
-      n = ns_get_glyph_string_clip_rect (s, r);
-      ns_focus (s->f, r, n);
-      ns_dumpglyphs_box_or_relief (s);
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *prev;
+
+      for (prev = s->prev; prev; prev = prev->prev)
+	if (prev->hl != s->hl
+	    && prev->x + prev->width + prev->right_overhang > s->x)
+	  {
+	    /* As prev was drawn while clipped to its own area, we
+	       must draw the right_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = prev->hl;
+	    struct face *save_face = prev->face;
+
+	    prev->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    prev->num_clips = 1;
+	    prev->hl = s->hl;
+	    if (prev->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (prev);
+	    else
+	      ns_draw_composite_glyph_string_foreground (prev);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    prev->hl = save;
+	    prev->face = save_face;
+	    prev->num_clips = 0;
+	  }
       ns_unfocus (s->f);
     }
 
+  if (s->next)
+    {
+      ns_focus (s->f, NULL, 0);
+      struct glyph_string *next;
+
+      for (next = s->next; next; next = next->next)
+	if (next->hl != s->hl
+	    && next->x - next->left_overhang < s->x + s->width)
+	  {
+	    /* As next will be drawn while clipped to its own area,
+	       we must draw the left_overhang part using s->hl now.  */
+	    enum draw_glyphs_face save = next->hl;
+	    struct face *save_face = next->face;
+
+	    next->hl = s->hl;
+	    next->face = s->face;
+	    NSRect r = NSMakeRect (s->x, s->y, s->width, s->height);
+	    [[NSGraphicsContext currentContext] saveGraphicsState];
+	    NSRectClip (r);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgsave ([NSGraphicsContext currentContext]);
+	    DPSrectclip ([NSGraphicsContext currentContext], s->x, s->y,
+			 s->width, s->height);
+#endif
+	    next->num_clips = 1;
+	    if (next->first_glyph->type == CHAR_GLYPH)
+	      ns_draw_glyph_string_foreground (next);
+	    else
+	      ns_draw_composite_glyph_string_foreground (next);
+#ifdef NS_IMPL_GNUSTEP
+	    DPSgrestore ([NSGraphicsContext currentContext]);
+#endif
+	    [[NSGraphicsContext currentContext] restoreGraphicsState];
+	    next->hl = save;
+	    next->num_clips = 0;
+	    next->face = save_face;
+	    next->clip_head = next;
+	    next->background_filled_p = 0;
+	  }
+      ns_unfocus (s->f);
+    }
   s->num_clips = 0;
 }
 
diff --git a/src/xdisp.c b/src/xdisp.c
index 646beed6f0..9814efd63c 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -29306,7 +29306,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
   for (s = head; s; s = s->next)
     FRAME_RIF (f)->draw_glyph_string (s);
 
-#ifndef HAVE_NS
   /* When focus a sole frame and move horizontally, this clears on_p
      causing a failure to erase prev cursor position. */
   if (area == TEXT_AREA
@@ -29325,7 +29324,6 @@ draw_glyphs (struct window *w, int x, struct glyph_row *row,
       notice_overwritten_cursor (w, TEXT_AREA, x0, x1,
 				 row->y, MATRIX_ROW_BOTTOM_Y (row));
     }
-#endif
 
   /* Value is the x-position up to which drawn, relative to AREA of W.
      This doesn't include parts drawn because of overhangs.  */
-- 
2.31.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-General-improvements-to-NS-port.patch --]
[-- Type: text/x-patch, Size: 12123 bytes --]

From f2a724811cfe11165eef4ec7a3dc12b14e31b2dc Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 23 Oct 2021 19:51:31 +0800
Subject: [PATCH 2/2] General improvements to NS port

* src/dispextern.h: Remove some !HAVE_NS conditionals around grab
related code.
* src/frame.c (gui_mouse_grabbed, gui_redo_mouse_highlight): Remove
!HAVE_NS conditionals around code.

* src/nsmenu.m (ns_update_menubar): Prevent recursive calls and enable
shallow updates on GNUstep.
(menuNeedsUpdate): Prevent recursive calls.
(ns_menu_show): Fix mysterious GC-related bug.
(update_frame_tool_bar_1): Work around mysterious toolbar sizing bug
on GNUstep.

* src/nsterm.h (struct ns_output): New field for tracking toolbar
visibility changes.

* src/nsterm.m (frame_set_mouse_pixel_position): Implement for
GNUstep.
(ns_redraw_scroll_bars): Enable for GNUstep.
(ns_clear_frame): Redraw scrollbars on GNUstep.
(ns_update_window_end): New function.
(ns_redisplay_interface): Add ns_update_window_end on GNUstep.
(- keyDown): Remove debug code that doesn't work on GNUstep.
(- mouseDown): Enable grab tracking on NS port.
(- resizeWithOldSuperviewSize): Fix build with NSTRACE.

* src/xdisp.c (note_tab_bar_highlight): Enable some code for NS port.
---
 src/dispextern.h |  2 --
 src/frame.c      |  4 ---
 src/nsmenu.m     | 76 +++++++++++++++++++++++++++++++++++++++---------
 src/nsterm.h     |  6 ++++
 src/nsterm.m     | 42 +++++++++++++++++++++-----
 src/xdisp.c      |  2 --
 6 files changed, 102 insertions(+), 30 deletions(-)

diff --git a/src/dispextern.h b/src/dispextern.h
index e03e21fddc..5b28fe7666 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3722,10 +3722,8 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask)			      \
                                           const char *, const char *,
                                           enum resource_types);
 
-#ifndef HAVE_NS /* These both used on W32 and X only.  */
 extern bool gui_mouse_grabbed (Display_Info *);
 extern void gui_redo_mouse_highlight (Display_Info *);
-#endif /* HAVE_NS */
 
 #endif /* HAVE_WINDOW_SYSTEM */
 
diff --git a/src/frame.c b/src/frame.c
index 2b1cb452ef..79a7c89e0d 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -5028,8 +5028,6 @@ gui_set_no_special_glyphs (struct frame *f, Lisp_Object new_value, Lisp_Object o
 }
 
 
-#ifndef HAVE_NS
-
 /* Non-zero if mouse is grabbed on DPYINFO
    and we know the frame where it is.  */
 
@@ -5054,8 +5052,6 @@ gui_redo_mouse_highlight (Display_Info *dpyinfo)
 			  dpyinfo->last_mouse_motion_y);
 }
 
-#endif /* HAVE_NS */
-
 /* Subroutines of creating an X frame.  */
 
 /* Make sure that Vx_resource_name is set to a reasonable value.
diff --git a/src/nsmenu.m b/src/nsmenu.m
index 05b89c2f56..b93d3a79bd 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -101,6 +101,15 @@
 static void
 ns_update_menubar (struct frame *f, bool deep_p)
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+
+  if (inside)
+    return;
+
+  inside++;
+#endif
+
   BOOL needsSet = NO;
   id menu = [NSApp mainMenu];
   bool owfi;
@@ -120,7 +129,12 @@
   NSTRACE ("ns_update_menubar");
 
   if (f != SELECTED_FRAME () || FRAME_EXTERNAL_MENU_BAR (f) == 0)
+    {
+#ifdef NS_IMPL_GNUSTEP
+      inside--;
+#endif
       return;
+    }
   XSETFRAME (Vmenu_updating_frame, f);
 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
 
@@ -144,10 +158,6 @@
   t = -(1000*tb.time+tb.millitm);
 #endif
 
-#ifdef NS_IMPL_GNUSTEP
-  deep_p = 1; /* See comment in menuNeedsUpdate.  */
-#endif
-
   if (deep_p)
     {
       /* Make a widget-value tree representing the entire menu trees.  */
@@ -275,6 +285,9 @@
 	  free_menubar_widget_value_tree (first_wv);
 	  discard_menu_items ();
 	  unbind_to (specpdl_count, Qnil);
+#ifdef NS_IMPL_GNUSTEP
+	  inside--;
+#endif
 	  return;
 	}
 
@@ -408,6 +421,10 @@
   if (needsSet)
     [NSApp setMainMenu: menu];
 
+#ifdef NS_IMPL_GNUSTEP
+  inside--;
+#endif
+
   unblock_input ();
 
 }
@@ -490,17 +507,34 @@ - (instancetype)initWithTitle: (NSString *)title
    call to ns_update_menubar.  */
 - (void)menuNeedsUpdate: (NSMenu *)menu
 {
+#ifdef NS_IMPL_GNUSTEP
+  static int inside = 0;
+#endif
+
   if (!FRAME_LIVE_P (SELECTED_FRAME ()))
     return;
 
-#ifdef NS_IMPL_COCOA
-/* TODO: GNUstep calls this method when the menu is still being built
-   which results in a recursive stack overflow.  One possible solution
-   is to use menuWillOpen instead, but the Apple docs explicitly warn
-   against changing the contents of the menu in it.  I don't know what
-   the right thing to do for GNUstep is.  */
+#ifdef NS_IMPL_GNUSTEP
+  /* GNUstep calls this method when the menu is still being built
+     which results in a recursive stack overflow, which this variable
+     prevents.  */
+
+  if (!inside)
+    ++inside;
+  else
+    return;
+#endif
+
   if (needsUpdate)
-    ns_update_menubar (SELECTED_FRAME (), true);
+    {
+#ifdef NS_IMPL_GNUSTEP
+      needsUpdate = NO;
+#endif
+      ns_update_menubar (SELECTED_FRAME (), true);
+    }
+
+#ifdef NS_IMPL_GNUSTEP
+  --inside;
 #endif
 }
 
@@ -827,6 +861,9 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   p.x = x; p.y = y;
 
+  /* Don't GC due to a mysterious bug.  */
+  inhibit_garbage_collection ();
+
   /* now parse stage 2 as in ns_update_menubar */
   wv = make_widget_value ("contextmenu", NULL, true, Qnil);
   wv->button_type = BUTTON_TYPE_NONE;
@@ -998,15 +1035,17 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
 
   pmenu = [[EmacsMenu alloc] initWithTitle:
                    NILP (title) ? @"" : [NSString stringWithLispString: title]];
+  /* On GNUstep, this call makes menu_items nil for whatever reason
+     when displaying a context menu from `context-menu-mode'.  */
+  Lisp_Object items = menu_items;
   [pmenu fillWithWidgetValue: first_wv->contents];
+  menu_items = items;
   free_menubar_widget_value_tree (first_wv);
-  unbind_to (specpdl_count, Qnil);
-
   popup_activated_flag = 1;
   tem = [pmenu runMenuAt: p forFrame: f keymaps: keymaps];
   popup_activated_flag = 0;
   [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
-
+  unbind_to (specpdl_count, Qnil);
   unblock_input ();
   return tem;
 }
@@ -1057,6 +1096,15 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
   [toolbar clearActive];
 #else
   [toolbar clearAll];
+  /* It takes at least 3 such adjustments to fix an issue where the
+     tool bar is 2x too tall when a frame's tool bar is first shown.
+     This is ugly, but I have no other solution for this problem.  */
+  if (FRAME_OUTPUT_DATA (f)->tool_bar_adjusted < 3)
+    {
+      [toolbar setVisible: NO];
+      FRAME_OUTPUT_DATA (f)->tool_bar_adjusted++;
+      [toolbar setVisible: YES];
+    }
 #endif
 
   /* Update EmacsToolbar as in GtkUtils, build items list.  */
diff --git a/src/nsterm.h b/src/nsterm.h
index 944dbd727c..8175f99664 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -978,6 +978,12 @@ #define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG(color) * 0x101)
 
   /* Non-zero if we are doing an animation, e.g. toggling the tool bar.  */
   int in_animation;
+
+#ifdef NS_IMPL_GNUSTEP
+  /* Zero if this is the first time a toolbar has been updated on this
+     frame. */
+  int tool_bar_adjusted;
+#endif
 };
 
 /* This dummy declaration needed to support TTYs.  */
diff --git a/src/nsterm.m b/src/nsterm.m
index 4c915dac5d..a63883cd08 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -65,6 +65,7 @@ Updated by Christian Limpach (chris@nice.ch)
 
 #ifdef NS_IMPL_GNUSTEP
 #include "process.h"
+#import <GNUstepGUI/GSDisplayServer.h>
 #endif
 
 #ifdef NS_IMPL_COCOA
@@ -2256,13 +2257,19 @@ Hide the window (X11 semantics)
 {
   NSTRACE ("frame_set_mouse_pixel_position");
 
-  /* FIXME: what about GNUstep?  */
 #ifdef NS_IMPL_COCOA
   CGPoint mouse_pos =
     CGPointMake(f->left_pos + pix_x,
                 f->top_pos + pix_y +
                 FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f));
   CGWarpMouseCursorPosition (mouse_pos);
+#else
+  GSDisplayServer *server = GSServerForWindow ([FRAME_NS_VIEW (f) window]);
+  [server setMouseLocation: NSMakePoint (f->left_pos + pix_x,
+					 f->top_pos + pix_y
+					 + FRAME_NS_TITLEBAR_HEIGHT(f)
+					 + FRAME_TOOLBAR_HEIGHT(f))
+		  onScreen: [[[FRAME_NS_VIEW (f) window] screen] screenNumber]];
 #endif
 }
 
@@ -2575,8 +2582,7 @@ Hide the window (X11 semantics)
    ========================================================================== */
 
 
-#if 0
-/* FIXME: Remove this function. */
+#ifdef NS_IMPL_GNUSTEP
 static void
 ns_redraw_scroll_bars (struct frame *f)
 {
@@ -2621,10 +2627,9 @@ Hide the window (X11 semantics)
   NSRectFill (r);
   ns_unfocus (f);
 
-  /* as of 2006/11 or so this is now needed */
-  /* FIXME: I don't see any reason for this and removing it makes no
-     difference here.  Do we need it for GNUstep?  */
-  //ns_redraw_scroll_bars (f);
+#ifdef NS_IMPL_GNUSTEP
+  ns_redraw_scroll_bars (f);
+#endif
   unblock_input ();
 }
 
@@ -4922,6 +4927,17 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
 {
 }
 
+#ifdef NS_IMPL_GNUSTEP
+static void
+ns_update_window_end (struct window *w, bool cursor_on_p,
+		      bool mouse_face_overwritten_p)
+{
+  NSTRACE ("ns_update_window_end (cursor_on_p = %d)", cursor_on_p);
+
+  ns_redraw_scroll_bars (WINDOW_XFRAME (w));
+}
+#endif
+
 /* This and next define (many of the) public functions in this file.  */
 /* gui_* are generic versions in xdisp.c that we, and other terms, get away
          with using despite presence in the "system dependent" redisplay
@@ -4938,7 +4954,11 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
   ns_scroll_run,
   ns_after_update_window_line,
   NULL, /* update_window_begin */
+#ifndef NS_IMPL_GNUSTEP
   NULL, /* update_window_end   */
+#else
+  ns_update_window_end,
+#endif
   0, /* flush_display */
   gui_clear_window_mouse_face,
   gui_get_glyph_overhangs,
@@ -6166,9 +6186,11 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
       Lisp_Object kind = fnKeysym ? QCfunction : QCordinary;
       emacs_event->modifiers = EV_MODIFIERS2 (flags, kind);
 
+#ifndef NS_IMPL_GNUSTEP
       if (NS_KEYLOG)
         fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
                  code, fnKeysym, flags, emacs_event->modifiers);
+#endif
 
       /* If it was a function key or had control-like modifiers, pass
          it directly to Emacs.  */
@@ -6681,6 +6703,11 @@ - (void)mouseDown: (NSEvent *)theEvent
       emacs_event->code = EV_BUTTON (theEvent);
       emacs_event->modifiers = EV_MODIFIERS (theEvent)
                              | EV_UDMODIFIERS (theEvent);
+
+      if (emacs_event->modifiers & down_modifier)
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed |= 1 << EV_BUTTON (theEvent);
+      else
+	FRAME_DISPLAY_INFO (emacsframe)->grabbed &= ~(1 << EV_BUTTON (theEvent));
     }
 
   XSETINT (emacs_event->x, lrint (p.x));
@@ -6981,7 +7008,6 @@ - (void)resizeWithOldSuperviewSize: (NSSize)oldSize
   height = (int)NSHeight (frame);
 
   NSTRACE_SIZE ("New size", NSMakeSize (width, height));
-  NSTRACE_SIZE ("Original size", size);
 
   /* Reset the frame size to match the bounds of the superview (the
      NSWindow's contentView).  We need to do this as sometimes the
diff --git a/src/xdisp.c b/src/xdisp.c
index 9814efd63c..86c4e704d5 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13894,7 +13894,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   clear_mouse_face (hlinfo);
 
   bool mouse_down_p = false;
-#ifndef HAVE_NS
   /* Mouse is down, but on different tab-bar item?  Or alternatively,
      the mouse might've been pressed somewhere we don't know about,
      and then have moved onto the tab bar.  In this case,
@@ -13907,7 +13906,6 @@ note_tab_bar_highlight (struct frame *f, int x, int y)
   if (mouse_down_p && f->last_tab_bar_item != prop_idx
       && f->last_tab_bar_item != -1)
     return;
-#endif
   draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED;
 
   /* If tab-bar item is not enabled, don't highlight it.  */
-- 
2.31.1


[-- Attachment #4: Type: text/plain, Size: 9 bytes --]


Thanks.

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* bug#51411: NS port cleanups
  2021-11-06  0:20                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2021-11-06 13:02                               ` Alan Third
  0 siblings, 0 replies; 22+ messages in thread
From: Alan Third @ 2021-11-06 13:02 UTC (permalink / raw)
  To: Po Lu; +Cc: 51411-done

On Sat, Nov 06, 2021 at 08:20:49AM +0800, Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors wrote:
> Po Lu <luangruo@yahoo.com> writes:
> 
> > As of 48af19c1 those patches don't apply anymore.  Here's a version that
> > does.  (I kept my version of the overhang computation code instead of
> > Daniel's, because the xterm code computes overhangs correctly, and
> > bug#51105 doesn't apply to it AFAIU.)
> 
> And it doesn't apply, again.  This should:

Sorry, I've not been very attentive to Emacs stuff recently. I've
pushed these changes to master.

Thank you!
-- 
Alan Third





^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2021-11-06 13:02 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CADwFkmnHko5UrsuNBKjTtt1LjgXet37zP+U9DTMv++njRHePmQ@mail.gmail.com>
2021-10-31  2:38 ` bug#51411: NS port cleanups Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-31  2:56   ` Stefan Kangas
2021-10-31  3:13     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-31  3:59       ` Stefan Kangas
     [not found] <87mtmwt3mc.fsf.ref@yahoo.com>
2021-10-26 11:41 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-26 12:39   ` Alan Third
2021-10-26 12:50     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-27 17:20       ` Alan Third
2021-10-28  1:09         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-28 10:17           ` Alan Third
2021-10-28 11:25             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-31 10:22               ` Alan Third
2021-10-31 10:34                 ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-31 10:54                   ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-31 10:59                   ` Alan Third
2021-10-31 11:20                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-31 11:26                     ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-10-31 12:55                       ` Alan Third
2021-10-31 13:12                         ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-05  7:44                           ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-06  0:20                             ` Po Lu via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-11-06 13:02                               ` Alan Third

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).