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();