[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
- branch scratch/ns/refactor created (now 2ee451b), Alan Third, 2021/07/17
- scratch/ns/refactor 40d30ea 03/10: Change NS port resize detection, Alan Third, 2021/07/17
- scratch/ns/refactor 4c10229 05/10: Move NS port toolbar handling to the window, Alan Third, 2021/07/17
- scratch/ns/refactor 87aae7e 09/10: Simplify NS sizing and positioning code, Alan Third, 2021/07/17
- scratch/ns/refactor a159bd0 01/10: Simplify macOS drawing code,
Alan Third <=
- scratch/ns/refactor c319def 02/10: Tidy up NS port OS window handling, Alan Third, 2021/07/17
- scratch/ns/refactor 91b1715 04/10: Fix macOS live resize drawing, Alan Third, 2021/07/17
- scratch/ns/refactor ae88acc 06/10: Move parent frame setting code into EmacsWindow, Alan Third, 2021/07/17
- scratch/ns/refactor d58e217 07/10: Fix some macOS problems, Alan Third, 2021/07/17
- scratch/ns/refactor 2fcd93d 08/10: * src/nsterm.m (ns_set_frame_alpha): Enable alpha on GNUstep., Alan Third, 2021/07/17
- scratch/ns/refactor 2ee451b 10/10: Fix NS inset rectangle corners, Alan Third, 2021/07/17