@@ -132,6 +132,10 @@ - (id)initWithFrame:(NSRect)frame
132
132
{
133
133
if (!(self = [super initWithFrame: frame]))
134
134
return nil ;
135
+
136
+ cgBufferDrawEnabled = [[NSUserDefaults standardUserDefaults ]
137
+ boolForKey: MMBufferedDrawingKey];
138
+ cgBufferDrawNeedsUpdateContext = NO ;
135
139
136
140
cgLayerEnabled = [[NSUserDefaults standardUserDefaults ]
137
141
boolForKey: MMUseCGLayerAlwaysKey];
@@ -168,6 +172,8 @@ - (void)dealloc
168
172
[drawData release ]; drawData = nil ;
169
173
[fontCache release ]; fontCache = nil ;
170
174
175
+ CGContextRelease (cgContext); cgContext = nil ;
176
+
171
177
[helper setTextView: nil ];
172
178
[helper release ]; helper = nil ;
173
179
@@ -215,6 +221,7 @@ - (void)setDefaultColorsBackground:(NSColor *)bgColor
215
221
[defaultForegroundColor release ];
216
222
defaultForegroundColor = fgColor ? [fgColor retain ] : nil ;
217
223
}
224
+ [self setNeedsDisplay: YES ];
218
225
}
219
226
220
227
- (NSColor *)defaultBackgroundColor
@@ -444,13 +451,33 @@ - (BOOL)_wantsKeyDownForEvent:(id)event
444
451
}
445
452
446
453
- (void )setFrameSize : (NSSize )newSize {
447
- if (!drawPending && !NSEqualSizes (newSize, self.frame .size ) && drawData.count == 0 ) {
448
- [NSAnimationContext beginGrouping ];
449
- drawPending = YES ;
454
+ if (!NSEqualSizes (newSize, self.bounds .size )) {
455
+ if (!drawPending && !cgBufferDrawEnabled) {
456
+ // When resizing a window, it will invalidate the buffer and cause
457
+ // MacVim to draw black until we get the draw commands from Vim and
458
+ // we draw them out in drawRect. Use beginGrouping to stop the
459
+ // window resize from happening until we get the draw calls.
460
+ //
461
+ // The updateLayer/cgBufferDrawEnabled path handles this differently
462
+ // and don't need this.
463
+ [NSAnimationContext beginGrouping ];
464
+ drawPending = YES ;
465
+ }
466
+ if (cgBufferDrawEnabled) {
467
+ cgBufferDrawNeedsUpdateContext = YES ;
468
+ }
450
469
}
470
+
451
471
[super setFrameSize: newSize];
452
472
}
453
473
474
+ - (void )viewDidChangeBackingProperties {
475
+ if (cgBufferDrawEnabled) {
476
+ cgBufferDrawNeedsUpdateContext = YES ;
477
+ }
478
+ [super viewDidChangeBackingProperties ];
479
+ }
480
+
454
481
- (void )keyDown : (NSEvent *)event
455
482
{
456
483
[helper keyDown: event];
@@ -606,6 +633,87 @@ - (BOOL)isFlipped
606
633
return NO ;
607
634
}
608
635
636
+ - (void )updateCGContext {
637
+ if (cgContext) {
638
+ CGContextRelease (cgContext);
639
+ cgContext = nil ;
640
+ }
641
+
642
+ NSRect backingRect = [self convertRectToBacking: self .bounds];
643
+ cgContext = CGBitmapContextCreate (NULL , NSWidth (backingRect), NSHeight (backingRect), 8 , 0 , self.window .colorSpace .CGColorSpace , kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host );
644
+ CGContextScaleCTM (cgContext, self.window .backingScaleFactor , self.window .backingScaleFactor );
645
+
646
+ cgBufferDrawNeedsUpdateContext = NO ;
647
+ }
648
+
649
+ - (BOOL )wantsUpdateLayer {
650
+ return cgBufferDrawEnabled;
651
+ }
652
+
653
+ - (void )updateLayer {
654
+ if (!cgContext) {
655
+ [self updateCGContext ];
656
+ } else if (cgBufferDrawNeedsUpdateContext) {
657
+ if ([drawData count ] != 0 ) {
658
+ [self updateCGContext ];
659
+ } else {
660
+ // In this case, we don't have a single draw command, meaning that
661
+ // Vim hasn't caught up yet and hasn't issued draw commands. We
662
+ // don't want to use [NSAnimationContext beginGrouping] as it's
663
+ // fragile (we may miss the endGrouping call due to order of
664
+ // operation), and also it makes the animation jerky.
665
+ // Instead, copy the image to the new context and align it to the
666
+ // top left and make sure it doesn't stretch. This makes the
667
+ // resizing smooth while Vim tries to catch up in issuing draws.
668
+ CGImageRef oldImage = CGBitmapContextCreateImage (cgContext);
669
+
670
+ [self updateCGContext ]; // This will make a new cgContext
671
+
672
+ CGContextSaveGState (cgContext);
673
+ CGContextSetBlendMode (cgContext, kCGBlendModeCopy );
674
+
675
+ // Filling the background so the edge won't be black.
676
+ NSRect newRect = [self bounds ];
677
+ float r = [defaultBackgroundColor redComponent ];
678
+ float g = [defaultBackgroundColor greenComponent ];
679
+ float b = [defaultBackgroundColor blueComponent ];
680
+ float a = [defaultBackgroundColor alphaComponent ];
681
+ CGContextSetRGBFillColor (cgContext, r, g, b, a);
682
+ CGContextFillRect (cgContext, *(CGRect*)&newRect);
683
+ CGContextSetBlendMode (cgContext, kCGBlendModeNormal );
684
+
685
+ // Copy the old image over to the new image, and make sure to
686
+ // respect scaling and remember that CGImage's Y origin is
687
+ // bottom-left.
688
+ CGFloat scale = self.window .backingScaleFactor ;
689
+ size_t oldWidth = CGImageGetWidth (oldImage) / scale;
690
+ size_t oldHeight = CGImageGetHeight (oldImage) / scale;
691
+ CGFloat newHeight = newRect.size .height ;
692
+ NSRect imageRect = NSMakeRect (0 , newHeight - oldHeight, (CGFloat)oldWidth, (CGFloat)oldHeight);
693
+
694
+ CGContextDrawImage (cgContext, imageRect, oldImage);
695
+ CGImageRelease (oldImage);
696
+ CGContextRestoreGState (cgContext);
697
+ }
698
+ }
699
+
700
+ // Now issue the batched draw commands
701
+ if ([drawData count ] != 0 ) {
702
+ [NSGraphicsContext saveGraphicsState ];
703
+ NSGraphicsContext .currentContext = [NSGraphicsContext graphicsContextWithCGContext: cgContext flipped: self .flipped];
704
+ id data;
705
+ NSEnumerator *e = [drawData objectEnumerator ];
706
+ while ((data = [e nextObject ]))
707
+ [self batchDrawData: data];
708
+ [drawData removeAllObjects ];
709
+ [NSGraphicsContext restoreGraphicsState ];
710
+ }
711
+
712
+ CGImageRef contentsImage = CGBitmapContextCreateImage (cgContext);
713
+ self.layer .contents = (id )contentsImage;
714
+ CGImageRelease (contentsImage);
715
+ }
716
+
609
717
- (void )drawRect : (NSRect )rect
610
718
{
611
719
NSGraphicsContext *context = [NSGraphicsContext currentContext ];
@@ -653,7 +761,25 @@ - (void)drawRect:(NSRect)rect
653
761
654
762
- (void )performBatchDrawWithData : (NSData *)data
655
763
{
656
- if (cgLayerEnabled && drawData.count == 0 && [self getCGContext ]) {
764
+ if (cgBufferDrawEnabled) {
765
+ // We batch up all the commands and actually perform the draw at
766
+ // updateLayer. The reason is that right now MacVim has a lot of
767
+ // different paths that could change the view size (zoom, user resizing
768
+ // from either dragging border or another program, Cmd-+/- to change
769
+ // font size, fullscreen, etc). Those different paths don't currently
770
+ // have a consistent order of operation of (Vim or MacVim go first), so
771
+ // sometimes Vim gets updated and issue a batch draw first, but
772
+ // sometimes MacVim gets notified first (e.g. when window is resized).
773
+ // If frame size has changed we need to call updateCGContext but we
774
+ // can't do it here because of the order of operation issue. That's why
775
+ // we wait till updateLayer to do it where everything has already been
776
+ // done and settled.
777
+ //
778
+ // Note: Should probably refactor the different ways window size could
779
+ // be changed and unify them instead of the status quo of spaghetti.
780
+ [drawData addObject: data];
781
+ [self setNeedsDisplay: YES ];
782
+ } else if (cgLayerEnabled && drawData.count == 0 && [self getCGContext ]) {
657
783
[cgLayerLock lock ];
658
784
[self batchDrawData: data];
659
785
[cgLayerLock unlock ];
@@ -669,6 +795,9 @@ - (void)performBatchDrawWithData:(NSData *)data
669
795
670
796
- (void )setCGLayerEnabled : (BOOL )enabled
671
797
{
798
+ if (cgContext)
799
+ return ;
800
+
672
801
cgLayerEnabled = enabled;
673
802
674
803
if (!cgLayerEnabled)
@@ -1491,7 +1620,18 @@ - (void)drawString:(const UniChar *)chars length:(UniCharCount)length
1491
1620
1492
1621
- (void )scrollRect : (NSRect )rect lineCount : (int )count
1493
1622
{
1494
- if (cgLayerEnabled) {
1623
+ if (cgContext) {
1624
+ NSRect fromRect = NSOffsetRect (self.bounds , 0 , -count * cellSize.height );
1625
+ NSRect toRect = NSOffsetRect (rect, 0 , -count * cellSize.height );
1626
+ CGContextSaveGState (cgContext);
1627
+ CGContextClipToRect (cgContext, toRect);
1628
+ CGContextSetBlendMode (cgContext, kCGBlendModeCopy );
1629
+ CGImageRef contentsImage = CGBitmapContextCreateImage (cgContext);
1630
+ CGContextDrawImage (cgContext, fromRect, contentsImage);
1631
+ CGImageRelease (contentsImage);
1632
+ CGContextRestoreGState (cgContext);
1633
+ [self setNeedsDisplayCGLayerInRect: toRect];
1634
+ } else if (cgLayerEnabled) {
1495
1635
CGContextRef context = [self getCGContext ];
1496
1636
int yOffset = count * cellSize.height ;
1497
1637
NSRect clipRect = rect;
0 commit comments