1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * MacVim GUI port by Bjorn Winckler
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
13 * MMBackend communicates with the frontend (MacVim). It maintains a queue of
14 * output which is flushed to the frontend under controlled circumstances (so
15 * as to maintain a steady framerate). Input from the frontend is also handled
18 * The frontend communicates with the backend via the MMBackendProtocol. In
19 * particular, input is sent to the backend via processInput:data: and Vim
20 * state can be queried from the frontend with evaluateExpression:.
22 * It is very important to realize that all state is held by the backend, the
23 * frontend must either ask for state [MMBackend evaluateExpression:] or wait
24 * for the backend to update [MMVimController processCommandQueue:].
26 * The client/server functionality of Vim is handled by the backend. It sets
27 * up a named NSConnection to which other Vim processes can connect.
34 // NOTE: Colors in MMBackend are stored as unsigned ints on the form 0xaarrggbb
35 // whereas colors in Vim are int without the alpha component. Also note that
36 // 'transp' is assumed to be a value between 0 and 100.
37 #define MM_COLOR(col) ((unsigned)( ((col)&0xffffff) | 0xff000000 ))
38 #define MM_COLOR_WITH_TRANSP(col,transp) \
39 ((unsigned)( ((col)&0xffffff) \
40 | ((((unsigned)((((100-(transp))*255)/100)+.5f))&0xff)<<24) ))
43 // This constant controls how often the command queue may be flushed. If it is
44 // too small the app might feel unresponsive; if it is too large there might be
45 // long periods without the screen updating (e.g. when sourcing a large session
46 // file). (The unit is seconds.)
47 static float MMFlushTimeoutInterval = 0.1f;
48 static int MMFlushQueueLenHint = 80*40;
50 static unsigned MMServerMax = 1000;
52 // TODO: Move to separate file.
53 static int eventModifierFlagsToVimModMask(int modifierFlags);
54 static int vimModMaskToEventModifierFlags(int mods);
55 static int eventModifierFlagsToVimMouseModMask(int modifierFlags);
56 static int eventButtonNumberToVimMouseButton(int buttonNumber);
57 static int specialKeyToNSKey(int key);
65 static NSString *MMSymlinkWarningString =
66 @"\n\n\tMost likely this is because you have symlinked directly to\n"
67 "\tthe Vim binary, which Cocoa does not allow. Please use an\n"
68 "\talias or the mvim shell script instead. If you have not used\n"
69 "\ta symlink, then your MacVim.app bundle is incomplete.\n\n";
73 @interface NSString (MMServerNameCompare)
74 - (NSComparisonResult)serverNameCompare:(NSString *)string;
79 @interface MMBackend (Private)
80 - (void)processInputQueue;
81 - (void)handleInputEvent:(int)msgid data:(NSData *)data;
82 + (NSDictionary *)specialKeys;
83 - (void)handleInsertText:(NSData *)data;
84 - (void)handleKeyDown:(NSString *)key modifiers:(int)mods;
85 - (void)queueMessage:(int)msgid data:(NSData *)data;
86 - (void)connectionDidDie:(NSNotification *)notification;
87 - (void)blinkTimerFired:(NSTimer *)timer;
88 - (void)focusChange:(BOOL)on;
89 - (void)handleToggleToolbar;
90 - (void)handleScrollbarEvent:(NSData *)data;
91 - (void)handleSetFont:(NSData *)data;
92 - (void)handleDropFiles:(NSData *)data;
93 - (void)handleDropString:(NSData *)data;
94 - (void)handleOdbEdit:(NSData *)data;
95 - (void)handleXcodeMod:(NSData *)data;
96 - (BOOL)checkForModifiedBuffers;
97 - (void)addInput:(NSString *)input;
102 @interface MMBackend (ClientServer)
103 - (NSString *)connectionNameFromServerName:(NSString *)name;
104 - (NSConnection *)connectionForServerName:(NSString *)name;
105 - (NSConnection *)connectionForServerPort:(int)port;
106 - (void)serverConnectionDidDie:(NSNotification *)notification;
107 - (void)addClient:(NSDistantObject *)client;
108 - (NSString *)alternateServerNameForName:(NSString *)name;
113 @implementation MMBackend
115 + (MMBackend *)sharedInstance
117 static MMBackend *singleton = nil;
118 return singleton ? singleton : (singleton = [MMBackend new]);
124 if (!self) return nil;
126 fontContainerRef = loadFonts();
128 outputQueue = [[NSMutableArray alloc] init];
129 inputQueue = [[NSMutableArray alloc] init];
130 drawData = [[NSMutableData alloc] initWithCapacity:1024];
131 connectionNameDict = [[NSMutableDictionary alloc] init];
132 clientProxyDict = [[NSMutableDictionary alloc] init];
133 serverReplyDict = [[NSMutableDictionary alloc] init];
135 NSBundle *mainBundle = [NSBundle mainBundle];
136 NSString *path = [mainBundle pathForResource:@"Colors" ofType:@"plist"];
138 colorDict = [[NSDictionary dictionaryWithContentsOfFile:path] retain];
140 path = [mainBundle pathForResource:@"SystemColors" ofType:@"plist"];
142 sysColorDict = [[NSDictionary dictionaryWithContentsOfFile:path]
145 if (!(colorDict && sysColorDict))
146 NSLog(@"ERROR: Failed to load color dictionaries.%@",
147 MMSymlinkWarningString);
154 //NSLog(@"%@ %s", [self className], _cmd);
155 [[NSNotificationCenter defaultCenter] removeObserver:self];
157 [oldWideFont release]; oldWideFont = nil;
158 [blinkTimer release]; blinkTimer = nil;
159 [alternateServerName release]; alternateServerName = nil;
160 [serverReplyDict release]; serverReplyDict = nil;
161 [clientProxyDict release]; clientProxyDict = nil;
162 [connectionNameDict release]; connectionNameDict = nil;
163 [inputQueue release]; inputQueue = nil;
164 [outputQueue release]; outputQueue = nil;
165 [drawData release]; drawData = nil;
166 [frontendProxy release]; frontendProxy = nil;
167 [connection release]; connection = nil;
168 [sysColorDict release]; sysColorDict = nil;
169 [colorDict release]; colorDict = nil;
174 - (void)setBackgroundColor:(int)color
176 backgroundColor = MM_COLOR_WITH_TRANSP(color,p_transp);
179 - (void)setForegroundColor:(int)color
181 foregroundColor = MM_COLOR(color);
184 - (void)setSpecialColor:(int)color
186 specialColor = MM_COLOR(color);
189 - (void)setDefaultColorsBackground:(int)bg foreground:(int)fg
191 defaultBackgroundColor = MM_COLOR_WITH_TRANSP(bg,p_transp);
192 defaultForegroundColor = MM_COLOR(fg);
194 NSMutableData *data = [NSMutableData data];
196 [data appendBytes:&defaultBackgroundColor length:sizeof(unsigned)];
197 [data appendBytes:&defaultForegroundColor length:sizeof(unsigned)];
199 [self queueMessage:SetDefaultColorsMsgID data:data];
202 - (NSConnection *)connection
205 // NOTE! If the name of the connection changes here it must also be
206 // updated in MMAppController.m.
207 NSString *name = [NSString stringWithFormat:@"%@-connection",
208 [[NSBundle mainBundle] bundleIdentifier]];
210 connection = [NSConnection connectionWithRegisteredName:name host:nil];
214 // NOTE: 'connection' may be nil here.
220 if (![self connection]) {
221 NSBundle *mainBundle = [NSBundle mainBundle];
226 // Launch MacVim using Launch Services (NSWorkspace would be nicer, but
227 // the API to pass Apple Event parameters is broken on 10.4).
228 NSString *path = [mainBundle bundlePath];
229 status = FSPathMakeRef((const UInt8 *)[path UTF8String], &ref, NULL);
230 if (noErr == status) {
231 // Pass parameter to the 'Open' Apple Event that tells MacVim not
232 // to open an untitled window.
233 NSAppleEventDescriptor *desc =
234 [NSAppleEventDescriptor recordDescriptor];
235 [desc setParamDescriptor:
236 [NSAppleEventDescriptor descriptorWithBoolean:NO]
237 forKeyword:keyMMUntitledWindow];
239 LSLaunchFSRefSpec spec = { &ref, 0, NULL, [desc aeDesc],
240 kLSLaunchDefaults, NULL };
241 status = LSOpenFromRefSpec(&spec, NULL);
244 if (noErr != status) {
245 NSLog(@"ERROR: Failed to launch MacVim (path=%@).%@",
246 path, MMSymlinkWarningString);
250 // Launch MacVim using NSTask. For some reason the above code using
251 // Launch Services sometimes fails on LSOpenFromRefSpec() (when it
252 // fails, the dock icon starts bouncing and never stops). It seems
253 // like rebuilding the Launch Services database takes care of this
254 // problem, but the NSTask way seems more stable so stick with it.
256 // NOTE! Using NSTask to launch the GUI has the negative side-effect
257 // that the GUI won't be activated (or raised) so there is a hack in
258 // MMAppController which raises the app when a new window is opened.
259 NSMutableArray *args = [NSMutableArray arrayWithObjects:
260 [NSString stringWithFormat:@"-%@", MMNoWindowKey], @"yes", nil];
261 NSString *exeName = [[mainBundle infoDictionary]
262 objectForKey:@"CFBundleExecutable"];
263 NSString *path = [mainBundle pathForAuxiliaryExecutable:exeName];
265 NSLog(@"ERROR: Could not find MacVim executable in bundle.%@",
266 MMSymlinkWarningString);
270 [NSTask launchedTaskWithLaunchPath:path arguments:args];
273 // HACK! Poll the mach bootstrap server until it returns a valid
274 // connection to detect that MacVim has finished launching. Also set a
275 // time-out date so that we don't get stuck doing this forever.
276 NSDate *timeOutDate = [NSDate dateWithTimeIntervalSinceNow:10];
277 while (![self connection] &&
278 NSOrderedDescending == [timeOutDate compare:[NSDate date]])
279 [[NSRunLoop currentRunLoop]
280 runMode:NSDefaultRunLoopMode
281 beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
283 // NOTE: [self connection] will set 'connection' as a side-effect.
285 NSLog(@"WARNING: Timed-out waiting for GUI to launch.");
290 id proxy = [connection rootProxy];
291 [proxy setProtocolForProxy:@protocol(MMAppProtocol)];
293 [[NSNotificationCenter defaultCenter] addObserver:self
294 selector:@selector(connectionDidDie:)
295 name:NSConnectionDidDieNotification object:connection];
297 int pid = [[NSProcessInfo processInfo] processIdentifier];
300 frontendProxy = [proxy connectBackend:self pid:pid];
302 @catch (NSException *e) {
303 NSLog(@"Exception caught when trying to connect backend: \"%@\"", e);
307 [frontendProxy retain];
308 [frontendProxy setProtocolForProxy:@protocol(MMAppProtocol)];
311 return connection && frontendProxy;
314 - (BOOL)openVimWindow
316 [self queueMessage:OpenVimWindowMsgID data:nil];
322 int type = ClearAllDrawType;
324 // Any draw commands in queue are effectively obsolete since this clearAll
325 // will negate any effect they have, therefore we may as well clear the
327 [drawData setLength:0];
329 [drawData appendBytes:&type length:sizeof(int)];
332 - (void)clearBlockFromRow:(int)row1 column:(int)col1
333 toRow:(int)row2 column:(int)col2
335 int type = ClearBlockDrawType;
337 [drawData appendBytes:&type length:sizeof(int)];
339 [drawData appendBytes:&defaultBackgroundColor length:sizeof(unsigned)];
340 [drawData appendBytes:&row1 length:sizeof(int)];
341 [drawData appendBytes:&col1 length:sizeof(int)];
342 [drawData appendBytes:&row2 length:sizeof(int)];
343 [drawData appendBytes:&col2 length:sizeof(int)];
346 - (void)deleteLinesFromRow:(int)row count:(int)count
347 scrollBottom:(int)bottom left:(int)left right:(int)right
349 int type = DeleteLinesDrawType;
351 [drawData appendBytes:&type length:sizeof(int)];
353 [drawData appendBytes:&defaultBackgroundColor length:sizeof(unsigned)];
354 [drawData appendBytes:&row length:sizeof(int)];
355 [drawData appendBytes:&count length:sizeof(int)];
356 [drawData appendBytes:&bottom length:sizeof(int)];
357 [drawData appendBytes:&left length:sizeof(int)];
358 [drawData appendBytes:&right length:sizeof(int)];
361 - (void)drawString:(char*)s length:(int)len row:(int)row column:(int)col
362 cells:(int)cells flags:(int)flags
364 if (len <= 0 || cells <= 0) return;
366 int type = DrawStringDrawType;
368 [drawData appendBytes:&type length:sizeof(int)];
370 [drawData appendBytes:&backgroundColor length:sizeof(unsigned)];
371 [drawData appendBytes:&foregroundColor length:sizeof(unsigned)];
372 [drawData appendBytes:&specialColor length:sizeof(unsigned)];
373 [drawData appendBytes:&row length:sizeof(int)];
374 [drawData appendBytes:&col length:sizeof(int)];
375 [drawData appendBytes:&cells length:sizeof(int)];
376 [drawData appendBytes:&flags length:sizeof(int)];
377 [drawData appendBytes:&len length:sizeof(int)];
378 [drawData appendBytes:s length:len];
381 - (void)insertLinesFromRow:(int)row count:(int)count
382 scrollBottom:(int)bottom left:(int)left right:(int)right
384 int type = InsertLinesDrawType;
386 [drawData appendBytes:&type length:sizeof(int)];
388 [drawData appendBytes:&defaultBackgroundColor length:sizeof(unsigned)];
389 [drawData appendBytes:&row length:sizeof(int)];
390 [drawData appendBytes:&count length:sizeof(int)];
391 [drawData appendBytes:&bottom length:sizeof(int)];
392 [drawData appendBytes:&left length:sizeof(int)];
393 [drawData appendBytes:&right length:sizeof(int)];
396 - (void)drawCursorAtRow:(int)row column:(int)col shape:(int)shape
397 fraction:(int)percent color:(int)color
399 int type = DrawCursorDrawType;
400 unsigned uc = MM_COLOR(color);
402 [drawData appendBytes:&type length:sizeof(int)];
404 [drawData appendBytes:&uc length:sizeof(unsigned)];
405 [drawData appendBytes:&row length:sizeof(int)];
406 [drawData appendBytes:&col length:sizeof(int)];
407 [drawData appendBytes:&shape length:sizeof(int)];
408 [drawData appendBytes:&percent length:sizeof(int)];
413 // Tend to the run loop, returning immediately if there are no events
415 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
416 beforeDate:[NSDate distantPast]];
419 // Keyboard and mouse input is handled directly, other input is queued and
420 // processed here. This call may enter a blocking loop.
421 if ([inputQueue count] > 0)
422 [self processInputQueue];
426 - (void)flushQueue:(BOOL)force
428 // NOTE! This method gets called a lot; if we were to flush every time it
429 // got called MacVim would feel unresponsive. So there is a time out which
430 // ensures that the queue isn't flushed too often.
431 if (!force && lastFlushDate && -[lastFlushDate timeIntervalSinceNow]
432 < MMFlushTimeoutInterval
433 && [drawData length] < MMFlushQueueLenHint)
436 if ([drawData length] > 0) {
437 // HACK! Detect changes to 'guifontwide'.
438 if (gui.wide_font != (GuiFont)oldWideFont) {
439 [oldWideFont release];
440 oldWideFont = [(NSFont*)gui.wide_font retain];
441 [self setWideFont:oldWideFont];
444 [self queueMessage:BatchDrawMsgID data:[drawData copy]];
445 [drawData setLength:0];
448 if ([outputQueue count] > 0) {
450 [frontendProxy processCommandQueue:outputQueue];
452 @catch (NSException *e) {
453 NSLog(@"Exception caught when processing command queue: \"%@\"", e);
456 [outputQueue removeAllObjects];
458 [lastFlushDate release];
459 lastFlushDate = [[NSDate date] retain];
463 - (BOOL)waitForInput:(int)milliseconds
465 //NSLog(@"|ENTER| %s%d", _cmd, milliseconds);
467 // Only start the run loop if the input queue is empty, otherwise process
468 // the input first so that the input on queue isn't delayed.
469 if ([inputQueue count]) {
472 NSDate *date = milliseconds > 0 ?
473 [NSDate dateWithTimeIntervalSinceNow:.001*milliseconds] :
474 [NSDate distantFuture];
476 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
480 // I know of no way to figure out if the run loop exited because input was
481 // found or because of a time out, so I need to manually indicate when
482 // input was received in processInput:data: and then reset it every time
484 BOOL yn = inputReceived;
487 // Keyboard and mouse input is handled directly, other input is queued and
488 // processed here. This call may enter a blocking loop.
489 if ([inputQueue count] > 0)
490 [self processInputQueue];
492 //NSLog(@"|LEAVE| %s input=%d", _cmd, yn);
498 #ifdef MAC_CLIENTSERVER
499 // The default connection is used for the client/server code.
500 [[NSConnection defaultConnection] setRootObject:nil];
501 [[NSConnection defaultConnection] invalidate];
504 // By invalidating the NSConnection the MMWindowController immediately
505 // finds out that the connection is down and as a result
506 // [MMWindowController connectionDidDie:] is invoked.
507 //NSLog(@"%@ %s", [self className], _cmd);
508 [[NSNotificationCenter defaultCenter] removeObserver:self];
509 [connection invalidate];
511 if (fontContainerRef) {
512 ATSFontDeactivate(fontContainerRef, NULL, kATSOptionFlagsDefault);
513 fontContainerRef = 0;
518 - (void)selectTab:(int)index
520 //NSLog(@"%s%d", _cmd, index);
523 NSData *data = [NSData dataWithBytes:&index length:sizeof(int)];
524 [self queueMessage:SelectTabMsgID data:data];
529 //NSLog(@"%s", _cmd);
531 NSMutableData *data = [NSMutableData data];
533 int idx = tabpage_index(curtab) - 1;
534 [data appendBytes:&idx length:sizeof(int)];
537 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
538 // This function puts the label of the tab in the global 'NameBuff'.
539 get_tabline_label(tp, FALSE);
540 char_u *s = NameBuff;
542 if (len <= 0) continue;
545 s = CONVERT_TO_UTF8(s);
548 // Count the number of windows in the tabpage.
549 //win_T *wp = tp->tp_firstwin;
551 //for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount);
553 //[data appendBytes:&wincount length:sizeof(int)];
554 [data appendBytes:&len length:sizeof(int)];
555 [data appendBytes:s length:len];
558 CONVERT_TO_UTF8_FREE(s);
562 [self queueMessage:UpdateTabBarMsgID data:data];
565 - (BOOL)tabBarVisible
567 return tabBarVisible;
570 - (void)showTabBar:(BOOL)enable
572 tabBarVisible = enable;
574 int msgid = enable ? ShowTabBarMsgID : HideTabBarMsgID;
575 [self queueMessage:msgid data:nil];
578 - (void)setRows:(int)rows columns:(int)cols
580 //NSLog(@"[VimTask] setRows:%d columns:%d", rows, cols);
582 int dim[] = { rows, cols };
583 NSData *data = [NSData dataWithBytes:&dim length:2*sizeof(int)];
585 [self queueMessage:SetTextDimensionsMsgID data:data];
588 - (void)setWindowTitle:(char *)title
590 NSMutableData *data = [NSMutableData data];
591 int len = strlen(title);
592 if (len <= 0) return;
594 [data appendBytes:&len length:sizeof(int)];
595 [data appendBytes:title length:len];
597 [self queueMessage:SetWindowTitleMsgID data:data];
600 - (char *)browseForFileInDirectory:(char *)dir title:(char *)title
603 //NSLog(@"browseForFileInDirectory:%s title:%s saving:%d", dir, title,
607 NSString *ds = dir ? [NSString stringWithUTF8String:dir] : nil;
608 NSString *ts = title ? [NSString stringWithUTF8String:title] : nil;
610 [frontendProxy showSavePanelForDirectory:ds title:ts saving:saving];
612 // Wait until a reply is sent from MMVimController.
613 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
614 beforeDate:[NSDate distantFuture]];
616 if (dialogReturn && [dialogReturn isKindOfClass:[NSString class]]) {
617 char_u *ret = (char_u*)[dialogReturn UTF8String];
619 ret = CONVERT_FROM_UTF8(ret);
621 s = vim_strsave(ret);
623 CONVERT_FROM_UTF8_FREE(ret);
627 [dialogReturn release]; dialogReturn = nil;
629 @catch (NSException *e) {
630 NSLog(@"Exception caught when showing save panel: \"%@\"", e);
636 - (oneway void)setDialogReturn:(in bycopy id)obj
638 // NOTE: This is called by
639 // - [MMVimController panelDidEnd:::], and
640 // - [MMVimController alertDidEnd:::],
641 // to indicate that a save/open panel or alert has finished.
643 if (obj != dialogReturn) {
644 [dialogReturn release];
645 dialogReturn = [obj retain];
649 - (int)presentDialogWithType:(int)type title:(char *)title message:(char *)msg
650 buttons:(char *)btns textField:(char *)txtfield
653 NSString *message = nil, *text = nil, *textFieldString = nil;
654 NSArray *buttons = nil;
655 int style = NSInformationalAlertStyle;
657 if (VIM_WARNING == type) style = NSWarningAlertStyle;
658 else if (VIM_ERROR == type) style = NSCriticalAlertStyle;
661 NSString *btnString = [NSString stringWithUTF8String:btns];
662 buttons = [btnString componentsSeparatedByString:@"\n"];
665 message = [NSString stringWithUTF8String:title];
667 text = [NSString stringWithUTF8String:msg];
669 // HACK! If there is a '\n\n' or '\n' sequence in the message, then
670 // make the part up to there into the title. We only do this
671 // because Vim has lots of dialogs without a title and they look
673 // TODO: Fix the actual dialog texts.
674 NSRange eolRange = [text rangeOfString:@"\n\n"];
675 if (NSNotFound == eolRange.location)
676 eolRange = [text rangeOfString:@"\n"];
677 if (NSNotFound != eolRange.location) {
678 message = [text substringToIndex:eolRange.location];
679 text = [text substringFromIndex:NSMaxRange(eolRange)];
684 textFieldString = [NSString stringWithUTF8String:txtfield];
687 [frontendProxy presentDialogWithStyle:style message:message
688 informativeText:text buttonTitles:buttons
689 textFieldString:textFieldString];
691 // Wait until a reply is sent from MMVimController.
692 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
693 beforeDate:[NSDate distantFuture]];
695 if (dialogReturn && [dialogReturn isKindOfClass:[NSArray class]]
696 && [dialogReturn count]) {
697 retval = [[dialogReturn objectAtIndex:0] intValue];
698 if (txtfield && [dialogReturn count] > 1) {
699 NSString *retString = [dialogReturn objectAtIndex:1];
700 char_u *ret = (char_u*)[retString UTF8String];
702 ret = CONVERT_FROM_UTF8(ret);
704 vim_strncpy((char_u*)txtfield, ret, IOSIZE - 1);
706 CONVERT_FROM_UTF8_FREE(ret);
711 [dialogReturn release]; dialogReturn = nil;
713 @catch (NSException *e) {
714 NSLog(@"Exception caught while showing alert dialog: \"%@\"", e);
720 - (void)addMenuWithTag:(int)tag parent:(int)parentTag name:(char *)name
723 //NSLog(@"addMenuWithTag:%d parent:%d name:%s atIndex:%d", tag, parentTag,
726 int namelen = name ? strlen(name) : 0;
727 NSMutableData *data = [NSMutableData data];
729 [data appendBytes:&tag length:sizeof(int)];
730 [data appendBytes:&parentTag length:sizeof(int)];
731 [data appendBytes:&namelen length:sizeof(int)];
732 if (namelen > 0) [data appendBytes:name length:namelen];
733 [data appendBytes:&index length:sizeof(int)];
735 [self queueMessage:AddMenuMsgID data:data];
738 - (void)addMenuItemWithTag:(int)tag parent:(int)parentTag name:(char *)name
739 tip:(char *)tip icon:(char *)icon
740 keyEquivalent:(int)key modifiers:(int)mods
741 action:(NSString *)action atIndex:(int)index
743 //NSLog(@"addMenuItemWithTag:%d parent:%d name:%s tip:%s atIndex:%d", tag,
744 // parentTag, name, tip, index);
746 int namelen = name ? strlen(name) : 0;
747 int tiplen = tip ? strlen(tip) : 0;
748 int iconlen = icon ? strlen(icon) : 0;
749 int eventFlags = vimModMaskToEventModifierFlags(mods);
750 int actionlen = [action lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
751 NSMutableData *data = [NSMutableData data];
753 key = specialKeyToNSKey(key);
755 [data appendBytes:&tag length:sizeof(int)];
756 [data appendBytes:&parentTag length:sizeof(int)];
757 [data appendBytes:&namelen length:sizeof(int)];
758 if (namelen > 0) [data appendBytes:name length:namelen];
759 [data appendBytes:&tiplen length:sizeof(int)];
760 if (tiplen > 0) [data appendBytes:tip length:tiplen];
761 [data appendBytes:&iconlen length:sizeof(int)];
762 if (iconlen > 0) [data appendBytes:icon length:iconlen];
763 [data appendBytes:&actionlen length:sizeof(int)];
764 if (actionlen > 0) [data appendBytes:[action UTF8String] length:actionlen];
765 [data appendBytes:&index length:sizeof(int)];
766 [data appendBytes:&key length:sizeof(int)];
767 [data appendBytes:&eventFlags length:sizeof(int)];
769 [self queueMessage:AddMenuItemMsgID data:data];
772 - (void)removeMenuItemWithTag:(int)tag
774 NSMutableData *data = [NSMutableData data];
775 [data appendBytes:&tag length:sizeof(int)];
777 [self queueMessage:RemoveMenuItemMsgID data:data];
780 - (void)enableMenuItemWithTag:(int)tag state:(int)enabled
782 NSMutableData *data = [NSMutableData data];
784 [data appendBytes:&tag length:sizeof(int)];
785 [data appendBytes:&enabled length:sizeof(int)];
787 [self queueMessage:EnableMenuItemMsgID data:data];
790 - (void)showPopupMenuWithName:(char *)name atMouseLocation:(BOOL)mouse
792 int len = strlen(name);
793 int row = -1, col = -1;
795 if (len <= 0) return;
797 if (!mouse && curwin) {
798 row = curwin->w_wrow;
799 col = curwin->w_wcol;
802 NSMutableData *data = [NSMutableData data];
804 [data appendBytes:&row length:sizeof(int)];
805 [data appendBytes:&col length:sizeof(int)];
806 [data appendBytes:&len length:sizeof(int)];
807 [data appendBytes:name length:len];
809 [self queueMessage:ShowPopupMenuMsgID data:data];
812 - (void)showToolbar:(int)enable flags:(int)flags
814 NSMutableData *data = [NSMutableData data];
816 [data appendBytes:&enable length:sizeof(int)];
817 [data appendBytes:&flags length:sizeof(int)];
819 [self queueMessage:ShowToolbarMsgID data:data];
822 - (void)createScrollbarWithIdentifier:(long)ident type:(int)type
824 NSMutableData *data = [NSMutableData data];
826 [data appendBytes:&ident length:sizeof(long)];
827 [data appendBytes:&type length:sizeof(int)];
829 [self queueMessage:CreateScrollbarMsgID data:data];
832 - (void)destroyScrollbarWithIdentifier:(long)ident
834 NSMutableData *data = [NSMutableData data];
835 [data appendBytes:&ident length:sizeof(long)];
837 [self queueMessage:DestroyScrollbarMsgID data:data];
840 - (void)showScrollbarWithIdentifier:(long)ident state:(int)visible
842 NSMutableData *data = [NSMutableData data];
844 [data appendBytes:&ident length:sizeof(long)];
845 [data appendBytes:&visible length:sizeof(int)];
847 [self queueMessage:ShowScrollbarMsgID data:data];
850 - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident
852 NSMutableData *data = [NSMutableData data];
854 [data appendBytes:&ident length:sizeof(long)];
855 [data appendBytes:&pos length:sizeof(int)];
856 [data appendBytes:&len length:sizeof(int)];
858 [self queueMessage:SetScrollbarPositionMsgID data:data];
861 - (void)setScrollbarThumbValue:(long)val size:(long)size max:(long)max
862 identifier:(long)ident
864 float fval = max-size+1 > 0 ? (float)val/(max-size+1) : 0;
865 float prop = (float)size/(max+1);
866 if (fval < 0) fval = 0;
867 else if (fval > 1.0f) fval = 1.0f;
868 if (prop < 0) prop = 0;
869 else if (prop > 1.0f) prop = 1.0f;
871 NSMutableData *data = [NSMutableData data];
873 [data appendBytes:&ident length:sizeof(long)];
874 [data appendBytes:&fval length:sizeof(float)];
875 [data appendBytes:&prop length:sizeof(float)];
877 [self queueMessage:SetScrollbarThumbMsgID data:data];
880 - (void)setFont:(NSFont *)font
882 NSString *fontName = [font displayName];
883 float size = [font pointSize];
884 int len = [fontName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
886 NSMutableData *data = [NSMutableData data];
888 [data appendBytes:&size length:sizeof(float)];
889 [data appendBytes:&len length:sizeof(int)];
890 [data appendBytes:[fontName UTF8String] length:len];
892 [self queueMessage:SetFontMsgID data:data];
896 - (void)setWideFont:(NSFont *)font
898 NSString *fontName = [font displayName];
899 float size = [font pointSize];
900 int len = [fontName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
901 NSMutableData *data = [NSMutableData data];
903 [data appendBytes:&size length:sizeof(float)];
904 [data appendBytes:&len length:sizeof(int)];
906 [data appendBytes:[fontName UTF8String] length:len];
908 [self queueMessage:SetWideFontMsgID data:data];
911 - (void)executeActionWithName:(NSString *)name
913 int len = [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
916 NSMutableData *data = [NSMutableData data];
918 [data appendBytes:&len length:sizeof(int)];
919 [data appendBytes:[name UTF8String] length:len];
921 [self queueMessage:ExecuteActionMsgID data:data];
925 - (void)setMouseShape:(int)shape
927 NSMutableData *data = [NSMutableData data];
928 [data appendBytes:&shape length:sizeof(int)];
929 [self queueMessage:SetMouseShapeMsgID data:data];
932 - (void)setBlinkWait:(int)wait on:(int)on off:(int)off
934 // Vim specifies times in milliseconds, whereas Cocoa wants them in
936 blinkWaitInterval = .001f*wait;
937 blinkOnInterval = .001f*on;
938 blinkOffInterval = .001f*off;
944 [blinkTimer invalidate];
945 [blinkTimer release];
949 if (blinkWaitInterval > 0 && blinkOnInterval > 0 && blinkOffInterval > 0
951 blinkState = MMBlinkStateOn;
953 [[NSTimer scheduledTimerWithTimeInterval:blinkWaitInterval
955 selector:@selector(blinkTimerFired:)
956 userInfo:nil repeats:NO] retain];
957 gui_update_cursor(TRUE, FALSE);
958 [self flushQueue:YES];
964 if (MMBlinkStateOff == blinkState) {
965 gui_update_cursor(TRUE, FALSE);
966 [self flushQueue:YES];
969 blinkState = MMBlinkStateNone;
972 - (void)adjustLinespace:(int)linespace
974 NSMutableData *data = [NSMutableData data];
975 [data appendBytes:&linespace length:sizeof(int)];
976 [self queueMessage:AdjustLinespaceMsgID data:data];
981 [self queueMessage:ActivateMsgID data:nil];
984 - (void)setPreEditRow:(int)row column:(int)col
986 NSMutableData *data = [NSMutableData data];
987 [data appendBytes:&row length:sizeof(int)];
988 [data appendBytes:&col length:sizeof(int)];
989 [self queueMessage:SetPreEditPositionMsgID data:data];
992 - (int)lookupColorWithKey:(NSString *)key
994 if (!(key && [key length] > 0))
997 NSString *stripKey = [[[[key lowercaseString]
998 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]
999 componentsSeparatedByString:@" "]
1000 componentsJoinedByString:@""];
1002 if (stripKey && [stripKey length] > 0) {
1003 // First of all try to lookup key in the color dictionary; note that
1004 // all keys in this dictionary are lowercase with no whitespace.
1005 id obj = [colorDict objectForKey:stripKey];
1006 if (obj) return [obj intValue];
1008 // The key was not in the dictionary; is it perhaps of the form
1010 if ([stripKey length] > 1 && [stripKey characterAtIndex:0] == '#') {
1011 NSScanner *scanner = [NSScanner scannerWithString:stripKey];
1012 [scanner setScanLocation:1];
1014 if ([scanner scanHexInt:&hex]) {
1019 // As a last resort, check if it is one of the system defined colors.
1020 // The keys in this dictionary are also lowercase with no whitespace.
1021 obj = [sysColorDict objectForKey:stripKey];
1023 NSColor *col = [NSColor performSelector:NSSelectorFromString(obj)];
1026 col = [col colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
1027 [col getRed:&r green:&g blue:&b alpha:&a];
1028 return (((int)(r*255+.5f) & 0xff) << 16)
1029 + (((int)(g*255+.5f) & 0xff) << 8)
1030 + ((int)(b*255+.5f) & 0xff);
1035 //NSLog(@"WARNING: No color with key %@ found.", stripKey);
1039 - (BOOL)hasSpecialKeyWithValue:(NSString *)value
1041 NSEnumerator *e = [[MMBackend specialKeys] objectEnumerator];
1044 while ((obj = [e nextObject])) {
1045 if ([value isEqual:obj])
1052 - (void)enterFullscreen
1054 [self queueMessage:EnterFullscreenMsgID data:nil];
1057 - (void)leaveFullscreen
1059 [self queueMessage:LeaveFullscreenMsgID data:nil];
1062 - (void)updateModifiedFlag
1064 // Notify MacVim if _any_ buffer has changed from unmodified to modified or
1066 int msgid = [self checkForModifiedBuffers]
1067 ? BuffersModifiedMsgID : BuffersNotModifiedMsgID;
1069 [self queueMessage:msgid data:nil];
1072 - (oneway void)processInput:(int)msgid data:(in bycopy NSData *)data
1074 // NOTE: This method might get called whenever the run loop is tended to.
1075 // Normal keyboard and mouse input is added to input buffers, so there is
1076 // no risk in handling these events directly (they return immediately, and
1077 // do not call any other Vim functions). However, other events such
1078 // as 'VimShouldCloseMsgID' may enter blocking loops that wait for key
1079 // events which would cause this method to be called recursively. This
1080 // in turn leads to various difficulties that we do not want to have to
1081 // deal with. To avoid recursive calls here we add all events except
1082 // keyboard and mouse events to an input queue which is processed whenever
1083 // gui_mch_update() is called (see processInputQueue).
1085 //NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
1087 // Don't flush too soon after receiving input or update speed will suffer.
1088 [lastFlushDate release];
1089 lastFlushDate = [[NSDate date] retain];
1091 // Handle keyboard and mouse input now. All other events are queued.
1092 if (InsertTextMsgID == msgid) {
1093 [self handleInsertText:data];
1094 } else if (KeyDownMsgID == msgid || CmdKeyMsgID == msgid) {
1096 const void *bytes = [data bytes];
1097 int mods = *((int*)bytes); bytes += sizeof(int);
1098 int len = *((int*)bytes); bytes += sizeof(int);
1099 NSString *key = [[NSString alloc] initWithBytes:bytes length:len
1100 encoding:NSUTF8StringEncoding];
1101 mods = eventModifierFlagsToVimModMask(mods);
1103 [self handleKeyDown:key modifiers:mods];
1106 } else if (ScrollWheelMsgID == msgid) {
1108 const void *bytes = [data bytes];
1110 int row = *((int*)bytes); bytes += sizeof(int);
1111 int col = *((int*)bytes); bytes += sizeof(int);
1112 int flags = *((int*)bytes); bytes += sizeof(int);
1113 float dy = *((float*)bytes); bytes += sizeof(float);
1115 int button = MOUSE_5;
1116 if (dy > 0) button = MOUSE_4;
1118 flags = eventModifierFlagsToVimMouseModMask(flags);
1120 gui_send_mouse_event(button, col, row, NO, flags);
1121 } else if (MouseDownMsgID == msgid) {
1123 const void *bytes = [data bytes];
1125 int row = *((int*)bytes); bytes += sizeof(int);
1126 int col = *((int*)bytes); bytes += sizeof(int);
1127 int button = *((int*)bytes); bytes += sizeof(int);
1128 int flags = *((int*)bytes); bytes += sizeof(int);
1129 int count = *((int*)bytes); bytes += sizeof(int);
1131 button = eventButtonNumberToVimMouseButton(button);
1133 flags = eventModifierFlagsToVimMouseModMask(flags);
1134 gui_send_mouse_event(button, col, row, count>1, flags);
1136 } else if (MouseUpMsgID == msgid) {
1138 const void *bytes = [data bytes];
1140 int row = *((int*)bytes); bytes += sizeof(int);
1141 int col = *((int*)bytes); bytes += sizeof(int);
1142 int flags = *((int*)bytes); bytes += sizeof(int);
1144 flags = eventModifierFlagsToVimMouseModMask(flags);
1146 gui_send_mouse_event(MOUSE_RELEASE, col, row, NO, flags);
1147 } else if (MouseDraggedMsgID == msgid) {
1149 const void *bytes = [data bytes];
1151 int row = *((int*)bytes); bytes += sizeof(int);
1152 int col = *((int*)bytes); bytes += sizeof(int);
1153 int flags = *((int*)bytes); bytes += sizeof(int);
1155 flags = eventModifierFlagsToVimMouseModMask(flags);
1157 gui_send_mouse_event(MOUSE_DRAG, col, row, NO, flags);
1158 } else if (MouseMovedMsgID == msgid) {
1159 const void *bytes = [data bytes];
1160 int row = *((int*)bytes); bytes += sizeof(int);
1161 int col = *((int*)bytes); bytes += sizeof(int);
1163 gui_mouse_moved(col, row);
1164 } else if (AddInputMsgID == msgid) {
1165 NSString *string = [[NSString alloc] initWithData:data
1166 encoding:NSUTF8StringEncoding];
1168 [self addInput:string];
1171 } else if (TerminateNowMsgID == msgid) {
1172 isTerminating = YES;
1174 // Not keyboard or mouse event, queue it and handle later.
1175 //NSLog(@"Add event %s to input event queue", MessageStrings[msgid]);
1176 [inputQueue addObject:[NSNumber numberWithInt:msgid]];
1177 [inputQueue addObject:(data ? (id)data : (id)[NSNull null])];
1180 // See waitForInput: for an explanation of this flag.
1181 inputReceived = YES;
1184 - (oneway void)processInputAndData:(in bycopy NSArray *)messages
1186 // TODO: Get rid of this method?
1187 //NSLog(@"%s%@", _cmd, messages);
1189 unsigned i, count = [messages count];
1190 for (i = 0; i < count; i += 2) {
1191 int msgid = [[messages objectAtIndex:i] intValue];
1192 id data = [messages objectAtIndex:i+1];
1193 if ([data isEqual:[NSNull null]])
1196 [self processInput:msgid data:data];
1200 - (NSString *)evaluateExpression:(in bycopy NSString *)expr
1202 NSString *eval = nil;
1203 char_u *s = (char_u*)[expr UTF8String];
1206 s = CONVERT_FROM_UTF8(s);
1209 char_u *res = eval_client_expr_to_string(s);
1212 CONVERT_FROM_UTF8_FREE(s);
1218 s = CONVERT_TO_UTF8(s);
1220 eval = [NSString stringWithUTF8String:(char*)s];
1222 CONVERT_TO_UTF8_FREE(s);
1230 - (BOOL)starRegisterToPasteboard:(byref NSPasteboard *)pboard
1232 if (VIsual_active && (State & NORMAL) && clip_star.available) {
1233 // If there is no pasteboard, return YES to indicate that there is text
1238 clip_copy_selection();
1240 // Get the text to put on the pasteboard.
1241 long_u llen = 0; char_u *str = 0;
1242 int type = clip_convert_selection(&str, &llen, &clip_star);
1246 // TODO: Avoid overflow.
1247 int len = (int)llen;
1249 if (output_conv.vc_type != CONV_NONE) {
1250 char_u *conv_str = string_convert(&output_conv, str, &len);
1258 NSString *string = [[NSString alloc]
1259 initWithBytes:str length:len encoding:NSUTF8StringEncoding];
1261 NSArray *types = [NSArray arrayWithObject:NSStringPboardType];
1262 [pboard declareTypes:types owner:nil];
1263 BOOL ok = [pboard setString:string forType:NSStringPboardType];
1274 - (oneway void)addReply:(in bycopy NSString *)reply
1275 server:(in byref id <MMVimServerProtocol>)server
1277 //NSLog(@"addReply:%@ server:%@", reply, (id)server);
1279 // Replies might come at any time and in any order so we keep them in an
1280 // array inside a dictionary with the send port used as key.
1282 NSConnection *conn = [(NSDistantObject*)server connectionForProxy];
1283 // HACK! Assume connection uses mach ports.
1284 int port = [(NSMachPort*)[conn sendPort] machPort];
1285 NSNumber *key = [NSNumber numberWithInt:port];
1287 NSMutableArray *replies = [serverReplyDict objectForKey:key];
1289 replies = [NSMutableArray array];
1290 [serverReplyDict setObject:replies forKey:key];
1293 [replies addObject:reply];
1296 - (void)addInput:(in bycopy NSString *)input
1297 client:(in byref id <MMVimClientProtocol>)client
1299 //NSLog(@"addInput:%@ client:%@", input, (id)client);
1301 [self addInput:input];
1302 [self addClient:(id)client];
1304 inputReceived = YES;
1307 - (NSString *)evaluateExpression:(in bycopy NSString *)expr
1308 client:(in byref id <MMVimClientProtocol>)client
1310 [self addClient:(id)client];
1311 return [self evaluateExpression:expr];
1314 - (void)registerServerWithName:(NSString *)name
1316 NSString *svrName = name;
1317 NSConnection *svrConn = [NSConnection defaultConnection];
1320 for (i = 0; i < MMServerMax; ++i) {
1321 NSString *connName = [self connectionNameFromServerName:svrName];
1323 if ([svrConn registerName:connName]) {
1324 //NSLog(@"Registered server with name: %@", svrName);
1326 // TODO: Set request/reply time-outs to something else?
1328 // Don't wait for requests (time-out means that the message is
1330 [svrConn setRequestTimeout:0];
1331 //[svrConn setReplyTimeout:MMReplyTimeout];
1332 [svrConn setRootObject:self];
1334 char_u *s = (char_u*)[svrName UTF8String];
1336 s = CONVERT_FROM_UTF8(s);
1338 // NOTE: 'serverName' is a global variable
1339 serverName = vim_strsave(s);
1341 CONVERT_FROM_UTF8_FREE(s);
1344 set_vim_var_string(VV_SEND_SERVER, serverName, -1);
1347 need_maketitle = TRUE;
1349 [self queueMessage:SetServerNameMsgID data:
1350 [svrName dataUsingEncoding:NSUTF8StringEncoding]];
1354 svrName = [NSString stringWithFormat:@"%@%d", name, i+1];
1358 - (BOOL)sendToServer:(NSString *)name string:(NSString *)string
1359 reply:(char_u **)reply port:(int *)port expression:(BOOL)expr
1362 // NOTE: If 'name' equals 'serverName' then the request is local (client
1363 // and server are the same). This case is not handled separately, so a
1364 // connection will be set up anyway (this simplifies the code).
1366 NSConnection *conn = [self connectionForServerName:name];
1369 char_u *s = (char_u*)[name UTF8String];
1371 s = CONVERT_FROM_UTF8(s);
1373 EMSG2(_(e_noserver), s);
1375 CONVERT_FROM_UTF8_FREE(s);
1382 // HACK! Assume connection uses mach ports.
1383 *port = [(NSMachPort*)[conn sendPort] machPort];
1386 id proxy = [conn rootProxy];
1387 [proxy setProtocolForProxy:@protocol(MMVimServerProtocol)];
1391 NSString *eval = [proxy evaluateExpression:string client:self];
1394 char_u *r = (char_u*)[eval UTF8String];
1396 r = CONVERT_FROM_UTF8(r);
1398 *reply = vim_strsave(r);
1400 CONVERT_FROM_UTF8_FREE(r);
1403 *reply = vim_strsave((char_u*)_(e_invexprmsg));
1410 [proxy addInput:string client:self];
1413 @catch (NSException *e) {
1414 NSLog(@"WARNING: Caught exception in %s: \"%@\"", _cmd, e);
1421 - (NSArray *)serverList
1423 NSArray *list = nil;
1425 if ([self connection]) {
1426 id proxy = [connection rootProxy];
1427 [proxy setProtocolForProxy:@protocol(MMAppProtocol)];
1430 list = [proxy serverList];
1432 @catch (NSException *e) {
1433 NSLog(@"Exception caught when listing servers: \"%@\"", e);
1436 EMSG(_("E???: No connection to MacVim, server listing not possible."));
1442 - (NSString *)peekForReplyOnPort:(int)port
1444 //NSLog(@"%s%d", _cmd, port);
1446 NSNumber *key = [NSNumber numberWithInt:port];
1447 NSMutableArray *replies = [serverReplyDict objectForKey:key];
1448 if (replies && [replies count]) {
1449 //NSLog(@" %d replies, topmost is: %@", [replies count],
1450 // [replies objectAtIndex:0]);
1451 return [replies objectAtIndex:0];
1454 //NSLog(@" No replies");
1458 - (NSString *)waitForReplyOnPort:(int)port
1460 //NSLog(@"%s%d", _cmd, port);
1462 NSConnection *conn = [self connectionForServerPort:port];
1466 NSNumber *key = [NSNumber numberWithInt:port];
1467 NSMutableArray *replies = nil;
1468 NSString *reply = nil;
1470 // Wait for reply as long as the connection to the server is valid (unless
1471 // user interrupts wait with Ctrl-C).
1472 while (!got_int && [conn isValid] &&
1473 !(replies = [serverReplyDict objectForKey:key])) {
1474 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
1475 beforeDate:[NSDate distantFuture]];
1479 if ([replies count] > 0) {
1480 reply = [[replies objectAtIndex:0] retain];
1481 //NSLog(@" Got reply: %@", reply);
1482 [replies removeObjectAtIndex:0];
1483 [reply autorelease];
1486 if ([replies count] == 0)
1487 [serverReplyDict removeObjectForKey:key];
1493 - (BOOL)sendReply:(NSString *)reply toPort:(int)port
1495 id client = [clientProxyDict objectForKey:[NSNumber numberWithInt:port]];
1498 //NSLog(@"sendReply:%@ toPort:%d", reply, port);
1499 [client addReply:reply server:self];
1502 @catch (NSException *e) {
1503 NSLog(@"WARNING: Exception caught in %s: \"%@\"", _cmd, e);
1506 EMSG2(_("E???: server2client failed; no client with id 0x%x"), port);
1516 @implementation MMBackend (Private)
1518 - (void)processInputQueue
1520 // NOTE: One of the input events may cause this method to be called
1521 // recursively, so copy the input queue to a local variable and clear it
1522 // before starting to process input events (otherwise we could get stuck in
1523 // an endless loop).
1524 NSArray *q = [inputQueue copy];
1525 unsigned i, count = [q count];
1527 [inputQueue removeAllObjects];
1529 for (i = 0; i < count-1; i += 2) {
1530 int msgid = [[q objectAtIndex:i] intValue];
1531 id data = [q objectAtIndex:i+1];
1532 if ([data isEqual:[NSNull null]])
1535 //NSLog(@"(%d) %s:%s", i, _cmd, MessageStrings[msgid]);
1536 [self handleInputEvent:msgid data:data];
1540 //NSLog(@"Clear input event queue");
1543 - (void)handleInputEvent:(int)msgid data:(NSData *)data
1545 // NOTE: Be careful with what you do in this method. Ideally, a message
1546 // should be handled by adding something to the input buffer and returning
1547 // immediately. If you call a Vim function then it should not enter a loop
1548 // waiting for key presses or in any other way block the process. The
1549 // reason for this being that only one message can be processed at a time,
1550 // so if another message is received while processing, then the new message
1551 // is dropped. See also the comment in processInput:data:.
1553 //NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
1555 if (SelectTabMsgID == msgid) {
1557 const void *bytes = [data bytes];
1558 int idx = *((int*)bytes) + 1;
1559 //NSLog(@"Selecting tab %d", idx);
1560 send_tabline_event(idx);
1561 } else if (CloseTabMsgID == msgid) {
1563 const void *bytes = [data bytes];
1564 int idx = *((int*)bytes) + 1;
1565 //NSLog(@"Closing tab %d", idx);
1566 send_tabline_menu_event(idx, TABLINE_MENU_CLOSE);
1567 } else if (AddNewTabMsgID == msgid) {
1568 //NSLog(@"Adding new tab");
1569 send_tabline_menu_event(0, TABLINE_MENU_NEW);
1570 } else if (DraggedTabMsgID == msgid) {
1572 const void *bytes = [data bytes];
1573 // NOTE! The destination index is 0 based, so do not add 1 to make it 1
1575 int idx = *((int*)bytes);
1578 } else if (SetTextDimensionsMsgID == msgid || LiveResizeMsgID == msgid) {
1580 const void *bytes = [data bytes];
1581 int rows = *((int*)bytes); bytes += sizeof(int);
1582 int cols = *((int*)bytes); bytes += sizeof(int);
1584 // NOTE! Vim doesn't call gui_mch_set_shellsize() after
1585 // gui_resize_shell(), so we have to manually set the rows and columns
1586 // here. (MacVim doesn't change the rows and columns to avoid
1587 // inconsistent states between Vim and MacVim.)
1588 [self queueMessage:msgid data:data];
1590 //NSLog(@"[VimTask] Resizing shell to %dx%d.", cols, rows);
1591 gui_resize_shell(cols, rows);
1592 } else if (ExecuteMenuMsgID == msgid) {
1594 const void *bytes = [data bytes];
1595 int tag = *((int*)bytes); bytes += sizeof(int);
1597 vimmenu_T *menu = (vimmenu_T*)tag;
1598 // TODO! Make sure 'menu' is a valid menu pointer!
1602 } else if (ToggleToolbarMsgID == msgid) {
1603 [self handleToggleToolbar];
1604 } else if (ScrollbarEventMsgID == msgid) {
1605 [self handleScrollbarEvent:data];
1606 } else if (SetFontMsgID == msgid) {
1607 [self handleSetFont:data];
1608 } else if (VimShouldCloseMsgID == msgid) {
1610 } else if (DropFilesMsgID == msgid) {
1611 [self handleDropFiles:data];
1612 } else if (DropStringMsgID == msgid) {
1613 [self handleDropString:data];
1614 } else if (GotFocusMsgID == msgid) {
1616 [self focusChange:YES];
1617 } else if (LostFocusMsgID == msgid) {
1619 [self focusChange:NO];
1620 } else if (SetMouseShapeMsgID == msgid) {
1621 const void *bytes = [data bytes];
1622 int shape = *((int*)bytes); bytes += sizeof(int);
1623 update_mouseshape(shape);
1624 } else if (ODBEditMsgID == msgid) {
1625 [self handleOdbEdit:data];
1626 } else if (XcodeModMsgID == msgid) {
1627 [self handleXcodeMod:data];
1629 NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid);
1633 + (NSDictionary *)specialKeys
1635 static NSDictionary *specialKeys = nil;
1638 NSBundle *mainBundle = [NSBundle mainBundle];
1639 NSString *path = [mainBundle pathForResource:@"SpecialKeys"
1641 specialKeys = [[NSDictionary alloc] initWithContentsOfFile:path];
1647 - (void)handleInsertText:(NSData *)data
1651 NSString *key = [[NSString alloc] initWithData:data
1652 encoding:NSUTF8StringEncoding];
1653 char_u *str = (char_u*)[key UTF8String];
1654 int i, len = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1657 char_u *conv_str = NULL;
1658 if (input_conv.vc_type != CONV_NONE) {
1659 conv_str = string_convert(&input_conv, str, &len);
1665 for (i = 0; i < len; ++i) {
1666 add_to_input_buf(str+i, 1);
1667 if (CSI == str[i]) {
1668 // NOTE: If the converted string contains the byte CSI, then it
1669 // must be followed by the bytes KS_EXTRA, KE_CSI or things
1671 static char_u extra[2] = { KS_EXTRA, KE_CSI };
1672 add_to_input_buf(extra, 2);
1683 - (void)handleKeyDown:(NSString *)key modifiers:(int)mods
1687 char_u *chars = (char_u*)[key UTF8String];
1689 char_u *conv_str = NULL;
1691 int length = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1693 // Special keys (arrow keys, function keys, etc.) are stored in a plist so
1694 // that new keys can easily be added.
1695 NSString *specialString = [[MMBackend specialKeys]
1697 if (specialString && [specialString length] > 1) {
1698 //NSLog(@"special key: %@", specialString);
1699 int ikey = TO_SPECIAL([specialString characterAtIndex:0],
1700 [specialString characterAtIndex:1]);
1702 ikey = simplify_key(ikey, &mods);
1707 special[1] = K_SECOND(ikey);
1708 special[2] = K_THIRD(ikey);
1712 } else if (1 == length && TAB == chars[0]) {
1713 // Tab is a trouble child:
1714 // - <Tab> is added to the input buffer as is
1715 // - <S-Tab> is translated to, {CSI,'k','B'} (i.e. 'Back-tab')
1716 // - <M-Tab> should be 0x80|TAB but this is not valid utf-8 so it needs
1717 // to be converted to utf-8
1718 // - <S-M-Tab> is translated to <S-Tab> with ALT modifier
1719 // - <C-Tab> is reserved by Mac OS X
1720 // - <D-Tab> is reserved by Mac OS X
1725 if (mods & MOD_MASK_SHIFT) {
1726 mods &= ~MOD_MASK_SHIFT;
1728 special[1] = K_SECOND(K_S_TAB);
1729 special[2] = K_THIRD(K_S_TAB);
1731 } else if (mods & MOD_MASK_ALT) {
1732 int mtab = 0x80 | TAB;
1736 special[0] = (mtab >> 6) + 0xc0;
1737 special[1] = mtab & 0xbf;
1745 mods &= ~MOD_MASK_ALT;
1747 } else if (length > 0) {
1748 unichar c = [key characterAtIndex:0];
1750 //NSLog(@"non-special: %@ (hex=%x, mods=%d)", key,
1751 // [key characterAtIndex:0], mods);
1753 if (length == 1 && ((c == Ctrl_C && ctrl_c_interrupts)
1754 || (c == intr_char && intr_char != Ctrl_C))) {
1759 // HACK! In most circumstances the Ctrl and Shift modifiers should be
1760 // cleared since they are already added to the key by the AppKit.
1761 // Unfortunately, the only way to deal with when to clear the modifiers
1762 // or not seems to be to have hard-wired rules like this.
1763 if ( !((' ' == c) || (0xa0 == c) || (mods & MOD_MASK_CMD)
1764 || 0x9 == c || 0xd == c || ESC == c) ) {
1765 mods &= ~MOD_MASK_SHIFT;
1766 mods &= ~MOD_MASK_CTRL;
1767 //NSLog(@"clear shift ctrl");
1770 // HACK! All Option+key presses go via 'insert text' messages, except
1771 // for <M-Space>. If the Alt flag is not cleared for <M-Space> it does
1772 // not work to map to it.
1773 if (0xa0 == c && !(mods & MOD_MASK_CMD)) {
1774 //NSLog(@"clear alt");
1775 mods &= ~MOD_MASK_ALT;
1779 if (input_conv.vc_type != CONV_NONE) {
1780 conv_str = string_convert(&input_conv, chars, &length);
1787 if (chars && length > 0) {
1789 //NSLog(@"adding mods: %d", mods);
1791 modChars[1] = KS_MODIFIER;
1793 add_to_input_buf(modChars, 3);
1796 //NSLog(@"add to input buf: 0x%x", chars[0]);
1797 // TODO: Check for CSI bytes?
1798 add_to_input_buf(chars, length);
1807 - (void)queueMessage:(int)msgid data:(NSData *)data
1809 [outputQueue addObject:[NSData dataWithBytes:&msgid length:sizeof(int)]];
1811 [outputQueue addObject:data];
1813 [outputQueue addObject:[NSData data]];
1816 - (void)connectionDidDie:(NSNotification *)notification
1818 // If the main connection to MacVim is lost this means that MacVim was
1819 // either quit (by the user chosing Quit on the MacVim menu), or it has
1820 // crashed. In the former case the flag 'isTerminating' is set and we then
1821 // quit cleanly; in the latter case we make sure the swap files are left
1824 //NSLog(@"%s isTerminating=%d", _cmd, isTerminating);
1828 getout_preserve_modified(1);
1831 - (void)blinkTimerFired:(NSTimer *)timer
1833 NSTimeInterval timeInterval = 0;
1835 [blinkTimer release];
1838 if (MMBlinkStateOn == blinkState) {
1839 gui_undraw_cursor();
1840 blinkState = MMBlinkStateOff;
1841 timeInterval = blinkOffInterval;
1842 } else if (MMBlinkStateOff == blinkState) {
1843 gui_update_cursor(TRUE, FALSE);
1844 blinkState = MMBlinkStateOn;
1845 timeInterval = blinkOnInterval;
1848 if (timeInterval > 0) {
1850 [[NSTimer scheduledTimerWithTimeInterval:timeInterval target:self
1851 selector:@selector(blinkTimerFired:)
1852 userInfo:nil repeats:NO] retain];
1853 [self flushQueue:YES];
1857 - (void)focusChange:(BOOL)on
1859 gui_focus_change(on);
1862 - (void)handleToggleToolbar
1864 // If 'go' contains 'T', then remove it, else add it.
1866 char_u go[sizeof(GO_ALL)+2];
1871 p = vim_strchr(go, GO_TOOLBAR);
1875 char_u *end = go + len;
1881 go[len] = GO_TOOLBAR;
1885 set_option_value((char_u*)"guioptions", 0, go, 0);
1887 // Force screen redraw (does it have to be this complicated?).
1888 redraw_all_later(CLEAR);
1889 update_screen(NOT_VALID);
1892 gui_update_cursor(FALSE, FALSE);
1896 - (void)handleScrollbarEvent:(NSData *)data
1900 const void *bytes = [data bytes];
1901 long ident = *((long*)bytes); bytes += sizeof(long);
1902 int hitPart = *((int*)bytes); bytes += sizeof(int);
1903 float fval = *((float*)bytes); bytes += sizeof(float);
1904 scrollbar_T *sb = gui_find_scrollbar(ident);
1907 scrollbar_T *sb_info = sb->wp ? &sb->wp->w_scrollbars[0] : sb;
1908 long value = sb_info->value;
1909 long size = sb_info->size;
1910 long max = sb_info->max;
1911 BOOL isStillDragging = NO;
1912 BOOL updateKnob = YES;
1915 case NSScrollerDecrementPage:
1916 value -= (size > 2 ? size - 2 : 1);
1918 case NSScrollerIncrementPage:
1919 value += (size > 2 ? size - 2 : 1);
1921 case NSScrollerDecrementLine:
1924 case NSScrollerIncrementLine:
1927 case NSScrollerKnob:
1928 isStillDragging = YES;
1930 case NSScrollerKnobSlot:
1931 value = (long)(fval * (max - size + 1));
1938 //NSLog(@"value %d -> %d", sb_info->value, value);
1939 gui_drag_scrollbar(sb, value, isStillDragging);
1942 // Dragging the knob or option+clicking automatically updates
1943 // the knob position (on the actual NSScroller), so we only
1944 // need to set the knob position in the other cases.
1946 // Update both the left&right vertical scrollbars.
1947 long identLeft = sb->wp->w_scrollbars[SBAR_LEFT].ident;
1948 long identRight = sb->wp->w_scrollbars[SBAR_RIGHT].ident;
1949 [self setScrollbarThumbValue:value size:size max:max
1950 identifier:identLeft];
1951 [self setScrollbarThumbValue:value size:size max:max
1952 identifier:identRight];
1954 // Update the horizontal scrollbar.
1955 [self setScrollbarThumbValue:value size:size max:max
1962 - (void)handleSetFont:(NSData *)data
1966 const void *bytes = [data bytes];
1967 float pointSize = *((float*)bytes); bytes += sizeof(float);
1968 //unsigned len = *((unsigned*)bytes); bytes += sizeof(unsigned);
1969 bytes += sizeof(unsigned); // len not used
1971 NSMutableString *name = [NSMutableString stringWithUTF8String:bytes];
1972 [name appendString:[NSString stringWithFormat:@":h%.2f", pointSize]];
1973 char_u *s = (char_u*)[name UTF8String];
1976 s = CONVERT_FROM_UTF8(s);
1979 set_option_value((char_u*)"guifont", 0, s, 0);
1982 CONVERT_FROM_UTF8_FREE(s);
1985 // Force screen redraw (does it have to be this complicated?).
1986 redraw_all_later(CLEAR);
1987 update_screen(NOT_VALID);
1990 gui_update_cursor(FALSE, FALSE);
1994 - (void)handleDropFiles:(NSData *)data
1996 // TODO: Get rid of this method; instead use Vim script directly. At the
1997 // moment I know how to do this to open files in tabs, but I'm not sure how
1998 // to add the filenames to the command line when in command line mode.
2003 const void *bytes = [data bytes];
2004 const void *end = [data bytes] + [data length];
2005 BOOL forceOpen = *((BOOL*)bytes); bytes += sizeof(BOOL);
2006 int n = *((int*)bytes); bytes += sizeof(int);
2008 if (!forceOpen && (State & CMDLINE)) {
2009 // HACK! If Vim is in command line mode then the files names
2010 // should be added to the command line, instead of opening the
2011 // files in tabs (unless forceOpen is set). This is taken care of by
2012 // gui_handle_drop().
2013 char_u **fnames = (char_u **)alloc(n * sizeof(char_u *));
2016 while (bytes < end && i < n) {
2017 int len = *((int*)bytes); bytes += sizeof(int);
2018 char_u *s = (char_u*)bytes;
2020 s = CONVERT_FROM_UTF8(s);
2022 fnames[i++] = vim_strsave(s);
2024 CONVERT_FROM_UTF8_FREE(s);
2029 // NOTE! This function will free 'fnames'.
2030 // HACK! It is assumed that the 'x' and 'y' arguments are
2031 // unused when in command line mode.
2032 gui_handle_drop(0, 0, 0, fnames, i < n ? i : n);
2035 // HACK! I'm not sure how to get Vim to open a list of files in
2036 // tabs, so instead I create a ':tab drop' command with all the
2037 // files to open and execute it.
2038 NSMutableString *cmd = [NSMutableString stringWithString:@":tab drop"];
2041 for (i = 0; i < n && bytes < end; ++i) {
2042 int len = *((int*)bytes); bytes += sizeof(int);
2043 NSString *file = [NSString stringWithUTF8String:bytes];
2044 file = [file stringByEscapingSpecialFilenameCharacters];
2047 [cmd appendString:@" "];
2048 [cmd appendString:file];
2051 // By going to the last tabpage we ensure that the new tabs will
2052 // appear last (if this call is left out, the taborder becomes
2056 char_u *s = (char_u*)[cmd UTF8String];
2058 s = CONVERT_FROM_UTF8(s);
2062 CONVERT_FROM_UTF8_FREE(s);
2065 // Force screen redraw (does it have to be this complicated?).
2066 // (This code was taken from the end of gui_handle_drop().)
2067 update_screen(NOT_VALID);
2070 gui_update_cursor(FALSE, FALSE);
2077 - (void)handleDropString:(NSData *)data
2082 char_u dropkey[3] = { CSI, KS_EXTRA, (char_u)KE_DROP };
2083 const void *bytes = [data bytes];
2084 int len = *((int*)bytes); bytes += sizeof(int);
2085 NSMutableString *string = [NSMutableString stringWithUTF8String:bytes];
2087 // Replace unrecognized end-of-line sequences with \x0a (line feed).
2088 NSRange range = { 0, [string length] };
2089 unsigned n = [string replaceOccurrencesOfString:@"\x0d\x0a"
2090 withString:@"\x0a" options:0
2093 n = [string replaceOccurrencesOfString:@"\x0d" withString:@"\x0a"
2094 options:0 range:range];
2097 len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
2098 char_u *s = (char_u*)[string UTF8String];
2100 if (input_conv.vc_type != CONV_NONE)
2101 s = string_convert(&input_conv, s, &len);
2103 dnd_yank_drag_data(s, len);
2105 if (input_conv.vc_type != CONV_NONE)
2108 add_to_input_buf(dropkey, sizeof(dropkey));
2112 - (void)handleOdbEdit:(NSData *)data
2114 #ifdef FEAT_ODB_EDITOR
2115 const void *bytes = [data bytes];
2117 OSType serverID = *((OSType*)bytes); bytes += sizeof(OSType);
2119 char_u *path = NULL;
2120 int pathLen = *((int*)bytes); bytes += sizeof(int);
2122 path = (char_u*)bytes;
2125 path = CONVERT_FROM_UTF8(path);
2129 NSAppleEventDescriptor *token = nil;
2130 DescType tokenType = *((DescType*)bytes); bytes += sizeof(DescType);
2131 int descLen = *((int*)bytes); bytes += sizeof(int);
2133 token = [NSAppleEventDescriptor descriptorWithDescriptorType:tokenType
2139 unsigned i, numFiles = *((unsigned*)bytes); bytes += sizeof(unsigned);
2140 for (i = 0; i < numFiles; ++i) {
2141 int len = *((int*)bytes); bytes += sizeof(int);
2142 char_u *filename = (char_u*)bytes;
2144 filename = CONVERT_FROM_UTF8(filename);
2146 buf_T *buf = buflist_findname(filename);
2148 if (buf->b_odb_token) {
2149 [(NSAppleEventDescriptor*)(buf->b_odb_token) release];
2150 buf->b_odb_token = NULL;
2153 if (buf->b_odb_fname) {
2154 vim_free(buf->b_odb_fname);
2155 buf->b_odb_fname = NULL;
2158 buf->b_odb_server_id = serverID;
2161 buf->b_odb_token = [token retain];
2163 buf->b_odb_fname = vim_strsave(path);
2165 NSLog(@"WARNING: Could not find buffer '%s' for ODB editing.",
2170 CONVERT_FROM_UTF8_FREE(filename);
2175 CONVERT_FROM_UTF8_FREE(path);
2177 #endif // FEAT_ODB_EDITOR
2180 - (void)handleXcodeMod:(NSData *)data
2183 const void *bytes = [data bytes];
2184 DescType type = *((DescType*)bytes); bytes += sizeof(DescType);
2185 unsigned len = *((unsigned*)bytes); bytes += sizeof(unsigned);
2189 NSAppleEventDescriptor *replyEvent = [NSAppleEventDescriptor
2190 descriptorWithDescriptorType:type
2196 - (BOOL)checkForModifiedBuffers
2199 for (buf = firstbuf; buf != NULL; buf = buf->b_next) {
2200 if (bufIsChanged(buf)) {
2208 - (void)addInput:(NSString *)input
2210 char_u *s = (char_u*)[input UTF8String];
2213 s = CONVERT_FROM_UTF8(s);
2216 server_to_input_buf(s);
2219 CONVERT_FROM_UTF8_FREE(s);
2223 @end // MMBackend (Private)
2228 @implementation MMBackend (ClientServer)
2230 - (NSString *)connectionNameFromServerName:(NSString *)name
2232 NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
2234 return [[NSString stringWithFormat:@"%@.%@", bundleIdentifier, name]
2238 - (NSConnection *)connectionForServerName:(NSString *)name
2240 // TODO: Try 'name%d' if 'name' fails.
2241 NSString *connName = [self connectionNameFromServerName:name];
2242 NSConnection *svrConn = [connectionNameDict objectForKey:connName];
2245 svrConn = [NSConnection connectionWithRegisteredName:connName
2247 // Try alternate server...
2248 if (!svrConn && alternateServerName) {
2249 //NSLog(@" trying to connect to alternate server: %@",
2250 // alternateServerName);
2251 connName = [self connectionNameFromServerName:alternateServerName];
2252 svrConn = [NSConnection connectionWithRegisteredName:connName
2256 // Try looking for alternate servers...
2258 //NSLog(@" looking for alternate servers...");
2259 NSString *alt = [self alternateServerNameForName:name];
2260 if (alt != alternateServerName) {
2261 //NSLog(@" found alternate server: %@", string);
2262 [alternateServerName release];
2263 alternateServerName = [alt copy];
2267 // Try alternate server again...
2268 if (!svrConn && alternateServerName) {
2269 //NSLog(@" trying to connect to alternate server: %@",
2270 // alternateServerName);
2271 connName = [self connectionNameFromServerName:alternateServerName];
2272 svrConn = [NSConnection connectionWithRegisteredName:connName
2277 [connectionNameDict setObject:svrConn forKey:connName];
2279 //NSLog(@"Adding %@ as connection observer for %@", self, svrConn);
2280 [[NSNotificationCenter defaultCenter] addObserver:self
2281 selector:@selector(serverConnectionDidDie:)
2282 name:NSConnectionDidDieNotification object:svrConn];
2289 - (NSConnection *)connectionForServerPort:(int)port
2292 NSEnumerator *e = [connectionNameDict objectEnumerator];
2294 while ((conn = [e nextObject])) {
2295 // HACK! Assume connection uses mach ports.
2296 if (port == [(NSMachPort*)[conn sendPort] machPort])
2303 - (void)serverConnectionDidDie:(NSNotification *)notification
2305 //NSLog(@"%s%@", _cmd, notification);
2307 NSConnection *svrConn = [notification object];
2309 //NSLog(@"Removing %@ as connection observer from %@", self, svrConn);
2310 [[NSNotificationCenter defaultCenter]
2312 name:NSConnectionDidDieNotification
2315 [connectionNameDict removeObjectsForKeys:
2316 [connectionNameDict allKeysForObject:svrConn]];
2318 // HACK! Assume connection uses mach ports.
2319 int port = [(NSMachPort*)[svrConn sendPort] machPort];
2320 NSNumber *key = [NSNumber numberWithInt:port];
2322 [clientProxyDict removeObjectForKey:key];
2323 [serverReplyDict removeObjectForKey:key];
2326 - (void)addClient:(NSDistantObject *)client
2328 NSConnection *conn = [client connectionForProxy];
2329 // HACK! Assume connection uses mach ports.
2330 int port = [(NSMachPort*)[conn sendPort] machPort];
2331 NSNumber *key = [NSNumber numberWithInt:port];
2333 if (![clientProxyDict objectForKey:key]) {
2334 [client setProtocolForProxy:@protocol(MMVimClientProtocol)];
2335 [clientProxyDict setObject:client forKey:key];
2338 // NOTE: 'clientWindow' is a global variable which is used by <client>
2339 clientWindow = port;
2342 - (NSString *)alternateServerNameForName:(NSString *)name
2344 if (!(name && [name length] > 0))
2347 // Only look for alternates if 'name' doesn't end in a digit.
2348 unichar lastChar = [name characterAtIndex:[name length]-1];
2349 if (lastChar >= '0' && lastChar <= '9')
2352 // Look for alternates among all current servers.
2353 NSArray *list = [self serverList];
2354 if (!(list && [list count] > 0))
2357 // Filter out servers starting with 'name' and ending with a number. The
2358 // (?i) pattern ensures that the match is case insensitive.
2359 NSString *pat = [NSString stringWithFormat:@"(?i)%@[0-9]+\\z", name];
2360 NSPredicate *pred = [NSPredicate predicateWithFormat:
2361 @"SELF MATCHES %@", pat];
2362 list = [list filteredArrayUsingPredicate:pred];
2363 if ([list count] > 0) {
2364 list = [list sortedArrayUsingSelector:@selector(serverNameCompare:)];
2365 return [list objectAtIndex:0];
2371 @end // MMBackend (ClientServer)
2376 @implementation NSString (MMServerNameCompare)
2377 - (NSComparisonResult)serverNameCompare:(NSString *)string
2379 return [self compare:string
2380 options:NSCaseInsensitiveSearch|NSNumericSearch];
2387 static int eventModifierFlagsToVimModMask(int modifierFlags)
2391 if (modifierFlags & NSShiftKeyMask)
2392 modMask |= MOD_MASK_SHIFT;
2393 if (modifierFlags & NSControlKeyMask)
2394 modMask |= MOD_MASK_CTRL;
2395 if (modifierFlags & NSAlternateKeyMask)
2396 modMask |= MOD_MASK_ALT;
2397 if (modifierFlags & NSCommandKeyMask)
2398 modMask |= MOD_MASK_CMD;
2403 static int vimModMaskToEventModifierFlags(int mods)
2407 if (mods & MOD_MASK_SHIFT)
2408 flags |= NSShiftKeyMask;
2409 if (mods & MOD_MASK_CTRL)
2410 flags |= NSControlKeyMask;
2411 if (mods & MOD_MASK_ALT)
2412 flags |= NSAlternateKeyMask;
2413 if (mods & MOD_MASK_CMD)
2414 flags |= NSCommandKeyMask;
2419 static int eventModifierFlagsToVimMouseModMask(int modifierFlags)
2423 if (modifierFlags & NSShiftKeyMask)
2424 modMask |= MOUSE_SHIFT;
2425 if (modifierFlags & NSControlKeyMask)
2426 modMask |= MOUSE_CTRL;
2427 if (modifierFlags & NSAlternateKeyMask)
2428 modMask |= MOUSE_ALT;
2433 static int eventButtonNumberToVimMouseButton(int buttonNumber)
2435 static int mouseButton[] = { MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE };
2437 return (buttonNumber >= 0 && buttonNumber < 3)
2438 ? mouseButton[buttonNumber] : -1;
2441 static int specialKeyToNSKey(int key)
2443 if (!IS_SPECIAL(key))
2450 { K_UP, NSUpArrowFunctionKey },
2451 { K_DOWN, NSDownArrowFunctionKey },
2452 { K_LEFT, NSLeftArrowFunctionKey },
2453 { K_RIGHT, NSRightArrowFunctionKey },
2454 { K_F1, NSF1FunctionKey },
2455 { K_F2, NSF2FunctionKey },
2456 { K_F3, NSF3FunctionKey },
2457 { K_F4, NSF4FunctionKey },
2458 { K_F5, NSF5FunctionKey },
2459 { K_F6, NSF6FunctionKey },
2460 { K_F7, NSF7FunctionKey },
2461 { K_F8, NSF8FunctionKey },
2462 { K_F9, NSF9FunctionKey },
2463 { K_F10, NSF10FunctionKey },
2464 { K_F11, NSF11FunctionKey },
2465 { K_F12, NSF12FunctionKey },
2466 { K_F13, NSF13FunctionKey },
2467 { K_F14, NSF14FunctionKey },
2468 { K_F15, NSF15FunctionKey },
2469 { K_F16, NSF16FunctionKey },
2470 { K_F17, NSF17FunctionKey },
2471 { K_F18, NSF18FunctionKey },
2472 { K_F19, NSF19FunctionKey },
2473 { K_F20, NSF20FunctionKey },
2474 { K_F21, NSF21FunctionKey },
2475 { K_F22, NSF22FunctionKey },
2476 { K_F23, NSF23FunctionKey },
2477 { K_F24, NSF24FunctionKey },
2478 { K_F25, NSF25FunctionKey },
2479 { K_F26, NSF26FunctionKey },
2480 { K_F27, NSF27FunctionKey },
2481 { K_F28, NSF28FunctionKey },
2482 { K_F29, NSF29FunctionKey },
2483 { K_F30, NSF30FunctionKey },
2484 { K_F31, NSF31FunctionKey },
2485 { K_F32, NSF32FunctionKey },
2486 { K_F33, NSF33FunctionKey },
2487 { K_F34, NSF34FunctionKey },
2488 { K_F35, NSF35FunctionKey },
2489 { K_DEL, NSBackspaceCharacter },
2490 { K_BS, NSDeleteCharacter },
2491 { K_HOME, NSHomeFunctionKey },
2492 { K_END, NSEndFunctionKey },
2493 { K_PAGEUP, NSPageUpFunctionKey },
2494 { K_PAGEDOWN, NSPageDownFunctionKey }
2498 for (i = 0; i < sizeof(sp2ns)/sizeof(sp2ns[0]); ++i) {
2499 if (sp2ns[i].special == key)
2500 return sp2ns[i].nskey;