diff --git a/PTYSession.m b/PTYSession.m index 06d17083f1..269682270f 100644 --- a/PTYSession.m +++ b/PTYSession.m @@ -1116,7 +1116,7 @@ - (void)threadedReadTask:(char *)buffer length:(int)length { // Parse the input stream into an array of tokens. STOPWATCH_START(parsing); - NSMutableArray *tokens = [[NSMutableArray alloc] init]; + NSMutableArray *tokens = [[NSMutableArray alloc] initWithCapacity:100]; [_terminal.parser addParsedTokensToArray:tokens]; STOPWATCH_LAP(parsing); @@ -1225,7 +1225,7 @@ - (void)readTask:(const char *)buffer length:(int)length } [_terminal.parser putStreamData:buffer length:length]; - NSMutableArray *tokens = [NSMutableArray array]; + NSMutableArray *tokens = [NSMutableArray arrayWithCapacity:100]; [_terminal.parser addParsedTokensToArray:tokens]; for (VT100Token *token in tokens) { [_terminal executeToken:token]; @@ -3466,7 +3466,7 @@ - (void)tmuxWindowRenamedWithId:(int)windowId to:(NSString *)newName - (void)tmuxPrintLine:(NSString *)line { - [_screen appendStringAtCursor:line ascii:NO]; + [_screen appendStringAtCursor:line]; [_screen crlf]; } @@ -3480,7 +3480,7 @@ - (void)tmuxHostDisconnected _tmuxGateway = nil; [_tmuxController release]; _tmuxController = nil; - [_screen appendStringAtCursor:@"Detached" ascii:YES]; + [_screen appendStringAtCursor:@"Detached"]; [_screen crlf]; self.tmuxMode = TMUX_NONE; _tmuxLogging = NO; @@ -4648,7 +4648,7 @@ - (void)printTmuxMessage:(NSString *)message alternateSemantics:YES]; [_terminal setBackgroundColor:ALTSEM_BG_DEFAULT alternateSemantics:YES]; - [_screen appendStringAtCursor:message ascii:YES]; + [_screen appendStringAtCursor:message]; [_screen crlf]; [_terminal setForegroundColor:savedFgColor.foregroundColor alternateSemantics:savedFgColor.foregroundColorMode == ColorModeAlternate]; @@ -4725,6 +4725,13 @@ - (void)screenDidAppendStringToCurrentLine:(NSString *)string { [self appendStringToTriggerLine:string]; } +- (void)screenDidAppendAsciiDataToCurrentLine:(NSData *)asciiData { + if ([_triggers count]) { + NSString *string = [[NSString alloc] initWithData:asciiData encoding:NSASCIIStringEncoding]; + [self screenDidAppendStringToCurrentLine:string]; + } +} + - (void)screenSetCursorType:(ITermCursorType)type { [[self textview] setCursorType:type]; } diff --git a/PTYTask.h b/PTYTask.h index 75dcb50b3e..cb13fd11ed 100644 --- a/PTYTask.h +++ b/PTYTask.h @@ -16,6 +16,8 @@ extern NSString *kCoprocessStatusChangeNotification; @interface PTYTask : NSObject +@property(atomic, readonly) BOOL hasMuteCoprocess; + - (id)init; - (void)dealloc; - (BOOL)hasBrokenPipe; @@ -60,7 +62,6 @@ extern NSString *kCoprocessStatusChangeNotification; - (Coprocess *)coprocess; - (BOOL)writeBufferHasRoom; - (BOOL)hasCoprocess; -- (BOOL)hasMuteCoprocess; - (void)stopCoprocess; - (void)logData:(const char *)buffer length:(int)length; diff --git a/PTYTask.m b/PTYTask.m index b6efb48fca..bde704552e 100644 --- a/PTYTask.m +++ b/PTYTask.m @@ -72,6 +72,10 @@ win->ws_ypixel = 0; } +@interface PTYTask () +@property(atomic, assign) BOOL hasMuteCoprocess; +@end + @implementation PTYTask { pid_t pid; @@ -276,7 +280,7 @@ - (BOOL)writeBufferHasRoom - (void)processRead { - int iterations = 10; + int iterations = 4; int bytesRead = 0; char buffer[MAXRW * iterations]; @@ -614,6 +618,7 @@ - (void)stopCoprocess [coprocess_ terminate]; [coprocess_ release]; coprocess_ = nil; + self.hasMuteCoprocess = NO; } if (thePid) { [[TaskNotifier sharedInstance] waitForPid:thePid]; @@ -628,6 +633,7 @@ - (void)setCoprocess:(Coprocess *)coprocess @synchronized (self) { [coprocess_ autorelease]; coprocess_ = [coprocess retain]; + self.hasMuteCoprocess = coprocess_.mute; } [[TaskNotifier sharedInstance] unblock]; } @@ -648,13 +654,5 @@ - (BOOL)hasCoprocess return NO; } -- (BOOL)hasMuteCoprocess -{ - @synchronized (self) { - return coprocess_ != nil && coprocess_.mute; - } - return NO; -} - @end diff --git a/TmuxHistoryParser.m b/TmuxHistoryParser.m index 8c4677b533..9abd7bf2c6 100644 --- a/TmuxHistoryParser.m +++ b/TmuxHistoryParser.m @@ -36,6 +36,10 @@ - (NSData *)dataForHistoryLine:(NSString *)hist for (VT100Token *token in tokens) { [terminal executeToken:token]; NSString *string = token.isStringType ? token.string : nil; + if (!string && token.isStringType && token.data) { + string = [[[NSString alloc] initWithData:token.data encoding:NSASCIIStringEncoding] autorelease]; + } + if (string) { // Allocate double space in case they're all double-width characters. screenChars = malloc(sizeof(screen_char_t) * 2 * string.length); diff --git a/VT100AnsiParser.h b/VT100AnsiParser.h index 82d2cf5220..7fb8a7354a 100644 --- a/VT100AnsiParser.h +++ b/VT100AnsiParser.h @@ -9,7 +9,7 @@ #import #import "VT100Token.h" -static BOOL isANSI(unsigned char *code, int len) { +NS_INLINE BOOL isANSI(unsigned char *code, int len) { // Currently, we only support esc-c as an ANSI code (other ansi codes are CSI). if (len >= 2 && code[0] == ESC && code[1] == 'c') { return YES; diff --git a/VT100CSIParser.h b/VT100CSIParser.h index 2526639567..2755709884 100644 --- a/VT100CSIParser.h +++ b/VT100CSIParser.h @@ -18,7 +18,7 @@ typedef enum { kIncidentalDeleteCharacterAtCursor } VT100CSIIncidentalType; -static BOOL isCSI(unsigned char *code, int len) { +NS_INLINE BOOL isCSI(unsigned char *code, int len) { if (len >= 2 && code[0] == ESC && (code[1] == '[')) { return YES; } diff --git a/VT100CSIParser.m b/VT100CSIParser.m index ab03d1ddc5..fc0bd5dd4b 100644 --- a/VT100CSIParser.m +++ b/VT100CSIParser.m @@ -42,7 +42,7 @@ static int advanceAndEatControlChars(unsigned char **ppdata, case VT100CC_SUB: case VT100CC_ESC: case VT100CC_DEL: - [incidentals addObject:[VT100Token tokenForControlCharacter:*ppdata]]; + [incidentals addObject:[VT100Token tokenForControlCharacter:**ppdata]]; break; default: if (**ppdata >= 0x20) diff --git a/VT100ControlParser.h b/VT100ControlParser.h index ab0d484961..8074f30eb1 100644 --- a/VT100ControlParser.h +++ b/VT100ControlParser.h @@ -9,7 +9,7 @@ #import #import "VT100Token.h" -static BOOL iscontrol(int c) { +NS_INLINE BOOL iscontrol(int c) { return c <= 0x1f; } diff --git a/VT100DCSParser.h b/VT100DCSParser.h index 26db12af2a..65c2bb323b 100644 --- a/VT100DCSParser.h +++ b/VT100DCSParser.h @@ -9,7 +9,7 @@ #import #import "VT100Token.h" -static BOOL isDCS(unsigned char *code, int len) { +NS_INLINE BOOL isDCS(unsigned char *code, int len) { return (len >= 2 && code[0] == ESC && code[1] == 'P'); } diff --git a/VT100Grid.m b/VT100Grid.m index c1ee16d2f5..bae5bb00f9 100644 --- a/VT100Grid.m +++ b/VT100Grid.m @@ -566,7 +566,6 @@ - (int)appendCharsAtCursor:(screen_char_t *)buffer #ifdef VERBOSE_STRING NSLog(@"Begin inserting line. cursor_.x=%d, WIDTH=%d", cursor_.x, WIDTH); #endif - NSAssert(buffer[idx].code != DWC_RIGHT, @"DWC cut off"); if (buffer[idx].code == DWC_SKIP) { // I'm pretty sure this can never happen and that this code is just a historical leftover. diff --git a/VT100Parser.m b/VT100Parser.m index cb36a113e5..dd77164902 100644 --- a/VT100Parser.m +++ b/VT100Parser.m @@ -40,7 +40,6 @@ - (BOOL)addNextParsedTokensToArray:(NSMutableArray *)output { int datalen; VT100Token *token = [VT100Token token]; - token->isControl = NO; token.string = nil; // get our current position in the stream datap = _stream + _streamOffset; @@ -86,7 +85,6 @@ - (BOOL)addNextParsedTokensToArray:(NSMutableArray *)output { } length = rmlen; position = datap; - token->isControl = YES; } else { if (isString(datap, self.encoding)) { [VT100StringParser decodeBytes:datap @@ -139,8 +137,9 @@ - (BOOL)addNextParsedTokensToArray:(NSMutableArray *)output { } } + token->savingData = _saveData; if (token->type != VT100_WAIT && token->type != VT100CC_NULL) { - if (_saveData) { + if (_saveData || token->type == VT100_ASCIISTRING) { token.data = [NSData dataWithBytes:position length:length]; } [output addObject:token]; diff --git a/VT100Screen.h b/VT100Screen.h index d4541dfb51..8a73414693 100644 --- a/VT100Screen.h +++ b/VT100Screen.h @@ -146,7 +146,8 @@ extern int kVT100ScreenMinRows; // Append a string to the screen at the current cursor position. The terminal's insert and wrap- // around modes are respected, the cursor is advanced, the screen may be scrolled, and the line // buffer may change. -- (void)appendStringAtCursor:(NSString *)s ascii:(BOOL)ascii; +- (void)appendStringAtCursor:(NSString *)string; +- (void)appendAsciiDataAtCursor:(NSData *)asciiData; // This is a hacky thing that moves the cursor to the next line, not respecting scroll regions. // It's used for the tmux status screen. diff --git a/VT100Screen.m b/VT100Screen.m index 3e7c8d7c39..10de84f8bc 100644 --- a/VT100Screen.m +++ b/VT100Screen.m @@ -761,131 +761,163 @@ - (void)clearScrollbackBuffer [self reloadMarkCache]; } -- (void)appendStringAtCursor:(NSString *)string ascii:(BOOL)ascii +- (void)appendAsciiDataAtCursor:(NSData *)asciiData { - DLog(@"setString: %ld chars starting with %c at x=%d, y=%d, line=%d", - (unsigned long)[string length], - [string characterAtIndex:0], + int len = [asciiData length]; + if (len < 1 || !asciiData) { + return; + } + + char firstChar = ((char *)asciiData.bytes)[0]; + + DLog(@"appendAsciiDataAtCursor: %ld chars starting with %c at x=%d, y=%d, line=%d", + (unsigned long)len, + firstChar, currentGrid_.cursorX, currentGrid_.cursorY, currentGrid_.cursorY + [linebuffer_ numLinesWithWidth:currentGrid_.size.width]); - int len = [string length]; - if (len < 1 || !string) { - return; - } - // Allocate a buffer of screen_char_t and place the new string in it. const int kStaticBufferElements = 1024; screen_char_t staticBuffer[kStaticBufferElements]; screen_char_t *dynamicBuffer = 0; screen_char_t *buffer; - if (ascii) { - // Only Unicode code points 0 through 127 occur in the string. - const int kStaticTempElements = kStaticBufferElements; - unichar staticTemp[kStaticTempElements]; - unichar* dynamicTemp = 0; - unichar *sc; - if ([string length] > kStaticTempElements) { - dynamicTemp = sc = (unichar *) calloc(len, sizeof(unichar)); - assert(dynamicTemp); - } else { - sc = staticTemp; - } - assert(terminal_); - screen_char_t fg = [terminal_ foregroundColorCode]; - screen_char_t bg = [terminal_ backgroundColorCode]; - - if ([string length] > kStaticBufferElements) { - buffer = dynamicBuffer = (screen_char_t *) calloc([string length], - sizeof(screen_char_t)); - assert(dynamicBuffer); - if (!buffer) { - NSLog(@"%s: Out of memory", __PRETTY_FUNCTION__); - return; - } - } else { - buffer = staticBuffer; + + const char *bytes = [asciiData bytes]; + assert(terminal_); + screen_char_t fg = [terminal_ foregroundColorCode]; + screen_char_t bg = [terminal_ backgroundColorCode]; + + BOOL zeroed = NO; + if ([asciiData length] > kStaticBufferElements) { + zeroed = YES; + buffer = dynamicBuffer = (screen_char_t *) calloc([asciiData length], + sizeof(screen_char_t)); + assert(dynamicBuffer); + if (!buffer) { + NSLog(@"%s: Out of memory", __PRETTY_FUNCTION__); + return; } + } else { + buffer = staticBuffer; + } - [string getCharacters:sc]; + if (zeroed) { + // This is a faster version for the dynamic buffer case. for (int i = 0; i < len; i++) { - buffer[i].code = sc[i]; + buffer[i].code = bytes[i]; + CopyForegroundColor(&buffer[i], fg); + CopyBackgroundColor(&buffer[i], bg); + } + } else { + // Make sure that any changes made here are also made in the loop above. + for (int i = 0; i < len; i++) { + buffer[i].code = bytes[i]; buffer[i].complexChar = NO; CopyForegroundColor(&buffer[i], fg); CopyBackgroundColor(&buffer[i], bg); buffer[i].unused = 0; } + } - // If a graphics character set was selected then translate buffer - // characters into graphics charaters. - if (charsetUsesLineDrawingMode_[[terminal_ charset]]) { - ConvertCharsToGraphicsCharset(buffer, len); - } - if (dynamicTemp) { - free(dynamicTemp); + // If a graphics character set was selected then translate buffer + // characters into graphics charaters. + if (charsetUsesLineDrawingMode_[[terminal_ charset]]) { + ConvertCharsToGraphicsCharset(buffer, len); + } + + [self appendScreenCharArrayAtCursor:buffer + length:len + shouldFree:(buffer == dynamicBuffer)]; +} + +- (void)appendStringAtCursor:(NSString *)string +{ + int len = [string length]; + if (len < 1 || !string) { + return; + } + + unichar firstChar = [string characterAtIndex:0]; + + DLog(@"appendStringAtCursor: %ld chars starting with %c at x=%d, y=%d, line=%d", + (unsigned long)len, + firstChar, + currentGrid_.cursorX, + currentGrid_.cursorY, + currentGrid_.cursorY + [linebuffer_ numLinesWithWidth:currentGrid_.size.width]); + + // Allocate a buffer of screen_char_t and place the new string in it. + const int kStaticBufferElements = 1024; + screen_char_t staticBuffer[kStaticBufferElements]; + screen_char_t *dynamicBuffer = 0; + screen_char_t *buffer; + string = [string precomposedStringWithCanonicalMapping]; + len = [string length]; + if (2 * len > kStaticBufferElements) { + buffer = dynamicBuffer = (screen_char_t *) calloc(2 * len, + sizeof(screen_char_t)); + assert(buffer); + if (!buffer) { + NSLog(@"%s: Out of memory", __PRETTY_FUNCTION__); + return; } } else { - string = [string precomposedStringWithCanonicalMapping]; - len = [string length]; - if (2 * len > kStaticBufferElements) { - buffer = dynamicBuffer = (screen_char_t *) calloc(2 * len, - sizeof(screen_char_t)); - assert(buffer); - if (!buffer) { - NSLog(@"%s: Out of memory", __PRETTY_FUNCTION__); - return; - } - } else { - buffer = staticBuffer; - } - - // Pick off leading combining marks and low surrogates and modify the - // character at the cursor position with them. - unichar firstChar = [string characterAtIndex:0]; - while ([string length] > 0 && - (IsCombiningMark(firstChar) || IsLowSurrogate(firstChar))) { - VT100GridCoord pred = [currentGrid_ coordinateBefore:currentGrid_.cursor]; - if (pred.x < 0 || - ![currentGrid_ addCombiningChar:firstChar toCoord:pred]) { - // Combining mark will need to stand alone rather than combine - // because nothing precedes it. - if (IsCombiningMark(firstChar)) { - // Prepend a space to it so the combining mark has something - // to combine with. - string = [NSString stringWithFormat:@" %@", string]; - } else { - // Got a low surrogate but can't find the matching high - // surrogate. Turn the low surrogate into a replacement - // char. This should never happen because decode_string - // ought to detect the broken unicode and substitute a - // replacement char. - string = [NSString stringWithFormat:@"%@%@", - ReplacementString(), - [string substringFromIndex:1]]; - } - len = [string length]; - break; - } - string = [string substringFromIndex:1]; - if ([string length] > 0) { - firstChar = [string characterAtIndex:0]; + buffer = staticBuffer; + } + + // Pick off leading combining marks and low surrogates and modify the + // character at the cursor position with them. + while ([string length] > 0 && + (IsCombiningMark(firstChar) || IsLowSurrogate(firstChar))) { + VT100GridCoord pred = [currentGrid_ coordinateBefore:currentGrid_.cursor]; + if (pred.x < 0 || + ![currentGrid_ addCombiningChar:firstChar toCoord:pred]) { + // Combining mark will need to stand alone rather than combine + // because nothing precedes it. + if (IsCombiningMark(firstChar)) { + // Prepend a space to it so the combining mark has something + // to combine with. + string = [NSString stringWithFormat:@" %@", string]; + } else { + // Got a low surrogate but can't find the matching high + // surrogate. Turn the low surrogate into a replacement + // char. This should never happen because decode_string + // ought to detect the broken unicode and substitute a + // replacement char. + string = [NSString stringWithFormat:@"%@%@", + ReplacementString(), + [string substringFromIndex:1]]; } + len = [string length]; + break; + } + string = [string substringFromIndex:1]; + if ([string length] > 0) { + firstChar = [string characterAtIndex:0]; } - - assert(terminal_); - // Add DWC_RIGHT after each double-byte character, build complex characters out of surrogates - // and combining marks, replace private codes with replacement characters, swallow zero- - // width spaces, and set fg/bg colors and attributes. - StringToScreenChars(string, - buffer, - [terminal_ foregroundColorCode], - [terminal_ backgroundColorCode], - &len, - [delegate_ screenShouldTreatAmbiguousCharsAsDoubleWidth], - NULL); } + assert(terminal_); + // Add DWC_RIGHT after each double-byte character, build complex characters out of surrogates + // and combining marks, replace private codes with replacement characters, swallow zero- + // width spaces, and set fg/bg colors and attributes. + StringToScreenChars(string, + buffer, + [terminal_ foregroundColorCode], + [terminal_ backgroundColorCode], + &len, + [delegate_ screenShouldTreatAmbiguousCharsAsDoubleWidth], + NULL); + + [self appendScreenCharArrayAtCursor:buffer + length:len + shouldFree:(buffer == dynamicBuffer)]; +} + +- (void)appendScreenCharArrayAtCursor:(screen_char_t *)buffer + length:(int)len + shouldFree:(BOOL)shouldFree { if (len >= 1) { [self incrementOverflowBy:[currentGrid_ appendCharsAtCursor:buffer length:len @@ -897,8 +929,8 @@ - (void)appendStringAtCursor:(NSString *)string ascii:(BOOL)ascii insert:_insert]]; } - if (dynamicBuffer) { - free(dynamicBuffer); + if (shouldFree) { + free(buffer); } if (commandStartX_ != -1) { @@ -1850,17 +1882,29 @@ - (VT100GridRange)lineNumberRangeOfInterval:(Interval *)interval { #pragma mark - VT100TerminalDelegate -- (void)terminalAppendString:(NSString *)string isAscii:(BOOL)isAscii -{ +- (void)terminalAppendString:(NSString *)string { if (collectInputForPrinting_) { [printBuffer_ appendString:string]; } else { // else display string on screen - [self appendStringAtCursor:string ascii:isAscii]; + [self appendStringAtCursor:string]; } [delegate_ screenDidAppendStringToCurrentLine:string]; } +- (void)terminalAppendAsciiData:(NSData *)asciiData { + if (collectInputForPrinting_) { + NSString *string = [[[NSString alloc] initWithData:asciiData + encoding:NSASCIIStringEncoding] autorelease]; + [self terminalAppendString:string]; + return; + } else { + // else display string on screen + [self appendAsciiDataAtCursor:asciiData]; + } + [delegate_ screenDidAppendAsciiDataToCurrentLine:asciiData]; +} + - (void)terminalRingBell { [delegate_ screenDidAppendStringToCurrentLine:@"\a"]; [self activateBell]; diff --git a/VT100ScreenDelegate.h b/VT100ScreenDelegate.h index ad41c61e15..3b94ff3970 100644 --- a/VT100ScreenDelegate.h +++ b/VT100ScreenDelegate.h @@ -33,6 +33,7 @@ // Called after text was added to the current line. Can be used to check triggers. - (void)screenDidAppendStringToCurrentLine:(NSString *)string; +- (void)screenDidAppendAsciiDataToCurrentLine:(NSData *)asciiData; // Change the cursor's appearance. - (void)screenSetCursorBlinking:(BOOL)blink; diff --git a/VT100StringParser.h b/VT100StringParser.h index 41dc3fc48d..508a097eda 100644 --- a/VT100StringParser.h +++ b/VT100StringParser.h @@ -37,11 +37,11 @@ #define isKREncoding(e) ((e)==0x80000422 || (e)==0x80000003|| \ (e)==0x80000840 || (e)==0x80000940) -static BOOL isAsciiString(unsigned char *code) { +NS_INLINE BOOL isAsciiString(unsigned char *code) { return *code >= 0x20 && *code <= 0x7f; } -static BOOL isString(unsigned char *code, NSStringEncoding encoding) { +NS_INLINE BOOL isString(unsigned char *code, NSStringEncoding encoding) { if (encoding == NSUTF8StringEncoding) { return (*code >= 0x80); } else if (isGBEncoding(encoding)) { diff --git a/VT100StringParser.m b/VT100StringParser.m index 4b7677c81c..6f75ef46c0 100644 --- a/VT100StringParser.m +++ b/VT100StringParser.m @@ -289,7 +289,9 @@ + (void)decodeBytes:(unsigned char *)datap result->type = VT100_UNKNOWNCHAR; result->code = datap[0]; + BOOL isAscii = NO; if (isAsciiString(datap)) { + isAscii = YES; DecodeASCIIBytes(datap, datalen, rmlen, result); encoding = NSASCIIStringEncoding; } else if (encoding == NSUTF8StringEncoding) { @@ -315,7 +317,7 @@ + (void)decodeBytes:(unsigned char *)datap datap[0] = ONECHAR_UNKNOWN; result.string = ReplacementString(); result->type = VT100_STRING; - } else if (result->type != VT100_WAIT) { + } else if (result->type != VT100_WAIT && !isAscii) { result.string = [[[NSString alloc] initWithBytes:datap length:*rmlen encoding:encoding] autorelease]; diff --git a/VT100Terminal.m b/VT100Terminal.m index dfd9a7dc6e..9b0bd1d6b7 100644 --- a/VT100Terminal.m +++ b/VT100Terminal.m @@ -387,16 +387,13 @@ - (void)setInsertMode:(BOOL)mode - (void)executeModeUpdates:(VT100Token *)token { - if (!token->isControl) { - return; - } BOOL mode; int i; switch (token->type) { case VT100CSI_DECSET: case VT100CSI_DECRST: - mode=(token->type == VT100CSI_DECSET); + mode = (token->type == VT100CSI_DECSET); for (i = 0; i < token.csi->count; i++) { switch (token.csi->p[i]) { @@ -620,9 +617,6 @@ - (void)resetSGR { - (void)executeSGR:(VT100Token *)token { - if (!token->isControl) { - return; - } if (token->type == VT100CSI_SGR) { if (token.csi->count == 0) { [self resetSGR]; @@ -978,16 +972,16 @@ - (void)executeToken:(VT100Token *)token { receivingFile_ = NO; } } - if (token->type != VT100_SKIP) { // VT100_SKIP = there was no data to read - if ([delegate_ terminalIsAppendingToPasteboard]) { - // We are probably copying text to the clipboard until esc]1337;EndCopy^G is received. - if (token->type != XTERMCC_SET_KVP || - ![token.string hasPrefix:@"CopyToClipboard"]) { - // Append text to clipboard except for initial command that turns on copying to - // the clipboard. - - [delegate_ terminalAppendDataToPasteboard:token.data]; - } + if (token->savingData && + token->type != VT100_SKIP && + [delegate_ terminalIsAppendingToPasteboard]) { + // We are probably copying text to the clipboard until esc]1337;EndCopy^G is received. + if (token->type != XTERMCC_SET_KVP || + ![token.string hasPrefix:@"CopyToClipboard"]) { + // Append text to clipboard except for initial command that turns on copying to + // the clipboard. + + [delegate_ terminalAppendDataToPasteboard:token.data]; } } @@ -1016,8 +1010,10 @@ - (void)executeToken:(VT100Token *)token { switch (token->type) { // our special code case VT100_STRING: + [delegate_ terminalAppendString:token.string]; + break; case VT100_ASCIISTRING: - [delegate_ terminalAppendString:token.string isAscii:token->type == VT100_ASCIISTRING]; + [delegate_ terminalAppendAsciiData:token.data]; break; case VT100_UNKNOWNCHAR: diff --git a/VT100TerminalDelegate.h b/VT100TerminalDelegate.h index ff6caac700..fcc815dfef 100644 --- a/VT100TerminalDelegate.h +++ b/VT100TerminalDelegate.h @@ -31,9 +31,9 @@ typedef enum { } VT100TerminalUnits; @protocol VT100TerminalDelegate -// Append a string at the cursor's position and advance the cursor, scrolling if necessary. If -// |ascii| is set then the string contains only ascii characters. -- (void)terminalAppendString:(NSString *)string isAscii:(BOOL)isAscii; +// Append a string at the cursor's position and advance the cursor, scrolling if necessary. +- (void)terminalAppendString:(NSString *)string; +- (void)terminalAppendAsciiData:(NSData *)asciiData; // Play/display the bell. - (void)terminalRingBell; diff --git a/VT100Token.h b/VT100Token.h index 1a5b8de369..c986785d19 100644 --- a/VT100Token.h +++ b/VT100Token.h @@ -176,24 +176,36 @@ typedef struct { @public VT100TerminalTokenType type; - unsigned char code; // For VT100_UNKNOWNCHAR and VT100CSI_SCS0...SCS3. - CSIParam *csi; + // data is populated because the current mode uses the raw input. data is + // always set for ascii strings regardless of mode. + BOOL savingData; - BOOL isControl; // Is this a control code? + unsigned char code; // For VT100_UNKNOWNCHAR and VT100CSI_SCS0...SCS3. } -// For VT100_STRING, VT100_ASCIISTRING +// For VT100_STRING @property(nonatomic, retain) NSString *string; + +// For VT100_ASCIISTRING and when saving data. +@property(nonatomic, retain) NSData *data; + +// For XTERMCC_SET_KVP. @property(nonatomic, retain) NSString *kvpKey; @property(nonatomic, retain) NSString *kvpValue; -@property(nonatomic, retain) NSData *data; + +// For VT100CSI_ codes that take paramters. +@property(nonatomic, readonly) CSIParam *csi; + +// Is this DCS_TMUX? +@property(nonatomic, readonly) BOOL startsTmuxMode; + +// Is this an ascii string? +@property(nonatomic, readonly) BOOL isAscii; + +// Is this a string or ascii string? +@property(nonatomic, readonly) BOOL isStringType; + (instancetype)token; + (instancetype)tokenForControlCharacter:(unsigned char)controlCharacter; -- (CSIParam *)csi; -- (BOOL)startsTmuxMode; -- (BOOL)isAscii; -- (BOOL)isStringType; - @end diff --git a/VT100Token.m b/VT100Token.m index 1412203a4a..99f1988711 100644 --- a/VT100Token.m +++ b/VT100Token.m @@ -12,6 +12,10 @@ static iTermObjectPool *gPool; +@interface VT100Token () +@property(nonatomic, readwrite) CSIParam *csi; +@end + @implementation VT100Token + (void)initialize { @@ -36,9 +40,9 @@ + (instancetype)tokenForControlCharacter:(unsigned char)controlCharacter { } - (void)destroyPooledObject { - if (csi) { - free(csi); - csi = NULL; + if (_csi) { + free(_csi); + _csi = NULL; } [_string release]; @@ -55,14 +59,13 @@ - (void)destroyPooledObject { type = 0; code = 0; - isControl = NO; } - (CSIParam *)csi { - if (!csi) { - csi = calloc(sizeof(*csi), 1); + if (!_csi) { + _csi = calloc(sizeof(*_csi), 1); } - return csi; + return _csi; } - (BOOL)startsTmuxMode { diff --git a/VT100XtermParser.h b/VT100XtermParser.h index 71cb6fe89f..a7dbd26790 100644 --- a/VT100XtermParser.h +++ b/VT100XtermParser.h @@ -9,7 +9,7 @@ #import #import "VT100Token.h" -static BOOL isXTERM(unsigned char *code, int len) +NS_INLINE BOOL isXTERM(unsigned char *code, int len) { if (len >= 2 && code[0] == ESC && (code[1] == ']')) return YES; diff --git a/iTerm.xcodeproj/project.pbxproj b/iTerm.xcodeproj/project.pbxproj index caf3ed83cb..7bca0eab33 100644 --- a/iTerm.xcodeproj/project.pbxproj +++ b/iTerm.xcodeproj/project.pbxproj @@ -3327,6 +3327,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -3388,6 +3389,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -3431,6 +3433,7 @@ ); PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + VALID_ARCHS = x86_64; WARNING_CFLAGS = ( "-Wall", "-Wno-shorten-64-to-32", @@ -3443,6 +3446,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -3486,6 +3490,7 @@ ); PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + VALID_ARCHS = x86_64; WARNING_CFLAGS = ( "-Wall", "-Wno-shorten-64-to-32", @@ -3498,6 +3503,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; diff --git a/iTermTests/VT100ScreenTest.m b/iTermTests/VT100ScreenTest.m index 927fa269cf..9cb832acd5 100644 --- a/iTermTests/VT100ScreenTest.m +++ b/iTermTests/VT100ScreenTest.m @@ -113,7 +113,7 @@ - (void)testInit { // Append some stuff to it to make sure we can retreive it. for (int i = 0; i < [screen height] - 1; i++) { - [screen terminalAppendString:[NSString stringWithFormat:@"Line %d", i] isAscii:YES]; + [screen terminalAppendString:[NSString stringWithFormat:@"Line %d", i]]; [screen terminalLineFeed]; [screen terminalCarriageReturn]; } @@ -183,7 +183,7 @@ - (void)testDestructivelySetScreenWidthHeight { for (int i = 0; i < w; i++) { NSString *toAppend = [NSString stringWithFormat:@"%c", letters[i % n]]; [expected appendString:toAppend]; - [screen appendStringAtCursor:toAppend ascii:YES]; + [screen appendStringAtCursor:toAppend]; } NSString* s; s = ScreenCharArrayToStringDebug([screen getLineAtScreenIndex:0], @@ -199,7 +199,7 @@ - (VT100Screen *)screenWithWidth:(int)width height:(int)height { - (void)appendLines:(NSArray *)lines toScreen:(VT100Screen *)screen { for (NSString *line in lines) { - [screen appendStringAtCursor:line ascii:YES]; + [screen appendStringAtCursor:line]; [screen terminalCarriageReturn]; [screen terminalLineFeed]; } @@ -208,7 +208,7 @@ - (void)appendLines:(NSArray *)lines toScreen:(VT100Screen *)screen { - (void)appendLinesNoNewline:(NSArray *)lines toScreen:(VT100Screen *)screen { for (int i = 0; i < lines.count; i++) { NSString *line = lines[i]; - [screen appendStringAtCursor:line ascii:YES]; + [screen appendStringAtCursor:line]; if (i + 1 != lines.count) { [screen terminalCarriageReturn]; [screen terminalLineFeed]; @@ -421,6 +421,12 @@ - (void)screenDidAppendStringToCurrentLine:(NSString *)string { [triggerLine_ appendString:string]; } +- (void)screenDidAppendAsciiDataToCurrentLine:(NSData *)asciiData { + [self screenDidAppendStringToCurrentLine:[[[NSString alloc] initWithData:asciiData + encoding:NSASCIIStringEncoding] + autorelease]]; +} + - (void)screenDidReset { } @@ -1241,6 +1247,14 @@ - (void)testTerminalResetPreservingPrompt { assert([screen allCharacterSetPropertiesHaveDefaultValues]); } +- (void)sendDataToTerminal:(NSData *)data { + [terminal_.parser putStreamData:data.bytes length:data.length]; + NSMutableArray *tokens = [NSMutableArray array]; + [terminal_.parser addParsedTokensToArray:tokens]; + assert(tokens.count == 1); + [terminal_ executeToken:tokens[0]]; +} + - (void)testAllCharacterSetPropertiesHaveDefaultValues { VT100Screen *screen = [self screenWithWidth:5 height:3]; assert([screen allCharacterSetPropertiesHaveDefaultValues]); @@ -1251,9 +1265,7 @@ - (void)testAllCharacterSetPropertiesHaveDefaultValues { char shiftOut = 14; char shiftIn = 15; NSData *data = [NSData dataWithBytes:&shiftOut length:1]; - [terminal_ putStreamData:data]; - assert([terminal_ parseNextToken]); - [terminal_ executeToken]; + [self sendDataToTerminal:data]; assert(![screen allCharacterSetPropertiesHaveDefaultValues]); [screen terminalSetCharset:1 toLineDrawingMode:YES]; assert(![screen allCharacterSetPropertiesHaveDefaultValues]); @@ -1262,9 +1274,7 @@ - (void)testAllCharacterSetPropertiesHaveDefaultValues { assert(![screen allCharacterSetPropertiesHaveDefaultValues]); data = [NSData dataWithBytes:&shiftIn length:1]; - [terminal_ putStreamData:data]; - assert([terminal_ parseNextToken]); - [terminal_ executeToken]; + [self sendDataToTerminal:data]; assert([screen allCharacterSetPropertiesHaveDefaultValues]); } @@ -1351,9 +1361,11 @@ - (void)sendEscapeCodes:(NSString *)codes { codes = [codes stringByReplacingOccurrencesOfString:@"^[" withString:esc]; codes = [codes stringByReplacingOccurrencesOfString:@"^G" withString:bel]; NSData *data = [codes dataUsingEncoding:NSUTF8StringEncoding]; - [terminal_ putStreamData:data]; - while ([terminal_ parseNextToken]) { - [terminal_ executeToken]; + [terminal_.parser putStreamData:data.bytes length:data.length]; + NSMutableArray *tokens = [NSMutableArray array]; + [terminal_.parser addParsedTokensToArray:tokens]; + for (VT100Token *token in tokens) { + [terminal_ executeToken:token]; } } @@ -1366,7 +1378,7 @@ - (void)testAppendStringAtCursorAscii { [terminal_ setForegroundColor:5 alternateSemantics:NO]; [terminal_ setBackgroundColor:6 alternateSemantics:NO]; [self sendEscapeCodes:@"^[[1m^[[3m^[[4m^[[5m"]; // Bold, italic, blink, underline - [screen appendStringAtCursor:@"Hello world" ascii:YES]; + [screen appendStringAtCursor:@"Hello world"]; assert([[screen compactLineDump] isEqualToString: @"Hello\n" @@ -1413,7 +1425,7 @@ - (void)testAppendStringAtCursorNonAscii { NSMutableString *s = [NSMutableString stringWithCharacters:chars length:sizeof(chars) / sizeof(unichar)]; - [screen appendStringAtCursor:s ascii:NO]; + [screen appendStringAtCursor:s]; screen_char_t *line = [screen getLineAtScreenIndex:0]; assert(line[0].foregroundColor == 5); @@ -1454,7 +1466,7 @@ - (void)testAppendStringAtCursorNonAscii { ambiguousIsDoubleWidth_ = YES; s = [NSMutableString stringWithCharacters:chars length:sizeof(chars) / sizeof(unichar)]; - [screen appendStringAtCursor:s ascii:NO]; + [screen appendStringAtCursor:s]; line = [screen getLineAtScreenIndex:0]; @@ -1488,10 +1500,10 @@ - (void)testAppendStringAtCursorNonAscii { ambiguousIsDoubleWidth_ = NO; screen = [self screenWithWidth:20 height:2]; screen.delegate = (id)self; - [screen appendStringAtCursor:@"e" ascii:NO]; + [screen appendStringAtCursor:@"e"]; unichar combiningAcuteAccent = 0x301; s = [NSMutableString stringWithCharacters:&combiningAcuteAccent length:1]; - [screen appendStringAtCursor:s ascii:NO]; + [screen appendStringAtCursor:s]; line = [screen getLineAtScreenIndex:0]; a = [ScreenCharToStr(line + 0) decomposedStringWithCompatibilityMapping]; e = [@"é" decomposedStringWithCompatibilityMapping]; @@ -1504,9 +1516,9 @@ - (void)testAppendStringAtCursorNonAscii { unichar highSurrogate = 0xD800; unichar lowSurrogate = 0xDD50; s = [NSMutableString stringWithCharacters:&highSurrogate length:1]; - [screen appendStringAtCursor:s ascii:NO]; + [screen appendStringAtCursor:s]; s = [NSMutableString stringWithCharacters:&lowSurrogate length:1]; - [screen appendStringAtCursor:s ascii:NO]; + [screen appendStringAtCursor:s]; line = [screen getLineAtScreenIndex:0]; a = [ScreenCharToStr(line + 0) decomposedStringWithCompatibilityMapping]; e = @"𐅐"; @@ -1516,9 +1528,9 @@ - (void)testAppendStringAtCursorNonAscii { ambiguousIsDoubleWidth_ = NO; screen = [self screenWithWidth:20 height:2]; screen.delegate = (id)self; - [screen appendStringAtCursor:@"g" ascii:NO]; + [screen appendStringAtCursor:@"g"]; s = [NSMutableString stringWithCharacters:&lowSurrogate length:1]; - [screen appendStringAtCursor:s ascii:NO]; + [screen appendStringAtCursor:s]; line = [screen getLineAtScreenIndex:0]; a = [ScreenCharToStr(line + 0) decomposedStringWithCompatibilityMapping]; @@ -2425,7 +2437,7 @@ - (void)testIsDirtyAt { screen.delegate = (id)self; [screen resetDirty]; assert(![screen isDirtyAtX:0 Y:0]); - [screen appendStringAtCursor:@"x" ascii:YES]; + [screen appendStringAtCursor:@"x"]; assert([screen isDirtyAtX:0 Y:0]); [screen clearBuffer]; // Marks everything dirty assert([screen isDirtyAtX:1 Y:1]); @@ -2472,7 +2484,7 @@ - (void)testPrinting { screen.delegate = (id)self; printingAllowed_ = YES; [screen terminalBeginRedirectingToPrintBuffer]; - [screen terminalAppendString:@"test" isAscii:YES]; + [screen terminalAppendString:@"test"]; [screen terminalLineFeed]; [screen terminalPrintBuffer]; assert([printed_ isEqualToString:@"test\n"]); @@ -2480,7 +2492,7 @@ - (void)testPrinting { printingAllowed_ = NO; [screen terminalBeginRedirectingToPrintBuffer]; - [screen terminalAppendString:@"test" isAscii:YES]; + [screen terminalAppendString:@"test"]; assert([triggerLine_ isEqualToString:@"test"]); [screen terminalLineFeed]; assert([triggerLine_ isEqualToString:@""]); @@ -2499,7 +2511,7 @@ - (void)testBackspace { // Normal case VT100Screen *screen = [self screenWithWidth:20 height:3]; screen.delegate = (id)self; - [screen appendStringAtCursor:@"Hello" ascii:YES]; + [screen appendStringAtCursor:@"Hello"]; [screen terminalMoveCursorToX:5 y:1]; [screen terminalBackspace]; assert(screen.cursorX == 4); @@ -2508,7 +2520,7 @@ - (void)testBackspace { // Wrap around soft eol screen = [self screenWithWidth:20 height:3]; screen.delegate = (id)self; - [screen appendStringAtCursor:@"12345678901234567890Hello" ascii:YES]; + [screen appendStringAtCursor:@"12345678901234567890Hello"]; [screen terminalMoveCursorToX:1 y:2]; [screen terminalBackspace]; assert(screen.cursorX == 20); @@ -2535,7 +2547,7 @@ - (void)testBackspace { // Over DWC_SKIP screen = [self screenWithWidth:20 height:3]; screen.delegate = (id)self; - [screen appendStringAtCursor:@"1234567890123456789W" ascii:NO]; + [screen appendStringAtCursor:@"1234567890123456789W"]; [screen terminalMoveCursorToX:1 y:2]; [screen terminalBackspace]; assert(screen.cursorX == 19); @@ -2584,7 +2596,7 @@ - (void)testTabStops { // Tabbing over text doesn't change it screen = [self screenWithWidth:20 height:3]; - [screen appendStringAtCursor:@"0123456789" ascii:YES]; + [screen appendStringAtCursor:@"0123456789"]; [screen terminalMoveCursorToX:1 y:1]; [screen terminalAppendTabAtCursor]; assert([ScreenCharArrayToStringDebug([screen getLineAtScreenIndex:0], @@ -2602,7 +2614,7 @@ - (void)testTabStops { // If there is a single non-nil, then the cursor just moves. screen = [self screenWithWidth:20 height:3]; [screen terminalMoveCursorToX:3 y:1]; - [screen appendStringAtCursor:@"x" ascii:YES]; + [screen appendStringAtCursor:@"x"]; [screen terminalMoveCursorToX:1 y:1]; [screen terminalAppendTabAtCursor]; assert([ScreenCharArrayToStringDebug([screen getLineAtScreenIndex:0], @@ -2712,7 +2724,7 @@ - (void)testSetTopBottomScrollRegion { assert(screen.cursorX == 1); assert(screen.cursorY == 1); [screen terminalMoveCursorToX:5 y:16]; - [screen terminalAppendString:@"Hello" isAscii:YES]; + [screen terminalAppendString:@"Hello"]; assert([ScreenCharArrayToStringDebug([screen getLineAtScreenIndex:15], screen.width) isEqualToString:@"Hello"]); [screen terminalLineFeed];