From 3c50385282d73ca15942f6872ae81abfe72c061a Mon Sep 17 00:00:00 2001 From: Bjorn Winckler Date: Sun, 19 Aug 2007 14:47:17 +0000 Subject: [PATCH] - Request/reply timeout set on root connection object - Check for unresponsive Vim processes in applicationShouldTerminate: and present alert if any are detectd - Send SIGINT to all Vim processes in applicationWillTerminate: - Started adding support for 'enc' (set MM_ENABLE_CONV in MacVim.h) - Cmd-. sends SIGINT to Vim - Use @try/@catch for starRegisterToPasteboard: calls - Removed starRegisterFromPasteboard: message git-svn-id: http://macvim.googlecode.com/svn/trunk@163 96c4425d-ca35-0410-94e5-3396d5c13a8f --- MMAppController.m | 72 +++++++++++++++++++++++++++++++++++++---- MMBackend.m | 28 +++++++++------- MMTextView.m | 18 ++++++++--- MMWindowController.m | 25 +++++++++----- MacVim.h | 13 ++++++-- gui_macvim.m | 77 +++++++++++++++++++++++++++++++++++--------- 6 files changed, 183 insertions(+), 50 deletions(-) diff --git a/MMAppController.m b/MMAppController.m index 41a90d9b8b..695edff41e 100644 --- a/MMAppController.m +++ b/MMAppController.m @@ -14,6 +14,14 @@ +// Default timeout intervals on all connections. +static NSTimeInterval MMRequestTimeout = 5; +static NSTimeInterval MMReplyTimeout = 5; + +// Timeout used when the app should terminate. +static NSTimeInterval MMTerminateTimeout = 3; + + @interface MMAppController (MMServices) - (void)openSelection:(NSPasteboard *)pboard userData:(NSString *)userData @@ -73,6 +81,8 @@ [[NSBundle mainBundle] bundleIdentifier]]; //NSLog(@"Registering connection with name '%@'", name); if ([connection registerName:name]) { + [connection setRequestTimeout:MMRequestTimeout]; + [connection setReplyTimeout:MMReplyTimeout]; [connection setRootObject:self]; // NOTE: When the user is resizing the window the AppKit puts the @@ -188,24 +198,53 @@ { int reply = NSTerminateNow; BOOL modifiedBuffers = NO; + BOOL notResponding = NO; + // Go through vim controllers, checking for modified buffers. If a process + // is not responding then note this as well. unsigned i, count = [vimControllers count]; for (i = 0; i < count; ++i) { MMVimController *controller = [vimControllers objectAtIndex:i]; id proxy = [controller backendProxy]; - if (proxy && [proxy checkForModifiedBuffers]) { - modifiedBuffers = YES; - break; + NSConnection *connection = [proxy connectionForProxy]; + if (connection) { + NSTimeInterval req = [connection requestTimeout]; + NSTimeInterval rep = [connection replyTimeout]; + [connection setRequestTimeout:MMTerminateTimeout]; + [connection setReplyTimeout:MMTerminateTimeout]; + + @try { + if ([proxy checkForModifiedBuffers]) + modifiedBuffers = YES; + } + @catch (NSException *e) { + NSLog(@"WARNING: Got exception while waiting for " + "checkForModifiedBuffers: \"%@\"", e); + notResponding = YES; + } + @finally { + [connection setRequestTimeout:req]; + [connection setReplyTimeout:rep]; + if (modifiedBuffers || notResponding) + break; + } } } - if (modifiedBuffers) { + if (modifiedBuffers || notResponding) { NSAlert *alert = [[NSAlert alloc] init]; [alert addButtonWithTitle:@"Quit"]; [alert addButtonWithTitle:@"Cancel"]; - [alert setMessageText:@"Quit without saving?"]; - [alert setInformativeText:@"There are modified buffers, " - " if you quit now all changes will be lost. Quit anyway?"]; + if (modifiedBuffers) { + [alert setMessageText:@"Quit without saving?"]; + [alert setInformativeText:@"There are modified buffers, " + "if you quit now all changes will be lost. Quit anyway?"]; + } else { + [alert setMessageText:@"Force Quit?"]; + [alert setInformativeText:@"At least one Vim process is not " + "responding, if you quit now any changes you have made " + "will be lost. Quit anyway?"]; + } [alert setAlertStyle:NSWarningAlertStyle]; if ([alert runModal] != NSAlertFirstButtonReturn) { @@ -220,6 +259,23 @@ - (void)applicationWillTerminate:(NSNotification *)aNotification { + // Send a SIGINT to all running Vim processes, so that they are sure to + // receive the connectionDidDie: notification (a process has to checking + // the run-loop for this to happen). + unsigned i, count = [vimControllers count]; + for (i = 0; i < count; ++i) { + MMVimController *controller = [vimControllers objectAtIndex:i]; + int pid = [controller pid]; + if (pid > 0) + kill(pid, SIGINT); + + id proxy = [controller backendProxy]; + NSConnection *connection = [proxy connectionForProxy]; + if (connection) { + [connection invalidate]; + } + } + // NOTE! Is this a correct way of releasing the MMAppController? [NSApp setDelegate:nil]; [self autorelease]; @@ -227,6 +283,8 @@ - (void)removeVimController:(id)controller { + //NSLog(@"%s%@", _cmd, controller); + [[controller windowController] close]; [vimControllers removeObject:controller]; diff --git a/MMBackend.m b/MMBackend.m index 250f6043a9..7b32b92e5b 100644 --- a/MMBackend.m +++ b/MMBackend.m @@ -810,15 +810,6 @@ static int specialKeyToNSKey(int key); return NO; } -- (BOOL)starRegisterFromPasteboard:(byref NSPasteboard *)pboard -{ - if (curbuf && !curbuf->b_p_ro) { - return YES; - } - - return NO; -} - @end // MMBackend @@ -837,12 +828,21 @@ static int specialKeyToNSKey(int key); if (!data) return; NSString *key = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - char_u *chars = (char_u*)[key UTF8String]; + char_u *str = (char_u*)[key UTF8String]; int i, len = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; +#if MM_ENABLE_CONV + char_u *conv_str = NULL; + if (input_conv.vc_type != CONV_NONE) { + conv_str = string_convert(&input_conv, str, &len); + if (conv_str) + str = conv_str; + } +#endif + for (i = 0; i < len; ++i) { - add_to_input_buf(chars+i, 1); - if (CSI == chars[i]) { + add_to_input_buf(str+i, 1); + if (CSI == str[i]) { // NOTE: If the converted string contains the byte CSI, then it // must be followed by the bytes KS_EXTRA, KE_CSI or things // won't work. @@ -851,6 +851,10 @@ static int specialKeyToNSKey(int key); } } +#if MM_ENABLE_CONV + if (conv_str) + vim_free(conv_str); +#endif [key release]; } else if (KeyDownMsgID == msgid || CmdKeyMsgID == msgid) { if (!data) return; diff --git a/MMTextView.m b/MMTextView.m index 69b2f79ef0..fb9b363323 100644 --- a/MMTextView.m +++ b/MMTextView.m @@ -118,16 +118,24 @@ //NSLog(@"%s%@", _cmd, event); - NSMutableData *data = [NSMutableData data]; NSString *string = [event charactersIgnoringModifiers]; int flags = [event modifierFlags]; int len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - [data appendBytes:&flags length:sizeof(int)]; - [data appendBytes:&len length:sizeof(int)]; - [data appendBytes:[string UTF8String] length:len]; + if (len > 0 && [string characterAtIndex:0] == '.') { + // HACK! Intercept Cmd-. and send SIGINT to Vim. + int pid = [[self vimController] pid]; + if (pid > 0) + kill(pid, SIGINT); + } else { + NSMutableData *data = [NSMutableData data]; - [[self vimController] sendMessage:CmdKeyMsgID data:data wait:NO]; + [data appendBytes:&flags length:sizeof(int)]; + [data appendBytes:&len length:sizeof(int)]; + [data appendBytes:[string UTF8String] length:len]; + + [[self vimController] sendMessage:CmdKeyMsgID data:data wait:NO]; + } return YES; } diff --git a/MMWindowController.m b/MMWindowController.m index a42a32be92..46bf965a84 100644 --- a/MMWindowController.m +++ b/MMWindowController.m @@ -662,13 +662,13 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) { id backendProxy = [vimController backendProxy]; - if ((!sendType || [sendType isEqual:NSStringPboardType]) - && (!returnType || [returnType isEqual:NSStringPboardType])) - { - if ((!sendType || [backendProxy starRegisterToPasteboard:nil]) && - (!returnType || [backendProxy starRegisterFromPasteboard:nil])) - { - return self; + if (backendProxy && [sendType isEqual:NSStringPboardType]) { + @try { + if ([backendProxy starRegisterToPasteboard:nil]) + return self; + } + @catch (NSException *e) { + NSLog(@"WARNING: Caught exception in %s: \"%@\"", _cmd, e); } } @@ -682,7 +682,16 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) return NO; id backendProxy = [vimController backendProxy]; - return [backendProxy starRegisterToPasteboard:pboard]; + if (backendProxy) { + @try { + return [backendProxy starRegisterToPasteboard:pboard]; + } + @catch (NSException *e) { + NSLog(@"WARNING: Caught exception in %s: \"%@\"", _cmd, e); + } + } + + return NO; } @end // MMWindowController diff --git a/MacVim.h b/MacVim.h index 5808d67b2b..334c6e710a 100644 --- a/MacVim.h +++ b/MacVim.h @@ -11,20 +11,29 @@ #import +// Enable to use experimental 'enc' support. +#define MM_ENABLE_CONV 0 + // // This is the protocol MMBackend implements. // // Only processInput:data: is allowed to cause state changes in Vim; all other -// messages should only read the Vim state. +// messages should only read the Vim state. (Note that setDialogReturn: is an +// exception to this rule; there really is no other way to deal with dialogs +// since they work with callbacks, so we cannot wait for them to return.) +// +// Be careful with messages with return type other than 'oneway void' -- there +// is a reply timeout set in MMAppController, if a message fails to get a +// response within the given timeout an exception will be thrown. Use +// @try/@catch/@finally to deal with timeouts. // @protocol MMBackendProtocol - (oneway void)processInput:(int)msgid data:(in NSData *)data; - (BOOL)checkForModifiedBuffers; - (oneway void)setDialogReturn:(in bycopy id)obj; - (BOOL)starRegisterToPasteboard:(byref NSPasteboard *)pboard; -- (BOOL)starRegisterFromPasteboard:(byref NSPasteboard *)pboard; @end diff --git a/gui_macvim.m b/gui_macvim.m index 8056e9c4a0..d7f6414ac9 100644 --- a/gui_macvim.m +++ b/gui_macvim.m @@ -305,6 +305,15 @@ gui_macvim_draw_string(int row, int col, char_u *s, int len, int flags) BOOL outPad = NO; MMBackend *backend = [MMBackend sharedInstance]; static char ZeroWidthSpace[] = { 0xe2, 0x80, 0x8b }; +#if MM_ENABLE_CONV + char_u *conv_str = NULL; + + if (output_conv.vc_type != CONV_NONE) { + char_u *conv_str = string_convert(&output_conv, s, &len); + if (conv_str) + s = conv_str; + } +#endif for (i = 0; i < len; i += cl) { c = utf_ptr2char(s + i); @@ -375,6 +384,11 @@ gui_macvim_draw_string(int row, int col, char_u *s, int len, int flags) row:row column:endcol-1 flags:flags]; } +#if MM_ENABLE_CONV + if (conv_str) + vim_free(conv_str); +#endif + return endcol - col; #else // This will fail abysmally when wide or composing characters are used. @@ -475,9 +489,9 @@ clip_mch_own_selection(VimClipboard *cbd) clip_mch_request_selection(VimClipboard *cbd) { NSPasteboard *pb = [NSPasteboard generalPasteboard]; - NSString *type = [pb availableTypeFromArray: + NSString *pbType = [pb availableTypeFromArray: [NSArray arrayWithObject:NSStringPboardType]]; - if (type) { + if (pbType) { NSMutableString *string = [[pb stringForType:NSStringPboardType] mutableCopy]; @@ -496,9 +510,24 @@ clip_mch_request_selection(VimClipboard *cbd) int type = MCHAR; if (0 < n || NSNotFound != [string rangeOfString:@"\n"].location) type = MLINE; - - const char *utf8chars = [string UTF8String]; - clip_yank_selection(type, (char_u*)utf8chars, strlen(utf8chars), cbd); + + char_u *str = (char_u*)[string UTF8String]; + int len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + +#if MM_ENABLE_CONV + if (input_conv.vc_type != CONV_NONE) { + NSLog(@"Converting from: '%@'", string); + char_u *conv_str = string_convert(&input_conv, str, &len); + if (conv_str) { + NSLog(@" to: '%s'", conv_str); + clip_yank_selection(type, conv_str, len, cbd); + vim_free(conv_str); + return; + } + } +#endif + + clip_yank_selection(type, str, len, cbd); } } @@ -515,19 +544,35 @@ clip_mch_set_selection(VimClipboard *cbd) cbd->owned = FALSE; // Get the text to put on the pasteboard. - long_u len = 0; char_u *str = 0; - int type = clip_convert_selection(&str, &len, cbd); + long_u llen = 0; char_u *str = 0; + int type = clip_convert_selection(&str, &llen, cbd); if (type < 0) return; - - NSString *string = [[NSString alloc] initWithBytes:str length:len - encoding:NSUTF8StringEncoding]; - - NSPasteboard *pb = [NSPasteboard generalPasteboard]; - [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - [pb setString:string forType:NSStringPboardType]; - - [string release]; + + // TODO: Avoid overflow. + int len = (int)llen; +#if MM_ENABLE_CONV + if (output_conv.vc_type != CONV_NONE) { + char_u *conv_str = string_convert(&output_conv, str, &len); + if (conv_str) { + vim_free(str); + str = conv_str; + } + } +#endif + + if (len > 0) { + NSString *string = [[NSString alloc] + initWithBytes:str length:len encoding:NSUTF8StringEncoding]; + + NSPasteboard *pb = [NSPasteboard generalPasteboard]; + [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] + owner:nil]; + [pb setString:string forType:NSStringPboardType]; + + [string release]; + } + vim_free(str); }