emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

scratch/ns/refactor a159bd0 01/10: Simplify macOS drawing code


From: Alan Third
Subject: scratch/ns/refactor a159bd0 01/10: Simplify macOS drawing code
Date: Sat, 17 Jul 2021 08:23:41 -0400 (EDT)

branch: scratch/ns/refactor
commit a159bd046c261b571263502423620dea7c6d0559
Author: Alan Third <alan@idiocy.org>
Commit: Alan Third <alan@idiocy.org>

    Simplify macOS drawing code
    
    Convert EmacsSurface into a CALayer subclass so we can use the
    built-in relationships.  Also simplify the macOS versioning code.
    This will result in more warnings on older versions of macOS but makes
    reading the code easier.
    
    * configure.ac: Add QuartzCore framework.
    * src/nsterm.h (NS_DRAW_TO_BUFFER): Remove define and all references.
    (EmacsSurface, EmacsLayer): Rename EmacsSurface to EmacsLayer and
    modify the definition to fit the new function.
    * src/nsterm.m (ns_update_begin):
    (ns_update_end):
    (ns_focus):
    (ns_unfocus): Use the new overridden lockFocus and unlockFocus and
    simplify the frame management.
    ([EmacsView dealloc]):
    ([EmacsView viewDidResize:]):Don't explicitly release surfaces.
    ([EmacsView initFrameFromEmacs:]): Move the layer code to after the
    NSWindow has been created as creating the layer now relies on some of
    it's properties.
    ([EmacsView makeBackingLayer]): New function.
    ([EmacsView lockFocus]):
    ([EmacsView focusOnDrawingBuffer]): Rename to lockFocus.
    ([EmacsView unlockFocus]):
    ([EmacsView unfocusDrawingBuffer]): Rename to unlockFocus.
    ([EmacsView windowDidChangeBackingProperties]): Don't explicitly
    release surfaces but reset EmacsLayer properties.
    ([EmacsView layout]):
    ([EmacsView viewWillDraw]): Rename to layout.
    ([EmacsView wantsUpdateLayer]): Remove function and change all callers
    to [EmacsView wantsLayer].
    (EmacsSurface, EmacsLayer): Rename to EmacsLayer.
    ([EmacsSurface getSize]):
    ([EmacsSurface initWithSize:ColorSpace:Scale:]): Remove methods.
    ([EmacsSurface initWithColorSpace:]):
    ([EmacsLayer checkDimensions]):
    ([EmacsLayer releaseSurfaces]):
    ([EmacsLayer display]): New functions.
    * src/nsterm.m ([EmacsLayer dealloc]): Use releaseSurfaces.
    ([EmacsSurface getContext]): Automatically detect frame property
    changes and clear the cache if required.  Use built-in CALayer
    properties where available.
    ([EmacsLayer copyContentsTo:]): Use [CALayer contents] as source.
---
 configure.ac |   3 +-
 src/nsterm.h |  35 +----
 src/nsterm.m | 496 ++++++++++++++++++++++++-----------------------------------
 3 files changed, 207 insertions(+), 327 deletions(-)

diff --git a/configure.ac b/configure.ac
index c924634..79cc56f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5660,7 +5660,8 @@ case "$opsys" in
    if test "$HAVE_NS" = "yes"; then
      libs_nsgui="-framework AppKit"
      if test "$NS_IMPL_COCOA" = "yes"; then
-        libs_nsgui="$libs_nsgui -framework IOKit -framework Carbon -framework 
IOSurface"
+        libs_nsgui="$libs_nsgui -framework IOKit -framework Carbon \
+                    -framework IOSurface -framework QuartzCore"
      fi
    else
      libs_nsgui=
diff --git a/src/nsterm.h b/src/nsterm.h
index b29e76c..40fd543 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -348,16 +348,6 @@ typedef id instancetype;
 #endif
 
 
