/**************************************************************************/ /* key_mapping_macos.mm */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /**************************************************************************/ /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ #include "key_mapping_macos.h" #import <Carbon/Carbon.h> #import <Cocoa/Cocoa.h> bool KeyMappingMacOS::is_numpad_key(unsigned int key) { static const unsigned int table[] = { 0x41, /* kVK_ANSI_KeypadDecimal */ 0x43, /* kVK_ANSI_KeypadMultiply */ 0x45, /* kVK_ANSI_KeypadPlus */ 0x47, /* kVK_ANSI_KeypadClear */ 0x4b, /* kVK_ANSI_KeypadDivide */ 0x4c, /* kVK_ANSI_KeypadEnter */ 0x4e, /* kVK_ANSI_KeypadMinus */ 0x51, /* kVK_ANSI_KeypadEquals */ 0x52, /* kVK_ANSI_Keypad0 */ 0x53, /* kVK_ANSI_Keypad1 */ 0x54, /* kVK_ANSI_Keypad2 */ 0x55, /* kVK_ANSI_Keypad3 */ 0x56, /* kVK_ANSI_Keypad4 */ 0x57, /* kVK_ANSI_Keypad5 */ 0x58, /* kVK_ANSI_Keypad6 */ 0x59, /* kVK_ANSI_Keypad7 */ 0x5b, /* kVK_ANSI_Keypad8 */ 0x5c, /* kVK_ANSI_Keypad9 */ 0x5f, /* kVK_JIS_KeypadComma */ 0x00 }; for (int i = 0; table[i] != 0; i++) { if (key == table[i]) { return true; } } return false; } // Keyboard symbol translation table. static const Key _macos_to_godot_table[128] = { /* 00 */ Key::A, /* 01 */ Key::S, /* 02 */ Key::D, /* 03 */ Key::F, /* 04 */ Key::H, /* 05 */ Key::G, /* 06 */ Key::Z, /* 07 */ Key::X, /* 08 */ Key::C, /* 09 */ Key::V, /* 0a */ Key::SECTION, /* ISO Section */ /* 0b */ Key::B, /* 0c */ Key::Q, /* 0d */ Key::W, /* 0e */ Key::E, /* 0f */ Key::R, /* 10 */ Key::Y, /* 11 */ Key::T, /* 12 */ Key::KEY_1, /* 13 */ Key::KEY_2, /* 14 */ Key::KEY_3, /* 15 */ Key::KEY_4, /* 16 */ Key::KEY_6, /* 17 */ Key::KEY_5, /* 18 */ Key::EQUAL, /* 19 */ Key::KEY_9, /* 1a */ Key::KEY_7, /* 1b */ Key::MINUS, /* 1c */ Key::KEY_8, /* 1d */ Key::KEY_0, /* 1e */ Key::BRACERIGHT, /* 1f */ Key::O, /* 20 */ Key::U, /* 21 */ Key::BRACELEFT, /* 22 */ Key::I, /* 23 */ Key::P, /* 24 */ Key::ENTER, /* 25 */ Key::L, /* 26 */ Key::J, /* 27 */ Key::APOSTROPHE, /* 28 */ Key::K, /* 29 */ Key::SEMICOLON, /* 2a */ Key::BACKSLASH, /* 2b */ Key::COMMA, /* 2c */ Key::SLASH, /* 2d */ Key::N, /* 2e */ Key::M, /* 2f */ Key::PERIOD, /* 30 */ Key::TAB, /* 31 */ Key::SPACE, /* 32 */ Key::QUOTELEFT, /* 33 */ Key::BACKSPACE, /* 34 */ Key::UNKNOWN, /* 35 */ Key::ESCAPE, /* 36 */ Key::META, /* 37 */ Key::META, /* 38 */ Key::SHIFT, /* 39 */ Key::CAPSLOCK, /* 3a */ Key::ALT, /* 3b */ Key::CTRL, /* 3c */ Key::SHIFT, /* 3d */ Key::ALT, /* 3e */ Key::CTRL, /* 3f */ Key::UNKNOWN, /* Function */ /* 40 */ Key::F17, /* 41 */ Key::KP_PERIOD, /* 42 */ Key::UNKNOWN, /* 43 */ Key::KP_MULTIPLY, /* 44 */ Key::UNKNOWN, /* 45 */ Key::KP_ADD, /* 46 */ Key::UNKNOWN, /* 47 */ Key::NUMLOCK, /* Really KeypadClear... */ /* 48 */ Key::VOLUMEUP, /* VolumeUp */ /* 49 */ Key::VOLUMEDOWN, /* VolumeDown */ /* 4a */ Key::VOLUMEMUTE, /* Mute */ /* 4b */ Key::KP_DIVIDE, /* 4c */ Key::KP_ENTER, /* 4d */ Key::UNKNOWN, /* 4e */ Key::KP_SUBTRACT, /* 4f */ Key::F18, /* 50 */ Key::F19, /* 51 */ Key::EQUAL, /* KeypadEqual */ /* 52 */ Key::KP_0, /* 53 */ Key::KP_1, /* 54 */ Key::KP_2, /* 55 */ Key::KP_3, /* 56 */ Key::KP_4, /* 57 */ Key::KP_5, /* 58 */ Key::KP_6, /* 59 */ Key::KP_7, /* 5a */ Key::F20, /* 5b */ Key::KP_8, /* 5c */ Key::KP_9, /* 5d */ Key::YEN, /* JIS Yen */ /* 5e */ Key::UNDERSCORE, /* JIS Underscore */ /* 5f */ Key::COMMA, /* JIS KeypadComma */ /* 60 */ Key::F5, /* 61 */ Key::F6, /* 62 */ Key::F7, /* 63 */ Key::F3, /* 64 */ Key::F8, /* 65 */ Key::F9, /* 66 */ Key::UNKNOWN, /* JIS Eisu */ /* 67 */ Key::F11, /* 68 */ Key::UNKNOWN, /* JIS Kana */ /* 69 */ Key::F13, /* 6a */ Key::F16, /* 6b */ Key::F14, /* 6c */ Key::UNKNOWN, /* 6d */ Key::F10, /* 6e */ Key::MENU, /* 6f */ Key::F12, /* 70 */ Key::UNKNOWN, /* 71 */ Key::F15, /* 72 */ Key::INSERT, /* Really Help... */ /* 73 */ Key::HOME, /* 74 */ Key::PAGEUP, /* 75 */ Key::KEY_DELETE, /* 76 */ Key::F4, /* 77 */ Key::END, /* 78 */ Key::F2, /* 79 */ Key::PAGEDOWN, /* 7a */ Key::F1, /* 7b */ Key::LEFT, /* 7c */ Key::RIGHT, /* 7d */ Key::DOWN, /* 7e */ Key::UP, /* 7f */ Key::UNKNOWN, }; // Translates a OS X keycode to a Godot keycode. Key KeyMappingMacOS::translate_key(unsigned int key) { if (key >= 128) { return Key::UNKNOWN; } return _macos_to_godot_table[key]; } // Translates a Godot keycode back to a macOS keycode. unsigned int KeyMappingMacOS::unmap_key(Key key) { for (int i = 0; i <= 126; i++) { if (_macos_to_godot_table[i] == key) { return i; } } return 127; } struct _KeyCodeMap { UniChar kchar; Key kcode; }; static const _KeyCodeMap _keycodes[55] = { { '`', Key::QUOTELEFT }, { '~', Key::ASCIITILDE }, { '0', Key::KEY_0 }, { '1', Key::KEY_1 }, { '2', Key::KEY_2 }, { '3', Key::KEY_3 }, { '4', Key::KEY_4 }, { '5', Key::KEY_5 }, { '6', Key::KEY_6 }, { '7', Key::KEY_7 }, { '8', Key::KEY_8 }, { '9', Key::KEY_9 }, { '-', Key::MINUS }, { '_', Key::UNDERSCORE }, { '=', Key::EQUAL }, { '+', Key::PLUS }, { 'q', Key::Q }, { 'w', Key::W }, { 'e', Key::E }, { 'r', Key::R }, { 't', Key::T }, { 'y', Key::Y }, { 'u', Key::U }, { 'i', Key::I }, { 'o', Key::O }, { 'p', Key::P }, { '[', Key::BRACELEFT }, { ']', Key::BRACERIGHT }, { '{', Key::BRACELEFT }, { '}', Key::BRACERIGHT }, { 'a', Key::A }, { 's', Key::S }, { 'd', Key::D }, { 'f', Key::F }, { 'g', Key::G }, { 'h', Key::H }, { 'j', Key::J }, { 'k', Key::K }, { 'l', Key::L }, { ';', Key::SEMICOLON }, { ':', Key::COLON }, { '\'', Key::APOSTROPHE }, { '\"', Key::QUOTEDBL }, { '\\', Key::BACKSLASH }, { '#', Key::NUMBERSIGN }, { 'z', Key::Z }, { 'x', Key::X }, { 'c', Key::C }, { 'v', Key::V }, { 'b', Key::B }, { 'n', Key::N }, { 'm', Key::M }, { ',', Key::COMMA }, { '.', Key::PERIOD }, { '/', Key::SLASH } }; // Remap key according to current keyboard layout. Key KeyMappingMacOS::remap_key(unsigned int key, unsigned int state) { if (is_numpad_key(key)) { return translate_key(key); } TISInputSourceRef current_keyboard = TISCopyCurrentKeyboardInputSource(); if (!current_keyboard) { return translate_key(key); } CFDataRef layout_data = (CFDataRef)TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData); if (!layout_data) { return translate_key(key); } const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data); UInt32 keys_down = 0; UniChar chars[4]; UniCharCount real_length; OSStatus err = UCKeyTranslate(keyboard_layout, key, kUCKeyActionDisplay, (state >> 8) & 0xFF, LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &keys_down, sizeof(chars) / sizeof(chars[0]), &real_length, chars); if (err != noErr) { return translate_key(key); } for (unsigned int i = 0; i < 55; i++) { if (_keycodes[i].kchar == chars[0]) { return _keycodes[i].kcode; } } return translate_key(key); } struct _KeyCodeText { Key code; char32_t text; }; static const _KeyCodeText _native_keycodes[] = { /* clang-format off */ {Key::ESCAPE ,0x001B}, {Key::TAB ,0x0009}, {Key::BACKTAB ,0x007F}, {Key::BACKSPACE ,0x0008}, {Key::ENTER ,0x000D}, {Key::INSERT ,NSInsertFunctionKey}, {Key::KEY_DELETE ,0x007F}, {Key::PAUSE ,NSPauseFunctionKey}, {Key::PRINT ,NSPrintScreenFunctionKey}, {Key::SYSREQ ,NSSysReqFunctionKey}, {Key::CLEAR ,NSClearLineFunctionKey}, {Key::HOME ,0x2196}, {Key::END ,0x2198}, {Key::LEFT ,0x001C}, {Key::UP ,0x001E}, {Key::RIGHT ,0x001D}, {Key::DOWN ,0x001F}, {Key::PAGEUP ,0x21DE}, {Key::PAGEDOWN ,0x21DF}, {Key::NUMLOCK ,NSClearLineFunctionKey}, {Key::SCROLLLOCK ,NSScrollLockFunctionKey}, {Key::F1 ,NSF1FunctionKey}, {Key::F2 ,NSF2FunctionKey}, {Key::F3 ,NSF3FunctionKey}, {Key::F4 ,NSF4FunctionKey}, {Key::F5 ,NSF5FunctionKey}, {Key::F6 ,NSF6FunctionKey}, {Key::F7 ,NSF7FunctionKey}, {Key::F8 ,NSF8FunctionKey}, {Key::F9 ,NSF9FunctionKey}, {Key::F10 ,NSF10FunctionKey}, {Key::F11 ,NSF11FunctionKey}, {Key::F12 ,NSF12FunctionKey}, {Key::F13 ,NSF13FunctionKey}, {Key::F14 ,NSF14FunctionKey}, {Key::F15 ,NSF15FunctionKey}, {Key::F16 ,NSF16FunctionKey}, {Key::F17 ,NSF17FunctionKey}, {Key::F18 ,NSF18FunctionKey}, {Key::F19 ,NSF19FunctionKey}, {Key::F20 ,NSF20FunctionKey}, {Key::F21 ,NSF21FunctionKey}, {Key::F22 ,NSF22FunctionKey}, {Key::F23 ,NSF23FunctionKey}, {Key::F24 ,NSF24FunctionKey}, {Key::F25 ,NSF25FunctionKey}, {Key::F26 ,NSF26FunctionKey}, {Key::F27 ,NSF27FunctionKey}, {Key::F28 ,NSF28FunctionKey}, {Key::F29 ,NSF29FunctionKey}, {Key::F30 ,NSF30FunctionKey}, {Key::F31 ,NSF31FunctionKey}, {Key::F32 ,NSF32FunctionKey}, {Key::F33 ,NSF33FunctionKey}, {Key::F34 ,NSF34FunctionKey}, {Key::F35 ,NSF35FunctionKey}, {Key::MENU ,NSMenuFunctionKey}, {Key::HELP ,NSHelpFunctionKey}, {Key::STOP ,NSStopFunctionKey}, {Key::LAUNCH0 ,NSUserFunctionKey}, {Key::SPACE ,0x0020}, {Key::EXCLAM ,'!'}, {Key::QUOTEDBL ,'\"'}, {Key::NUMBERSIGN ,'#'}, {Key::DOLLAR ,'$'}, {Key::PERCENT ,'\%'}, {Key::AMPERSAND ,'&'}, {Key::APOSTROPHE ,'\''}, {Key::PARENLEFT ,'('}, {Key::PARENRIGHT ,')'}, {Key::ASTERISK ,'*'}, {Key::PLUS ,'+'}, {Key::COMMA ,','}, {Key::MINUS ,'-'}, {Key::PERIOD ,'.'}, {Key::SLASH ,'/'}, {Key::KEY_0 ,'0'}, {Key::KEY_1 ,'1'}, {Key::KEY_2 ,'2'}, {Key::KEY_3 ,'3'}, {Key::KEY_4 ,'4'}, {Key::KEY_5 ,'5'}, {Key::KEY_6 ,'6'}, {Key::KEY_7 ,'7'}, {Key::KEY_8 ,'8'}, {Key::KEY_9 ,'9'}, {Key::COLON ,':'}, {Key::SEMICOLON ,';'}, {Key::LESS ,'<'}, {Key::EQUAL ,'='}, {Key::GREATER ,'>'}, {Key::QUESTION ,'?'}, {Key::AT ,'@'}, {Key::A ,'a'}, {Key::B ,'b'}, {Key::C ,'c'}, {Key::D ,'d'}, {Key::E ,'e'}, {Key::F ,'f'}, {Key::G ,'g'}, {Key::H ,'h'}, {Key::I ,'i'}, {Key::J ,'j'}, {Key::K ,'k'}, {Key::L ,'l'}, {Key::M ,'m'}, {Key::N ,'n'}, {Key::O ,'o'}, {Key::P ,'p'}, {Key::Q ,'q'}, {Key::R ,'r'}, {Key::S ,'s'}, {Key::T ,'t'}, {Key::U ,'u'}, {Key::V ,'v'}, {Key::W ,'w'}, {Key::X ,'x'}, {Key::Y ,'y'}, {Key::Z ,'z'}, {Key::BRACKETLEFT ,'['}, {Key::BACKSLASH ,'\\'}, {Key::BRACKETRIGHT ,']'}, {Key::ASCIICIRCUM ,'^'}, {Key::UNDERSCORE ,'_'}, {Key::QUOTELEFT ,'`'}, {Key::BRACELEFT ,'{'}, {Key::BAR ,'|'}, {Key::BRACERIGHT ,'}'}, {Key::ASCIITILDE ,'~'}, {Key::NONE ,0x0000} /* clang-format on */ }; String KeyMappingMacOS::keycode_get_native_string(Key p_keycode) { const _KeyCodeText *kct = &_native_keycodes[0]; while (kct->text) { if (kct->code == p_keycode) { return String::chr(kct->text); } kct++; } return String(); } unsigned int KeyMappingMacOS::keycode_get_native_mask(Key p_keycode) { unsigned int mask = 0; if ((p_keycode & KeyModifierMask::CTRL) != Key::NONE) { mask |= NSEventModifierFlagControl; } if ((p_keycode & KeyModifierMask::ALT) != Key::NONE) { mask |= NSEventModifierFlagOption; } if ((p_keycode & KeyModifierMask::SHIFT) != Key::NONE) { mask |= NSEventModifierFlagShift; } if ((p_keycode & KeyModifierMask::META) != Key::NONE) { mask |= NSEventModifierFlagCommand; } if ((p_keycode & KeyModifierMask::KPAD) != Key::NONE) { mask |= NSEventModifierFlagNumericPad; } return mask; }