Backport: Allow for mapping scancodes to current layout

Co-authored-by: Frixuu <kontakt@lukasz.xyz>
This commit is contained in:
bruvzg 2021-12-17 12:39:40 +02:00
parent 9c04c75e7d
commit b4ec1c5817
No known key found for this signature in database
GPG key ID: 7960FCF39844EC38
15 changed files with 255 additions and 135 deletions

View file

@ -572,6 +572,10 @@ String _OS::keyboard_get_layout_name(int p_index) const {
return OS::get_singleton()->keyboard_get_layout_name(p_index);
}
uint32_t _OS::keyboard_get_scancode_from_physical(uint32_t p_scancode) const {
return OS::get_singleton()->keyboard_get_scancode_from_physical(p_scancode);
}
String _OS::get_model_name() const {
return OS::get_singleton()->get_model_name();
}
@ -1354,6 +1358,7 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("keyboard_set_current_layout", "index"), &_OS::keyboard_set_current_layout);
ClassDB::bind_method(D_METHOD("keyboard_get_layout_language", "index"), &_OS::keyboard_get_layout_language);
ClassDB::bind_method(D_METHOD("keyboard_get_layout_name", "index"), &_OS::keyboard_get_layout_name);
ClassDB::bind_method(D_METHOD("keyboard_get_scancode_from_physical", "scancode"), &_OS::keyboard_get_scancode_from_physical);
ClassDB::bind_method(D_METHOD("can_draw"), &_OS::can_draw);
ClassDB::bind_method(D_METHOD("is_userfs_persistent"), &_OS::is_userfs_persistent);

View file

@ -263,6 +263,7 @@ public:
void keyboard_set_current_layout(int p_index);
String keyboard_get_layout_language(int p_index) const;
String keyboard_get_layout_name(int p_index) const;
uint32_t keyboard_get_scancode_from_physical(uint32_t p_scancode) const;
String get_model_name() const;

View file

@ -215,6 +215,10 @@ int OS::get_virtual_keyboard_height() const {
return 0;
}
uint32_t OS::keyboard_get_scancode_from_physical(uint32_t p_scancode) const {
return p_scancode;
}
void OS::set_cursor_shape(CursorShape p_shape) {
}

View file

@ -411,6 +411,7 @@ public:
// returns height of the currently shown virtual keyboard (0 if keyboard is hidden)
virtual int get_virtual_keyboard_height() const;
virtual uint32_t keyboard_get_scancode_from_physical(uint32_t p_scancode) const;
virtual void set_cursor_shape(CursorShape p_shape);
virtual CursorShape get_cursor_shape() const;

View file