-/* macOS 10.14 and above cannot draw directly "to the glass" and
-   therefore we draw to an offscreen buffer and swap it in when the
-   toolkit wants to draw the frame. GNUstep and macOS 10.7 and below
-   do not support this method, so we revert to drawing directly to the
-   glass.  */
-#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
-#define NS_DRAW_TO_BUFFER 1
-#endif
-
-
 /* ==========================================================================
 
    NSColor, EmacsColor category.
@@ -423,7 +413,7 @@ typedef id instancetype;
    ========================================================================== 
*/
 
 @class EmacsToolbar;
-@class EmacsSurface;
+@class EmacsLayer;
 
 #ifdef NS_IMPL_COCOA
 @interface EmacsView : NSView <NSTextInput, NSWindowDelegate>
@@ -443,9 +433,6 @@ typedef id instancetype;
    int maximized_width, maximized_height;
    NSWindow *nonfs_window;
    BOOL fs_is_native;
-#ifdef NS_DRAW_TO_BUFFER
-   EmacsSurface *surface;
-#endif
 @public
    struct frame *emacsframe;
    int scrollbarsNeedingUpdate;
@@ -483,9 +470,9 @@ typedef id instancetype;
 #endif
 - (int)fullscreenState;
 
-#ifdef NS_DRAW_TO_BUFFER
-- (void)focusOnDrawingBuffer;
-- (void)unfocusDrawingBuffer;
+#ifdef NS_IMPL_COCOA
+- (void)lockFocus;
+- (void)unlockFocus;
 #endif
 - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect;
 
@@ -716,23 +703,17 @@ typedef id instancetype;
 + (CGFloat)scrollerWidth;
 @end
 
-#ifdef NS_DRAW_TO_BUFFER
-@interface EmacsSurface : NSObject
+#ifdef NS_IMPL_COCOA
+@interface EmacsLayer : CALayer
 {
   NSMutableArray *cache;
-  NSSize size;
   CGColorSpaceRef colorSpace;
   IOSurfaceRef currentSurface;
-  IOSurfaceRef lastSurface;
   CGContextRef context;
-  CGFloat scale;
 }
-- (id) initWithSize: (NSSize)s ColorSpace: (CGColorSpaceRef)cs Scale: 
(CGFloat)scale;
-- (void) dealloc;
-- (NSSize) getSize;
+- (id) initWithColorSpace: (CGColorSpaceRef)cs;
+- (void) setColorSpace: (CGColorSpaceRef)cs;
 - (CGContextRef) getContext;
-- (void) releaseContext;
-- (IOSurfaceRef) getSurface;
 @end
 #endif
 
diff --git a/src/nsterm.m b/src/nsterm.m
index b9e2c9b..0a7cd8c 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -70,9 +70,6 @@ GNUstep port and post-20 update by Adrian Robert 
(arobert@cogsci.ucsd.edu)
 #ifdef NS_IMPL_COCOA
 #include "macfont.h"
 #include <Carbon/Carbon.h>
-#endif
-
-#ifdef NS_DRAW_TO_BUFFER
 #include <IOSurface/IOSurface.h>
 #endif
 
@@ -272,9 +269,6 @@ long context_menu_value = 0;
 
 /* display update */
 static struct frame *ns_updating_frame;
-#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-static NSView *focus_view = NULL;
-#endif
 static int ns_window_num = 0;
 static BOOL gsaved = NO;
 #ifdef NS_IMPL_COCOA
@@ -1039,26 +1033,7 @@ ns_update_begin (struct frame *f)
 #endif
 
   ns_updating_frame = f;
-#ifdef NS_DRAW_TO_BUFFER
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-  if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
-    {
-#endif
-      [view focusOnDrawingBuffer];
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-    }
-  else
-    {
-#endif
-#endif /* NS_DRAW_TO_BUFFER */
-
-#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-      [view lockFocus];
-#endif
-#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-    }
-#endif
-
+  [view lockFocus];
 }
 
 
