applidium / Vim

Port of the Vim text editor to the iOS
https://applidium.com/en
533 stars 100 forks source link

Better support for Bluetooth keyboards (especially ESC). #13

Open polidore opened 12 years ago

polidore commented 12 years ago

I use a bluetooth keyboard with my ipad, and I can use esc to exit insert mode when I'm using vim through iSSH, so I know it works. iSSH has really good bluetooth keyboard support, and it would be great if vim supported this too. Not just esc, but control keys.

Thank you for this great app.

N-Holzschuch commented 12 years ago

I'd love to have that too. I actually managed to use the Escape key on my keyboard (by editing the code), but there's still work. Control keys and arrow keys would be nice.

polidore commented 12 years ago

By the way! BT keyboard works fine, but only if you have your iPad keyboard in normal mode. That is, not spread out, but together.

On Tue, Jun 19, 2012 at 10:18 AM, N-Holzschuch < reply@reply.github.com

wrote:

I'd love to have that too. I actually managed to use the Escape key on my keyboard (by editing the code), but there's still work. Control keys and arrow keys would be nice.


Reply to this email directly or view it on GitHub: https://github.com/applidium/Vim/issues/13#issuecomment-6426015

N-Holzschuch commented 12 years ago

For the adventurous, I have a patch that allows the use of external keyboards. About 300 lines, so I'm not posting it here, right?

What works: arrow keys, escape. What doesn't work (yet): control + letter (control + arrow is fine). Also erasing in insert mode after moving with arrows.

In short, it's still a work in progress, but it can be released for use by others.

rcarmo commented 12 years ago

I'd love to have this fixed, together with the standard cut, copy & paste bindings (just like MacVim, which also supports Cmd+X,C,V)

N-Holzschuch commented 12 years ago

All right, I think I've got it.

There's only one file to edit, gui_ios.m. At line 309, you need to replace :

#pragma mark VimAppDelegate

@interface VimAppDelegate : NSObject {
}
@end

@implementation VimAppDelegate

with the following code:

#pragma mark VimApplication
@interface VimApplication : UIApplication
@end
@implementation VimApplication

// Support for external keyboards
/* Key mapping */
#define vk_Return   0x28
#define vk_Esc      0x29
#define vk_Delete   0x30
#define vk_Tab      0x31

#define vk_F1       0x3a
#define vk_F2       0x3b
#define vk_F3       0x3c
#define vk_F4       0x3d
#define vk_F5       0x3e
#define vk_F6       0x3f
#define vk_F7       0x40
#define vk_F8       0x41
#define vk_F9       0x42
#define vk_F10      0x43
#define vk_F11      0x44
#define vk_F12      0x45
#define vk_F13      0x46
#define vk_F14      0x47
#define vk_F15      0x48

#define vk_Right    0x4F
#define vk_Left     0x50
#define vk_Down     0x51
#define vk_Up       0x52

#define shiftKey        0x20000
#define rightShiftKey   0x200000
#define controlKey      0x100000
#define rightControlKey controlKey // Haven't found a keyboard with a right ctrl key. 
#define cmdKey       0x10000
#define rightCmdKey  0x1000000
#define optionKey          0x80000
#define rightOptionKey     0x400000

#define KeySym  char

static struct
{
    KeySym  key_sym;
    char_u  vim_code0;
    char_u  vim_code1;
} special_keys[] =
{
    {vk_Up,     'k', 'u'},
    {vk_Down,       'k', 'd'},
    {vk_Left,       'k', 'l'},
    {vk_Right,      'k', 'r'},
    {vk_Delete,     'k', 'b'},

    {vk_F1,     'k', '1'},
    {vk_F2,     'k', '2'},
    {vk_F3,     'k', '3'},
    {vk_F4,     'k', '4'},
    {vk_F5,     'k', '5'},
    {vk_F6,     'k', '6'},
    {vk_F7,     'k', '7'},
    {vk_F8,     'k', '8'},
    {vk_F9,     'k', '9'},
    {vk_F10,        'k', ';'},

    {vk_F11,        'F', '1'},
    {vk_F12,        'F', '2'},
    {vk_F13,        'F', '3'},
    {vk_F14,        'F', '4'},
    {vk_F15,        'F', '5'},
    /* End of list marker: */
    {(KeySym)0,     0, 0}
};

