unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Alan Third <alan@idiocy.org>
To: Robert Pluim <rpluim@gmail.com>
Cc: emacs-devel@gnu.org
Subject: Re: Some NS port changes
Date: Mon, 11 Feb 2019 22:14:04 +0000	[thread overview]
Message-ID: <20190211221404.GA24399@breton.holly.idiocy.org> (raw)
In-Reply-To: <m25ztqlhf7.fsf@gmail.com>

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

On Mon, Feb 11, 2019 at 11:59:24AM +0100, Robert Pluim wrote:
> 
> I just tried this against emacs-26, and the resulting emacs is not
> functional: the display doesnʼt update, the minibuffer doesnʼt
> show. This is on Mojave (10.14.3)

Thanks for trying it. I don’t know why I expect anything to work right
any more.

Please try the attached. I don’t think the performance is terribly
good. I was caching the graphics context as recreating it appears to
be an expensive operation, but it looks like Mojave doesn’t like that.

-- 
Alan Third

[-- Attachment #2: offscreen-buffer.mbox --]
[-- Type: text/plain, Size: 58644 bytes --]

From d0d919061c71bd22f9e6193e68b8632e88598d18 Mon Sep 17 00:00:00 2001
From: Alan Third <alan@idiocy.org>
Date: Sat, 9 Feb 2019 08:57:02 +0000
Subject: [PATCH 1/4] Revert "Fix some NS drawing issues (bug#32932)"

This reverts commit 7e8eee60a9dbb0c59cf26f237b21efe7fd1043c9.
---
 src/nsterm.m | 46 +++++++++++++++++++++++++---------------------
 1 file changed, 25 insertions(+), 21 deletions(-)

diff --git a/src/nsterm.m b/src/nsterm.m
index bbd2c84214..0cf16642bd 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -277,6 +277,7 @@ - (NSColor *)colorUsingDefaultColorSpace
 
 /* display update */
 static int ns_window_num = 0;
+static BOOL gsaved = NO;
 static BOOL ns_fake_keydown = NO;
 #ifdef NS_IMPL_COCOA
 static BOOL ns_menu_bar_is_hidden = NO;
@@ -1177,6 +1178,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
             NSRectClipList (r, 2);
           else
             NSRectClip (*r);
+          gsaved = YES;
 
           return YES;
         }
@@ -1200,7 +1202,11 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
 {
   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_reset_clipping");
 
-  [[NSGraphicsContext currentContext] restoreGraphicsState];
+  if (gsaved)
+    {
+      [[NSGraphicsContext currentContext] restoreGraphicsState];
+      gsaved = NO;
+    }
 }
 
 
@@ -2667,8 +2673,6 @@ so some key presses (TAB) are swallowed by the system. */
 static void
 ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
 {
-  NSSize delta = NSMakeSize (dest.origin.x - src.origin.x,
-                             dest.origin.y - src.origin.y);
   NSTRACE ("ns_copy_bits");
 
   if (FRAME_NS_VIEW (f))
@@ -2677,21 +2681,10 @@ so some key presses (TAB) are swallowed by the system. */
 
       /* FIXME: scrollRect:by: is deprecated in macOS 10.14.  There is
          no obvious replacement so we may have to come up with our own.  */
-      [FRAME_NS_VIEW (f) scrollRect: src by: delta];
-
-#ifdef NS_IMPL_COCOA
-      /* As far as I can tell from the documentation, scrollRect:by:,
-         above, should copy the dirty rectangles from our source
-         rectangle to our destination, however it appears it clips the
-         operation to src.  As a result we need to use
-         translateRectsNeedingDisplayInRect:by: below, and we have to
-         union src and dest so it can pick up the dirty rectangles,
-         and place them, as it also clips to the rectangle.
-
-         FIXME: We need a GNUstep equivalent.  */
-      [FRAME_NS_VIEW (f) translateRectsNeedingDisplayInRect:NSUnionRect (src, dest)
-                                                         by:delta];
-#endif
+      [FRAME_NS_VIEW (f) scrollRect: src
+                                 by: NSMakeSize (dest.origin.x - src.origin.x,
+                                                 dest.origin.y - src.origin.y)];
+      [FRAME_NS_VIEW (f) setNeedsDisplay:YES];
     }
 }
 
@@ -3097,6 +3090,15 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
       else
         [FRAME_CURSOR_COLOR (f) set];
 
+#ifdef NS_IMPL_COCOA
+      /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
+         atomic.  Cleaner ways of doing this should be investigated.
+         One way would be to set a global variable DRAWING_CURSOR
+         when making the call to draw_phys..(), don't focus in that
+         case, then move the ns_reset_clipping() here after that call.  */
+      NSDisableScreenUpdates ();
+#endif
+
       switch (cursor_type)
         {
         case DEFAULT_CURSOR:
@@ -4962,7 +4964,7 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
   ns_after_update_window_line,
   ns_update_window_begin,
   ns_update_window_end,
-  0, /* flush_display */
+  ns_flush_display, /* flush_display */
   x_clear_window_mouse_face,
   x_get_glyph_overhangs,
   x_fix_overlapping_area,
@@ -7031,6 +7033,7 @@ - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize
         size_title = xmalloc (strlen (old_title) + 40);
 	esprintf (size_title, "%s  —  (%d x %d)", old_title, cols, rows);
         [window setTitle: [NSString stringWithUTF8String: size_title]];
+        [window display];
         xfree (size_title);
       }
   }
@@ -8087,8 +8090,8 @@ - (void)viewWillDraw
 
 - (void)drawRect: (NSRect)rect
 {
-  const NSRect *rectList;
-  NSInteger numRects;
+  int x = NSMinX (rect), y = NSMinY (rect);
+  int width = NSWidth (rect), height = NSHeight (rect);
 
   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
            NSTRACE_ARG_RECT(rect));
@@ -8096,6 +8099,7 @@ - (void)drawRect: (NSRect)rect
   if (!emacsframe || !emacsframe->output_data.ns)
     return;
 
+  ns_clear_frame_area (emacsframe, x, y, width, height);
   block_input ();
 
   /* Get only the precise dirty rectangles to avoid redrawing
-- 
2.20.1

From 537188acb6f90c06e62acbb46be55429efa71209 Mon Sep 17 00:00:00 2001
From: Alan Third <alan@idiocy.org>
Date: Sat, 9 Feb 2019 08:57:22 +0000
Subject: [PATCH 2/4] Revert "Ensure NS frame is redrawn correctly  after
 scroll"

This reverts commit a6ab8db3a3dc5ec107ef023c6659620584309c97.
---
 src/nsterm.m | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/nsterm.m b/src/nsterm.m
index 0cf16642bd..dd8755065b 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -2684,7 +2684,6 @@ so some key presses (TAB) are swallowed by the system. */
       [FRAME_NS_VIEW (f) scrollRect: src
                                  by: NSMakeSize (dest.origin.x - src.origin.x,
                                                  dest.origin.y - src.origin.y)];
-      [FRAME_NS_VIEW (f) setNeedsDisplay:YES];
     }
 }
 
-- 
2.20.1

From db3a84ed1dd9172afacadfc9e457add944beb207 Mon Sep 17 00:00:00 2001
From: Alan Third <alan@idiocy.org>
Date: Sat, 9 Feb 2019 09:01:07 +0000
Subject: [PATCH 3/4] Revert "Make all NS drawing be done from drawRect"

