diff --git a/MovingAverage.h b/MovingAverage.h index d1f3e7938f..24c77d5fbb 100644 --- a/MovingAverage.h +++ b/MovingAverage.h @@ -11,13 +11,16 @@ @interface MovingAverage : NSObject { double _alpha; double _value; - NSTimeInterval _time; + NSTimeInterval _time; // Time when -startTimer was called, or 0 if stopped. + NSTimeInterval _timePaused; // Time at which -pauseTimer was called. } -@property (nonatomic, assign) double alpha; // Initialized to 0.5. +@property (nonatomic, assign) double alpha; // Initialized to 0.5. Small values make updates affect the moving average more. @property (nonatomic, assign) double value; - (void)startTimer; +- (void)pauseTimer; +- (void)resumeTimer; - (NSTimeInterval)timeSinceTimerStarted; - (void)addValue:(double)value; - (BOOL)haveStartedTimer; diff --git a/MovingAverage.m b/MovingAverage.m index 9df43f2da8..be87f9b3e8 100644 --- a/MovingAverage.m +++ b/MovingAverage.m @@ -23,10 +23,15 @@ - (id)init { - (void)startTimer { _time = [NSDate timeIntervalSinceReferenceDate]; + _timePaused = 0; } - (NSTimeInterval)timeSinceTimerStarted { - return [NSDate timeIntervalSinceReferenceDate] - _time; + if (_timePaused) { + return _timePaused - _time; + } else { + return [NSDate timeIntervalSinceReferenceDate] - _time; + } } - (void)addValue:(double)value { @@ -37,4 +42,18 @@ - (BOOL)haveStartedTimer { return _time > 0; } +- (void)pauseTimer { + assert([self haveStartedTimer]); + assert(_timePaused == 0); + _timePaused = [NSDate timeIntervalSinceReferenceDate]; +} + +- (void)resumeTimer { + assert(_timePaused > 0); + assert(_time > 0); + NSTimeInterval lengthOfPreviousRun = _timePaused - _time; + _time = [NSDate timeIntervalSinceReferenceDate] - lengthOfPreviousRun; + _timePaused = 0; +} + @end diff --git a/PTYTextView.h b/PTYTextView.h index 2c75b26e6d..255dd75a93 100644 --- a/PTYTextView.h +++ b/PTYTextView.h @@ -640,7 +640,11 @@ typedef enum { - (NSString *)_getURLForX:(int)x y:(int)y; // Returns true if any char in the line is blinking. -- (BOOL)_drawLine:(int)line AtY:(double)curY toPoint:(NSPoint*)toPoint; +- (BOOL)_drawLine:(int)line + AtY:(double)curY + toPoint:(NSPoint*)toPoint + charRange:(NSRange)charRange; + - (void)_drawCursor; - (void)_drawCursorTo:(NSPoint*)toOrigin; - (void)_drawCharacter:(screen_char_t)screenChar diff --git a/PTYTextView.m b/PTYTextView.m index 23674055b4..ac0fdb5d04 100644 --- a/PTYTextView.m +++ b/PTYTextView.m @@ -569,18 +569,21 @@ - (void)setBlinkAllowed:(BOOL)value blinkAllowed_ = value; } -- (void)markCursorAsDirty { +- (void)setCursorNeedsDisplay { + int lineStart = [dataSource numberOfLines] - [dataSource height]; int cursorX = [dataSource cursorX] - 1; int cursorY = [dataSource cursorY] - 1; - // Set a different bit for the cursor's dirty position because we don't - // want to save an instant replay from when only the cursor is dirty. - [dataSource setCharDirtyAtX:cursorX Y:cursorY value:2]; + NSRect dirtyRect = NSMakeRect(MARGIN + cursorX * charWidth, + (lineStart + cursorY) * lineHeight, + charWidth, + lineHeight); + [self setNeedsDisplayInRect:dirtyRect]; } - (void)setCursorType:(ITermCursorType)value { cursorType_ = value; - [self markCursorAsDirty]; + [self setCursorNeedsDisplay]; [self refresh]; } @@ -1849,7 +1852,6 @@ - (void)drawRect:(NSRect)rect // If there are two or more rects that need display, the OS will pass in |rect| as the smallest // bounding rect that contains them all. Luckily, we can get the list of the "real" dirty rects // and they're guaranteed to be disjoint. So draw each of them individually. - static NSTimeInterval lastTime; const NSRect *rectArray; NSInteger rectCount; if (drawRectDuration_) { @@ -1974,6 +1976,13 @@ - (void)drawOutlineInRect:(NSRect)rect topOnly:(BOOL)topOnly - (void)drawRect:(NSRect)rect to:(NSPoint*)toOrigin { + // The range of chars int he line that need to be drawn. + NSRange charRange = NSMakeRange(MAX(0, (rect.origin.x - MARGIN) / charWidth), + (rect.origin.x + rect.size.width - MARGIN) / charWidth); + charRange.length -= charRange.location; + if (charRange.location + charRange.length > [dataSource width]) { + charRange.length = [dataSource width] - charRange.location; + } #ifdef DEBUG_DRAWING static int iteration=0; static BOOL prevBad=NO; @@ -1982,7 +1991,7 @@ - (void)drawRect:(NSRect)rect to:(NSPoint*)toOrigin NSLog(@"Last was bad."); prevBad = NO; } - DebugLog([NSString stringWithFormat:@"%s(0x%x): rect=(%f,%f,%f,%f) frameRect=(%f,%f,%f,%f)]", + DebugLog([NSString stringWithFormat:@"%s(%p): rect=(%f,%f,%f,%f) frameRect=(%f,%f,%f,%f)]", __PRETTY_FUNCTION__, self, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, [self frame].origin.x, [self frame].origin.y, [self frame].size.width, [self frame].size.height]); @@ -2021,11 +2030,11 @@ - (void)drawRect:(NSRect)rect to:(NSPoint*)toOrigin #ifdef DEBUG_DRAWING DebugLog([NSString stringWithFormat:@"drawRect: Draw lines in range [%d, %d)", lineStart, lineEnd]); // Draw each line - NSMutableDictionary* dct = - [NSDictionary dictionaryWithObjectsAndKeys: - [NSColor textBackgroundColor], NSBackgroundColorAttributeName, - [NSColor textColor], NSForegroundColorAttributeName, - [NSFont userFixedPitchFontOfSize: 0], NSFontAttributeName, NULL]; + NSDictionary* dct = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSColor textBackgroundColor], NSBackgroundColorAttributeName, + [NSColor textColor], NSForegroundColorAttributeName, + [NSFont userFixedPitchFontOfSize: 0], NSFontAttributeName, NULL]; #endif int overflow = [dataSource scrollbackOverflow]; #ifdef DEBUG_DRAWING @@ -2053,7 +2062,10 @@ - (void)drawRect:(NSRect)rect to:(NSPoint*)toOrigin const CGFloat offsetFromTopOfScreen = y - initialY; temp = NSMakePoint(toOrigin->x, toOrigin->y + offsetFromTopOfScreen); } - anyBlinking |= [self _drawLine:line-overflow AtY:y toPoint:toOrigin ? &temp : nil]; + anyBlinking |= [self _drawLine:line-overflow + AtY:y + toPoint:toOrigin ? &temp : nil + charRange:charRange]; } #ifdef DEBUG_DRAWING // if overflow > line then the requested line cannot be drawn @@ -2077,12 +2089,12 @@ - (void)drawRect:(NSRect)rect to:(NSPoint*)toOrigin [lineDebug appendFormat:@"%@", ScreenCharToStr(&theLine[i])]; } [lineDebug appendString:@"\n"]; - [[NSString stringWithFormat:@"Iter %d, line %d, y=%d", - iteration, line, (int)(y)] drawInRect:NSMakeRect(rect.size.width-200, - y, - 200, - lineHeight) - withAttributes:dct]; + [[NSString stringWithFormat:@"Iter %d, line %d, y=%d", iteration, line, (int)(y)] + drawInRect:NSMakeRect(rect.size.width-200, + y, + 200, + lineHeight) + withAttributes:dct]; #endif } y += lineHeight; @@ -6578,7 +6590,10 @@ - (void)_drawStripesInRect:(NSRect)rect [NSGraphicsContext restoreGraphicsState]; } -- (BOOL)_drawLine:(int)line AtY:(double)curY toPoint:(NSPoint*)toPoint +- (BOOL)_drawLine:(int)line + AtY:(double)curY + toPoint:(NSPoint*)toPoint + charRange:(NSRange)charRange { BOOL anyBlinking = NO; #ifdef DEBUG_DRAWING @@ -6596,7 +6611,7 @@ - (BOOL)_drawLine:(int)line AtY:(double)curY toPoint:(NSPoint*)toPoint BOOL reversed = [[dataSource terminal] screenMode]; NSColor *aColor = nil; - // Redraw margins + // Redraw margins ------------------------------------------------------------------------------ NSRect leftMargin = NSMakeRect(0, curY, MARGIN, lineHeight); NSRect rightMargin; NSRect visibleRect = [self visibleRect]; @@ -6670,10 +6685,11 @@ - (BOOL)_drawLine:(int)line AtY:(double)curY toPoint:(NSPoint*)toPoint } } + // Draw text and background -------------------------------------------------------------------- // Contiguous sections of background with the same colour // are combined into runs and draw as one operation int bgstart = -1; - int j = 0; + int j = charRange.location; int bgColor = 0; int bgGreen = 0; int bgBlue = 0; @@ -6683,8 +6699,10 @@ - (BOOL)_drawLine:(int)line AtY:(double)curY toPoint:(NSPoint*)toPoint NSData* matches = [resultMap_ objectForKey:[NSNumber numberWithLongLong:line + [dataSource totalScrollbackOverflow]]]; const char* matchBytes = [matches bytes]; - // Iterate over each character in the line - while (j <= WIDTH) { + // Iterate over each character in the line. + // Go one past where we really need to go to simplify the code. // TODO(georgen): Fix that. + int limit = charRange.location + charRange.length; + while (j <= limit) { if (theLine[j].code == DWC_RIGHT) { // Do not draw the right-hand side of double-width characters. j++; @@ -6717,7 +6735,7 @@ - (BOOL)_drawLine:(int)line AtY:(double)curY toPoint:(NSPoint*)toPoint match = theIndex < [matches length] && (matchBytes[theIndex] & bitMask); } - if (j != WIDTH && bgstart < 0) { + if (j != limit && bgstart < 0) { // Start new run bgstart = j; bgColor = theLine[j].backgroundColor; @@ -6728,7 +6746,7 @@ - (BOOL)_drawLine:(int)line AtY:(double)curY toPoint:(NSPoint*)toPoint isMatch = match; } - if (j != WIDTH && + if (j != limit && bgselected == selected && theLine[j].backgroundColor == bgColor && theLine[j].bgGreen == bgGreen && @@ -6820,7 +6838,7 @@ - (BOOL)_drawLine:(int)line AtY:(double)curY toPoint:(NSPoint*)toPoint textOrigin = NSMakePoint(toPoint->x + MARGIN + bgstart * charWidth, toPoint->y); } else { - textOrigin = NSMakePoint(MARGIN + bgstart*charWidth, + textOrigin = NSMakePoint(MARGIN + bgstart * charWidth, curY); } [self _drawCharactersInLine:theLine @@ -8370,8 +8388,8 @@ - (BOOL)updateDirtyRects if (prevCursorX != currentCursorX || prevCursorY != currentCursorY) { // Mark previous and current cursor position dirty - [dataSource setCharDirtyAtCursorX:prevCursorX Y:prevCursorY value:1]; - [dataSource setCharDirtyAtCursorX:currentCursorX Y:currentCursorY value:1]; + [dataSource setCharDirtyAtCursorX:prevCursorX Y:prevCursorY]; + [dataSource setCharDirtyAtCursorX:currentCursorX Y:currentCursorY]; // Set prevCursor[XY] to new cursor position prevCursorX = currentCursorX; @@ -8379,36 +8397,34 @@ - (BOOL)updateDirtyRects } } for (int y = lineStart; y < lineEnd; y++) { - NSMutableData* matches = [resultMap_ objectForKey:[NSNumber numberWithLongLong:y + totalScrollbackOverflow]]; for (int x = 0; x < WIDTH; x++) { - int dirtyFlags = ([dataSource dirtyAtX:x Y:y-lineStart] | allDirty); + int dirtyFlags = allDirty || [dataSource dirtyAtX:x Y:y-lineStart]; if (dirtyFlags) { - if (irEnabled) { - if (dirtyFlags & 1) { - foundDirty = YES; - if (matches) { - // Remove highlighted search matches on this line. - [resultMap_ removeObjectForKey:[NSNumber numberWithLongLong:y + totalScrollbackOverflow]]; - matches = nil; - } - } else { - for (int j = x+1; j < WIDTH; ++j) { - if ([dataSource dirtyAtX:j Y:y-lineStart] & 1) { - foundDirty = YES; - } + foundDirty = YES; + // Remove highlighted search matches on this line. + [resultMap_ removeObjectForKey:[NSNumber numberWithLongLong:y + totalScrollbackOverflow]]; + + // Compute the dirty rect for this line. + NSRect dirtyRect = [self visibleRect]; + dirtyRect.origin.y = y * lineHeight; + dirtyRect.size.height = lineHeight; + if (!allDirty) { + dirtyRect.origin.x = MARGIN + x * charWidth; + int maxX; + for (maxX = WIDTH - 1; maxX > x; maxX--) { + if ([dataSource dirtyAtX:maxX Y:y-lineStart]) { + break; } } + dirtyRect.size.width = (maxX - x + 1) * charWidth; } - NSRect dirtyRect = [self visibleRect]; - dirtyRect.origin.y = y*lineHeight; - dirtyRect.size.height = lineHeight; if (gDebugLogging) { DebugLog([NSString stringWithFormat:@"%d is dirty", y]); } [self setNeedsDisplayInRect:dirtyRect]; #ifdef DEBUG_DRAWING - char temp[100]; + char temp[WIDTH + 1]; screen_char_t* p = [dataSource getLineAtScreenIndex:screenindex]; for (int i = 0; i < WIDTH; ++i) { temp[i] = p[i].complexChar ? '#' : p[i].code; @@ -8677,8 +8693,8 @@ - (BOOL)_updateBlink if ([self blinkingCursor] && [[self window] isKeyWindow]) { - // Blink flag flipped and there is a blinking cursor. Mark it dirty. - [self markCursorAsDirty]; + // Blink flag flipped and there is a blinking cursor. Make it redraw. + [self setCursorNeedsDisplay]; } DebugLog(@"time to redraw blinking text"); } diff --git a/VT100Screen.h b/VT100Screen.h index d47185ec5a..f1778b48ac 100644 --- a/VT100Screen.h +++ b/VT100Screen.h @@ -284,10 +284,10 @@ void TranslateCharacterSet(screen_char_t *s, int len); // Set the cursor dirty. Cursor coords are different because of how they handle // being in the WIDTH'th column (it wraps to the start of the next line) // whereas that wouldn't normally be a legal X value. -- (void)setCharDirtyAtCursorX:(int)x Y:(int)y value:(int)v; +- (void)setCharDirtyAtCursorX:(int)x Y:(int)y; -// OR in a value into the dirty array at an x,y coordinate -- (void)setCharDirtyAtX:(int)x Y:(int)y value:(int)v; +// Set the char at x,y dirty. +- (void)setCharDirtyAtX:(int)x Y:(int)y; // Retrieve the dirty flags at an x,y coordinate - (int)dirtyAtX:(int)x Y:(int)y; diff --git a/VT100Screen.m b/VT100Screen.m index f634774717..c9d9228ce5 100644 --- a/VT100Screen.m +++ b/VT100Screen.m @@ -589,13 +589,13 @@ - (void)setDirtyFromX:(int)fromX Y:(int)fromY toX:(int)toX Y:(int)toY [self setRangeDirty:NSMakeRange(i, toX + toY * WIDTH - i)]; } -- (void)setDirtyAtOffset:(int)i value:(int)v +- (void)setDirtyAtOffset:(int)i { i = MIN(i, WIDTH*HEIGHT-1); assert(i >= 0); assert(i < dirtySize); - dirty[i] |= v; + dirty[i] = 1; } - (void)setRangeDirty:(NSRange)range @@ -640,7 +640,7 @@ - (int)dirtyAtX:(int)x Y:(int)y return [self dirtyAtOffset:i]; } -- (void)setCharDirtyAtCursorX:(int)x Y:(int)y value:(int)v +- (void)setCharDirtyAtCursorX:(int)x Y:(int)y { int xToMark = x; int yToMark = y; @@ -648,10 +648,10 @@ - (void)setCharDirtyAtCursorX:(int)x Y:(int)y value:(int)v xToMark = 0; yToMark++; } - [self setCharDirtyAtX:xToMark Y:yToMark value:v]; + [self setCharDirtyAtX:xToMark Y:yToMark]; } -- (void)setCharDirtyAtX:(int)x Y:(int)y value:(int)v +- (void)setCharDirtyAtX:(int)x Y:(int)y { if (x == WIDTH) { x = WIDTH-1; @@ -661,16 +661,16 @@ - (void)setCharDirtyAtX:(int)x Y:(int)y value:(int)v y >= 0 && y < HEIGHT) { int i = x + y * WIDTH; - [self setDirtyAtOffset:i value:v]; + [self setDirtyAtOffset:i]; } } - (void)setCharAtCursorDirty:(int)value { if (cursorX == WIDTH && cursorY < HEIGHT - 1) { - [self setCharDirtyAtX:0 Y:cursorY+1 value:value]; + [self setCharDirtyAtX:0 Y:cursorY+1]; } - [self setCharDirtyAtX:cursorX Y:cursorY value:value]; + [self setCharDirtyAtX:cursorX Y:cursorY]; } - (void)setCursorX:(int)x Y:(int)y @@ -3102,8 +3102,8 @@ - (void)setString:(NSString *)string ascii:(BOOL)ascii aLine[cursorX].complexChar = NO; aLine[cursorX-1].code = ' '; aLine[cursorX-1].complexChar = NO; - [self setDirtyAtOffset:screenIdx + cursorX value:1]; - [self setDirtyAtOffset:screenIdx + cursorX - 1 value:1]; + [self setDirtyAtOffset:screenIdx + cursorX]; + [self setDirtyAtOffset:screenIdx + cursorX - 1]; } // This is an ugly little optimization--if we're inserting just one character, see if it would @@ -3223,8 +3223,7 @@ - (void)setNewLine // Mark the cursor's previous location dirty. This fixes a rare race condition where // the cursor is not erased. [self setCharDirtyAtX:MAX(0, cursorX - 1) - Y:MAX(0, cursorY-1) - value:1]; + Y:MAX(0, cursorY - 1)]; // Top line can move into scroll area; we need to draw only bottom line. [self moveDirtyRangeFromX:0 Y:1 toX:0 Y:0 size:WIDTH*(HEIGHT - 1)]; @@ -4282,18 +4281,18 @@ - (void)setDirty // There was a call to [display setNeedsDisplay:YES] here which was // a remnant of iTerm 0.1 (see bug 1124) that was killing performance. // I'm almost sure it wasn't doing any good. - allDirty_ = YES; + allDirty_ = YES; DebugLog(@"setDirty (screen scrolled)"); } - (BOOL)isAllDirty { - return allDirty_; + return allDirty_; } - (void)resetAllDirty { - allDirty_ = NO; + allDirty_ = NO; } - (void)doPrint