/*
 * Convert the modifiers of an Event into vim's modifiers (keys)
 */
static int_u
EventFlags2VimModifiers(int eventFlags)
{
    int_u vimModifiers = 0x00;

    if (eventFlags & (shiftKey | rightShiftKey))
        vimModifiers |= MOD_MASK_SHIFT; 
    if (eventFlags & (optionKey | rightOptionKey))
        vimModifiers |= MOD_MASK_ALT;
    if (eventFlags & (controlKey | rightControlKey))
        vimModifiers |= MOD_MASK_CTRL;
    if (eventFlags & (cmdKey | rightCmdKey))
        vimModifiers |= MOD_MASK_CMD; 
    return (vimModifiers);
}

- (void)sendEvent:(UIEvent *)anEvent
{
#define GS_EVENT_TYPE_OFFSET 2
#define GSEVENT_TYPE_KEYUP 11
#define GSEVENT_FLAGS 12
#define GSEVENTKEY_KEYCODE_CHARIGNORINGMOD 15

    // Traverse from the UIEvent to the GSEvent to the type
    int *eventMemory = (int *)[anEvent performSelector:@selector(_gsEvent)];
    int eventType = eventMemory[GS_EVENT_TYPE_OFFSET];

    // Look for status bar touches by event type
    if (eventType == GSEVENT_TYPE_KEYUP)
    {        
        int eventFlags = eventMemory[GSEVENT_FLAGS];
        int tmp = eventMemory[GSEVENTKEY_KEYCODE_CHARIGNORINGMOD];
        //which length?
        UniChar *keycode = (UniChar *)&tmp; // Cast to silence the warning
        char_u  result[80];
        short   len = 0;

        // Escape requires special treatment. Maybe also Cmd . / Ctrl-C
        if (keycode[0] == vk_Esc) {
            char_u escapeString[] = {ESC, 0};
            add_to_input_buf(escapeString, 1);
            return;
        }
        if (keycode[0] == vk_Return) {
            char_u escapeString[] = {Ctrl_M, 0};
            add_to_input_buf(escapeString, 1);
            return;            
        }
        // Identify the modifiers (Shift, Ctrl, Alt, Option)
        UInt32 vimModifiers = EventFlags2VimModifiers(eventFlags);
        // Are we on a special key?
        int isSpecial = 0;
        int i;
        int key_char;
        for (i = 0; special_keys[i].key_sym != (KeySym)0; ++i)
            if (special_keys[i].key_sym == keycode[0])
            {
                key_char = TO_SPECIAL(special_keys[i].vim_code0,
                                      special_keys[i].vim_code1);
                key_char = simplify_key(key_char, (int *)&vimModifiers);
                isSpecial = TRUE;
                break;
            }

        // Here, we need to be careful: we only insert chars if they won't be handled by insertText
        // shift and option + letter are done over there. 
        if (isSpecial && IS_SPECIAL(key_char))
        {
            // Special keys: up, down, F1...
            if (vimModifiers)
            {
                // We place the modifiers in the buffer
                result[len++] = CSI;
                result[len++] = KS_MODIFIER;
                result[len++] = vimModifiers;
            }
            // Then the special key:
            result[len++] = CSI;
            result[len++] = K_SECOND(key_char);
            result[len++] = K_THIRD(key_char);
        } else if (vimModifiers & MOD_MASK_CTRL) {
            // remove the control modifier
            vimModifiers &= ~MOD_MASK_CTRL;
            if (vimModifiers)
            {
                // If there's still sthg else, insert it.
                result[len++] = CSI;
                result[len++] = KS_MODIFIER;
                result[len++] = vimModifiers;
            }
            result[len++] = keycode[0] - 0x03;
        } else if (vimModifiers & MOD_MASK_CMD) {
            // Possible to simplify??? Try again with CTRL
            result[len++] = CSI;
            result[len++] = KS_MODIFIER;
            result[len++] = vimModifiers;
            result[len++] = keycode[0] + 0x5d;
        }
        if (len > 0) {
            add_to_input_buf(result, len);
            return;
        }
    } 
    // If we didn't do anything with the event, so we forward it.
    // This includes all standard text events.
    [super sendEvent:anEvent]; 
}