This reverts commit 7946445962372c4255180af45cb7c857f1b0b5fa.
---
 src/nsterm.m | 792 +++++++++++++++++++++++++++------------------------
 1 file changed, 418 insertions(+), 374 deletions(-)

diff --git a/src/nsterm.m b/src/nsterm.m
index dd8755065b..6529a50298 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -276,7 +276,12 @@ - (NSColor *)colorUsingDefaultColorSpace
 long context_menu_value = 0;
 
 /* display update */
+static struct frame *ns_updating_frame;
+static NSView *focus_view = NULL;
 static int ns_window_num = 0;
+#ifdef NS_IMPL_GNUSTEP
+static NSRect uRect;            // TODO: This is dead, remove it?
+#endif
 static BOOL gsaved = NO;
 static BOOL ns_fake_keydown = NO;
 #ifdef NS_IMPL_COCOA
@@ -1055,13 +1060,12 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
    external (RIF) call; whole frame, called before update_window_begin
    -------------------------------------------------------------------------- */
 {
-#ifdef NS_IMPL_COCOA
   EmacsView *view = FRAME_NS_VIEW (f);
-
   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_begin");
 
   ns_update_auto_hide_menu_bar ();
 
+#ifdef NS_IMPL_COCOA
   if ([view isFullscreen] && [view fsIsNative])
   {
     // Fix reappearing tool bar in fullscreen for Mac OS X 10.7
@@ -1071,6 +1075,36 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
       [toolbar setVisible: tbar_visible];
   }
 #endif
+
+  ns_updating_frame = f;
+  [view lockFocus];
+
+  /* drawRect may have been called for say the minibuffer, and then clip path
+     is for the minibuffer.  But the display engine may draw more because
+     we have set the frame as garbaged.  So reset clip path to the whole
+     view.  */
+#ifdef NS_IMPL_COCOA
+  {
+    NSBezierPath *bp;
+    NSRect r = [view frame];
+    NSRect cr = [[view window] frame];
+    /* If a large frame size is set, r may be larger than the window frame
+       before constrained.  In that case don't change the clip path, as we
+       will clear in to the tool bar and title bar.  */
+    if (r.size.height
+        + FRAME_NS_TITLEBAR_HEIGHT (f)
+        + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height)
+      {
+        bp = [[NSBezierPath bezierPathWithRect: r] retain];
+        [bp setClip];
+        [bp release];
+      }
+  }
+#endif
+
+#ifdef NS_IMPL_GNUSTEP
+  uRect = NSMakeRect (0, 0, 0, 0);
+#endif
 }
 
 
@@ -1151,62 +1185,118 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
    external (RIF) call; for whole frame, called after update_window_end
    -------------------------------------------------------------------------- */
 {
+  EmacsView *view = FRAME_NS_VIEW (f);
+
   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end");
 
 /*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
-}
 
+  block_input ();
 
-static BOOL
-ns_clip_to_rect (struct frame *f, NSRect *r, int n)
+  [view unlockFocus];
+  [[view window] flushWindow];
+
+  unblock_input ();
+  ns_updating_frame = NULL;
+}
+
+static void
+ns_focus (struct frame *f, NSRect *r, int n)
 /* --------------------------------------------------------------------------
-   Clip the drawing area to rectangle r in frame f.  If drawing is not
-   currently possible mark r as dirty and return NO, otherwise return
-   YES.
+   Internal: Focus on given frame.  During small local updates this is used to
+     draw, however during large updates, ns_update_begin and ns_update_end are
+     called to wrap the whole thing, in which case these calls are stubbed out.
+     Except, on GNUstep, we accumulate the rectangle being drawn into, because
+     the back end won't do this automatically, and will just end up flushing
+     the entire window.
    -------------------------------------------------------------------------- */
 {
-  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_clip_to_rect");
-  if (r)
+  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
+  if (r != NULL)
     {
       NSTRACE_RECT ("r", *r);
+    }
 
-      if ([NSView focusView] == FRAME_NS_VIEW (f))
+  if (f != ns_updating_frame)
+    {
+      NSView *view = FRAME_NS_VIEW (f);
+      if (view != focus_view)
         {
-          [[NSGraphicsContext currentContext] saveGraphicsState];
-          if (n == 2)
-            NSRectClipList (r, 2);
-          else
-            NSRectClip (*r);
-          gsaved = YES;
+          if (focus_view != NULL)
+            {
+              [focus_view unlockFocus];
+              [[focus_view window] flushWindow];
+/*debug_lock--; */
+            }
 
-          return YES;
-        }
-      else
-        {
-          NSView *view = FRAME_NS_VIEW (f);
-          int i;
-          for (i = 0 ; i < n ; i++)
-            [view setNeedsDisplayInRect:r[i]];
+          if (view)
+            [view lockFocus];
+          focus_view = view;
+/*if (view) debug_lock++; */
         }
     }
 
-  return NO;
+  /* clipping */
+  if (r)
+    {
+      [[NSGraphicsContext currentContext] saveGraphicsState];
+      if (n == 2)
+        NSRectClipList (r, 2);
+      else
+        NSRectClip (*r);
+      gsaved = YES;
+    }
 }
 
 
 static void
-ns_reset_clipping (struct frame *f)
-/* Internal: Restore the previous graphics state, unsetting any
-   clipping areas.  */
+ns_unfocus (struct frame *f)
+/* --------------------------------------------------------------------------
+     Internal: Remove focus on given frame
+   -------------------------------------------------------------------------- */
 {
-  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_reset_clipping");
+  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_unfocus");
 
   if (gsaved)
     {
       [[NSGraphicsContext currentContext] restoreGraphicsState];
       gsaved = NO;
     }
+
+  if (f != ns_updating_frame)
+    {
+      if (focus_view != NULL)
+        {
+          [focus_view unlockFocus];
+          [[focus_view window] flushWindow];
+          focus_view = NULL;
+/*debug_lock--; */
+        }
+    }
+}
+
+
+static void
+ns_clip_to_row (struct window *w, struct glyph_row *row,
+		enum glyph_row_area area, BOOL gc)
+/* --------------------------------------------------------------------------
+     Internal (but parallels other terms): Focus drawing on given row
+   -------------------------------------------------------------------------- */
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  NSRect clip_rect;
+  int window_x, window_y, window_width;
+
+  window_box (w, area, &window_x, &window_y, &window_width, 0);
+
+  clip_rect.origin.x = window_x;
+  clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
+  clip_rect.origin.y = max (clip_rect.origin.y, window_y);
+  clip_rect.size.width = window_width;
+  clip_rect.size.height = row->visible_height;
+
+  ns_focus (f, &clip_rect, 1);
 }
 
 
@@ -2630,16 +2720,14 @@ so some key presses (TAB) are swallowed by the system. */
   r = [view bounds];
 
   block_input ();