@@ -1069,39 +1044,21 @@ ns_update_end (struct frame *f)
    external (RIF) call; for whole frame, called after gui_update_window_end
    -------------------------------------------------------------------------- 
*/
 {
-#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
   EmacsView *view = FRAME_NS_VIEW (f);
-#endif
 
   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;
 
-#ifdef NS_DRAW_TO_BUFFER
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-  if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
-    {
-#endif
-      [FRAME_NS_VIEW (f) unfocusDrawingBuffer];
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-    }
-  else
-    {
-#endif
-#endif /* NS_DRAW_TO_BUFFER */
-
-#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-      block_input ();
-
-      [view unlockFocus];
-      [[view window] flushWindow];
+  block_input ();
 
-      unblock_input ();
-#endif
-#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-    }
+  [view unlockFocus];
+#if defined (NS_IMPL_GNUSTEP)
+  [[view window] flushWindow];
 #endif
+
+  unblock_input ();
   ns_updating_frame = NULL;
 }
 
@@ -1116,8 +1073,6 @@ ns_focus (struct frame *f, NSRect *r, int n)
      the entire window.
    -------------------------------------------------------------------------- 
*/
 {
-  EmacsView *view = FRAME_NS_VIEW (f);
-
   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus");
   if (r != NULL)
     {
@@ -1126,39 +1081,10 @@ ns_focus (struct frame *f, NSRect *r, int n)
 
   if (f != ns_updating_frame)
     {
-#ifdef NS_DRAW_TO_BUFFER
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-      if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
-        {
-#endif
-          [view focusOnDrawingBuffer];
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-        }
-      else
-        {
-#endif
-#endif /* NS_DRAW_TO_BUFFER */
-
-#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-          if (view != focus_view)
-            {
-              if (focus_view != NULL)
-                {
-                  [focus_view unlockFocus];
-                  [[focus_view window] flushWindow];
-                }
-
-              if (view)
-                [view lockFocus];
-              focus_view = view;
-            }
-#endif
-#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-        }
-#endif
+      EmacsView *view = FRAME_NS_VIEW (f);
+      [view lockFocus];
     }
 
-
   /* clipping */
   if (r)
     {
@@ -1186,35 +1112,14 @@ ns_unfocus (struct frame *f)
       gsaved = NO;
     }
 
-#ifdef NS_DRAW_TO_BUFFER
-  #if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-  if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
-    {
-#endif
-      if (! ns_updating_frame)
-        [FRAME_NS_VIEW (f) unfocusDrawingBuffer];
-      [FRAME_NS_VIEW (f) setNeedsDisplay:YES];
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-    }
-  else
+  if (f != ns_updating_frame)
     {
+      EmacsView *view = FRAME_NS_VIEW (f);
+      [view unlockFocus];
+#if defined (NS_IMPL_GNUSTEP)
+      [[view window] flushWindow];
 #endif
-#endif /* NS_DRAW_TO_BUFFER */
-
-#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-      if (f != ns_updating_frame)
-        {
-          if (focus_view != NULL)
-            {
-              [focus_view unlockFocus];
-              [[focus_view window] flushWindow];
-              focus_view = NULL;
-            }
-        }
-#endif
-#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
     }
-#endif
 }
 
 
@@ -1381,7 +1286,7 @@ ns_ring_bell (struct frame *f)
     }
 }
 
-#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+#if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
 static void
 hide_bell (void)
 /* --------------------------------------------------------------------------
@@ -6198,10 +6103,6 @@ not_in_argv (NSString *arg)
               name:NSViewFrameDidChangeNotification
             object:nil];
 
-#ifdef NS_DRAW_TO_BUFFER
-  [surface release];
-#endif
-
   [toolbar release];
   if (fs_state == FULLSCREEN_BOTH)
     [nonfs_window release];
@@ -7212,24 +7113,6 @@ not_in_argv (NSString *arg)
 
   NSTRACE ("[EmacsView viewDidResize]");
 
-#ifdef NS_DRAW_TO_BUFFER
-  /* If the buffer size doesn't match the view's backing size, destroy
-     the buffer and let it be recreated at the correct size later.  */
-  if ([self wantsUpdateLayer] && surface)
-    {
-      NSRect surfaceRect = {{0, 0}, [surface getSize]};
-      NSRect frameRect = [[self window] convertRectToBacking:frame];
-
-      if (!NSEqualRects (frameRect, surfaceRect))
-        {
-          [surface release];
-          surface = nil;
-
-          [self setNeedsDisplay:YES];
-        }
-    }
-#endif
-
   neww = (int)NSWidth (frame);
   newh = (int)NSHeight (frame);
   oldw = FRAME_PIXEL_WIDTH (emacsframe);
@@ -7408,16 +7291,6 @@ not_in_argv (NSString *arg)
   [self initWithFrame: r];
   [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
 
-#ifdef NS_DRAW_TO_BUFFER
-  /* These settings mean AppKit will retain the contents of the frame
-     on resize.  Unfortunately it also means the frame will not be
-     automatically marked for display, but we can do that ourselves in
-     viewDidResize.  */
-  [self setLayerContentsRedrawPolicy:
-          NSViewLayerContentsRedrawOnSetNeedsDisplay];
-  [self setLayerContentsPlacement:NSViewLayerContentsPlacementTopLeft];
-#endif
-
   FRAME_NS_VIEW (f) = self;
   emacsframe = f;
 #ifdef NS_IMPL_COCOA
@@ -7457,6 +7330,17 @@ not_in_argv (NSString *arg)
 
   [[win contentView] addSubview: self];
 
+#ifdef NS_IMPL_COCOA
+  /* These settings mean AppKit will retain the contents of the frame
+     on resize.  Unfortunately it also means the frame will not be
+     automatically marked for display, but we can do that ourselves in
+     viewDidResize.  */
+  [self setWantsLayer:YES];
+  [self setLayerContentsRedrawPolicy:
+          NSViewLayerContentsRedrawOnSetNeedsDisplay];
+  [self setLayerContentsPlacement:NSViewLayerContentsPlacementTopLeft];
+#endif
+
   if (ns_drag_types)
     [self registerForDraggedTypes: ns_drag_types];
 
@@ -8221,44 +8105,54 @@ not_in_argv (NSString *arg)
 }
 
 
-#ifdef NS_DRAW_TO_BUFFER
-- (void)focusOnDrawingBuffer
+#ifdef NS_IMPL_COCOA
+- (CALayer *)makeBackingLayer;
 {
-  CGFloat scale = [[self window] backingScaleFactor];
-
-  NSTRACE ("[EmacsView focusOnDrawingBuffer]");
+  EmacsLayer *l = [[EmacsLayer alloc]
+                    initWithColorSpace:[[[self window] colorSpace] 
CGColorSpace]];
+  [l setDelegate:(id)self];
+  [l setContentsScale:[[self window] backingScaleFactor]];
 
-  if (! surface)
-    {
-      NSRect frame = [self frame];
-      NSSize s = NSMakeSize (NSWidth (frame) * scale, NSHeight (frame) * 
scale);
+  return l;
+}
 
-      surface = [[EmacsSurface alloc] initWithSize:s
-                                        ColorSpace:[[[self window] colorSpace]
-                                                     CGColorSpace]
-                                             Scale:scale];
 
-      /* Since we're using NSViewLayerContentsRedrawOnSetNeedsDisplay
-         the layer's scale factor is not set automatically, so do it
-         now.  */
-      [[self layer] setContentsScale:scale];
-    }
+- (void)lockFocus
+{
+  NSTRACE ("[EmacsView lockFocus]");
 
-  CGContextRef context = [surface getContext];
+  if ([self wantsLayer])
+    {
+      CGContextRef context = [(EmacsLayer*)[self layer] getContext];
 
-  [NSGraphicsContext
-    setCurrentContext:[NSGraphicsContext
-                        graphicsContextWithCGContext:context
-                                             flipped:YES]];
+      [NSGraphicsContext
+        setCurrentContext:[NSGraphicsContext
+                            graphicsContextWithCGContext:context
+                                                 flipped:YES]];
+    }
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+  else
+    [super lockFocus];
+#endif
 }
 
 
-- (void)unfocusDrawingBuffer
+- (void)unlockFocus
 {
-  NSTRACE ("[EmacsView unfocusDrawingBuffer]");
+  NSTRACE ("[EmacsView unlockFocus]");
 
-  [NSGraphicsContext setCurrentContext:nil];
-  [self setNeedsDisplay:YES];
+  if ([self wantsLayer])
+    {
+      [NSGraphicsContext setCurrentContext:nil];
+      [self setNeedsDisplay:YES];
+    }
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+  else
+    {
+      [super unlockFocus];
+      [super flushWindow];
+    }
+#endif
 }
 
 
@@ -8267,18 +8161,19 @@ not_in_argv (NSString *arg)
 {
   NSTRACE ("EmacsView windowDidChangeBackingProperties:]");
 
-  if ([self wantsUpdateLayer])
+  if ([self wantsLayer])
     {
       NSRect frame = [self frame];
+      EmacsLayer *layer = (EmacsLayer *)[self layer];
 
-      [surface release];
-      surface = nil;
+      [layer setContentsScale:[[notification object] backingScaleFactor]];
+      [layer setColorSpace:[[[notification object] colorSpace] CGColorSpace]];
 
       ns_clear_frame (emacsframe);
       expose_frame (emacsframe, 0, 0, NSWidth (frame), NSHeight (frame));
     }
 }
-#endif /* NS_DRAW_TO_BUFFER */
+#endif /* NS_IMPL_COCOA */
 
 
 - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect
@@ -8287,11 +8182,9 @@ not_in_argv (NSString *arg)
   NSTRACE_RECT ("Source", srcRect);
   NSTRACE_RECT ("Destination", dstRect);
 
-#ifdef NS_DRAW_TO_BUFFER
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-  if ([self wantsUpdateLayer])
+#ifdef NS_IMPL_COCOA
+  if ([self wantsLayer])
     {
-#endif
       double scale = [[self window] backingScaleFactor];
       CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
       int bpp = CGBitmapContextGetBitsPerPixel (context) / 8;
@@ -8317,14 +8210,14 @@ not_in_argv (NSString *arg)
                    (char *) srcPixels + y * rowSize,
                    srcRowSize);
 
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
     }
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
   else
     {
 #endif
-#endif /* NS_DRAW_TO_BUFFER */
+#endif /* NS_IMPL_COCOA */
 
-#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+#if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
       hide_bell();              // Ensure the bell image isn't scrolled.
 
       ns_focus (emacsframe, &dstRect, 1);
@@ -8333,77 +8226,40 @@ not_in_argv (NSString *arg)
                                     dstRect.origin.y - srcRect.origin.y)];
       ns_unfocus (emacsframe);
 #endif
