unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Some NS port changes
@ 2019-01-13 23:19 Alan Third
  2019-01-14 15:42 ` Eli Zaretskii
                   ` (2 more replies)
  0 siblings, 3 replies; 20+ messages in thread
From: Alan Third @ 2019-01-13 23:19 UTC (permalink / raw)
  To: emacs-devel

I’ve had some time to work on fixing some stuff in the NS port this
weekend. I’ve uploaded the changes to scratch/ns/next. It’s based on
master.

I think the branch should have got rid of most (if not all) of the
flickering, although it has introduced a strange bug related to
non‐native fullscreen. Horizontal scrollbars are also not right under
GNUstep, but I don’t know where I’ve gone wrong.

I’ve not tried it myself, but it may be worth cherrypicking commit
7dcce0a574a6b614961fb61c2b0936f56472f4df into emacs-26.
-- 
Alan Third



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

* Re: Some NS port changes
  2019-01-13 23:19 Alan Third
@ 2019-01-14 15:42 ` Eli Zaretskii
  2019-01-15 16:12   ` Alan Third
  2019-01-15 20:35 ` Charles A. Roelli
  2019-02-10 22:14 ` Alan Third
  2 siblings, 1 reply; 20+ messages in thread
From: Eli Zaretskii @ 2019-01-14 15:42 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel

> Date: Sun, 13 Jan 2019 23:19:50 +0000
> From: Alan Third <alan@idiocy.org>
> 
> I’ve not tried it myself, but it may be worth cherrypicking commit
> 7dcce0a574a6b614961fb61c2b0936f56472f4df into emacs-26.

If this helps, it's fine with me to cherrypick that commit.

Thanks.



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

* Re: Some NS port changes
  2019-01-14 15:42 ` Eli Zaretskii
@ 2019-01-15 16:12   ` Alan Third
  2019-01-15 16:33     ` Stefan Monnier
  0 siblings, 1 reply; 20+ messages in thread
From: Alan Third @ 2019-01-15 16:12 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

On Mon, Jan 14, 2019 at 05:42:27PM +0200, Eli Zaretskii wrote:
> > Date: Sun, 13 Jan 2019 23:19:50 +0000
> > From: Alan Third <alan@idiocy.org>
> > 
> > I’ve not tried it myself, but it may be worth cherrypicking commit
> > 7dcce0a574a6b614961fb61c2b0936f56472f4df into emacs-26.
> 
> If this helps, it's fine with me to cherrypick that commit.

Thanks I’ve done some testing and it looks good in emacs-26, so I’ve
pushed it up.

BTW, I’ve accidentally pushed a commit with an invalid email address
to scratch/ns/next. Now that I’ve published it I know I’m not supposed
to rewrite history, but do we care too much about that on scratch
branches?
-- 
Alan Third



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

* Re: Some NS port changes
  2019-01-15 16:12   ` Alan Third
@ 2019-01-15 16:33     ` Stefan Monnier
  2019-01-15 16:58       ` Alan Third
  0 siblings, 1 reply; 20+ messages in thread
From: Stefan Monnier @ 2019-01-15 16:33 UTC (permalink / raw)
  To: emacs-devel

> BTW, I’ve accidentally pushed a commit with an invalid email address
> to scratch/ns/next.  Now that I’ve published it I know I’m not supposed
> to rewrite history, but do we care too much about that on scratch
> branches?

Branches under the `scratch/` prefix can be ... scratched!
So rewrite at will,


        Stefan




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

* Re: Some NS port changes
  2019-01-15 16:33     ` Stefan Monnier
@ 2019-01-15 16:58       ` Alan Third
  0 siblings, 0 replies; 20+ messages in thread
From: Alan Third @ 2019-01-15 16:58 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On Tue, Jan 15, 2019 at 11:33:19AM -0500, Stefan Monnier wrote:
> > BTW, I’ve accidentally pushed a commit with an invalid email address
> > to scratch/ns/next.  Now that I’ve published it I know I’m not supposed
> > to rewrite history, but do we care too much about that on scratch
> > branches?
> 
> Branches under the `scratch/` prefix can be ... scratched!
> So rewrite at will,

Thanks!
-- 
Alan Third



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

* Re: Some NS port changes
  2019-01-13 23:19 Alan Third
  2019-01-14 15:42 ` Eli Zaretskii
@ 2019-01-15 20:35 ` Charles A. Roelli
  2019-01-15 22:22   ` Alan Third
  2019-02-10 22:14 ` Alan Third
  2 siblings, 1 reply; 20+ messages in thread