-  if (ns_clip_to_rect (f, &r, 1))
-    {
-      [ns_lookup_indexed_color (NS_FACE_BACKGROUND
-                                (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
-      NSRectFill (r);
-      ns_reset_clipping (f);
-
-      /* as of 2006/11 or so this is now needed */
-      ns_redraw_scroll_bars (f);
-    }
+  ns_focus (f, &r, 1);
+  [ns_lookup_indexed_color (NS_FACE_BACKGROUND
+			    (FACE_FROM_ID (f, DEFAULT_FACE_ID)), f) set];
+  NSRectFill (r);
+  ns_unfocus (f);
+
+  /* as of 2006/11 or so this is now needed */
+  ns_redraw_scroll_bars (f);
   unblock_input ();
 }
 
@@ -2660,14 +2748,13 @@ so some key presses (TAB) are swallowed by the system. */
   NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_clear_frame_area");
 
   r = NSIntersectionRect (r, [view frame]);
-  if (ns_clip_to_rect (f, &r, 1))
-    {
-      [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
+  ns_focus (f, &r, 1);
+  [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), f) set];
 
-      NSRectFill (r);
+  NSRectFill (r);
 
-      ns_reset_clipping (f);
-    }
+  ns_unfocus (f);
+  return;
 }
 
 static void
@@ -2679,11 +2766,11 @@ so some key presses (TAB) are swallowed by the system. */
     {
       hide_bell();              // Ensure the bell image isn't scrolled.
 
-      /* FIXME: scrollRect:by: is deprecated in macOS 10.14.  There is
-         no obvious replacement so we may have to come up with our own.  */
+      ns_focus (f, &dest, 1);
       [FRAME_NS_VIEW (f) scrollRect: src
                                  by: NSMakeSize (dest.origin.x - src.origin.x,
                                                  dest.origin.y - src.origin.y)];
+      ns_unfocus (f);
     }
 }
 
@@ -2904,95 +2991,86 @@ so some key presses (TAB) are swallowed by the system. */
       nBimgs = max_used_fringe_bitmap;
     }
 
-  /* Work out the rectangle we will composite into.  */
-  if (p->which)
-    imageRect = NSMakeRect (p->x, p->y, p->wd, p->h);
+  /* Must clip because of partially visible lines.  */
+  ns_clip_to_row (w, row, ANY_AREA, YES);
 
-  /* Work out the rectangle we will need to clear.  Because we're
-     compositing rather than blitting, we need to clear the area under
-     the image regardless of anything else.  */
-  if (p->bx >= 0 && !p->overlay_p)
+  if (!p->overlay_p)
     {
-      clearRect = NSMakeRect (p->bx, p->by, p->nx, p->ny);
-      clearRect = NSUnionRect (clearRect, imageRect);
-    }
-  else
-    {
-      clearRect = imageRect;
-    }
+      int bx = p->bx, by = p->by, nx = p->nx, ny = p->ny;
 
-  /* Handle partially visible rows.  */
-  clearRect = NSIntersectionRect (clearRect, rowRect);
-
-  /* The visible portion of imageRect will always be contained within
-     clearRect.  */
-  if (ns_clip_to_rect (f, &clearRect, 1))
-    {
-      if (! NSIsEmptyRect (clearRect))
+      if (bx >= 0 && nx > 0)
         {
-          NSTRACE_RECT ("clearRect", clearRect);
-
-          [ns_lookup_indexed_color(face->background, f) set];
-          NSRectFill (clearRect);
+          NSRect r = NSMakeRect (bx, by, nx, ny);
+          NSRectClip (r);
+          [ns_lookup_indexed_color (face->background, f) set];
+          NSRectFill (r);
         }
+    }
 
-      if (p->which)
+  if (p->which)
+    {
+      NSRect r = NSMakeRect (p->x, p->y, p->wd, p->h);
+      EmacsImage *img = bimgs[p->which - 1];
+
+      if (!img)
         {
-          EmacsImage *img = bimgs[p->which - 1];
+          // Note: For "periodic" images, allocate one EmacsImage for
+          // the base image, and use it for all dh:s.
+          unsigned short *bits = p->bits;
+          int full_height = p->h + p->dh;
+          int i;
+          unsigned char *cbits = xmalloc (full_height);
+
+          for (i = 0; i < full_height; i++)
+            cbits[i] = bits[i];
+          img = [[EmacsImage alloc] initFromXBM: cbits width: 8
+                                         height: full_height
+                                             fg: 0 bg: 0];
+          bimgs[p->which - 1] = img;
+          xfree (cbits);
+        }
 
-          if (!img)
-            {
-              // Note: For "periodic" images, allocate one EmacsImage for
-              // the base image, and use it for all dh:s.
-              unsigned short *bits = p->bits;
-              int full_height = p->h + p->dh;
-              int i;
-              unsigned char *cbits = xmalloc (full_height);
-
-              for (i = 0; i < full_height; i++)
-                cbits[i] = bits[i];
-              img = [[EmacsImage alloc] initFromXBM: cbits width: 8
-                                             height: full_height
-                                                 fg: 0 bg: 0];
-              bimgs[p->which - 1] = img;
-              xfree (cbits);
-            }
+      NSTRACE_RECT ("r", r);
 
+      NSRectClip (r);
+      /* Since we composite the bitmap instead of just blitting it, we need
+         to erase the whole background. */
+      [ns_lookup_indexed_color(face->background, f) set];
+      NSRectFill (r);
 
-          {
-            NSColor *bm_color;
-            if (!p->cursor_p)
-              bm_color = ns_lookup_indexed_color(face->foreground, f);
-            else if (p->overlay_p)
-              bm_color = ns_lookup_indexed_color(face->background, f);
-            else
-              bm_color = f->output_data.ns->cursor_color;
-            [img setXBMColor: bm_color];
-          }
+      {
+        NSColor *bm_color;
+        if (!p->cursor_p)
+          bm_color = ns_lookup_indexed_color(face->foreground, f);
+        else if (p->overlay_p)
+          bm_color = ns_lookup_indexed_color(face->background, f);
+        else
+          bm_color = f->output_data.ns->cursor_color;
+        [img setXBMColor: bm_color];
+      }
 
 #ifdef NS_IMPL_COCOA
-          // Note: For periodic images, the full image height is "h + hd".
-          // By using the height h, a suitable part of the image is used.
-          NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
-
-          NSTRACE_RECT ("fromRect", fromRect);
-
-          [img drawInRect: imageRect
-                 fromRect: fromRect
-                operation: NSCompositingOperationSourceOver
-                 fraction: 1.0
-               respectFlipped: YES
-                    hints: nil];
+      // Note: For periodic images, the full image height is "h + hd".
+      // By using the height h, a suitable part of the image is used.
+      NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
+
+      NSTRACE_RECT ("fromRect", fromRect);
+
+      [img drawInRect: r
+              fromRect: fromRect
+             operation: NSCompositingOperationSourceOver
+              fraction: 1.0
+           respectFlipped: YES
+                hints: nil];
 #else
-          {
-            NSPoint pt = imageRect.origin;
-            pt.y += p->h;
-            [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
-          }
+      {
+        NSPoint pt = r.origin;
+        pt.y += p->h;
+        [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
+      }
 #endif
-        }
-      ns_reset_clipping (f);
     }
+  ns_unfocus (f);
 }
 
 
@@ -3074,71 +3152,67 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   r.size.height = h;
   r.size.width = w->phys_cursor_width;
 