-#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400
     }
 #endif
 }
 
 
-#ifdef NS_DRAW_TO_BUFFER
+#ifdef NS_IMPL_COCOA
 /* If the frame has been garbaged but the toolkit wants to draw, for
    example when resizing the frame, we end up with a blank screen.
    Sometimes this results in an unpleasant flicker, so try to
-   redisplay before drawing.  */
-- (void)viewWillDraw
-{
-  if (FRAME_GARBAGED_P (emacsframe)
-      && !redisplaying_p
-      && [self wantsUpdateLayer])
-    {
-      /* If there is IO going on when redisplay is run here Emacs
-         crashes.  I think it's because this code will always be run
-         within the run loop and for whatever reason processing input
-         is dangerous.  This technique was stolen wholesale from
-         nsmenu.m and seems to work.  */
-      bool owfi = waiting_for_input;
-      waiting_for_input = 0;
-      block_input ();
-
-      redisplay ();
-
-      unblock_input ();
-      waiting_for_input = owfi;
-    }
-}
-
+   redisplay before drawing.
 
-- (BOOL)wantsUpdateLayer
+   This used to be done in viewWillDraw, but with the custom layer
+   that method is not called.  */
+- (void)layout
 {
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
-  if (NSAppKitVersionNumber < 1671)
-    return NO;
-#endif
-
-  /* Running on macOS 10.14 or above.  */
-  return YES;
-}
+  [super layout];
 