From: Charles A. Roelli @ 2019-01-15 20:35 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel

> Date: Sun, 13 Jan 2019 23:19:50 +0000
> From: Alan Third <alan@idiocy.org>
> 
> I’ve had some time to work on fixing some stuff in the NS port this
> weekend. I’ve uploaded the changes to scratch/ns/next. It’s based on
> master.

Thanks!  It builds and runs fine on 10.6.  In all cases where I could
reliably trigger a flicker with master, there is no more flickering
with this branch.



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

* Re: Some NS port changes
  2019-01-15 20:35 ` Charles A. Roelli
@ 2019-01-15 22:22   ` Alan Third
  0 siblings, 0 replies; 20+ messages in thread
From: Alan Third @ 2019-01-15 22:22 UTC (permalink / raw)
  To: Charles A. Roelli; +Cc: emacs-devel

On Tue, Jan 15, 2019 at 09:35:49PM +0100, Charles A. Roelli wrote:
> > Date: Sun, 13 Jan 2019 23:19:50 +0000
> > From: Alan Third <alan@idiocy.org>
> > 
> > I’ve had some time to work on fixing some stuff in the NS port this
> > weekend. I’ve uploaded the changes to scratch/ns/next. It’s based on
> > master.
> 
> Thanks!  It builds and runs fine on 10.6.  In all cases where I could
> reliably trigger a flicker with master, there is no more flickering
> with this branch.

That’s great news! Thanks for testing, Charles.
-- 
Alan Third



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

* Re: Some NS port changes
@ 2019-01-20  6:11 Zhang Haijun
  2019-01-20 11:09 ` Alan Third
  2019-01-20 12:23 ` Some NS port changes Eli Zaretskii
  0 siblings, 2 replies; 20+ messages in thread
From: Zhang Haijun @ 2019-01-20  6:11 UTC (permalink / raw)
  To: emacs-devel@gnu.org; +Cc: Alan Third

There are still too many flickers on macOS 10.13.6.

Steps to reproduce:
1. emacs -Q
2. (setq redisplay-dont-pause nil)
3. Select text across lines with touchpad

I can see blanks and then refreshing, sometimes just a blank retangle without refreshing.


macOS: 10.13.6
Xcode version: 9.2





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

* Re: Some NS port changes
  2019-01-20  6:11 Some NS port changes Zhang Haijun
@ 2019-01-20 11:09 ` Alan Third
  2019-01-20 11:20   ` Zhang Haijun
  2019-01-20 12:23 ` Some NS port changes Eli Zaretskii
  1 sibling, 1 reply; 20+ messages in thread
From: Alan Third @ 2019-01-20 11:09 UTC (permalink / raw)
  To: Zhang Haijun; +Cc: emacs-devel@gnu.org

On Sun, Jan 20, 2019 at 06:11:17AM +0000, Zhang Haijun wrote:
> There are still too many flickers on macOS 10.13.6.
> 
> Steps to reproduce:
> 1. emacs -Q
> 2. (setq redisplay-dont-pause nil)
> 3. Select text across lines with touchpad
> 
> I can see blanks and then refreshing, sometimes just a blank retangle without refreshing.

FWIW that’s exactly what I would expect to happen with that setting
set to nil. We can’t really stop the NS toolkit trying to update the
screen, but if you pause redisplay part‐way through then it’s not
going to be able to update the areas already marked as needing
redrawn.

Once it’s tried to redraw those areas, it marks them as drawn whether
it was succesful or not, so the next time round they will be ignored.
There’s not very much I can do about that.

What’s the use‐case for setting this variable?
-- 
Alan Third



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

* Re: Some NS port changes
  2019-01-20 11:09 ` Alan Third
@ 2019-01-20 11:20   ` Zhang Haijun
  2019-01-20 12:26     ` Eli Zaretskii
  0 siblings, 1 reply; 20+ messages in thread
From: Zhang Haijun @ 2019-01-20 11:20 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel@gnu.org



> 在 2019年1月20日,下午7:09,Alan Third <alan@idiocy.org> 写道:
> 
> FWIW that’s exactly what I would expect to happen with that setting
> set to nil. We can’t really stop the NS toolkit trying to update the
> screen, but if you pause redisplay part‐way through then it’s not
> going to be able to update the areas already marked as needing
> redrawn.
> 
> Once it’s tried to redraw those areas, it marks them as drawn whether
> it was succesful or not, so the next time round they will be ignored.
> There’s not very much I can do about that.
> 
> What’s the use‐case for setting this variable?
> -- 
> Alan Third