-  /* Prevent the cursor from being drawn outside the text area.  */
-  r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
+  /* Prevent the cursor from being drawn outside the text area. */
+  ns_clip_to_row (w, glyph_row, TEXT_AREA, NO); /* do ns_focus(f, &r, 1); if remove */
 
-  if (ns_clip_to_rect (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))
     {
-      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];
+      [ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), f) set];
+      hollow_color = FRAME_CURSOR_COLOR (f);
+    }
+  else
+    [FRAME_CURSOR_COLOR (f) set];
 
 #ifdef NS_IMPL_COCOA
-      /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
-         atomic.  Cleaner ways of doing this should be investigated.
-         One way would be to set a global variable DRAWING_CURSOR
-         when making the call to draw_phys..(), don't focus in that
-         case, then move the ns_reset_clipping() here after that call.  */
-      NSDisableScreenUpdates ();
+  /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
+           atomic.  Cleaner ways of doing this should be investigated.
+           One way would be to set a global variable DRAWING_CURSOR
+           when making the call to draw_phys..(), don't focus in that
+           case, then move the ns_unfocus() here after that call. */
+  NSDisableScreenUpdates ();
 #endif
 
-      switch (cursor_type)
-        {
-        case DEFAULT_CURSOR:
-        case NO_CURSOR:
-          break;
-        case FILLED_BOX_CURSOR:
-          NSRectFill (r);
-          break;
-        case HOLLOW_BOX_CURSOR:
-          NSRectFill (r);
-          [hollow_color set];
-          NSRectFill (NSInsetRect (r, 1, 1));
-          [FRAME_CURSOR_COLOR (f) set];
-          break;
-        case HBAR_CURSOR:
-          NSRectFill (r);
-          break;
-        case BAR_CURSOR:
-          s = r;
-          /* If the character under cursor is R2L, draw the bar cursor
-             on the right of its glyph, rather than on the left.  */
-          cursor_glyph = get_phys_cursor_glyph (w);
-          if ((cursor_glyph->resolved_level & 1) != 0)
-            s.origin.x += cursor_glyph->pixel_width - s.size.width;
-
-          NSRectFill (s);
-          break;
-        }
-
-      /* draw the character under the cursor */
-      if (cursor_type != NO_CURSOR)
-        draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
-
-      ns_reset_clipping (f);
-    }
-  else if (! redisplaying_p)
+  switch (cursor_type)
     {
-      /* If this function is called outside redisplay, it probably
-         means we need an immediate update.  */
-      [FRAME_NS_VIEW (f) display];
+    case DEFAULT_CURSOR:
+    case NO_CURSOR:
+      break;
+    case FILLED_BOX_CURSOR:
+      NSRectFill (r);
+      break;
+    case HOLLOW_BOX_CURSOR:
+      NSRectFill (r);
+      [hollow_color set];
+      NSRectFill (NSInsetRect (r, 1, 1));
+      [FRAME_CURSOR_COLOR (f) set];
+      break;
+    case HBAR_CURSOR:
+      NSRectFill (r);
+      break;
+    case BAR_CURSOR:
+      s = r;
+      /* If the character under cursor is R2L, draw the bar cursor
+         on the right of its glyph, rather than on the left.  */
+      cursor_glyph = get_phys_cursor_glyph (w);
+      if ((cursor_glyph->resolved_level & 1) != 0)
+        s.origin.x += cursor_glyph->pixel_width - s.size.width;
+
+      NSRectFill (s);
+      break;
     }
+  ns_unfocus (f);
+
+  /* draw the character under the cursor */
+  if (cursor_type != NO_CURSOR)
+    draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+
+#ifdef NS_IMPL_COCOA
+  NSEnableScreenUpdates ();
+#endif
+
 }
 
 
@@ -3156,14 +3230,12 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
 
-  if (ns_clip_to_rect (f, &r, 1))
-    {
-      if (face)
-        [ns_lookup_indexed_color(face->foreground, f) set];
+  ns_focus (f, &r, 1);
+  if (face)
+    [ns_lookup_indexed_color(face->foreground, f) set];
 
-      NSRectFill(r);
-      ns_reset_clipping (f);
-    }
+  NSRectFill(r);
+  ns_unfocus (f);
 }
 
 
@@ -3190,40 +3262,39 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
 
   NSTRACE ("ns_draw_window_divider");
 
-  if (ns_clip_to_rect (f, &divider, 1))
-    {
-      if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
-        /* A vertical divider, at least three pixels wide: Draw first and
-           last pixels differently.  */
-        {
-          [ns_lookup_indexed_color(color_first, f) set];
-          NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0));
-          [ns_lookup_indexed_color(color, f) set];
-          NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0));
-          [ns_lookup_indexed_color(color_last, f) set];
-          NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0));
-        }
-      else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
-        /* A horizontal divider, at least three pixels high: Draw first and
-           last pixels differently.  */
-        {
-          [ns_lookup_indexed_color(color_first, f) set];
-          NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1));
-          [ns_lookup_indexed_color(color, f) set];
-          NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2));
-          [ns_lookup_indexed_color(color_last, f) set];
-          NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1));
-        }
-      else
-        {
-          /* In any other case do not draw the first and last pixels
-             differently.  */
-          [ns_lookup_indexed_color(color, f) set];
-          NSRectFill(divider);
-        }
+  ns_focus (f, &divider, 1);
 
-      ns_reset_clipping (f);
+  if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
+    /* A vertical divider, at least three pixels wide: Draw first and
+       last pixels differently.  */
+    {
+      [ns_lookup_indexed_color(color_first, f) set];
+      NSRectFill(NSMakeRect (x0, y0, 1, y1 - y0));
+      [ns_lookup_indexed_color(color, f) set];
+      NSRectFill(NSMakeRect (x0 + 1, y0, x1 - x0 - 2, y1 - y0));
+      [ns_lookup_indexed_color(color_last, f) set];
+      NSRectFill(NSMakeRect (x1 - 1, y0, 1, y1 - y0));
+    }
+  else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
+    /* A horizontal divider, at least three pixels high: Draw first and
+       last pixels differently.  */
+    {
+      [ns_lookup_indexed_color(color_first, f) set];
+      NSRectFill(NSMakeRect (x0, y0, x1 - x0, 1));
+      [ns_lookup_indexed_color(color, f) set];
+      NSRectFill(NSMakeRect (x0, y0 + 1, x1 - x0, y1 - y0 - 2));
+      [ns_lookup_indexed_color(color_last, f) set];
+      NSRectFill(NSMakeRect (x0, y1 - 1, x1 - x0, 1));
+    }
+  else
+    {
+      /* In any other case do not draw the first and last pixels
+         differently.  */
+      [ns_lookup_indexed_color(color, f) set];
+      NSRectFill(divider);
     }
+
+  ns_unfocus (f);
 }
 
 static void
@@ -3807,84 +3878,83 @@ Function modeled after x_draw_glyph_string_box ().
       n = ns_get_glyph_string_clip_rect (s, r);
       *r = NSMakeRect (s->x, s->y, s->background_width, s->height);
 