+  /* If there is IO going on when redisplay is run here Emacs
+     crashes.  I think it's because this code will always be run
+     within the run loop and for whatever reason processing input
+     is dangerous.  This technique was stolen wholesale from
+     nsmenu.m and seems to work.  */
+  bool owfi = waiting_for_input;
+  waiting_for_input = 0;
+  block_input ();
 
-- (void)updateLayer
-{
-  NSTRACE ("[EmacsView updateLayer]");
-
-  /* We run redisplay on frames that are garbaged, but marked for
-     display, before updateLayer is called so if the frame is still
-     garbaged that means the last redisplay must have refused to
-     update the frame.  */
-  if (FRAME_GARBAGED_P (emacsframe))
-    return;
+  redisplay ();
 
-  /* This can fail to update the screen if the same surface is
-     provided twice in a row, even if its contents have changed.
-     There's a private method, -[CALayer setContentsChanged], that we
-     could use to force it, but we shouldn't often get the same
-     surface twice in a row.  */
-  [surface releaseContext];
-  [[self layer] setContents:(id)[surface getSurface]];
-  [surface performSelectorOnMainThread:@selector (getContext)
-                            withObject:nil
-                         waitUntilDone:NO];
+  unblock_input ();
+  waiting_for_input = owfi;
 }
 #endif
 