@ -709,6 +709,14 @@
[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
</description>
</method>
<method name="keyboard_get_scancode_from_physical" qualifiers="const">
<return type="int" />
<argument index="0" name="scancode" type="int" />
<description>
Converts a physical (US QWERTY) [code]scancode[/code] to one in the active keyboard layout.
[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
</description>
</method>
<method name="keyboard_set_current_layout">
<return type="void" />
<argument index="0" name="index" type="int" />

View file

@ -263,6 +263,7 @@ public:
virtual void keyboard_set_current_layout(int p_index);
virtual String keyboard_get_layout_language(int p_index) const;
virtual String keyboard_get_layout_name(int p_index) const;
virtual uint32_t keyboard_get_scancode_from_physical(uint32_t p_scancode) const;
virtual void move_window_to_foreground();

View file

@ -932,11 +932,8 @@ static bool isNumpadKey(unsigned int key) {
return false;
}
// Translates a OS X keycode to a Godot keycode
//
static int translateKey(unsigned int key) {
// Keyboard symbol translation table
static const unsigned int table[128] = {
static const unsigned int _osx_to_godot_table[128] = {
/* 00 */ KEY_A,
/* 01 */ KEY_S,
/* 02 */ KEY_D,
@ -1067,10 +1064,22 @@ static int translateKey(unsigned int key) {
/* 7f */ KEY_UNKNOWN,
};
// Translates a OS X keycode to a Godot keycode
static int translateKey(unsigned int key) {
if (key >= 128)
return KEY_UNKNOWN;
return table[key];
return _osx_to_godot_table[key];
}
// Translates a Godot keycode back to a OSX keycode
static unsigned int unmapKey(int key) {
for (int i = 0; i <= 126; i++) {
if (_osx_to_godot_table[i] == key) {
return i;
}
}
return 127;
}
struct _KeyCodeMap {
@ -3207,6 +3216,17 @@ String OS_OSX::keyboard_get_layout_name(int p_index) const {
return kbd_layouts[p_index].name;
}
uint32_t OS_OSX::keyboard_get_scancode_from_physical(uint32_t p_scancode) const {
if (p_scancode == KEY_PAUSE) {
return p_scancode;
}
unsigned int modifiers = p_scancode & KEY_MODIFIER_MASK;
unsigned int scancode_no_mod = p_scancode & KEY_CODE_MASK;
unsigned int osx_scancode = unmapKey((uint32_t)scancode_no_mod);
return (uint32_t)(remapKey(osx_scancode, 0) | modifiers);
}
void OS_OSX::process_events() {
while (true) {
NSEvent *event = [NSApp

View file

@ -347,6 +347,16 @@ unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) {
return KEY_UNKNOWN;
}
unsigned int KeyMappingWindows::get_scancode(unsigned int p_keycode) {
for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
if (_scancode_to_keycode[i].keysym == p_keycode) {
return _scancode_to_keycode[i].keycode;
}
}
return 0;
}
unsigned int KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended) {
unsigned int keycode = KEY_UNKNOWN;
for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) {

View file

@ -42,6 +42,7 @@ class KeyMappingWindows {
public:
static unsigned int get_keysym(unsigned int p_code);
static unsigned int get_scancode(unsigned int p_keycode);
static unsigned int get_scansym(unsigned int p_code, bool p_extended);
static bool is_extended_key(unsigned int p_code);
};

View file

@ -3307,6 +3307,42 @@ String OS_Windows::keyboard_get_layout_language(int p_index) const {
return String(buf).substr(0, 2);
}
uint32_t OS_Windows::keyboard_get_scancode_from_physical(uint32_t p_scancode) const {
unsigned int modifiers = p_scancode & KEY_MODIFIER_MASK;
uint32_t scancode_no_mod = (uint32_t)(p_scancode & KEY_CODE_MASK);
if (scancode_no_mod == KEY_PRINT ||
scancode_no_mod == KEY_KP_ADD ||
scancode_no_mod == KEY_KP_5 ||
(scancode_no_mod >= KEY_0 && scancode_no_mod <= KEY_9)) {
return p_scancode;
}
unsigned int scancode = KeyMappingWindows::get_scancode(scancode_no_mod);
if (scancode == 0) {
return p_scancode;
}
HKL current_layout = GetKeyboardLayout(0);
UINT vk = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK, current_layout);
if (vk == 0) {
return p_scancode;
}
UINT char_code = MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, current_layout) & 0x7FFF;
// Unlike a similar Linux/BSD check which matches full Latin-1 range,
// we limit these to ASCII to fix some layouts, including Arabic ones
if (char_code >= 32 && char_code <= 127) {
// Godot uses 'braces' instead of 'brackets'
if (char_code == KEY_BRACKETLEFT || char_code == KEY_BRACKETRIGHT) {
char_code += 32;
}
return (uint32_t)(char_code | modifiers);
}
return (uint32_t)(KeyMappingWindows::get_keysym(vk) | modifiers);
}
String _get_full_layout_name_from_registry(HKL p_layout) {
String id = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + String::num_int64((int64_t)p_layout, 16, false).lpad(8, "0");
String ret;

View file

@ -532,6 +532,7 @@ public:
virtual void keyboard_set_current_layout(int p_index);
virtual String keyboard_get_layout_language(int p_index) const;
virtual String keyboard_get_layout_name(int p_index) const;
virtual uint32_t keyboard_get_scancode_from_physical(uint32_t p_scancode) const;
virtual void enable_for_stealing_focus(ProcessID pid);
virtual void move_window_to_foreground();

View file

@ -310,6 +310,18 @@ unsigned int KeyMappingX11::get_scancode(unsigned int p_code) {
return keycode;
}
unsigned int KeyMappingX11::get_xlibcode(unsigned int p_keysym) {
unsigned int code = 0;
for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
if (_scancode_to_keycode[i].keysym == p_keysym) {
code = _scancode_to_keycode[i].keycode;
break;
}
}
return code;
}
unsigned int KeyMappingX11::get_keycode(KeySym p_keysym) {
// kinda bruteforce.. could optimize.

View file

@ -45,6 +45,7 @@ class KeyMappingX11 {
public:
static unsigned int get_keycode(KeySym p_keysym);
static unsigned int get_xlibcode(unsigned int p_keysym);
static unsigned int get_scancode(unsigned int p_code);
static KeySym get_keysym(unsigned int p_code);
static unsigned int get_unicode_from_keysym(KeySym p_keysym);

View file

@ -4100,6 +4100,24 @@ String OS_X11::keyboard_get_layout_name(int p_index) const {
return ret;
}
uint32_t OS_X11::keyboard_get_scancode_from_physical(uint32_t p_scancode) const {
unsigned int modifiers = p_scancode & KEY_MODIFIER_MASK;
unsigned int scancode_no_mod = p_scancode & KEY_CODE_MASK;
unsigned int xkeycode = KeyMappingX11::get_xlibcode((uint32_t)scancode_no_mod);
KeySym xkeysym = XkbKeycodeToKeysym(x11_display, xkeycode, 0, 0);
if (xkeysym >= 'a' && xkeysym <= 'z') {
xkeysym -= ('a' - 'A');
}
uint32_t key = KeyMappingX11::get_keycode(xkeysym);
// If not found, fallback to QWERTY.
// This should match the behavior of the event pump
if (key == 0) {
return p_scancode;
}
return (uint32_t)(key | modifiers);
}
void OS_X11::update_real_mouse_position() {
Window root_return, child_return;
int root_x, root_y, win_x, win_y;

View file

@ -361,6 +361,7 @@ public:
virtual void keyboard_set_current_layout(int p_index);
virtual String keyboard_get_layout_language(int p_index) const;
virtual String keyboard_get_layout_name(int p_index) const;
virtual uint32_t keyboard_get_scancode_from_physical(uint32_t p_scancode) const;
void update_real_mouse_position();
OS_X11();