-      if (ns_clip_to_rect (s->f, r, n))
+      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);
+
+      bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
+      fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
+
+      for (i = 0; i < n; ++i)
         {
-          if (s->hl == DRAW_MOUSE_FACE)
+          if (!s->row->full_width_p)
             {
-              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);
+	      int overrun, leftoverrun;
 
-          bgCol = ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f);
-          fgCol = ns_lookup_indexed_color (NS_FACE_FOREGROUND (face), s->f);
+              /* truncate to avoid overwriting fringe and/or scrollbar */
+	      overrun = max (0, (s->x + s->background_width)
+			     - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
+				- WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
+              r[i].size.width -= overrun;
 
-          for (i = 0; i < n; ++i)
-            {
-              if (!s->row->full_width_p)
-                {
-                  int overrun, leftoverrun;
-
-                  /* truncate to avoid overwriting fringe and/or scrollbar */
-                  overrun = max (0, (s->x + s->background_width)
-                                 - (WINDOW_BOX_RIGHT_EDGE_X (s->w)
-                                    - WINDOW_RIGHT_FRINGE_WIDTH (s->w)));
-                  r[i].size.width -= overrun;
-
-                  /* truncate to avoid overwriting to left of the window box */
-                  leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
-                                 + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
-
-                    if (leftoverrun > 0)
-                      {
-                        r[i].origin.x += leftoverrun;
-                        r[i].size.width -= leftoverrun;
-                      }
-
-                    /* XXX: Try to work between problem where a stretch glyph on
-                       a partially-visible bottom row will clear part of the
-                       modeline, and another where list-buffers headers and similar
-                       rows erroneously have visible_height set to 0.  Not sure
-                       where this is coming from as other terms seem not to show.  */
-                    r[i].size.height = min (s->height, s->row->visible_height);
-                }
+	      /* truncate to avoid overwriting to left of the window box */
+	      leftoverrun = (WINDOW_BOX_LEFT_EDGE_X (s->w)
+			     + WINDOW_LEFT_FRINGE_WIDTH (s->w)) - s->x;
 
-              [bgCol set];
+	      if (leftoverrun > 0)
+		{
+		  r[i].origin.x += leftoverrun;
+		  r[i].size.width -= leftoverrun;
+		}
 
-              /* 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;
+              /* XXX: Try to work between problem where a stretch glyph on
+                 a partially-visible bottom row will clear part of the
+                 modeline, and another where list-buffers headers and similar
+                 rows erroneously have visible_height set to 0.  Not sure
+                 where this is coming from as other terms seem not to show. */
+              r[i].size.height = min (s->height, s->row->visible_height);
+            }
+
+          [bgCol set];
 
-                  x = r[i].origin.x;
-                  width = s->w->phys_cursor_width;
-                  r[i].size.width -= width;
-                  r[i].origin.x += width;
+          /* 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;
 
-                  NSRectFill (r[i]);
+              x = r[i].origin.x;
+              width = s->w->phys_cursor_width;
+              r[i].size.width -= width;
+              r[i].origin.x += width;
 
-                  /* 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 (r[i]);
-                }
+              NSRectFill (r[i]);
 
-              /* Draw overlining, etc. on the stretch glyph (or the part
-                 of the stretch glyph after the cursor).  */
-              ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
-                                       r[i].origin.x);
+              /* 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 (r[i]);
             }
-          ns_reset_clipping (s->f);
+
+          /* Draw overlining, etc. on the stretch glyph (or the part
+             of the stretch glyph after the cursor). */
+          ns_draw_text_decoration (s, face, fgCol, r[i].size.width,
+                                   r[i].origin.x);
         }
+      ns_unfocus (s->f);
       s->background_filled_p = 1;
     }
 }
@@ -4034,11 +4104,9 @@ overwriting cursor (usually when cursor on a tab).  */
             if (next->first_glyph->type != STRETCH_GLYPH)
               {
                 n = ns_get_glyph_string_clip_rect (s->next, r);
-                if (ns_clip_to_rect (s->f, r, n))
-                  {
-                    ns_maybe_dumpglyphs_background (s->next, 1);
-                    ns_reset_clipping (s->f);
-                  }
+                ns_focus (s->f, r, n);
+                ns_maybe_dumpglyphs_background (s->next, 1);
+                ns_unfocus (s->f);
               }
             else
               {
@@ -4053,12 +4121,10 @@ overwriting cursor (usually when cursor on a tab).  */
 	    || s->first_glyph->type == COMPOSITE_GLYPH))
     {
       n = ns_get_glyph_string_clip_rect (s, r);
-      if (ns_clip_to_rect (s->f, r, n))
-        {
-          ns_maybe_dumpglyphs_background (s, 1);
-          ns_dumpglyphs_box_or_relief (s);
-          ns_reset_clipping (s->f);
-        }
+      ns_focus (s->f, r, n);
+      ns_maybe_dumpglyphs_background (s, 1);
+      ns_dumpglyphs_box_or_relief (s);
+      ns_unfocus (s->f);
       box_drawn_p = 1;
     }
 
@@ -4067,11 +4133,9 @@ overwriting cursor (usually when cursor on a tab).  */
 
     case IMAGE_GLYPH:
       n = ns_get_glyph_string_clip_rect (s, r);
-      if (ns_clip_to_rect (s->f, r, n))
-        {
-          ns_dumpglyphs_image (s, r[0]);
-          ns_reset_clipping (s->f);
-        }
+      ns_focus (s->f, r, n);
+      ns_dumpglyphs_image (s, r[0]);
+      ns_unfocus (s->f);
       break;
 
     case STRETCH_GLYPH:
@@ -4081,68 +4145,66 @@ overwriting cursor (usually when cursor on a tab).  */
     case CHAR_GLYPH:
     case COMPOSITE_GLYPH:
       n = ns_get_glyph_string_clip_rect (s, r);
-      if (ns_clip_to_rect (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);
+      ns_focus (s->f, r, n);
 
-          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;
-            }
+      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);
 
-          {
-            BOOL isComposite = 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;
+        }
 
-            if (isComposite)
-              ns_draw_composite_glyph_string_foreground (s);
-            else
-              ns_draw_glyph_string_foreground (s);
-          }
+      {
+        BOOL isComposite = s->first_glyph->type == COMPOSITE_GLYPH;
 
-          {
-            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 (isComposite)
+          ns_draw_composite_glyph_string_foreground (s);
+        else
+          ns_draw_glyph_string_foreground (s);
+      }
 
-          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;
-            }
+      {
+        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);
+      }
 
-          ns_reset_clipping (s->f);
+      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);
-      if (ns_clip_to_rect (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);
-          /* ... */
-          /* Not yet implemented.  */
-          /* ... */
-          ns_reset_clipping (s->f);
-        }
+      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);
+      /* ... */
+      /* Not yet implemented.  */
+      /* ... */
+      ns_unfocus (s->f);
       break;
 
     default:
@@ -4153,11 +4215,9 @@ overwriting cursor (usually when cursor on a tab).  */
   if (!s->for_overlaps && !box_drawn_p && s->face->box != FACE_NO_BOX)
     {
       n = ns_get_glyph_string_clip_rect (s, r);
-      if (ns_clip_to_rect (s->f, r, n))
-        {
-          ns_dumpglyphs_box_or_relief (s);
-          ns_reset_clipping (s->f);
-        }
+      ns_focus (s->f, r, n);
+      ns_dumpglyphs_box_or_relief (s);
+      ns_unfocus (s->f);
     }
 
   s->num_clips = 0;