-
 - (void)drawRect: (NSRect)rect
 {
   NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]",
@@ -9570,7 +9426,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
 @end  /* EmacsScroller */
 
 
-#ifdef NS_DRAW_TO_BUFFER
+#ifdef NS_IMPL_COCOA
 
 /* ==========================================================================
 
@@ -9578,7 +9434,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
 
    ========================================================================== 
*/
 
-@implementation EmacsSurface
+@implementation EmacsLayer
 
 
 /* An IOSurface is a pixel buffer that is efficiently copied to VRAM
@@ -9591,80 +9447,106 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
    ability to draw to the screen at any time, we need to keep a cache
    of multiple surfaces that we can use at will.
 
-   The EmacsSurface class maintains this cache of surfaces, and
+   The EmacsLayer class maintains this cache of surfaces, and
    handles the conversion to a CGGraphicsContext that AppKit can use
    to draw on.
 
    The cache is simple: if a free surface is found it is removed from
-   the cache and set as the "current" surface.  Once Emacs is done
-   with drawing to the current surface, the previous surface that was
-   drawn to is added to the cache for reuse, and the current one is
-   set as the last surface.  If no free surfaces are found in the
-   cache then a new one is created.
-
-   When AppKit wants to update the screen, we provide it with the last
-   surface, as that has the most recent data.
-
-   FIXME: It is possible for the cache to grow if Emacs draws faster
-   than the surfaces can be drawn to the screen, so there should
-   probably be some sort of pruning job that removes excess
-   surfaces.  */
+   the cache and set as the "current" surface.  Emacs draws to the
+   surface and when the layer wants to update the screen we set it's
+   contents to the surface and then add it back on to the end of the
+   cache.  If no free surfaces are found in the cache then a new one
+   is created.  */
 
 #define CACHE_MAX_SIZE 2
 
