diff --git a/include/freerdp/locale/locale.h b/include/freerdp/locale/locale.h index 54ab2c660..6647bb22c 100644 --- a/include/freerdp/locale/locale.h +++ b/include/freerdp/locale/locale.h @@ -62,6 +62,7 @@ #define BRETON 0x047E #define BULGARIAN 0x0402 #define CATALAN 0x0403 +#define CHEROKEE 0x045C #define CHINESE_TAIWAN 0x0404 #define CHINESE_PRC 0x0804 #define CHINESE_HONG_KONG 0x0C04 @@ -113,12 +114,14 @@ #define GREEK 0x0408 #define GREENLANDIC 0x046F #define GUJARATI 0x0447 +#define HAWAIIAN 0x0475 #define HEBREW 0x040D #define HINDI 0x0439 #define HUNGARIAN 0x040E #define ICELANDIC 0x040F #define IGBO 0x0470 #define INDONESIAN 0x0421 +#define INUKTITUT 0x045D #define IRISH 0x083C #define ITALIAN_STANDARD 0x0410 #define ITALIAN_SWISS 0x0810 @@ -146,6 +149,7 @@ #define MARATHI 0x044E #define MOHAWK 0x047C #define MONGOLIAN 0x0450 +#define MYANMAR 0x0455 #define NEPALI 0x0461 #define NORWEGIAN_BOKMAL 0x0414 #define NORWEGIAN_NYNORSK 0x0814 diff --git a/libfreerdp/locale/CMakeLists.txt b/libfreerdp/locale/CMakeLists.txt index 1a300f22f..9c74e20dc 100644 --- a/libfreerdp/locale/CMakeLists.txt +++ b/libfreerdp/locale/CMakeLists.txt @@ -38,10 +38,20 @@ set(${MODULE_PREFIX}_SUN_SRCS keyboard_sun.c keyboard_sun.h) +set(${MODULE_PREFIX}_APPLE_SRCS + keyboard_apple.c + keyboard_apple.h) + if(CMAKE_SYSTEM_NAME MATCHES Solaris) set(WITH_SUN true) endif() +if(APPLE AND (NOT IOS)) + set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${${MODULE_PREFIX}_APPLE_SRCS}) + find_library(CARBON Carbon) + freerdp_library_add(${CARBON}) +endif() + if(WITH_X11) freerdp_definition_add(-DWITH_X11) freerdp_include_directory_add(${X11_INCLUDE_DIRS}) diff --git a/libfreerdp/locale/keyboard.c b/libfreerdp/locale/keyboard.c index 4ee955de0..1e9547d6f 100644 --- a/libfreerdp/locale/keyboard.c +++ b/libfreerdp/locale/keyboard.c @@ -32,6 +32,10 @@ #include "liblocale.h" +#if defined(__MACOSX__) +#include "keyboard_apple.h" +#endif + #ifdef WITH_X11 #include "keyboard_x11.h" @@ -63,6 +67,11 @@ int freerdp_detect_keyboard(DWORD* keyboardLayoutId) *keyboardLayoutId = ((DWORD)GetKeyboardLayout(0) >> 16) & 0x0000FFFF; #endif +#if defined(__MACOSX__) + if (*keyboardLayoutId == 0) + freerdp_detect_keyboard_layout_from_cf(keyboardLayoutId); +#endif + #ifdef WITH_X11 if (*keyboardLayoutId == 0) freerdp_detect_keyboard_layout_from_xkb(keyboardLayoutId); @@ -147,7 +156,7 @@ DWORD freerdp_keyboard_init(DWORD keyboardLayoutId) for (keycode = 0; keycode < ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE); keycode++) { VIRTUAL_SCANCODE_TO_X11_KEYCODE - [RDP_SCANCODE_CODE(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode])] + [RDP_SCANCODE_CODE(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode])] [RDP_SCANCODE_EXTENDED(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode]) ? 1 : 0] = keycode; } diff --git a/libfreerdp/locale/keyboard_apple.c b/libfreerdp/locale/keyboard_apple.c new file mode 100644 index 000000000..d0d59216e --- /dev/null +++ b/libfreerdp/locale/keyboard_apple.c @@ -0,0 +1,244 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Apple Core Foundation Keyboard Mapping + * + * Copyright 2021 Thincast Technologies GmbH + * Copyright 2021 Martin Fleisz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "liblocale.h" + +#include +#include + +#include "keyboard_apple.h" + +struct KEYBOARD_LAYOUT_MAPPING_ +{ + const char* inputSourceId; /* Apple input source id (com.apple.keylayout or inputmethod) */ + DWORD code; /* mapped rdp keyboard layout id */ +}; +typedef struct KEYBOARD_LAYOUT_MAPPING_ KEYBOARD_LAYOUT_MAPPING; + +static const KEYBOARD_LAYOUT_MAPPING KEYBOARD_MAPPING_TABLE[] = { + { "com.apple.inputmethod.Kotoeri.Japanese", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Japanese.FullWidthRoman", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Japanese.HalfWidthKana", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Japanese.Katakana", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Katakana", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Roman", JAPANESE }, + { "com.apple.inputmethod.kotoeri.Ainu", JAPANESE }, + { "com.apple.keylayout.2SetHangul", KOREAN }, + { "com.apple.keylayout.390Hangul", KOREAN }, + { "com.apple.keylayout.3SetHangul", KOREAN }, + { "com.apple.keylayout.AfghanDari", DARI }, + { "com.apple.keylayout.AfghanPashto", PASHTO }, + { "com.apple.keylayout.AfghanUzbek", UZBEK_LATIN }, + { "com.apple.keylayout.Arabic", ARABIC_EGYPT }, + { "com.apple.keylayout.Arabic-QWERTY", ARABIC_EGYPT }, + { "com.apple.keylayout.ArabicPC", ARABIC_EGYPT }, + { "com.apple.keylayout.Armenian-HMQWERTY", ARMENIAN }, + { "com.apple.keylayout.Armenian-WesternQWERTY", ARMENIAN }, + { "com.apple.keylayout.Australian", ENGLISH_AUSTRALIAN }, + { "com.apple.keylayout.Austrian", GERMAN_AUSTRIAN }, + { "com.apple.keylayout.Azeri", AZERI_LATIN }, + { "com.apple.keylayout.Bangla", BENGALI_INDIA }, + { "com.apple.keylayout.Bangla-QWERTY", BENGALI_INDIA }, + { "com.apple.keylayout.Belgian", DUTCH_BELGIAN }, + { "com.apple.keylayout.Brazilian", PORTUGUESE_BRAZILIAN }, + { "com.apple.keylayout.British", ENGLISH_UNITED_KINGDOM }, + { "com.apple.keylayout.British-PC", ENGLISH_UNITED_KINGDOM }, + { "com.apple.keylayout.Bulgarian", BULGARIAN }, + { "com.apple.keylayout.Bulgarian-Phonetic", BULGARIAN }, + { "com.apple.keylayout.Byelorussian", BELARUSIAN }, + { "com.apple.keylayout.Canadian", ENGLISH_CANADIAN }, + { "com.apple.keylayout.Canadian-CSA", FRENCH_CANADIAN }, + { "com.apple.keylayout.CangjieKeyboard", CHINESE_TAIWAN }, + { "com.apple.keylayout.Cherokee-Nation", CHEROKEE }, + { "com.apple.keylayout.Cherokee-QWERTY", CHEROKEE }, + { "com.apple.keylayout.Colemak", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Croatian", CROATIAN }, + { "com.apple.keylayout.Croatian-PC", CROATIAN }, + { "com.apple.keylayout.Czech", CZECH }, + { "com.apple.keylayout.Czech-QWERTY", CZECH }, + { "com.apple.keylayout.DVORAK-QWERTYCMD", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Danish", DANISH }, + { "com.apple.keylayout.Devanagari", HINDI }, + { "com.apple.keylayout.Devanagari-QWERTY", HINDI }, + { "com.apple.keylayout.Dutch", DUTCH_STANDARD }, + { "com.apple.keylayout.Dvorak", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Dvorak-Left", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Dvorak-Right", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Estonian", ESTONIAN }, + { "com.apple.keylayout.Faroese", FAEROESE }, + { "com.apple.keylayout.Finnish", FINNISH }, + { "com.apple.keylayout.FinnishExtended", FINNISH }, + { "com.apple.keylayout.FinnishSami-PC", FINNISH }, + { "com.apple.keylayout.French", FRENCH_STANDARD }, + { "com.apple.keylayout.French-PC", FRENCH_STANDARD }, + { "com.apple.keylayout.French-numerical", FRENCH_STANDARD }, + { "com.apple.keylayout.GJCRomaja", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Georgian-QWERTY", GEORGIAN }, + { "com.apple.keylayout.German", GERMAN_STANDARD }, + { "com.apple.keylayout.Greek", GREEK }, + { "com.apple.keylayout.GreekPolytonic", GREEK }, + { "com.apple.keylayout.Gujarati", GUJARATI }, + { "com.apple.keylayout.Gujarati-QWERTY", GUJARATI }, + { "com.apple.keylayout.Gurmukhi", PUNJABI }, + { "com.apple.keylayout.Gurmukhi-QWERTY", PUNJABI }, + { "com.apple.keylayout.HNCRomaja", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Hawaiian", HAWAIIAN }, + { "com.apple.keylayout.Hebrew", HEBREW }, + { "com.apple.keylayout.Hebrew-PC", HEBREW }, + { "com.apple.keylayout.Hebrew-QWERTY", HEBREW }, + { "com.apple.keylayout.Hungarian", HUNGARIAN }, + { "com.apple.keylayout.Hungarian-QWERTY", HUNGARIAN }, + { "com.apple.keylayout.Icelandic", ICELANDIC }, + { "com.apple.keylayout.Inuktitut-Nunavut", INUKTITUT }, + { "com.apple.keylayout.Inuktitut-Nutaaq", INUKTITUT }, + { "com.apple.keylayout.Inuktitut-QWERTY", INUKTITUT }, + { "com.apple.keylayout.InuttitutNunavik", INUKTITUT }, + { "com.apple.keylayout.Irish", ENGLISH_IRELAND }, + { "com.apple.keylayout.IrishExtended", IRISH }, + { "com.apple.keylayout.Italian", ITALIAN_STANDARD }, + { "com.apple.keylayout.Italian-Pro", ITALIAN_STANDARD }, + { "com.apple.keylayout.Jawi-QWERTY", ARABIC_SAUDI_ARABIA }, + { "com.apple.keylayout.Kannada", KANNADA }, + { "com.apple.keylayout.Kannada-QWERTY", KANNADA }, + { "com.apple.keylayout.Kazakh", KAZAKH }, + { "com.apple.keylayout.Khmer", KHMER }, + { "com.apple.keylayout.Latvian", LATVIAN }, + { "com.apple.keylayout.Lithuanian", LITHUANIAN }, + { "com.apple.keylayout.Macedonian", MACEDONIAN }, + { "com.apple.keylayout.Malayalam", MALAYALAM }, + { "com.apple.keylayout.Malayalam-QWERTY", MALAYALAM }, + { "com.apple.keylayout.Maltese", MALTESE }, + { "com.apple.keylayout.Maori", MAORI }, + { "com.apple.keylayout.Myanmar-QWERTY", MYANMAR }, + { "com.apple.keylayout.Nepali", NEPALI }, + { "com.apple.keylayout.NorthernSami", SAMI_NORTHERN_NORWAY }, + { "com.apple.keylayout.Norwegian", NORWEGIAN_BOKMAL }, + { "com.apple.keylayout.NorwegianExtended", NORWEGIAN_BOKMAL }, + { "com.apple.keylayout.NorwegianSami-PC", NORWEGIAN_BOKMAL }, + { "com.apple.keylayout.Oriya", ORIYA }, + { "com.apple.keylayout.Persian", FARSI }, + { "com.apple.keylayout.Persian-ISIRI2901", FARSI }, + { "com.apple.keylayout.Polish", POLISH }, + { "com.apple.keylayout.PolishPro", POLISH }, + { "com.apple.keylayout.Portuguese", PORTUGUESE_STANDARD }, + { "com.apple.keylayout.Romanian", ROMANIAN }, + { "com.apple.keylayout.Romanian-Standard", ROMANIAN }, + { "com.apple.keylayout.Russian", RUSSIAN }, + { "com.apple.keylayout.Russian-Phonetic", RUSSIAN }, + { "com.apple.keylayout.RussianWin", RUSSIAN }, + { "com.apple.keylayout.Sami-PC", SAMI_NORTHERN_SWEDEN }, + { "com.apple.keylayout.Serbian", SERBIAN_CYRILLIC_BOSNIA_HERZEGOVINA }, + { "com.apple.keylayout.Serbian-Latin", SERBIAN_LATIN_BOSNIA_HERZEGOVINA }, + { "com.apple.keylayout.Sinhala", SINHALA }, + { "com.apple.keylayout.Sinhala-QWERTY", SINHALA }, + { "com.apple.keylayout.Slovak", SLOVAK }, + { "com.apple.keylayout.Slovak-QWERTY", SLOVAK }, + { "com.apple.keylayout.Slovenian", SLOVENIAN }, + { "com.apple.keylayout.Spanish", SPANISH_TRADITIONAL_SORT }, + { "com.apple.keylayout.Spanish-ISO", SPANISH_MODERN_SORT }, + { "com.apple.keylayout.Swedish", SWEDISH }, + { "com.apple.keylayout.Swedish-Pro", SWEDISH }, + { "com.apple.keylayout.SwedishSami-PC", SWEDISH }, + { "com.apple.keylayout.SwissFrench", FRENCH_SWISS }, + { "com.apple.keylayout.SwissGerman", GERMAN_SWISS }, + { "com.apple.keylayout.Telugu", TELUGU }, + { "com.apple.keylayout.Telugu-QWERTY", TELUGU }, + { "com.apple.keylayout.Thai", THAI }, + { "com.apple.keylayout.Thai-PattaChote", THAI }, + { "com.apple.keylayout.Tibetan-QWERTY", TIBETAN_PRC }, + { "com.apple.keylayout.Tibetan-Wylie", TIBETAN_PRC }, + { "com.apple.keylayout.TibetanOtaniUS", TIBETAN_PRC }, + { "com.apple.keylayout.Turkish", TURKISH }, + { "com.apple.keylayout.Turkish-QWERTY", TURKISH }, + { "com.apple.keylayout.Turkish-QWERTY-PC", TURKISH }, + { "com.apple.keylayout.US", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.USExtended", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.USInternational-PC", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Ukrainian", UKRAINIAN }, + { "com.apple.keylayout.Ukrainian-PC", UKRAINIAN }, + { "com.apple.keylayout.UnicodeHexInput", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Urdu", URDU }, + { "com.apple.keylayout.Uyghur", UIGHUR }, + { "com.apple.keylayout.Vietnamese", VIETNAMESE }, + { "com.apple.keylayout.Welsh", WELSH } +}; + +int freerdp_detect_keyboard_layout_from_cf(DWORD* keyboardLayoutId) +{ + int i; + CFIndex length; + char* inputSourceId = NULL; + CFStringRef inputSourceIdRef; + TISInputSourceRef inputSrc = TISCopyCurrentKeyboardLayoutInputSource(); + if (!inputSrc) + { + DEBUG_KBD("Failed to get current keyboard layout input source!"); + return 0; + } + + /* get current input source id */ + inputSourceIdRef = (CFStringRef)TISGetInputSourceProperty(inputSrc, kTISPropertyInputSourceID); + if (!inputSourceIdRef) + { + DEBUG_KBD("Failed to get input source id!"); + goto done; + } + + /* convert it to a C-string */ + length = CFStringGetLength(inputSourceIdRef); + length = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + inputSourceId = (char*)malloc(length); + if (!inputSourceId) + { + DEBUG_KBD("Failed to allocate string buffer!"); + goto done; + } + + if (!CFStringGetCString(inputSourceIdRef, inputSourceId, length, kCFStringEncodingUTF8)) + { + DEBUG_KBD("Failed to convert CFString to C-string!"); + goto done; + } + + /* Search for the id in the mapping table */ + for (i = 0; i < ARRAYSIZE(KEYBOARD_MAPPING_TABLE); ++i) + { + if (strcmp(inputSourceId, KEYBOARD_MAPPING_TABLE[i].inputSourceId) == 0) + { + *keyboardLayoutId = KEYBOARD_MAPPING_TABLE[i].code; + break; + } + } + +done: + free(inputSourceId); + CFRelease(inputSrc); + if (*keyboardLayoutId > 0) + return *keyboardLayoutId; + + return 0; +} diff --git a/libfreerdp/locale/keyboard_apple.h b/libfreerdp/locale/keyboard_apple.h new file mode 100644 index 000000000..cd3c3e7be --- /dev/null +++ b/libfreerdp/locale/keyboard_apple.h @@ -0,0 +1,28 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Apple Core Foundation Keyboard Mapping + * + * Copyright 2021 Thincast Technologies GmbH + * Copyright 2021 Martin Fleisz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_LOCALE_KEYBOARD_APPLE_H +#define FREERDP_LOCALE_KEYBOARD_APPLE_H + +#include + +FREERDP_LOCAL int freerdp_detect_keyboard_layout_from_cf(DWORD* keyboardLayoutId); + +#endif /* FREERDP_LOCALE_KEYBOARD_APPLE_H */ diff --git a/libfreerdp/locale/locale.c b/libfreerdp/locale/locale.c index 97ae9e469..bac4f880b 100644 --- a/libfreerdp/locale/locale.c +++ b/libfreerdp/locale/locale.c @@ -3,6 +3,8 @@ * Microsoft Locales * * Copyright 2009-2012 Marc-Andre Moreau + * Copyright 2021 Thincast Technologies GmbH + * Copyright 2021 Martin Fleisz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +23,11 @@ #include "config.h" #endif +#if defined(__APPLE__) +#include +#include +#endif + #include #include #include @@ -32,11 +39,15 @@ #include +#define LOCALE_LANGUAGE_LEN 4 +#define LOCALE_COUNTRY_LEN 10 + struct _SYSTEM_LOCALE { - char language[4]; /* Two or three letter language code */ - char country[10]; /* Two or three letter country code (Sometimes with Cyrl_ prefix) */ - DWORD code; /* 32-bit unsigned integer corresponding to the locale */ + char language[LOCALE_LANGUAGE_LEN]; /* Two or three letter language code */ + char country[LOCALE_COUNTRY_LEN]; /* Two or three letter country code (Sometimes with Cyrl_ + prefix) */ + DWORD code; /* 32-bit unsigned integer corresponding to the locale */ }; typedef struct _SYSTEM_LOCALE SYSTEM_LOCALE; @@ -644,6 +655,31 @@ static const LOCALE_KEYBOARD_LAYOUTS LOCALE_KEYBOARD_LAYOUTS_TABLE[] = { static BOOL freerdp_get_system_language_and_country_codes(char* language, char* country) { +#if defined(__APPLE__) + CFIndex strSize; + CFStringRef langRef, countryRef; + CFLocaleRef localeRef = CFLocaleCopyCurrent(); + if (!localeRef) + return FALSE; + + langRef = (CFStringRef)CFLocaleGetValue(localeRef, kCFLocaleLanguageCode); + countryRef = (CFStringRef)CFLocaleGetValue(localeRef, kCFLocaleCountryCode); + if (!langRef || !countryRef) + { + CFRelease(localeRef); + return FALSE; + } + + if (!CFStringGetCString(langRef, language, LOCALE_LANGUAGE_LEN, kCFStringEncodingUTF8) || + !CFStringGetCString(countryRef, country, LOCALE_COUNTRY_LEN, kCFStringEncodingUTF8)) + { + CFRelease(localeRef); + return FALSE; + } + + CFRelease(localeRef); + return TRUE; +#else int dot; DWORD nSize; int underscore; @@ -697,6 +733,7 @@ static BOOL freerdp_get_system_language_and_country_codes(char* language, char* free(env_lang); return TRUE; +#endif } static SYSTEM_LOCALE* freerdp_detect_system_locale(void)