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.
This commit is contained in:
parent
0992335d12
commit
a4d2c88cde
3 changed files with 213 additions and 333 deletions
|
@ -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=
|
||||
|
|
35
src/nsterm.h
35
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;
|
||||
|
||||
|
@ -714,23 +701,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
|
||||
|
||||
|
|
508
src/nsterm.m
508
src/nsterm.m
|
@ -70,9 +70,6 @@ Updated by Christian Limpach (chris@nice.ch)
|
|||
#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 @@ - (NSColor *)colorUsingDefaultColorSpace
|
|||
|
||||
/* 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 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
|||
#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 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
|||
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 */
|
||||
block_input ();
|
||||
|
||||
#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400
|
||||
block_input ();
|
||||
|
||||
[view unlockFocus];
|
||||
[[view window] flushWindow];
|
||||
|
||||
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 @@ 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)
|
||||
{
|
||||
|
@ -1126,39 +1081,10 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
|||
|
||||
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 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
|||
gsaved = NO;
|
||||
}
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
|
||||
if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
|
||||
if (f != ns_updating_frame)
|
||||
{
|
||||
EmacsView *view = FRAME_NS_VIEW (f);
|
||||
[view unlockFocus];
|
||||
#if defined (NS_IMPL_GNUSTEP)
|
||||
[[view window] flushWindow];
|
||||
#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
|
||||
{
|
||||
#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 @@ -(void)remove
|
|||
}
|
||||
}
|
||||
|
||||
#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)
|
||||
/* --------------------------------------------------------------------------
|
||||
|
@ -6178,10 +6083,6 @@ - (void)dealloc
|
|||
name:NSViewFrameDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
[surface release];
|
||||
#endif
|
||||
|
||||
[toolbar release];
|
||||
if (fs_state == FULLSCREEN_BOTH)
|
||||
[nonfs_window release];
|
||||
|
@ -7192,24 +7093,6 @@ - (void)viewDidResize:(NSNotification *)notification
|
|||
|
||||
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);
|
||||
|
@ -7388,16 +7271,6 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
|
|||
[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
|
||||
|
@ -7437,6 +7310,17 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
|
|||
|
||||
[[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];
|
||||
|
||||
|
@ -8201,44 +8085,54 @@ - (instancetype)toggleToolbar: (id)sender
|
|||
}
|
||||
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
- (void)focusOnDrawingBuffer
|
||||
#ifdef NS_IMPL_COCOA
|
||||
- (CALayer *)makeBackingLayer;
|
||||
{
|
||||
CGFloat scale = [[self window] backingScaleFactor];
|
||||
EmacsLayer *l = [[EmacsLayer alloc]
|
||||
initWithColorSpace:[[[self window] colorSpace] CGColorSpace]];
|
||||
[l setDelegate:(id)self];
|
||||
[l setContentsScale:[[self window] backingScaleFactor]];
|
||||
|
||||
NSTRACE ("[EmacsView focusOnDrawingBuffer]");
|
||||
|
||||
if (! surface)
|
||||
{
|
||||
NSRect frame = [self frame];
|
||||
NSSize s = NSMakeSize (NSWidth (frame) * scale, NSHeight (frame) * scale);
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
CGContextRef context = [surface getContext];
|
||||
|
||||
[NSGraphicsContext
|
||||
setCurrentContext:[NSGraphicsContext
|
||||
graphicsContextWithCGContext:context
|
||||
flipped:YES]];
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
- (void)unfocusDrawingBuffer
|
||||
- (void)lockFocus
|
||||
{
|
||||
NSTRACE ("[EmacsView unfocusDrawingBuffer]");
|
||||
NSTRACE ("[EmacsView lockFocus]");
|
||||
|
||||
[NSGraphicsContext setCurrentContext:nil];
|
||||
[self setNeedsDisplay:YES];
|
||||
if ([self wantsLayer])
|
||||
{
|
||||
CGContextRef context = [(EmacsLayer*)[self layer] getContext];
|
||||
|
||||
[NSGraphicsContext
|
||||
setCurrentContext:[NSGraphicsContext
|
||||
graphicsContextWithCGContext:context
|
||||
flipped:YES]];
|
||||
}
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
|
||||
else
|
||||
[super lockFocus];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
- (void)unlockFocus
|
||||
{
|
||||
NSTRACE ("[EmacsView unlockFocus]");
|
||||
|
||||
if ([self wantsLayer])
|
||||
{
|
||||
[NSGraphicsContext setCurrentContext:nil];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
|
||||
else
|
||||
{
|
||||
[super unlockFocus];
|
||||
[super flushWindow];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -8247,18 +8141,19 @@ - (void)windowDidChangeBackingProperties:(NSNotification *)notification
|
|||
{
|
||||
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
|
||||
|
@ -8267,11 +8162,9 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect
|
|||
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;
|
||||
|
@ -8297,14 +8190,14 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect
|
|||
(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);
|
||||
|
@ -8313,77 +8206,40 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect
|
|||
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
|
||||
redisplay before drawing.
|
||||
|
||||
This used to be done in viewWillDraw, but with the custom layer
|
||||
that method is not called. */
|
||||
- (void)layout
|
||||
{
|
||||
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 ();
|
||||
[super layout];
|
||||
|
||||
redisplay ();
|
||||
/* 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 ();
|
||||
|
||||
unblock_input ();
|
||||
waiting_for_input = owfi;
|
||||
}
|
||||
}
|
||||
redisplay ();
|
||||
|
||||
|
||||
- (BOOL)wantsUpdateLayer
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
|
||||
if (NSAppKitVersionNumber < 1671)
|
||||
return NO;
|
||||
#endif
|
||||
|
||||
/* Running on macOS 10.14 or above. */
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (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;
|
||||
|
||||
/* 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 "]",
|
||||
|
@ -9550,7 +9406,7 @@ - (void) scrollWheel: (NSEvent *)theEvent
|
|||
@end /* EmacsScroller */
|
||||
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
#ifdef NS_IMPL_COCOA
|
||||
|
||||
/* ==========================================================================
|
||||
|
||||
|
@ -9558,7 +9414,7 @@ - (void) scrollWheel: (NSEvent *)theEvent
|
|||
|
||||
========================================================================== */
|
||||
|
||||
@implementation EmacsSurface
|
||||
@implementation EmacsLayer
|
||||
|
||||
|
||||
/* An IOSurface is a pixel buffer that is efficiently copied to VRAM
|
||||
|
@ -9571,80 +9427,106 @@ @implementation EmacsSurface
|
|||
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:]");
|
||||
NSTRACE ("[EmacsLayer initWithColorSpace:]");
|
||||
|
||||
[super init];
|
||||
|
||||
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) setColorSpace: (CGColorSpaceRef)cs
|
||||
{
|
||||
/* We don't need to clear the cache because the new colorspace will
|
||||
be used next time we create a new context. */
|
||||
colorSpace = cs;
|
||||
}
|
||||
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
if (context)
|
||||
CGContextRelease (context);
|
||||
|
||||
if (currentSurface)
|
||||
CFRelease (currentSurface);
|
||||
|
||||
for (id object in cache)
|
||||
CFRelease ((IOSurfaceRef)object);
|
||||
|
||||
[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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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. */
|
||||
/* 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. */
|
||||
- (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)
|
||||
{
|
||||
|
@ -9667,11 +9549,11 @@ - (CGContextRef) getContext
|
|||
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']});
|
||||
|
@ -9694,7 +9576,7 @@ - (CGContextRef) getContext
|
|||
(kCGImageAlphaPremultipliedFirst
|
||||
| kCGBitmapByteOrder32Host));
|
||||
|
||||
CGContextTranslateCTM(context, 0, size.height);
|
||||
CGContextTranslateCTM(context, 0, IOSurfaceGetHeight (currentSurface));
|
||||
CGContextScaleCTM(context, scale, -scale);
|
||||
}
|
||||
|
||||
|
@ -9706,7 +9588,7 @@ - (CGContextRef) getContext
|
|||
IOSurface, so it will be sent to VRAM. */
|
||||
- (void) releaseContext
|
||||
{
|
||||
NSTRACE ("[EmacsSurface releaseContextAndGetSurface]");
|
||||
NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer releaseContext]");
|
||||
|
||||
if (!context)
|
||||
return;
|
||||
|
@ -9717,19 +9599,34 @@ - (void) releaseContext
|
|||
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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -9739,19 +9636,20 @@ - (IOSurfaceRef) getSurface
|
|||
- (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
|
||||
|
@ -9759,17 +9657,17 @@ - (void) copyContentsTo: (IOSurfaceRef) destination
|
|||
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
|
||||
|
|
Loading…
Add table
Reference in a new issue