and in the "main" function, replace the line:

int retVal = UIApplicationMain(argc, argv, nil, @"VimAppDelegate");

with:

int retVal = UIApplicationMain(argc, argv,  @"VimApplication", NSStringFromClass([VimApplication class]));

and add a special case in inserttext:

- (void)insertText:(NSString *)text {
    char_u* keycode = [text UTF8String];
    int len = [text lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    if ((len > 1) || (keycode[0] != 0xa)) {
        // I have to make a special case for Enter
        add_to_input_buf(keycode, len); 
    }
    [_textView setNeedsDisplay];
}

and that should be all (assuming your keyboard is identical to mine). I have escape, arrows, control/alt/command, function keys...

You can add the following lines in your vimrc:

" D (= Cmd) works with special characters:
nmap    <M-Down>    }
nmap    <D-Down>    <C-End>
nmap    <M-Up>      {
nmap    <D-Up>      <C-Home>
nmap    <M-Right>   <C-Right>
nmap    <D-Right>   <End>
nmap    <M-Left>    <C-Left>
nmap    <D-Left>    <Home>

imap  <D-BS>      <C-U>
imap  <M-BS>      <C-W>
imap  <M-Down>    <C-O>}
imap  <D-Down>    <C-End>
imap  <M-Up>      <C-O>{
imap  <D-Up>      <C-Home>
map!  <M-Right>   <C-Right>
map!  <D-Right>   <End>
map!  <M-Left>    <C-Left>
map!  <D-Left>    <Home>

I've been playing with Cmd-A, Cmd-C and Cmd-V all afternoon.

SethMilliken commented 12 years ago

@N-Holzschuch I know this is a long shot, but have you looked at all into remapping CapsLock?

N-Holzschuch commented 12 years ago

I don't know, what do you have in mind?

With these changes, caps lock works as a caps lock key. But that's pretty useless, so I guess you want something else.

There are many guys who remap the Caps Lock key into an escape key. If that's what you want to do, it can probably be done inside the Objective-C code (but that's not a very beautiful solution; it raises many questions, such as how to deactivate it...) I don't think it can be done in the .vimrc file.

You would need to add the following modifier:

define capsLockKey 0x40000

The problem is that caps lock is a modifier, not a key in itself. I don't know if "pressing caps lock" is an event that gets forwarded to the application. Someone would need to test it.

SethMilliken commented 12 years ago

@N-Holzschuch That sounds promising. I'd want to remap CapsLock as Control.

rcarmo commented 12 years ago

I actually wouldn't - I can use Control just fine in apps like Prompt...

On Jul 11, 2012, at 22:31 , Seth Milliken wrote:

@N-Holzschuch That sounds promising. I'd want to remap CapsLock as Control.


Reply to this email directly or view it on GitHub: https://github.com/applidium/Vim/issues/13#issuecomment-6920412

SethMilliken commented 12 years ago

@rcarmo Just to be clear, I'm not suggesting that this be a hardcoded default, but rather an option. I was trying to glean from @N-Holzschuch whether it was possible to do anything with CapsLock at all.

I find Control to be in an awkward position on my Bluetooth keyboard. My left pinky starts to hurt after hacking for a while without CapsLock mapped to Control.

rcarmo commented 12 years ago

Sure :)

I'm used to it - the same keyboard across all my devices, etc.

On 11/07/2012, at 23:19, Seth Millikenreply@reply.github.com wrote:

@rcarmo Just to be clear, I'm not suggesting that this be a hardcoded default, but rather an option. I was trying to glean from @N-Holzschuch whether it was possible to do anything with CapsLock at all.

I find Control to be in an awkward position on my Bluetooth keyboard. My left pinky starts to hurt after hacking for a while without CapsLock mapped to Control.


Reply to this email directly or view it on GitHub: https://github.com/applidium/Vim/issues/13#issuecomment-6921603

nod commented 10 years ago

Did the patch from @N-Holzschuch ever get rolled into master as a pull request?