From 2256946f796c1fb1de290b896ebd80cf89971b27 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Fri, 12 Jun 2020 15:51:51 +0300 Subject: [PATCH] [3.2] Add keyboard layout enumeration / set / get functions (macOS, Windows, Linux/X11). --- core/bind/core_bind.cpp | 26 +++++ core/bind/core_bind.h | 5 + core/os/os.cpp | 18 ++++ core/os/os.h | 6 ++ doc/classes/OS.xml | 46 +++++++++ platform/osx/os_osx.h | 5 + platform/osx/os_osx.mm | 166 +++++++++++++++++++++++++++----- platform/windows/os_windows.cpp | 95 ++++++++++++++++++ platform/windows/os_windows.h | 5 + platform/x11/os_x11.cpp | 102 ++++++++++++++++++++ platform/x11/os_x11.h | 5 + 11 files changed, 455 insertions(+), 24 deletions(-) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 326bdead210..2f01bb636bf 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -569,6 +569,26 @@ String _OS::get_latin_keyboard_variant() const { } } +int _OS::keyboard_get_layout_count() const { + return OS::get_singleton()->keyboard_get_layout_count(); +} + +int _OS::keyboard_get_current_layout() const { + return OS::get_singleton()->keyboard_get_current_layout(); +} + +void _OS::keyboard_set_current_layout(int p_index) { + OS::get_singleton()->keyboard_set_current_layout(p_index); +} + +String _OS::keyboard_get_layout_language(int p_index) const { + return OS::get_singleton()->keyboard_get_layout_language(p_index); +} + +String _OS::keyboard_get_layout_name(int p_index) const { + return OS::get_singleton()->keyboard_get_layout_name(p_index); +} + String _OS::get_model_name() const { return OS::get_singleton()->get_model_name(); @@ -1323,6 +1343,12 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_latin_keyboard_variant"), &_OS::get_latin_keyboard_variant); ClassDB::bind_method(D_METHOD("get_model_name"), &_OS::get_model_name); + ClassDB::bind_method(D_METHOD("keyboard_get_layout_count"), &_OS::keyboard_get_layout_count); + ClassDB::bind_method(D_METHOD("keyboard_get_current_layout"), &_OS::keyboard_get_current_layout); + 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("can_draw"), &_OS::can_draw); ClassDB::bind_method(D_METHOD("is_userfs_persistent"), &_OS::is_userfs_persistent); ClassDB::bind_method(D_METHOD("is_stdout_verbose"), &_OS::is_stdout_verbose); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 22dab3cf3ee..603ebb4e0d8 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -242,6 +242,11 @@ public: String get_locale() const; String get_latin_keyboard_variant() const; + int keyboard_get_layout_count() const; + int keyboard_get_current_layout() const; + 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; String get_model_name() const; diff --git a/core/os/os.cpp b/core/os/os.cpp index 633433e79ca..6875ce79bd2 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -551,6 +551,24 @@ OS::LatinKeyboardVariant OS::get_latin_keyboard_variant() const { return LATIN_KEYBOARD_QWERTY; } +int OS::keyboard_get_layout_count() const { + return 0; +} + +int OS::keyboard_get_current_layout() const { + return -1; +} + +void OS::keyboard_set_current_layout(int p_index) {} + +String OS::keyboard_get_layout_language(int p_index) const { + return ""; +} + +String OS::keyboard_get_layout_name(int p_index) const { + return ""; +} + bool OS::is_joy_known(int p_device) { return true; } diff --git a/core/os/os.h b/core/os/os.h index 3f79e876517..c5411ccc5f4 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -493,6 +493,12 @@ public: virtual LatinKeyboardVariant get_latin_keyboard_variant() const; + virtual int keyboard_get_layout_count() const; + virtual int keyboard_get_current_layout() const; + 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 bool is_joy_known(int p_device); virtual String get_joy_guid(int p_device) const; diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 47160936289..41635212c57 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -682,6 +682,52 @@ [b]Note:[/b] Only implemented on desktop platforms. On other platforms, it will always return [code]true[/code]. + + + + + Returns active keyboard layout index. + [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + + + + + + + Returns the number of keyboard layouts. + [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + + + + + + + + + Returns the ISO-639/BCP-47 language code of the keyboard layout at position [code]index[/code]. + [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + + + + + + + + + Returns the localized name of the keyboard layout at position [code]index[/code]. + [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + + + + + + + + + Sets active keyboard layout. + [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + + diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index fb895595e64..fb2485b2305 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -248,6 +248,11 @@ public: virtual String get_executable_path() const; virtual LatinKeyboardVariant get_latin_keyboard_variant() const; + virtual int keyboard_get_layout_count() const; + virtual int keyboard_get_current_layout() const; + 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 void move_window_to_foreground(); diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 2b4cf769851..180ae4cabd6 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -1437,8 +1437,18 @@ void OS_OSX::initialize_core() { SemaphoreOSX::make_default(); } +struct LayoutInfo { + String name; + String code; +}; + +static Vector kbd_layouts; +static int current_layout = 0; static bool keyboard_layout_dirty = true; +static OS::LatinKeyboardVariant latin_variant = OS::LATIN_KEYBOARD_QWERTY; static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) { + kbd_layouts.clear(); + current_layout = 0; keyboard_layout_dirty = true; } @@ -2787,38 +2797,146 @@ static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) { return (NSString *)output; } -OS::LatinKeyboardVariant OS_OSX::get_latin_keyboard_variant() const { +void _update_keyboard_layouts() { + @autoreleasepool { + TISInputSourceRef cur_source = TISCopyCurrentKeyboardInputSource(); + NSString *cur_name = (NSString *)TISGetInputSourceProperty(cur_source, kTISPropertyLocalizedName); + CFRelease(cur_source); - static LatinKeyboardVariant layout = LATIN_KEYBOARD_QWERTY; + // Enum IME layouts + NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode }; + NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false); + for (NSUInteger i = 0; i < [list_ime count]; i++) { + LayoutInfo ly; + NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName); + ly.name.parse_utf8([name UTF8String]); - if (keyboard_layout_dirty) { + NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages); + ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]); + kbd_layouts.push_back(ly); - layout = LATIN_KEYBOARD_QWERTY; - - CGKeyCode keys[] = { kVK_ANSI_Q, kVK_ANSI_W, kVK_ANSI_E, kVK_ANSI_R, kVK_ANSI_T, kVK_ANSI_Y }; - NSString *test = createStringForKeys(keys, 6); - - if ([test isEqualToString:@"qwertz"]) { - layout = LATIN_KEYBOARD_QWERTZ; - } else if ([test isEqualToString:@"azerty"]) { - layout = LATIN_KEYBOARD_AZERTY; - } else if ([test isEqualToString:@"qzerty"]) { - layout = LATIN_KEYBOARD_QZERTY; - } else if ([test isEqualToString:@"',.pyf"]) { - layout = LATIN_KEYBOARD_DVORAK; - } else if ([test isEqualToString:@"xvlcwk"]) { - layout = LATIN_KEYBOARD_NEO; - } else if ([test isEqualToString:@"qwfpgj"]) { - layout = LATIN_KEYBOARD_COLEMAK; + if ([name isEqualToString:cur_name]) { + current_layout = kbd_layouts.size() - 1; + } } + [list_ime release]; - [test release]; + // Enum plain keyboard layouts + NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout }; + NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false); + for (NSUInteger i = 0; i < [list_kbd count]; i++) { + LayoutInfo ly; + NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName); + ly.name.parse_utf8([name UTF8String]); - keyboard_layout_dirty = false; - return layout; + NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyInputSourceLanguages); + ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]); + kbd_layouts.push_back(ly); + + if ([name isEqualToString:cur_name]) { + current_layout = kbd_layouts.size() - 1; + } + } + [list_kbd release]; } - return layout; + // Update latin variant + latin_variant = OS::LATIN_KEYBOARD_QWERTY; + + CGKeyCode keys[] = { kVK_ANSI_Q, kVK_ANSI_W, kVK_ANSI_E, kVK_ANSI_R, kVK_ANSI_T, kVK_ANSI_Y }; + NSString *test = createStringForKeys(keys, 6); + + if ([test isEqualToString:@"qwertz"]) { + latin_variant = OS::LATIN_KEYBOARD_QWERTZ; + } else if ([test isEqualToString:@"azerty"]) { + latin_variant = OS::LATIN_KEYBOARD_AZERTY; + } else if ([test isEqualToString:@"qzerty"]) { + latin_variant = OS::LATIN_KEYBOARD_QZERTY; + } else if ([test isEqualToString:@"',.pyf"]) { + latin_variant = OS::LATIN_KEYBOARD_DVORAK; + } else if ([test isEqualToString:@"xvlcwk"]) { + latin_variant = OS::LATIN_KEYBOARD_NEO; + } else if ([test isEqualToString:@"qwfpgj"]) { + latin_variant = OS::LATIN_KEYBOARD_COLEMAK; + } + + [test release]; + + keyboard_layout_dirty = false; +} + +OS::LatinKeyboardVariant OS_OSX::get_latin_keyboard_variant() const { + + if (keyboard_layout_dirty) { + _update_keyboard_layouts(); + } + + return latin_variant; +} + +int OS_OSX::keyboard_get_layout_count() const { + if (keyboard_layout_dirty) { + _update_keyboard_layouts(); + } + return kbd_layouts.size(); +} + +void OS_OSX::keyboard_set_current_layout(int p_index) { + if (keyboard_layout_dirty) { + _update_keyboard_layouts(); + } + + ERR_FAIL_INDEX(p_index, kbd_layouts.size()); + + NSString *cur_name = [NSString stringWithUTF8String:kbd_layouts[p_index].name.utf8().get_data()]; + + NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout }; + NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false); + for (NSUInteger i = 0; i < [list_kbd count]; i++) { + NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName); + if ([name isEqualToString:cur_name]) { + TISSelectInputSource((TISInputSourceRef)[list_kbd objectAtIndex:i]); + break; + } + } + [list_kbd release]; + + NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode }; + NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false); + for (NSUInteger i = 0; i < [list_ime count]; i++) { + NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName); + if ([name isEqualToString:cur_name]) { + TISSelectInputSource((TISInputSourceRef)[list_ime objectAtIndex:i]); + break; + } + } + [list_ime release]; +} + +int OS_OSX::keyboard_get_current_layout() const { + if (keyboard_layout_dirty) { + _update_keyboard_layouts(); + } + + return current_layout; +} + +String OS_OSX::keyboard_get_layout_language(int p_index) const { + if (keyboard_layout_dirty) { + _update_keyboard_layouts(); + } + + ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), ""); + return kbd_layouts[p_index].code; +} + +String OS_OSX::keyboard_get_layout_name(int p_index) const { + if (keyboard_layout_dirty) { + _update_keyboard_layouts(); + } + + ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), ""); + return kbd_layouts[p_index].name; } void OS_OSX::process_events() { diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index a8879cf4888..08bdd670d24 100755 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -3258,6 +3258,101 @@ OS::LatinKeyboardVariant OS_Windows::get_latin_keyboard_variant() const { return LATIN_KEYBOARD_QWERTY; } +int OS_Windows::keyboard_get_layout_count() const { + return GetKeyboardLayoutList(0, NULL); +} + +int OS_Windows::keyboard_get_current_layout() const { + HKL cur_layout = GetKeyboardLayout(0); + + int layout_count = GetKeyboardLayoutList(0, NULL); + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + + for (int i = 0; i < layout_count; i++) { + if (cur_layout == layouts[i]) { + memfree(layouts); + return i; + } + } + memfree(layouts); + return -1; +} + +void OS_Windows::keyboard_set_current_layout(int p_index) { + int layout_count = GetKeyboardLayoutList(0, NULL); + + ERR_FAIL_INDEX(p_index, layout_count); + + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + ActivateKeyboardLayout(layouts[p_index], KLF_SETFORPROCESS); + memfree(layouts); +} + +String OS_Windows::keyboard_get_layout_language(int p_index) const { + int layout_count = GetKeyboardLayoutList(0, NULL); + + ERR_FAIL_INDEX_V(p_index, layout_count, ""); + + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + + wchar_t buf[LOCALE_NAME_MAX_LENGTH]; + memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t)); + LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); + + memfree(layouts); + + return String(buf).substr(0, 2); +} + +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; + + HKEY hkey; + wchar_t layout_text[1024]; + memset(layout_text, 0, 1024 * sizeof(wchar_t)); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)id.c_str(), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { + return ret; + } + + DWORD buffer = 1024; + DWORD vtype = REG_SZ; + if (RegQueryValueExW(hkey, L"Layout Text", NULL, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) { + ret = String(layout_text); + } + RegCloseKey(hkey); + return ret; +} + +String OS_Windows::keyboard_get_layout_name(int p_index) const { + int layout_count = GetKeyboardLayoutList(0, NULL); + + ERR_FAIL_INDEX_V(p_index, layout_count, ""); + + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + + String ret = _get_full_layout_name_from_registry(layouts[p_index]); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine). + if (ret == String()) { + wchar_t buf[LOCALE_NAME_MAX_LENGTH]; + memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t)); + LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); + + wchar_t name[1024]; + memset(name, 0, 1024 * sizeof(wchar_t)); + GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024); + + ret = String(name); + } + memfree(layouts); + + return ret; +} + void OS_Windows::release_rendering_thread() { gl_context->release_current(); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 2668e52923c..69767b6764b 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -516,6 +516,11 @@ public: virtual int get_processor_count() const; virtual LatinKeyboardVariant get_latin_keyboard_variant() const; + virtual int keyboard_get_layout_count() const; + virtual int keyboard_get_current_layout() const; + 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 void enable_for_stealing_focus(ProcessID pid); virtual void move_window_to_foreground(); diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 9b9d141f7ad..6feb3be5e48 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -3474,6 +3474,108 @@ OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const { return LATIN_KEYBOARD_QWERTY; } +int OS_X11::keyboard_get_layout_count() const { + int _group_count = 0; + XkbDescRec *kbd = XkbAllocKeyboard(); + if (kbd) { + kbd->dpy = x11_display; + XkbGetControls(x11_display, XkbAllControlsMask, kbd); + XkbGetNames(x11_display, XkbSymbolsNameMask, kbd); + + const Atom *groups = kbd->names->groups; + if (kbd->ctrls != NULL) { + _group_count = kbd->ctrls->num_groups; + } else { + while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) { + _group_count++; + } + } + XkbFreeKeyboard(kbd, 0, true); + } + return _group_count; +} + +int OS_X11::keyboard_get_current_layout() const { + XkbStateRec state; + XkbGetState(x11_display, XkbUseCoreKbd, &state); + return state.group; +} + +void OS_X11::keyboard_set_current_layout(int p_index) { + ERR_FAIL_INDEX(p_index, keyboard_get_layout_count()); + XkbLockGroup(x11_display, XkbUseCoreKbd, p_index); +} + +String OS_X11::keyboard_get_layout_language(int p_index) const { + String ret; + XkbDescRec *kbd = XkbAllocKeyboard(); + if (kbd) { + kbd->dpy = x11_display; + XkbGetControls(x11_display, XkbAllControlsMask, kbd); + XkbGetNames(x11_display, XkbSymbolsNameMask, kbd); + XkbGetNames(x11_display, XkbGroupNamesMask, kbd); + + int _group_count = 0; + const Atom *groups = kbd->names->groups; + if (kbd->ctrls != NULL) { + _group_count = kbd->ctrls->num_groups; + } else { + while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) { + _group_count++; + } + } + + Atom names = kbd->names->symbols; + if (names != None) { + char *name = XGetAtomName(x11_display, names); + Vector info = String(name).split("+"); + if (p_index >= 0 && p_index < _group_count) { + if (p_index + 1 < info.size()) { + ret = info[p_index + 1]; // Skip "pc" at the start and "inet"/"group" at the end of symbols. + } else { + ret = "en"; // No symbol for layout fallback to "en". + } + } else { + ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ")."); + } + XFree(name); + } + XkbFreeKeyboard(kbd, 0, true); + } + return ret.substr(0, 2); +} + +String OS_X11::keyboard_get_layout_name(int p_index) const { + String ret; + XkbDescRec *kbd = XkbAllocKeyboard(); + if (kbd) { + kbd->dpy = x11_display; + XkbGetControls(x11_display, XkbAllControlsMask, kbd); + XkbGetNames(x11_display, XkbSymbolsNameMask, kbd); + XkbGetNames(x11_display, XkbGroupNamesMask, kbd); + + int _group_count = 0; + const Atom *groups = kbd->names->groups; + if (kbd->ctrls != NULL) { + _group_count = kbd->ctrls->num_groups; + } else { + while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) { + _group_count++; + } + } + + if (p_index >= 0 && p_index < _group_count) { + char *full_name = XGetAtomName(x11_display, groups[p_index]); + ret.parse_utf8(full_name); + XFree(full_name); + } else { + ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ")."); + } + XkbFreeKeyboard(kbd, 0, true); + } + return ret; +} + void OS_X11::update_real_mouse_position() { Window root_return, child_return; int root_x, root_y, win_x, win_y; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index be11d2be500..6da326c0540 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -329,6 +329,11 @@ public: virtual Error move_to_trash(const String &p_path); virtual LatinKeyboardVariant get_latin_keyboard_variant() const; + virtual int keyboard_get_layout_count() const; + virtual int keyboard_get_current_layout() const; + 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; void update_real_mouse_position(); OS_X11();