-- (id) initWithSize: (NSSize)s
-         ColorSpace: (CGColorSpaceRef)cs
-              Scale: (CGFloat)scl
+- (id) initWithColorSpace: (CGColorSpaceRef)cs
 {
-  NSTRACE ("[EmacsSurface initWithSize:ColorSpace:]");
-
-  [super init];
+  NSTRACE ("[EmacsLayer initWithColorSpace:]");
 
-  cache = [[NSMutableArray arrayWithCapacity:CACHE_MAX_SIZE] retain];
-  size = s;
-  colorSpace = cs;
-  scale = scl;
+  self = [super init];
+  if (self)
+    {
+      cache = [[NSMutableArray arrayWithCapacity:CACHE_MAX_SIZE] retain];
+      colorSpace = cs;
+    }
+  else
+    {
+      return nil;
+    }
 
   return self;
 }
 
 
-- (void) dealloc
+- (void) setColorSpace: (CGColorSpaceRef)cs
 {
-  if (context)
-    CGContextRelease (context);
-
-  if (currentSurface)
-    CFRelease (currentSurface);
+  /* We don't need to clear the cache because the new colorspace will
+     be used next time we create a new context.  */
+  colorSpace = cs;
+}
 
-  for (id object in cache)
-    CFRelease ((IOSurfaceRef)object);
 
+- (void) dealloc
+{
+  [self releaseSurfaces];
   [cache release];
 
   [super dealloc];
 }
 
 
-/* Return the size values our cached data is using.  */
-- (NSSize) getSize
+- (void) releaseSurfaces
 {
-  return size;
+  [self setContents:nil];
+  [self releaseContext];
+
+  if (currentSurface)
+    {
+      CFRelease (currentSurface);
+      currentSurface = nil;
+    }
+
+  if (cache)
+    {
+      for (id object in cache)
+        CFRelease ((IOSurfaceRef)object);
+
+      [cache removeAllObjects];
+    }
+}
+
+
+/* Check whether the current bounds match the IOSurfaces we are using.
+   If they do return YES, otherwise NO.  */
+- (BOOL) checkDimensions
+{
+  int width = NSWidth ([self bounds]) * [self contentsScale];
+  int height = NSHeight ([self bounds]) * [self contentsScale];
+  IOSurfaceRef s = currentSurface ? currentSurface
+    : (IOSurfaceRef)[cache firstObject];
+
+  return !s || (IOSurfaceGetWidth (s) == width
+                && IOSurfaceGetHeight (s) == height);
 }
 
 
-/* Return a CGContextRef that can be used for drawing to the screen.
-   This must ALWAYS be paired with a call to releaseContext, and the
-   calls cannot be nested.  */
+/* Return a CGContextRef that can be used for drawing to the screen.  */
 - (CGContextRef) getContext
 {
-  NSTRACE ("[EmacsSurface getContext]");
+  CGFloat scale = [self contentsScale];
+
+  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer getContext]");
+  NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (currentSurface ? 1 : 
0));
+
+  if (![self checkDimensions])
+    [self releaseSurfaces];
 
   if (!context)
     {
       IOSurfaceRef surface = NULL;
-
-      NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (lastSurface ? 1 : 
0));
+      int width = NSWidth ([self bounds]) * scale;
+      int height = NSHeight ([self bounds]) * scale;
 
       for (id object in cache)
         {
@@ -9687,11 +9569,11 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
       else if (!surface)
         {
           int bytesPerRow = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow,
-                                                    size.width * 4);
+                                                    width * 4);
 
           surface = IOSurfaceCreate
-            ((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber 
numberWithInt:size.width],
-                (id)kIOSurfaceHeight:[NSNumber numberWithInt:size.height],
+            ((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber 
numberWithInt:width],
+                (id)kIOSurfaceHeight:[NSNumber numberWithInt:height],
                 (id)kIOSurfaceBytesPerRow:[NSNumber numberWithInt:bytesPerRow],
                 (id)kIOSurfaceBytesPerElement:[NSNumber numberWithInt:4],
                 (id)kIOSurfacePixelFormat:[NSNumber 
numberWithUnsignedInt:'BGRA']});
@@ -9714,7 +9596,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
                                        (kCGImageAlphaPremultipliedFirst
                                         | kCGBitmapByteOrder32Host));
 