@@ -4963,7 +5023,7 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
   ns_after_update_window_line,
   ns_update_window_begin,
   ns_update_window_end,
-  ns_flush_display, /* flush_display */
+  0, /* flush_display */
   x_clear_window_mouse_face,
   x_get_glyph_overhangs,
   x_fix_overlapping_area,
@@ -8101,23 +8161,7 @@ - (void)drawRect: (NSRect)rect
   ns_clear_frame_area (emacsframe, x, y, width, height);
   block_input ();
 
-  /* Get only the precise dirty rectangles to avoid redrawing
-     potentially large areas of the frame that haven't changed.
-
-     I'm not sure this actually provides much of a performance benefit
-     as it's hard to benchmark, but it certainly doesn't seem to
-     hurt.  */
-  [self getRectsBeingDrawn:&rectList count:&numRects];
-  for (int i = 0 ; i < numRects ; i++)
-    {
-      NSRect r = rectList[i];
-
-      NSTRACE_RECT ("r", r);
-
-      expose_frame (emacsframe,
-                    NSMinX (r), NSMinY (r),
-                    NSWidth (r), NSHeight (r));
-    }
+  expose_frame (emacsframe, x, y, width, height);
 
   unblock_input ();
 
-- 
2.20.1

From f7d62d0c8bae4ea31b670a32711893392298444c Mon Sep 17 00:00:00 2001
From: Alan Third <alan@idiocy.org>
Date: Sun, 10 Feb 2019 10:59:29 +0000
Subject: [PATCH 4/4] Draw to offscreen buffer on macOS

* src/nsfns.m (x_set_background_color): Clear the frame after changing
the background color, not before.
* src/nsterm.h (drawingBuffer): New variable.
([EmacsView focusOnDrawingBuffer]):
([EmacsView copyRect:to:]):
([EmacsView createDrawingBufferWithRect:]): New methods.
* src/nsterm.m (ns_update_begin):
(ns_update_end):
(ns_focus):
(ns_unfocus): Handle drawing to offscreen buffer.
(ns_clip_to_row): Use ns_row_rect.
(ns_copy_bits): Remove unused function.
(ns_scroll_run):
(ns_shift_glyphs_for_insert): Use new scrolling method.
(ns_draw_fringe_bitmap):
(ns_dumpglyphs_image): When drawing to the offscreen buffer, flip
images so they appear the right way up.
(ns_draw_window_cursor): Don't disable screen updates.
([EmacsView updateFrameSize:]): Update the size of the offscreen
buffer.
([EmacsView initFrameFromEmacs:]): Create offscreen buffer.
([EmacsView windowDidChangeBackingProperties:]):
([EmacsView createDrawingBufferWithRect:]):
([EmacsView focusOnDrawingBuffer]):
([EmacsView copyRect]): New methods.
([EmacsView viewWillDraw]): Remove method as it no longer does
anything useful.
([EmacsView drawRect:]): Handle drawing from offscreen buffer.
---
 src/nsfns.m  |  10 +-
 src/nsterm.h |   9 ++
 src/nsterm.m | 255 ++++++++++++++++++++++++++++++---------------------
 3 files changed, 161 insertions(+), 113 deletions(-)

diff --git a/src/nsfns.m b/src/nsfns.m
index 59798d3bdd..edcdb988f7 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -286,11 +286,6 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side
       error ("Unknown color");
     }
 
-  /* clear the frame; in some instances the NS-internal GC appears not to
-     update, or it does update and cannot clear old text properly */
-  if (FRAME_VISIBLE_P (f))
-    ns_clear_frame (f);
-
   [col retain];
   [f->output_data.ns->background_color release];
   f->output_data.ns->background_color = col;
@@ -319,7 +314,10 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side
         }
 
       if (FRAME_VISIBLE_P (f))
-        SET_FRAME_GARBAGED (f);
+        {
+          SET_FRAME_GARBAGED (f);
+          ns_clear_frame (f);
+        }
     }
   unblock_input ();
 }
diff --git a/src/nsterm.h b/src/nsterm.h
index 35dd9b3c3b..7ef5bba6a3 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -416,6 +416,9 @@ typedef id instancetype;
    int maximized_width, maximized_height;
    NSWindow *nonfs_window;
    BOOL fs_is_native;
+#ifdef NS_IMPL_COCOA
+   NSBitmapImageRep *drawingBuffer;
+#endif
 @public
    struct frame *emacsframe;
    int rows, cols;
@@ -456,6 +459,12 @@ typedef id instancetype;
 #endif
 - (int)fullscreenState;
 
+#ifdef NS_IMPL_COCOA
+- (void)focusOnDrawingBuffer;
+#endif
+- (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect;
+- (void)createDrawingBufferWithRect:(NSRect)rect;
+
 /* Non-notification versions of NSView methods. Used for direct calls. */
 - (void)windowWillEnterFullScreen;
 - (void)windowDidEnterFullScreen;
diff --git a/src/nsterm.m b/src/nsterm.m
index 6529a50298..3bd61aa53f 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -279,9 +279,6 @@ - (NSColor *)colorUsingDefaultColorSpace
 static struct frame *ns_updating_frame;
 static NSView *focus_view = NULL;
 static int ns_window_num = 0;
-#ifdef NS_IMPL_GNUSTEP
-static NSRect uRect;            // TODO: This is dead, remove it?
-#endif
 static BOOL gsaved = NO;
 static BOOL ns_fake_keydown = NO;
 #ifdef NS_IMPL_COCOA
@@ -1077,33 +1074,10 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
 #endif
 
   ns_updating_frame = f;
-  [view lockFocus];
-
-  /* drawRect may have been called for say the minibuffer, and then clip path
-     is for the minibuffer.  But the display engine may draw more because
-     we have set the frame as garbaged.  So reset clip path to the whole
-     view.  */
 #ifdef NS_IMPL_COCOA
-  {
-    NSBezierPath *bp;
-    NSRect r = [view frame];
-    NSRect cr = [[view window] frame];
-    /* If a large frame size is set, r may be larger than the window frame
-       before constrained.  In that case don't change the clip path, as we
-       will clear in to the tool bar and title bar.  */
-    if (r.size.height
-        + FRAME_NS_TITLEBAR_HEIGHT (f)
-        + FRAME_TOOLBAR_HEIGHT (f) <= cr.size.height)
-      {
-        bp = [[NSBezierPath bezierPathWithRect: r] retain];
-        [bp setClip];
-        [bp release];
-      }
-  }
-#endif
-
-#ifdef NS_IMPL_GNUSTEP
-  uRect = NSMakeRect (0, 0, 0, 0);
+  [view focusOnDrawingBuffer];
+#else
+  [view lockFocus];
 #endif
 }
 
@@ -1192,12 +1166,17 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
 /*   if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */
   MOUSE_HL_INFO (f)->mouse_face_defer = 0;
 
+#ifdef NS_IMPL_COCOA
+  [NSGraphicsContext setCurrentContext:nil];
+  [view display];
+#else
   block_input ();
 
   [view unlockFocus];
   [[view window] flushWindow];
 
   unblock_input ();
+#endif
   ns_updating_frame = NULL;
 }
 