Because of a bug for CJK: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=+23412

Can the area be marked as drawn after it be redrawn?


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

* Re: Some NS port changes
  2019-01-20  6:11 Some NS port changes Zhang Haijun
  2019-01-20 11:09 ` Alan Third
@ 2019-01-20 12:23 ` Eli Zaretskii
  1 sibling, 0 replies; 20+ messages in thread
From: Eli Zaretskii @ 2019-01-20 12:23 UTC (permalink / raw)
  To: emacs-devel, Zhang Haijun, emacs-devel@gnu.org; +Cc: Alan Third

On January 20, 2019 8:11:17 AM GMT+02:00, Zhang Haijun <ccsmile2008@outlook.com> wrote:
> There are still too many flickers on macOS 10.13.6.
> 
> Steps to reproduce:
> 1. emacs -Q
> 2. (setq redisplay-dont-pause nil)
> 3. Select text across lines with touchpad
> 
> I can see blanks and then refreshing, sometimes just a blank retangle
> without refreshing.
> 
> 
> macOS: 10.13.6
> Xcode version: 9.2

The variable redisplay-dont-pause is deprecated.  Don't use it.



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

* Re: Some NS port changes
  2019-01-20 11:20   ` Zhang Haijun
@ 2019-01-20 12:26     ` Eli Zaretskii
  2019-01-21  6:02       ` Some NS port changes(recent 3) jun
  0 siblings, 1 reply; 20+ messages in thread
From: Eli Zaretskii @ 2019-01-20 12:26 UTC (permalink / raw)
  To: emacs-devel, Zhang Haijun, Alan Third; +Cc: emacs-devel@gnu.org

On January 20, 2019 1:20:13 PM GMT+02:00, Zhang Haijun <ccsmile2008@outlook.com> wrote:
> 
> 
> > 在 2019年1月20日,下午7:09,Alan Third <alan@idiocy.org> 写道:
> > 
> > FWIW that’s exactly what I would expect to happen with that setting
> > set to nil. We can’t really stop the NS toolkit trying to update the
> > screen, but if you pause redisplay part‐way through then it’s not
> > going to be able to update the areas already marked as needing
> > redrawn.
> > 
> > Once it’s tried to redraw those areas, it marks them as drawn
> whether
> > it was succesful or not, so the next time round they will be
> ignored.
> > There’s not very much I can do about that.
> > 
> > What’s the use‐case for setting this variable?
> > -- 
> > Alan Third
> 
> Because of a bug for CJK:
> https://debbugs.gnu.org/cgi/bugreport.cgi?bug=+23412
> 
> Can the area be marked as drawn after it be redrawn?

In that bug report you talk about "shaking".  Did you mean flickering?  Because I don't think I see any shaking in the clip.

In any case, this input method is not from Emacs, right?  So I couldn't try reproducing the problem on my machine.



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

* Re: Some NS port changes(recent 3)
  2019-01-20 12:26     ` Eli Zaretskii