-      CGContextTranslateCTM(context, 0, size.height);
+      CGContextTranslateCTM(context, 0, IOSurfaceGetHeight (currentSurface));
       CGContextScaleCTM(context, scale, -scale);
     }
 
@@ -9726,7 +9608,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
    IOSurface, so it will be sent to VRAM.  */
 - (void) releaseContext
 {
-  NSTRACE ("[EmacsSurface releaseContextAndGetSurface]");
+  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer releaseContext]");
 
   if (!context)
     return;
@@ -9737,19 +9619,34 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
   IOReturn lockStatus = IOSurfaceUnlock (currentSurface, 0, nil);
   if (lockStatus != kIOReturnSuccess)
     NSLog (@"Failed to unlock surface: %x", lockStatus);
-
-  /* Put currentSurface back on the end of the cache.  */
-  [cache addObject:(id)currentSurface];
-  lastSurface = currentSurface;
-  currentSurface = NULL;
 }
 
 
-/* Get the IOSurface that we want to draw to the screen.  */
-- (IOSurfaceRef) getSurface
+- (void) display
 {
-  /* lastSurface always contains the most up-to-date and complete data.  */
-  return lastSurface;
+  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer display]");
+
+  if (context)
+    {
+      [self releaseContext];
+
+#if CACHE_MAX_SIZE == 1
+      /* This forces the layer to see the surface as updated.  */
+      [self setContents:nil];
+#endif
+
+      [self setContents:(id)currentSurface];
+
+      /* Put currentSurface back on the end of the cache.  */
+      [cache addObject:(id)currentSurface];
+      currentSurface = NULL;
+
+      /* Schedule a run of getContext so that if Emacs is idle it will
+         perform the buffer copy, etc.  */
+      [self performSelectorOnMainThread:@selector (getContext)
+                             withObject:nil
+                          waitUntilDone:NO];
+    }
 }
 
 
@@ -9759,19 +9656,20 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
 - (void) copyContentsTo: (IOSurfaceRef) destination
 {
   IOReturn lockStatus;
+  IOSurfaceRef source = (IOSurfaceRef)[self contents];
   void *sourceData, *destinationData;
   int numBytes = IOSurfaceGetAllocSize (destination);
 
-  NSTRACE ("[EmacsSurface copyContentsTo:]");
+  NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer copyContentsTo:]");
 
-  if (!lastSurface || lastSurface == destination)
+  if (!source || source == destination)
     return;
 
-  lockStatus = IOSurfaceLock (lastSurface, kIOSurfaceLockReadOnly, nil);
+  lockStatus = IOSurfaceLock (source, kIOSurfaceLockReadOnly, nil);
   if (lockStatus != kIOReturnSuccess)
     NSLog (@"Failed to lock source surface: %x", lockStatus);
 
-  sourceData = IOSurfaceGetBaseAddress (lastSurface);
+  sourceData = IOSurfaceGetBaseAddress (source);
   destinationData = IOSurfaceGetBaseAddress (destination);
 
   /* Since every IOSurface should have the exact same settings, a
@@ -9779,17 +9677,17 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
      the other.  */
   memcpy (destinationData, sourceData, numBytes);
 
-  lockStatus = IOSurfaceUnlock (lastSurface, kIOSurfaceLockReadOnly, nil);
+  lockStatus = IOSurfaceUnlock (source, kIOSurfaceLockReadOnly, nil);
   if (lockStatus != kIOReturnSuccess)
     NSLog (@"Failed to unlock source surface: %x", lockStatus);
 }
 
 #undef CACHE_MAX_SIZE
 
-@end /* EmacsSurface */
+@end /* EmacsLayer */
 
 
-#endif
+#endif /* NS_IMPL_COCOA */
 
 
 #ifdef NS_IMPL_GNUSTEP



reply via email to

[Prev in Thread] Current Thread [Next in Thread]