Improve drawing performance on macOS
* configure.ac: Require IOSurface framework. * src/nsterm.h: New EmacsSurface class and update EmacsView definitions. * src/nsterm.m (ns_update_end): (ns_unfocus): Use new unfocusDrawingBuffer method. (ns_draw_window_cursor): Move ns_focus to before we set colors. ([EmacsView dealloc]): ([EmacsView viewDidResize:]): Handle new EmacsSurface class. ([EmacsView initFrameFromEmacs:]): Remove reference to old method. ([EmacsView createDrawingBuffer]): Remove method. ([EmacsView focusOnDrawingBuffer]): ([EmacsView windowDidChangeBackingProperties:]): Use new EmacsSurface class. ([EmacsView unfocusDrawingBuffer]): New method. ([EmacsView copyRect:to:]): Get information from the context instead of direct from the IOSurface. ([EmacsView updateLayer]): Use new EmacsSurface class. ([EmacsView copyRect:to:]): Use memcpy to copy bits around instead of using NS image functions. ([EmacsSurface initWithSize:ColorSpace:]): ([EmacsSurface dealloc]): ([EmacsSurface getSize]): ([EmacsSurface getContext]): ([EmacsSurface releaseContext]): ([EmacsSurface getSurface]): ([EmacsSurface copyContentsTo:]): New class and methods.
This commit is contained in:
parent
aac17c9dca
commit
107978365e
3 changed files with 298 additions and 78 deletions
|
@ -5496,7 +5496,7 @@ 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"
|
||||
libs_nsgui="$libs_nsgui -framework IOKit -framework Carbon -framework IOSurface"
|
||||
fi
|
||||
else
|
||||
libs_nsgui=
|
||||
|
|
23
src/nsterm.h
23
src/nsterm.h
|
@ -414,6 +414,7 @@ typedef id instancetype;
|
|||
========================================================================== */
|
||||
|
||||
@class EmacsToolbar;
|
||||
@class EmacsSurface;
|
||||
|
||||
#ifdef NS_IMPL_COCOA
|
||||
@interface EmacsView : NSView <NSTextInput, NSWindowDelegate>
|
||||
|
@ -435,7 +436,7 @@ typedef id instancetype;
|
|||
BOOL fs_is_native;
|
||||
BOOL in_fullscreen_transition;
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
CGContextRef drawingBuffer;
|
||||
EmacsSurface *surface;
|
||||
#endif
|
||||
@public
|
||||
struct frame *emacsframe;
|
||||
|
@ -478,7 +479,7 @@ typedef id instancetype;
|
|||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
- (void)focusOnDrawingBuffer;
|
||||
- (void)createDrawingBuffer;
|
||||
- (void)unfocusDrawingBuffer;
|
||||
#endif
|
||||
- (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect;
|
||||
|
||||
|
@ -705,6 +706,24 @@ typedef id instancetype;
|
|||
@end
|
||||
|
||||
|
||||
@interface EmacsSurface : NSObject
|
||||
{
|
||||
NSMutableArray *cache;
|
||||
NSSize size;
|
||||
CGColorSpaceRef colorSpace;
|
||||
IOSurfaceRef currentSurface;
|
||||
IOSurfaceRef lastSurface;
|
||||
CGContextRef context;
|
||||
}
|
||||
- (id) initWithSize: (NSSize)s ColorSpace: (CGColorSpaceRef)cs;
|
||||
- (void) dealloc;
|
||||
- (NSSize) getSize;
|
||||
- (CGContextRef) getContext;
|
||||
- (void) releaseContext;
|
||||
- (IOSurfaceRef) getSurface;
|
||||
@end
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
|
||||
Rendering
|
||||
|
|
351
src/nsterm.m
351
src/nsterm.m
|
@ -72,6 +72,10 @@ Updated by Christian Limpach (chris@nice.ch)
|
|||
#include <Carbon/Carbon.h>
|
||||
#endif
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
#include <IOSurface/IOSurface.h>
|
||||
#endif
|
||||
|
||||
static EmacsMenu *dockMenu;
|
||||
#ifdef NS_IMPL_COCOA
|
||||
static EmacsMenu *mainMenu;
|
||||
|
@ -1147,7 +1151,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
|||
if ([FRAME_NS_VIEW (f) wantsUpdateLayer])
|
||||
{
|
||||
#endif
|
||||
[NSGraphicsContext setCurrentContext:nil];
|
||||
[FRAME_NS_VIEW (f) unfocusDrawingBuffer];
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
|
||||
}
|
||||
else
|
||||
|
@ -1255,6 +1259,8 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen)
|
|||
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
|
||||
}
|
||||
|
@ -3386,6 +3392,8 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
|
|||
/* Prevent the cursor from being drawn outside the text area. */
|
||||
r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA));
|
||||
|
||||
ns_focus (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))
|
||||
|
@ -3396,8 +3404,6 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
|
|||
else
|
||||
[FRAME_CURSOR_COLOR (f) set];
|
||||
|
||||
ns_focus (f, &r, 1);
|
||||
|
||||
switch (cursor_type)
|
||||
{
|
||||
case DEFAULT_CURSOR:
|
||||
|
@ -6267,7 +6273,7 @@ - (void)dealloc
|
|||
object:nil];
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
CGContextRelease (drawingBuffer);
|
||||
[surface release];
|
||||
#endif
|
||||
|
||||
[toolbar release];
|
||||
|
@ -7290,8 +7296,9 @@ - (void)viewDidResize:(NSNotification *)notification
|
|||
if ([self wantsUpdateLayer])
|
||||
{
|
||||
CGFloat scale = [[self window] backingScaleFactor];
|
||||
int oldw = (CGFloat)CGBitmapContextGetWidth (drawingBuffer) / scale;
|
||||
int oldh = (CGFloat)CGBitmapContextGetHeight (drawingBuffer) / scale;
|
||||
NSSize size = [surface getSize];
|
||||
int oldw = size.width / scale;
|
||||
int oldh = size.height / scale;
|
||||
|
||||
NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh));
|
||||
|
||||
|
@ -7301,6 +7308,9 @@ - (void)viewDidResize:(NSNotification *)notification
|
|||
NSTRACE_MSG ("No change");
|
||||
return;
|
||||
}
|
||||
|
||||
[surface release];
|
||||
surface = nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -7313,9 +7323,6 @@ - (void)viewDidResize:(NSNotification *)notification
|
|||
FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
|
||||
0, YES, 0, 1);
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
[self createDrawingBuffer];
|
||||
#endif
|
||||
SET_FRAME_GARBAGED (emacsframe);
|
||||
cancel_mouse_face (emacsframe);
|
||||
}
|
||||
|
@ -7586,10 +7593,6 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
|
|||
[NSApp registerServicesMenuSendTypes: ns_send_types
|
||||
returnTypes: [NSArray array]];
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
[self createDrawingBuffer];
|
||||
#endif
|
||||
|
||||
/* Set up view resize notifications. */
|
||||
[self setPostsFrameChangedNotifications:YES];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
|
@ -8309,45 +8312,41 @@ - (instancetype)toggleToolbar: (id)sender
|
|||
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
- (void)createDrawingBuffer
|
||||
/* Create and store a new CGGraphicsContext for Emacs to draw into.
|
||||
|
||||
We can't do this in GNUstep as there's no equivalent, so under
|
||||
GNUstep we retain the old method of drawing direct to the
|
||||
EmacsView. */
|
||||
- (void)focusOnDrawingBuffer
|
||||
{
|
||||
NSTRACE ("EmacsView createDrawingBuffer]");
|
||||
|
||||
if (! [self wantsUpdateLayer])
|
||||
return;
|
||||
|
||||
NSGraphicsContext *screen;
|
||||
CGColorSpaceRef colorSpace = [[[self window] colorSpace] CGColorSpace];
|
||||
CGFloat scale = [[self window] backingScaleFactor];
|
||||
NSRect frame = [self frame];
|
||||
|
||||
if (drawingBuffer != nil)
|
||||
CGContextRelease (drawingBuffer);
|
||||
NSTRACE ("[EmacsView focusOnDrawingBuffer]");
|
||||
|
||||
drawingBuffer = CGBitmapContextCreate (nil, NSWidth (frame) * scale, NSHeight (frame) * scale,
|
||||
8, 0, colorSpace,
|
||||
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
|
||||
if (! surface)
|
||||
{
|
||||
NSRect frame = [self frame];
|
||||
NSSize s = NSMakeSize (NSWidth (frame) * scale, NSHeight (frame) * scale);
|
||||
|
||||
/* This fixes the scale to match the backing scale factor, and flips the image. */
|
||||
CGContextTranslateCTM(drawingBuffer, 0, NSHeight (frame) * scale);
|
||||
CGContextScaleCTM(drawingBuffer, scale, -scale);
|
||||
surface = [[EmacsSurface alloc] initWithSize:s
|
||||
ColorSpace:[[[self window] colorSpace]
|
||||
CGColorSpace]];
|
||||
}
|
||||
|
||||
CGContextRef context = [surface getContext];
|
||||
|
||||
CGContextTranslateCTM(context, 0, [surface getSize].height);
|
||||
CGContextScaleCTM(context, scale, -scale);
|
||||
|
||||
[NSGraphicsContext
|
||||
setCurrentContext:[NSGraphicsContext
|
||||
graphicsContextWithCGContext:context
|
||||
flipped:YES]];
|
||||
}
|
||||
|
||||
|
||||
- (void)focusOnDrawingBuffer
|
||||
- (void)unfocusDrawingBuffer
|
||||
{
|
||||
NSTRACE ("EmacsView focusOnDrawingBuffer]");
|
||||
NSTRACE ("[EmacsView unfocusDrawingBuffer]");
|
||||
|
||||
NSGraphicsContext *buf =
|
||||
[NSGraphicsContext
|
||||
graphicsContextWithCGContext:drawingBuffer flipped:YES];
|
||||
|
||||
[NSGraphicsContext setCurrentContext:buf];
|
||||
[NSGraphicsContext setCurrentContext:nil];
|
||||
[surface releaseContext];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
|
||||
|
@ -8356,11 +8355,11 @@ - (void)windowDidChangeBackingProperties:(NSNotification *)notification
|
|||
{
|
||||
NSTRACE ("EmacsView windowDidChangeBackingProperties:]");
|
||||
|
||||
if (! [self wantsUpdateLayer])
|
||||
return;
|
||||
|
||||
NSRect frame = [self frame];
|
||||
[self createDrawingBuffer];
|
||||
|
||||
[surface release];
|
||||
surface = nil;
|
||||
|
||||
ns_clear_frame (emacsframe);
|
||||
expose_frame (emacsframe, 0, 0, NSWidth (frame), NSHeight (frame));
|
||||
}
|
||||
|
@ -8378,33 +8377,28 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect
|
|||
if ([self wantsUpdateLayer])
|
||||
{
|
||||
#endif
|
||||
CGImageRef copy;
|
||||
NSRect frame = [self frame];
|
||||
NSAffineTransform *setOrigin = [NSAffineTransform transform];
|
||||
double scale = [[self window] backingScaleFactor];
|
||||
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
|
||||
int bpp = CGBitmapContextGetBitsPerPixel (context) / 8;
|
||||
void *pixels = CGBitmapContextGetData (context);
|
||||
int rowSize = CGBitmapContextGetBytesPerRow (context);
|
||||
int srcRowSize = NSWidth (srcRect) * scale * bpp;
|
||||
void *srcPixels = pixels + (int)(NSMinY (srcRect) * scale * rowSize
|
||||
+ NSMinX (srcRect) * scale * bpp);
|
||||
void *dstPixels = pixels + (int)(NSMinY (dstRect) * scale * rowSize
|
||||
+ NSMinX (dstRect) * scale * bpp);
|
||||
|
||||
[[NSGraphicsContext currentContext] saveGraphicsState];
|
||||
|
||||
/* Set the clipping before messing with the buffer's
|
||||
orientation. */
|
||||
NSRectClip (dstRect);
|
||||
|
||||
/* Unflip the buffer as the copied image will be unflipped, and
|
||||
offset the top left so when we draw back into the buffer the
|
||||
correct part of the image is drawn. */
|
||||
CGContextScaleCTM(drawingBuffer, 1, -1);
|
||||
CGContextTranslateCTM(drawingBuffer,
|
||||
NSMinX (dstRect) - NSMinX (srcRect),
|
||||
-NSHeight (frame) - (NSMinY (dstRect) - NSMinY (srcRect)));
|
||||
|
||||
/* Take a copy of the buffer and then draw it back to the buffer,
|
||||
limited by the clipping rectangle. */
|
||||
copy = CGBitmapContextCreateImage (drawingBuffer);
|
||||
CGContextDrawImage (drawingBuffer, frame, copy);
|
||||
|
||||
CGImageRelease (copy);
|
||||
|
||||
[[NSGraphicsContext currentContext] restoreGraphicsState];
|
||||
[self setNeedsDisplayInRect:dstRect];
|
||||
if (NSIntersectsRect (srcRect, dstRect)
|
||||
&& NSMinY (srcRect) < NSMinY (dstRect))
|
||||
for (int y = NSHeight (srcRect) * scale - 1 ; y >= 0 ; y--)
|
||||
memmove (dstPixels + y * rowSize,
|
||||
srcPixels + y * rowSize,
|
||||
srcRowSize);
|
||||
else
|
||||
for (int y = 0 ; y < NSHeight (srcRect) * scale ; y++)
|
||||
memmove (dstPixels + y * rowSize,
|
||||
srcPixels + y * rowSize,
|
||||
srcRowSize);
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400
|
||||
}
|
||||
|
@ -8445,9 +8439,12 @@ - (void)updateLayer
|
|||
{
|
||||
NSTRACE ("[EmacsView updateLayer]");
|
||||
|
||||
CGImageRef contentsImage = CGBitmapContextCreateImage(drawingBuffer);
|
||||
[[self layer] setContents:(id)contentsImage];
|
||||
CGImageRelease(contentsImage);
|
||||
/* 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. */
|
||||
[[self layer] setContents:(id)[surface getSurface]];
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -9490,6 +9487,210 @@ - (void) scrollWheel: (NSEvent *)theEvent
|
|||
@end /* EmacsScroller */
|
||||
|
||||
|
||||
#ifdef NS_DRAW_TO_BUFFER
|
||||
|
||||
/* ==========================================================================
|
||||
|
||||
A class to handle the screen buffer.
|
||||
|
||||
========================================================================== */
|
||||
|
||||
@implementation EmacsSurface
|
||||
|
||||
|
||||
/* An IOSurface is a pixel buffer that is efficiently copied to VRAM
|
||||
for display. In order to use an IOSurface we must first lock it,
|
||||
write to it, then unlock it. At this point it is transferred to
|
||||
VRAM and if we modify it during this transfer we may see corruption
|
||||
of the output. To avoid this problem we can check if the surface
|
||||
is "in use", and if it is then avoid using it. Unfortunately to
|
||||
avoid writing to a surface that's in use, but still maintain the
|
||||
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
|
||||
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. */
|
||||
|
||||
|
||||
- (id) initWithSize: (NSSize)s
|
||||
ColorSpace: (CGColorSpaceRef)cs
|
||||
{
|
||||
NSTRACE ("[EmacsSurface initWithSize:ColorSpace:]");
|
||||
|
||||
[super init];
|
||||
|
||||
cache = [[NSMutableArray arrayWithCapacity:3] retain];
|
||||
size = s;
|
||||
colorSpace = cs;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
if (context)
|
||||
CGContextRelease (context);
|
||||
|
||||
if (currentSurface)
|
||||
CFRelease (currentSurface);
|
||||
if (lastSurface)
|
||||
CFRelease (lastSurface);
|
||||
|
||||
for (id object in cache)
|
||||
CFRelease ((IOSurfaceRef)object);
|
||||
|
||||
[cache removeAllObjects];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
||||
/* Return the size values our cached data is using. */
|
||||
- (NSSize) getSize
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/* 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. */
|
||||
- (CGContextRef) getContext
|
||||
{
|
||||
IOSurfaceRef surface = NULL;
|
||||
|
||||
NSTRACE ("[EmacsSurface getContextWithSize:]");
|
||||
NSTRACE_MSG (@"IOSurface count: %lu", [cache count] + (lastSurface ? 1 : 0));
|
||||
|
||||
for (id object in cache)
|
||||
{
|
||||
if (!IOSurfaceIsInUse ((IOSurfaceRef)object))
|
||||
{
|
||||
surface = (IOSurfaceRef)object;
|
||||
[cache removeObject:object];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!surface)
|
||||
{
|
||||
int bytesPerRow = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow,
|
||||
size.width * 4);
|
||||
|
||||
surface = IOSurfaceCreate
|
||||
((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber numberWithInt:size.width],
|
||||
(id)kIOSurfaceHeight:[NSNumber numberWithInt:size.height],
|
||||
(id)kIOSurfaceBytesPerRow:[NSNumber numberWithInt:bytesPerRow],
|
||||
(id)kIOSurfaceBytesPerElement:[NSNumber numberWithInt:4],
|
||||
(id)kIOSurfacePixelFormat:[NSNumber numberWithUnsignedInt:'BGRA']});
|
||||
}
|
||||
|
||||
IOReturn lockStatus = IOSurfaceLock (surface, 0, nil);
|
||||
if (lockStatus != kIOReturnSuccess)
|
||||
NSLog (@"Failed to lock surface: %x", lockStatus);
|
||||
|
||||
[self copyContentsTo:surface];
|
||||
|
||||
currentSurface = surface;
|
||||
|
||||
context = CGBitmapContextCreate (IOSurfaceGetBaseAddress (currentSurface),
|
||||
IOSurfaceGetWidth (currentSurface),
|
||||
IOSurfaceGetHeight (currentSurface),
|
||||
8,
|
||||
IOSurfaceGetBytesPerRow (currentSurface),
|
||||
colorSpace,
|
||||
(kCGImageAlphaPremultipliedFirst
|
||||
| kCGBitmapByteOrder32Host));
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
/* Releases the CGGraphicsContext and unlocks the associated
|
||||
IOSurface, so it will be sent to VRAM. */
|
||||
- (void) releaseContext
|
||||
{
|
||||
NSTRACE ("[EmacsSurface releaseContextAndGetSurface]");
|
||||
|
||||
CGContextRelease (context);
|
||||
context = NULL;
|
||||
|
||||
IOReturn lockStatus = IOSurfaceUnlock (currentSurface, 0, nil);
|
||||
if (lockStatus != kIOReturnSuccess)
|
||||
NSLog (@"Failed to unlock surface: %x", lockStatus);
|
||||
|
||||
/* Put lastSurface back on the end of the cache. It may not have
|
||||
been displayed on the screen yet, but we probably want the new
|
||||
data and not some stale data anyway. */
|
||||
if (lastSurface)
|
||||
[cache addObject:(id)lastSurface];
|
||||
lastSurface = currentSurface;
|
||||
currentSurface = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Get the IOSurface that we want to draw to the screen. */
|
||||
- (IOSurfaceRef) getSurface
|
||||
{
|
||||
/* lastSurface always contains the most up-to-date and complete data. */
|
||||
return lastSurface;
|
||||
}
|
||||
|
||||
|
||||
/* Copy the contents of lastSurface to DESTINATION. This is required
|
||||
every time we want to use an IOSurface as its contents are probably
|
||||
blanks (if it's new), or stale. */
|
||||
- (void) copyContentsTo: (IOSurfaceRef) destination
|
||||
{
|
||||
IOReturn lockStatus;
|
||||
void *sourceData, *destinationData;
|
||||
int numBytes = IOSurfaceGetAllocSize (destination);
|
||||
|
||||
NSTRACE ("[EmacsSurface copyContentsTo:]");
|
||||
|
||||
if (! lastSurface)
|
||||
return;
|
||||
|
||||
lockStatus = IOSurfaceLock (lastSurface, kIOSurfaceLockReadOnly, nil);
|
||||
if (lockStatus != kIOReturnSuccess)
|
||||
NSLog (@"Failed to lock source surface: %x", lockStatus);
|
||||
|
||||
sourceData = IOSurfaceGetBaseAddress (lastSurface);
|
||||
destinationData = IOSurfaceGetBaseAddress (destination);
|
||||
|
||||
/* Since every IOSurface should have the exact same settings, a
|
||||
memcpy seems like the fastest way to copy the data from one to
|
||||
the other. */
|
||||
memcpy (destinationData, sourceData, numBytes);
|
||||
|
||||
lockStatus = IOSurfaceUnlock (lastSurface, kIOSurfaceLockReadOnly, nil);
|
||||
if (lockStatus != kIOReturnSuccess)
|
||||
NSLog (@"Failed to unlock source surface: %x", lockStatus);
|
||||
}
|
||||
|
||||
|
||||
@end /* EmacsSurface */
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef NS_IMPL_GNUSTEP
|
||||
/* Dummy class to get rid of startup warnings. */
|
||||
@implementation EmacsDocument
|
||||
|
|
Loading…
Add table
Reference in a new issue