@ 2019-01-21  6:02       ` jun
  0 siblings, 0 replies; 20+ messages in thread
From: jun @ 2019-01-21  6:02 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Alan Third, Zhang Haijun, emacs-devel

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

I sent this mail 10 hours ago. But I didn’t see it on http://lists.gnu.org/archive/html/emacs-devel/2019-01/index.html.
So I resent it with another mail provider.


At 2019-01-20 20:26:08, "Eli Zaretskii" <eliz@gnu.org> wrote:
>
>In that bug report you talk about "shaking".  Did you mean flickering?  Because I don't think I see any shaking in the clip.
>

>In any case, this input method is not from Emacs, right?  So I couldn't try reproducing the problem on my machine.


Yes, it should be flickering. The same content is cleared(at the same time, the cursor goes to the beginning of the content area) 
and then redisplayed(and the cursor comes back) during inputing.

Yes, the input method is not from emacs. It is provided by the OS.

That bug only happens on macOS. And other apps(including macVim) on the OS don’t have same problem. So it must be a bug of emacs.


[-- Attachment #2: Type: text/html, Size: 2255 bytes --]

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

* Re: Some NS port changes
  2019-01-13 23:19 Alan Third
  2019-01-14 15:42 ` Eli Zaretskii
  2019-01-15 20:35 ` Charles A. Roelli
@ 2019-02-10 22:14 ` Alan Third
  2019-02-11 10:59   ` Robert Pluim
  2 siblings, 1 reply; 20+ messages in thread
From: Alan Third @ 2019-02-10 22:14 UTC (permalink / raw)
  To: emacs-devel

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

The scratch/ns/next branch turned out to not be great. It didn’t work
at all with GNUstep.

I’ve pretty much given up on the ‘expose_frame’ method and have gone
back to the idea of drawing to an offscreen buffer. When I tried this
before there were severe performance problems, but I believe I’ve
worked round them this time.

This also doesn’t work in GNUstep, but the new technique is so close
to the old, deprecated, one that it’s possible to just continue with
that under GNUstep. The problem appears to be a GNUstep bug.

I think performance‐wise, this is on a par with the expose_frame
method we’re currently using, and it has proper scrolling, which is a
plus.

The attached file is an mbox you should be able to apply to emacs-26
with:

    git am offscreen-buffer.mbox

I hope. It probably won’t apply directly to master, but I haven’t
tried it.
-- 
Alan Third

[-- Attachment #2: offscreen-buffer.mbox --]
[-- Type: text/plain, Size: 57616 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 e344535dead54eb63b6c9a7d2135a005ef0181b2 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, gc): New variables.
([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 |  10 +++
 src/nsterm.m | 230 ++++++++++++++++++++++++++++++++++-----------------
 3 files changed, 166 insertions(+), 84 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..74ffa0b564 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -416,6 +416,10 @@ typedef id instancetype;
    int maximized_width, maximized_height;
    NSWindow *nonfs_window;
    BOOL fs_is_native;
+#ifdef NS_IMPL_COCOA
+   NSBitmapImageRep *drawingBuffer;
+   NSGraphicsContext *gc;
+#endif
 @public
    struct frame *emacsframe;
    int rows, cols;
@@ -456,6 +460,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..b6ef05ce27 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1077,7 +1077,11 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
 #endif
 
   ns_updating_frame = f;
+#ifdef NS_IMPL_COCOA
+  [FRAME_NS_VIEW (f) focusOnDrawingBuffer];
+#else
   [view lockFocus];
+#endif
 
   /* 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
@@ -1192,12 +1196,16 @@ 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];
+#else
   block_input ();
 
   [view unlockFocus];
   [[view window] flushWindow];
 
   unblock_input ();
+#endif
   ns_updating_frame = NULL;
 }
 
@@ -1212,6 +1220,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 +1229,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 +1281,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 +1289,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 +1303,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 +2766,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 +2818,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 +2877,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 +2963,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 +3042,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 +3164,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 +3197,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 +3281,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 +3779,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 +7012,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 +7352,8 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
   maximizing_resize = NO;
 #endif
 
+  [self createDrawingBufferWithRect:r];
+
   win = [[EmacsWindow alloc]
             initWithContentRect: r
                       styleMask: (FRAME_UNDECORATED (f)
@@ -8139,40 +8144,109 @@ - (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)
+    {
+      [gc release];
+      [drawingBuffer release];
+    }
+
+  drawingBuffer = [[self bitmapImageRepForCachingDisplayInRect:rect] retain];
+
+  /* Cache the graphics context, as recreating it each time we want to
+     draw is very slow.  */
+  gc = [[NSGraphicsContext graphicsContextWithBitmapImageRep:drawingBuffer]
+         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);
+  [NSGraphicsContext setCurrentContext:gc];
+}
+
+
+- (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


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

* Re: Some NS port changes
  2019-02-10 22:14 ` Alan Third
@ 2019-02-11 10:59   ` Robert Pluim
  2019-02-11 22:14     ` Alan Third
  0 siblings, 1 reply; 20+ messages in thread
From: Robert Pluim @ 2019-02-11 10:59 UTC (permalink / raw)
  To: emacs-devel

Robert Pluim <rpluim@gmail.com> writes:

> Alan Third <alan@idiocy.org> writes:
>
>> The scratch/ns/next branch turned out to not be great. It didn’t work
>> at all with GNUstep.
>>
>> I’ve pretty much given up on the ‘expose_frame’ method and have gone
>> back to the idea of drawing to an offscreen buffer. When I tried this
>> before there were severe performance problems, but I believe I’ve
>> worked round them this time.
>>
>> This also doesn’t work in GNUstep, but the new technique is so close
>> to the old, deprecated, one that it’s possible to just continue with
>> that under GNUstep. The problem appears to be a GNUstep bug.
>>
>> I think performance‐wise, this is on a par with the expose_frame
>> method we’re currently using, and it has proper scrolling, which is a
>> plus.
>>
>> The attached file is an mbox you should be able to apply to emacs-26
>> with:

Hi Alan,

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)

Robert



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

* Re: Some NS port changes
  2019-02-11 10:59   ` Robert Pluim
@ 2019-02-11 22:14     ` Alan Third
  2019-02-12  9:21       ` Robert Pluim
  0 siblings, 1 reply; 20+ messages in thread
From: Alan Third @ 2019-02-11 22:14 UTC (permalink / raw)
  To: Robert Pluim; +Cc: emacs-devel

[-- 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


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

* Re: Some NS port changes
  2019-02-11 22:14     ` Alan Third
@ 2019-02-12  9:21       ` Robert Pluim
  2019-02-14  9:08         ` Robert Pluim
  0 siblings, 1 reply; 20+ messages in thread
From: Robert Pluim @ 2019-02-12  9:21 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel

Alan Third <alan@idiocy.org> writes:

> 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.

That works better. Iʼll keep running with it for a while.

Thanks

Robert



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

* Re: Some NS port changes
  2019-02-12  9:21       ` Robert Pluim
@ 2019-02-14  9:08         ` Robert Pluim
  2019-02-14 18:17           ` Alan Third
  0 siblings, 1 reply; 20+ messages in thread
From: Robert Pluim @ 2019-02-14  9:08 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel

Robert Pluim <rpluim@gmail.com> writes:

> Alan Third <alan@idiocy.org> writes:
>
>> 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.
>
> That works better. Iʼll keep running with it for a while.

Youʼre right about the performance: moving point in a buffer is
glacial, as is auto-repeat.

Robert



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

* Re: Some NS port changes
  2019-02-14  9:08         ` Robert Pluim
@ 2019-02-14 18:17           ` Alan Third
  2019-02-15 10:51             ` Robert Pluim
  0 siblings, 1 reply; 20+ messages in thread
From: Alan Third @ 2019-02-14 18:17 UTC (permalink / raw)
  To: emacs-devel

On Thu, Feb 14, 2019 at 10:08:30AM +0100, Robert Pluim wrote:
> Robert Pluim <rpluim@gmail.com> writes:
> 
> > Alan Third <alan@idiocy.org> writes:
> >
> >> 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.
> >
> > That works better. Iʼll keep running with it for a while.
> 
> Youʼre right about the performance: moving point in a buffer is
> glacial, as is auto-repeat.

I was talking more about scrolling a complex file in a fullscreen
window. Is it always slow or is it only in certain circumstances?
-- 
Alan Third



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

* Re: Some NS port changes
  2019-02-14 18:17           ` Alan Third
@ 2019-02-15 10:51             ` Robert Pluim
  0 siblings, 0 replies; 20+ messages in thread
From: Robert Pluim @ 2019-02-15 10:51 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel

Alan Third <alan@idiocy.org> writes:

> On Thu, Feb 14, 2019 at 10:08:30AM +0100, Robert Pluim wrote:
>> Robert Pluim <rpluim@gmail.com> writes:
>> 
>> > Alan Third <alan@idiocy.org> writes:
>> >
>> >> 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.
>> >
>> > That works better. Iʼll keep running with it for a while.
>> 
>> Youʼre right about the performance: moving point in a buffer is
>> glacial, as is auto-repeat.
>
> I was talking more about scrolling a complex file in a fullscreen
> window. Is it always slow or is it only in certain circumstances?

Scrolling my main org file (which is displayed using a mix of
fixed-width and variable-width faces) with 'scroll-up-command' is
noticeably slower with your patch compared to stock emacs-26

Itʼs always slower, as far as I can tell, almost to the point of
unusability for me.

Robert



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

end of thread, other threads:[~2019-02-15 10:51 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-01-20  6:11 Some NS port changes 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-21  6:02       ` Some NS port changes(recent 3) jun
2019-01-20 12:23 ` Some NS port changes Eli Zaretskii
  -- strict thread matches above, loose matches on Subject: below --
2019-01-13 23:19 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
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

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).