@@ -1212,6 +1191,8 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
      the entire window.
    -------------------------------------------------------------------------- */
 {
+  EmacsView *view = FRAME_NS_VIEW (f);
+
   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
   if (r != NULL)
     {
@@ -1219,27 +1200,34 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
     }
 
   if (f != ns_updating_frame)
+#ifdef NS_IMPL_COCOA
+    [view focusOnDrawingBuffer];
+#else
     {
-      NSView *view = FRAME_NS_VIEW (f);
       if (view != focus_view)
         {
           if (focus_view != NULL)
             {
               [focus_view unlockFocus];
               [[focus_view window] flushWindow];
-/*debug_lock--; */
             }
 
           if (view)
             [view lockFocus];
           focus_view = view;
-/*if (view) debug_lock++; */
         }
     }
+#endif
 
   /* clipping */
   if (r)
     {
+#ifdef NS_IMPL_COCOA
+      int i;
+      for (i = 0 ; i < n ; i++)
+        [view setNeedsDisplayInRect:r[i]];
+#endif
+
       [[NSGraphicsContext currentContext] saveGraphicsState];
       if (n == 2)
         NSRectClipList (r, 2);
@@ -1264,6 +1252,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
       gsaved = NO;
     }
 
+#ifdef NS_IMPL_GNUSTEP
   if (f != ns_updating_frame)
     {
       if (focus_view != NULL)
@@ -1271,9 +1260,9 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
           [focus_view unlockFocus];
           [[focus_view window] flushWindow];
           focus_view = NULL;
-/*debug_lock--; */
         }
     }
+#endif
 }
 
 
@@ -1285,16 +1274,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
    -------------------------------------------------------------------------- */
 {
   struct frame *f = XFRAME (WINDOW_FRAME (w));
-  NSRect clip_rect;
-  int window_x, window_y, window_width;
-
-  window_box (w, area, &window_x, &window_y, &window_width, 0);
-
-  clip_rect.origin.x = window_x;
-  clip_rect.origin.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
-  clip_rect.origin.y = max (clip_rect.origin.y, window_y);
-  clip_rect.size.width = window_width;
-  clip_rect.size.height = row->visible_height;
+  NSRect clip_rect = ns_row_rect (w, row, area);
 
   ns_focus (f, &clip_rect, 1);
 }
@@ -2757,22 +2737,6 @@ so some key presses (TAB) are swallowed by the system. */
   return;
 }
 
-static void
-ns_copy_bits (struct frame *f, NSRect src, NSRect dest)
-{
-  NSTRACE ("ns_copy_bits");
-
-  if (FRAME_NS_VIEW (f))
-    {
-      hide_bell();              // Ensure the bell image isn't scrolled.
-
-      ns_focus (f, &dest, 1);
-      [FRAME_NS_VIEW (f) scrollRect: src
-                                 by: NSMakeSize (dest.origin.x - src.origin.x,
-                                                 dest.origin.y - src.origin.y)];
-      ns_unfocus (f);
-    }
-}
 
 static void
 ns_scroll_run (struct window *w, struct run *run)
@@ -2825,8 +2789,12 @@ so some key presses (TAB) are swallowed by the system. */
   {
     NSRect srcRect = NSMakeRect (x, from_y, width, height);
     NSRect dstRect = NSMakeRect (x, to_y, width, height);
+    EmacsView *view = FRAME_NS_VIEW (f);
 
-    ns_copy_bits (f, srcRect , dstRect);
+    [view copyRect:srcRect to:dstRect];
+#ifdef NS_IMPL_COCOA
+    [view setNeedsDisplayInRect:srcRect];
+#endif
   }
 
   unblock_input ();
@@ -2880,20 +2848,12 @@ so some key presses (TAB) are swallowed by the system. */
     External (RIF): copy an area horizontally, don't worry about clearing src
    -------------------------------------------------------------------------- */
 {
-  //NSRect srcRect = NSMakeRect (x, y, width, height);
+  NSRect srcRect = NSMakeRect (x, y, width, height);
   NSRect dstRect = NSMakeRect (x+shift_by, y, width, height);
 
   NSTRACE ("ns_shift_glyphs_for_insert");
 
-  /* This doesn't work now as we copy the "bits" before we've had a
-     chance to actually draw any changes to the screen.  This means in
-     certain circumstances we end up with copies of the cursor all
-     over the place.  Just mark the area dirty so it is redrawn later.
-
-     FIXME: Work out how to do this properly.  */
-  // ns_copy_bits (f, srcRect, dstRect);
-
-  [FRAME_NS_VIEW (f) setNeedsDisplayInRect:dstRect];
+  [FRAME_NS_VIEW (f) copyRect:srcRect to:dstRect];
 }
 
 
@@ -2974,9 +2934,6 @@ so some key presses (TAB) are swallowed by the system. */
   struct face *face = p->face;
   static EmacsImage **bimgs = NULL;
   static int nBimgs = 0;
