diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 1fb0ad2ffd..cb270e601b 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -91,7 +91,7 @@ static NSString *MMSymlinkWarningString = - (void)processInputQueue; - (void)handleInputEvent:(int)msgid data:(NSData *)data; + (NSDictionary *)specialKeys; -- (void)handleInsertText:(NSData *)data; +- (void)handleInsertText:(NSString *)text; - (void)handleKeyDown:(NSString *)key modifiers:(int)mods; - (void)queueMessage:(int)msgid data:(NSData *)data; - (void)connectionDidDie:(NSNotification *)notification; @@ -1048,11 +1048,16 @@ static NSString *MMSymlinkWarningString = BOOL interrupt = NO; if (msgid == InterruptMsgID) { interrupt = YES; - } else if (InsertTextMsgID == msgid && data != nil && [data length] == 1) { - char_u *str = (char_u*)[data bytes]; - if ((str[0] == Ctrl_C && ctrl_c_interrupts) || - (str[0] == intr_char && intr_char != Ctrl_C)) - interrupt = YES; + } else if (InsertTextMsgID == msgid && data != nil) { + const void *bytes = [data bytes]; + bytes += sizeof(int); + int len = *((int*)bytes); bytes += sizeof(int); + if (1 == len) { + char_u *str = (char_u*)bytes; + if ((str[0] == Ctrl_C && ctrl_c_interrupts) || + (str[0] == intr_char && intr_char != Ctrl_C)) + interrupt = YES; + } } if (interrupt) { @@ -1063,18 +1068,36 @@ static NSString *MMSymlinkWarningString = // Remove all previous instances of this message from the input queue, else // the input queue may fill up as a result of Vim not being able to keep up - // with the speed at which new messages are received. This avoids annoying - // situations such as when the keyboard repeat rate is higher than what Vim - // can cope with (which would cause a 'stutter' when scrolling by holding - // down 'j' and then when 'j' was released the screen kept scrolling for a - // little while). + // with the speed at which new messages are received. + // Keyboard input is never dropped, unless the input represents and + // auto-repeated key. - int i, count = [inputQueue count]; - for (i = 1; i < count; i+=2) { - if ([[inputQueue objectAtIndex:i-1] intValue] == msgid) { - [inputQueue removeObjectAtIndex:i]; - [inputQueue removeObjectAtIndex:i-1]; - break; + BOOL isKeyRepeat = NO; + BOOL isKeyboardInput = NO; + + if (data && (InsertTextMsgID == msgid || KeyDownMsgID == msgid || + CmdKeyMsgID == msgid)) { + isKeyboardInput = YES; + + // The lowest bit of the first int is set if this key is a repeat. + int flags = *((int*)[data bytes]); + if (flags & 1) + isKeyRepeat = YES; + } + + // Keyboard input is not removed from the queue; repeats are ignored if + // there already is keyboard input on the input queue. + if (isKeyRepeat || !isKeyboardInput) { + int i, count = [inputQueue count]; + for (i = 1; i < count; i+=2) { + if ([[inputQueue objectAtIndex:i-1] intValue] == msgid) { + if (isKeyRepeat) + return; + + [inputQueue removeObjectAtIndex:i]; + [inputQueue removeObjectAtIndex:i-1]; + break; + } } } @@ -1539,18 +1562,21 @@ static NSString *MMSymlinkWarningString = - (void)handleInputEvent:(int)msgid data:(NSData *)data { - if (InsertTextMsgID == msgid) { - [self handleInsertText:data]; - } else if (KeyDownMsgID == msgid || CmdKeyMsgID == msgid) { + if (InsertTextMsgID == msgid || KeyDownMsgID == msgid || + CmdKeyMsgID == msgid) { if (!data) return; const void *bytes = [data bytes]; int mods = *((int*)bytes); bytes += sizeof(int); int len = *((int*)bytes); bytes += sizeof(int); - NSString *key = [[NSString alloc] initWithBytes:bytes length:len - encoding:NSUTF8StringEncoding]; + NSString *key = [[NSString alloc] initWithBytes:bytes + length:len + encoding:NSUTF8StringEncoding]; mods = eventModifierFlagsToVimModMask(mods); - [self handleKeyDown:key modifiers:mods]; + if (InsertTextMsgID == msgid) + [self handleInsertText:key]; + else + [self handleKeyDown:key modifiers:mods]; [key release]; } else if (ScrollWheelMsgID == msgid) { @@ -1732,14 +1758,12 @@ static NSString *MMSymlinkWarningString = return specialKeys; } -- (void)handleInsertText:(NSData *)data +- (void)handleInsertText:(NSString *)text { - if (!data) return; + if (!text) return; - NSString *key = [[NSString alloc] initWithData:data - encoding:NSUTF8StringEncoding]; - char_u *str = (char_u*)[key UTF8String]; - int i, len = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + char_u *str = (char_u*)[text UTF8String]; + int i, len = [text lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; #ifdef FEAT_MBYTE char_u *conv_str = NULL; @@ -1765,7 +1789,6 @@ static NSString *MMSymlinkWarningString = if (conv_str) vim_free(conv_str); #endif - [key release]; } - (void)handleKeyDown:(NSString *)key modifiers:(int)mods diff --git a/src/MacVim/MMTextViewHelper.m b/src/MacVim/MMTextViewHelper.m index 3916d08e04..6142b11082 100644 --- a/src/MacVim/MMTextViewHelper.m +++ b/src/MacVim/MMTextViewHelper.m @@ -38,7 +38,8 @@ static float MMDragAreaSize = 73.0f; - (MMWindowController *)windowController; - (MMVimController *)vimController; - (void)dispatchKeyEvent:(NSEvent *)event; -- (void)sendKeyDown:(const char *)chars length:(int)len modifiers:(int)flags; +- (void)sendKeyDown:(const char *)chars length:(int)len modifiers:(int)flags + isARepeat:(BOOL)isARepeat; - (void)hideMouseCursor; - (void)startDragTimerWithInterval:(NSTimeInterval)t; - (void)dragTimerFired:(NSTimer *)timer; @@ -157,8 +158,17 @@ static float MMDragAreaSize = 73.0f; //NSLog(@"send InsertTextMsgID: %@", string); - [[self vimController] sendMessage:InsertTextMsgID - data:[string dataUsingEncoding:NSUTF8StringEncoding]]; + NSMutableData *data = [NSMutableData data]; + int len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + int flags = [event modifierFlags] & 0xffff0000U; + if ([event isARepeat]) + flags |= 1; + + [data appendBytes:&flags length:sizeof(int)]; + [data appendBytes:&len length:sizeof(int)]; + [data appendBytes:[string UTF8String] length:len]; + + [[self vimController] sendMessage:InsertTextMsgID data:data]; } - (void)doCommandBySelector:(SEL)selector @@ -193,7 +203,8 @@ static float MMDragAreaSize = 73.0f; chars = MMKeypadEnter; } - [self sendKeyDown:chars length:len modifiers:[event modifierFlags]]; + [self sendKeyDown:chars length:len modifiers:[event modifierFlags] + isARepeat:[event isARepeat]]; } else { [self dispatchKeyEvent:event]; } @@ -214,7 +225,7 @@ static float MMDragAreaSize = 73.0f; // stroke (some input methods use e.g. arrow keys). The function key down // event will still reach Vim though (via keyDown:). The exceptions to // this rule are: PageUp/PageDown (keycode 116/121). - int flags = [event modifierFlags]; + int flags = [event modifierFlags] & 0xffff0000U; if ([event type] != NSKeyDown || flags & NSFunctionKeyMask && !(116 == [event keyCode] || 121 == [event keyCode])) return NO; @@ -272,6 +283,9 @@ static float MMDragAreaSize = 73.0f; len = [unmodchars lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; } + if ([event isARepeat]) + flags |= 1; + [data appendBytes:&flags length:sizeof(int)]; [data appendBytes:&len length:sizeof(int)]; [data appendBytes:[unmodchars UTF8String] length:len]; @@ -748,14 +762,22 @@ static float MMDragAreaSize = 73.0f; bytes = [chars UTF8String]; } - [self sendKeyDown:bytes length:len modifiers:mods]; + [self sendKeyDown:bytes length:len modifiers:mods + isARepeat:[event isARepeat]]; } - (void)sendKeyDown:(const char *)chars length:(int)len modifiers:(int)flags + isARepeat:(BOOL)isARepeat { if (chars && len > 0) { NSMutableData *data = [NSMutableData data]; + // The low 16 bits are not used for modifier flags by NSEvent. Use + // these bits for custom flags. + flags &= 0xffff0000; + if (isARepeat) + flags |= 1; + [data appendBytes:&flags length:sizeof(int)]; [data appendBytes:&len length:sizeof(int)]; [data appendBytes:chars length:len];