497 lines
16 KiB
Plaintext
497 lines
16 KiB
Plaintext
/**************************************************************************/
|
|
/* 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;
|
|
}
|