-  NSRect clearRect = NSZeroRect;
-  NSRect imageRect = NSZeroRect;
-  NSRect rowRect = ns_row_rect (w, row, ANY_AREA);
 
   NSTRACE_WHEN (NSTRACE_GROUP_FRINGE, "ns_draw_fringe_bitmap");
   NSTRACE_MSG ("which:%d cursor:%d overlay:%d width:%d height:%d period:%d",
@@ -3056,6 +3013,18 @@ so some key presses (TAB) are swallowed by the system. */
 
       NSTRACE_RECT ("fromRect", fromRect);
 
+      /* Because we're drawing into an offscreen buffer which isn't
+         flipped, the images come out upside down.  To work around it
+         we need to do some fancy transforms.  */
+      {
+        NSAffineTransform *transform = [NSAffineTransform transform];
+        [transform translateXBy:0 yBy:NSMaxY(r)];
+        [transform scaleXBy:1 yBy:-1];
+        [transform concat];
+
+        r.origin.y = 0;
+      }
+
       [img drawInRect: r
               fromRect: fromRect
              operation: NSCompositingOperationSourceOver
@@ -3166,15 +3135,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   else
     [FRAME_CURSOR_COLOR (f) set];
 
-#ifdef NS_IMPL_COCOA
-  /* TODO: This makes drawing of cursor plus that of phys_cursor_glyph
-           atomic.  Cleaner ways of doing this should be investigated.
-           One way would be to set a global variable DRAWING_CURSOR
-           when making the call to draw_phys..(), don't focus in that
-           case, then move the ns_unfocus() here after that call. */
-  NSDisableScreenUpdates ();
-#endif
-
   switch (cursor_type)
     {
     case DEFAULT_CURSOR:
@@ -3208,11 +3168,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   /* draw the character under the cursor */
   if (cursor_type != NO_CURSOR)
     draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
-
-#ifdef NS_IMPL_COCOA
-  NSEnableScreenUpdates ();
-#endif
-
 }
 
 
@@ -3297,6 +3252,7 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
   ns_unfocus (f);
 }
 
+
 static void
 ns_show_hourglass (struct frame *f)
 {
@@ -3794,12 +3750,29 @@ Function modeled after x_draw_glyph_string_box ().
       NSRect ir = NSMakeRect (s->slice.x,
                               s->img->height - s->slice.y - s->slice.height,
                               s->slice.width, s->slice.height);
+
+      /* Because we're drawing into an offscreen buffer which isn't
+         flipped, the images come out upside down.  To work around it
+         we need to do some fancy transforms.  */
+      NSAffineTransform *transform = [NSAffineTransform transform];
+
+      /* FIXME: It should be faster to just reverse the transform than
+         save and restore the graphics state.  */
+      [NSGraphicsContext saveGraphicsState];
+      [transform translateXBy:0 yBy:NSMaxY(dr)];
+      [transform scaleXBy:1 yBy:-1];
+      [transform concat];
+
+      dr.origin.y = 0;
+
       [img drawInRect: dr
              fromRect: ir
              operation: NSCompositingOperationSourceOver
               fraction: 1.0
            respectFlipped: YES
                 hints: nil];
+
+      [NSGraphicsContext restoreGraphicsState];
 #else
       [img compositeToPoint: NSMakePoint (x, y + s->slice.height)
                   operation: NSCompositingOperationSourceOver];
@@ -7010,6 +6983,7 @@ - (void) updateFrameSize: (BOOL) delay
          from non-native fullscreen, in other circumstances it appears
          to be a noop.  (bug#28872) */
       wr = NSMakeRect (0, 0, neww, newh);
+      [self createDrawingBufferWithRect:wr];
       [view setFrame: wr];
 
       // to do: consider using [NSNotificationCenter postNotificationName:].
@@ -7349,6 +7323,8 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
   maximizing_resize = NO;
 #endif
 
+  [self createDrawingBufferWithRect:r];
+
   win = [[EmacsWindow alloc]
             initWithContentRect: r
                       styleMask: (FRAME_UNDECORATED (f)
@@ -8139,40 +8115,105 @@ - (instancetype)toggleToolbar: (id)sender
 }
 
 
-- (void)viewWillDraw
+- (void)createDrawingBufferWithRect:(NSRect)rect
+  /* Create and store a new NSBitmapImageRep for Emacs to draw
+     into.
+
+     Drawing to an offscreen bitmap doesn't work in GNUstep as there's
+     a bug in graphicsContextWithBitmapImageRep
+     (https://savannah.gnu.org/bugs/?38405).  So under GNUstep we
+     retain the old method of drawing direct to the EmacsView.  */
 {
-  /* If the frame has been garbaged there's no point in redrawing
-     anything.  */
-  if (FRAME_GARBAGED_P (emacsframe))
-    [self setNeedsDisplay:NO];
+#ifdef NS_IMPL_COCOA
+  if (drawingBuffer != nil)
+    [drawingBuffer release];
+
+  drawingBuffer = [[self bitmapImageRepForCachingDisplayInRect:rect] retain];
+#endif
 }
 
-- (void)drawRect: (NSRect)rect
+
+#ifdef NS_IMPL_COCOA
+- (void)focusOnDrawingBuffer
 {
-  int x = NSMinX (rect), y = NSMinY (rect);
-  int width = NSWidth (rect), height = NSHeight (rect);
+  /* Creating the graphics context each time is very slow, but it
+     doesn't seem possible to cache and reuse it.  */
+  [NSGraphicsContext
+    setCurrentContext:
+      [NSGraphicsContext graphicsContextWithBitmapImageRep:drawingBuffer]];
+}
+
+
+- (void)windowDidChangeBackingProperties:(NSNotification *)notification
+  /* Update the drawing buffer when the backing scale factor changes.  */
+{
+   CGFloat old = [[[notification userInfo]
+                    objectForKey:@"NSBackingPropertyOldScaleFactorKey"]
+                   doubleValue];
+   CGFloat new = [[self window] backingScaleFactor];
+
+   if (old != new)
+     {
+       NSRect frame = [self frame];
+       [self createDrawingBufferWithRect:frame];
+       ns_clear_frame (emacsframe);
+       expose_frame (emacsframe, 0, 0, NSWidth (frame), NSHeight (frame));
+     }
+}
+#endif
+
+
+- (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect
+{
+  NSTRACE ("[EmacsView copyRect:To:]");
+  NSTRACE_RECT ("Source", srcRect);
+  NSTRACE_RECT ("Destination", dstRect);
+
+#ifdef NS_IMPL_COCOA
+  [drawingBuffer drawInRect:dstRect
+                   fromRect:srcRect
+                  operation:NSCompositingOperationCopy
+                   fraction:1.0
+             respectFlipped:NO
+                      hints:nil];
+
+  [self setNeedsDisplayInRect:dstRect];
+#else
+  hide_bell();              // Ensure the bell image isn't scrolled.
 
+  ns_focus (emacsframe, &dstRect, 1);
+  [self scrollRect: srcRect
+                by: NSMakeSize (dstRect.origin.x - srcRect.origin.x,
+                                dstRect.origin.y - srcRect.origin.y)];
+  ns_unfocus (emacsframe);
+#endif
+}
+
+
+- (void)drawRect: (NSRect)rect
+{
   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
            NSTRACE_ARG_RECT(rect));
 
   if (!emacsframe || !emacsframe->output_data.ns)
     return;
 
-  ns_clear_frame_area (emacsframe, x, y, width, height);
-  block_input ();
+#ifdef NS_IMPL_COCOA
+  [drawingBuffer drawInRect:rect
+                   fromRect:rect
+                  operation:NSCompositingOperationSourceOver
+                   fraction:1
+             respectFlipped:NO
+                      hints:nil];
+#else
+  int x = NSMinX (rect), y = NSMinY (rect);
+  int width = NSWidth (rect), height = NSHeight (rect);
 
+  block_input ();
+  ns_clear_frame_area (emacsframe, x, y, width, height);
   expose_frame (emacsframe, x, y, width, height);
-
   unblock_input ();
-
-  /*
-    drawRect: may be called (at least in Mac OS X 10.5) for invisible
-    views as well for some reason.  Thus, do not infer visibility
-    here.
-
-    emacsframe->async_visible = 1;
-    emacsframe->async_iconified = 0;
-  */
+#endif
 }
 
 
-- 
2.20.1


  reply	other threads:[~2019-02-11 22:14 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-13 23:19 Some NS port changes Alan Third
2019-01-14 15:42 ` Eli Zaretskii
2019-01-15 16:12   ` Alan Third
2019-01-15 16:33     ` Stefan Monnier
2019-01-15 16:58       ` Alan Third
2019-01-15 20:35 ` Charles A. Roelli
2019-01-15 22:22   ` Alan Third
2019-02-10 22:14 ` Alan Third
2019-02-11 10:59   ` Robert Pluim
2019-02-11 22:14     ` Alan Third [this message]
2019-02-12  9:21       ` Robert Pluim
2019-02-14  9:08         ` Robert Pluim
2019-02-14 18:17           ` Alan Third
2019-02-15 10:51             ` Robert Pluim
  -- strict thread matches above, loose matches on Subject: below --
2019-01-20  6:11 Zhang Haijun
2019-01-20 11:09 ` Alan Third
2019-01-20 11:20   ` Zhang Haijun
2019-01-20 12:26     ` Eli Zaretskii
2019-01-20 12:23 ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190211221404.GA24399@breton.holly.idiocy.org \
    --to=alan@idiocy.org \
    --cc=emacs-devel@gnu.org \
    --cc=rpluim@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).