summaryrefslogtreecommitdiff
path: root/platform/osx
diff options
context:
space:
mode:
Diffstat (limited to 'platform/osx')
-rw-r--r--platform/osx/display_server_osx.h6
-rw-r--r--platform/osx/display_server_osx.mm152
-rw-r--r--platform/osx/export/export.cpp119
3 files changed, 221 insertions, 56 deletions
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index 8133dfe2c4..fddb1d0ca6 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -281,7 +281,11 @@ public:
virtual bool get_swap_ok_cancel();
- 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 process_events();
virtual void force_process_and_drop_events();
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 9a1191490c..4a94e09c1c 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -2987,85 +2987,129 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
}
}
+struct LayoutInfo {
+ String name;
+ String code;
+};
+
+static Vector<LayoutInfo> kbd_layouts;
+static int current_layout = 0;
static bool keyboard_layout_dirty = true;
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;
}
-// Returns string representation of keys, if they are printable.
-static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) {
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- if (!currentKeyboard)
- return nil;
+void _update_keyboard_layouts() {
+ @autoreleasepool {
+ TISInputSourceRef cur_source = TISCopyCurrentKeyboardInputSource();
+ NSString *cur_name = (NSString *)TISGetInputSourceProperty(cur_source, kTISPropertyLocalizedName);
+ CFRelease(cur_source);
- CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (!layoutData)
- return nil;
+ // 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]);
- const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+ NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages);
+ ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
+ kbd_layouts.push_back(ly);
- OSStatus err;
- CFMutableStringRef output = CFStringCreateMutable(NULL, 0);
+ if ([name isEqualToString:cur_name]) {
+ current_layout = kbd_layouts.size() - 1;
+ }
+ }
+ [list_ime release];
- for (int i = 0; i < length; ++i) {
- UInt32 keysDown = 0;
- UniChar chars[4];
- UniCharCount realLength;
+ // 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]);
- err = UCKeyTranslate(keyboardLayout,
- keyCode[i],
- kUCKeyActionDisplay,
- 0,
- LMGetKbdType(),
- kUCKeyTranslateNoDeadKeysBit,
- &keysDown,
- sizeof(chars) / sizeof(chars[0]),
- &realLength,
- chars);
+ 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 (err != noErr) {
- CFRelease(output);
- return nil;
+ if ([name isEqualToString:cur_name]) {
+ current_layout = kbd_layouts.size() - 1;
+ }
}
-
- CFStringAppendCharacters(output, chars, 1);
+ [list_kbd release];
}
- return (NSString *)output;
+ keyboard_layout_dirty = false;
}
-DisplayServerOSX::LatinKeyboardVariant DisplayServerOSX::get_latin_keyboard_variant() const {
- _THREAD_SAFE_METHOD_
-
- static LatinKeyboardVariant layout = LATIN_KEYBOARD_QWERTY;
+int DisplayServerOSX::keyboard_get_layout_count() const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
+ }
+ return kbd_layouts.size();
+}
+void DisplayServerOSX::keyboard_set_current_layout(int p_index) {
if (keyboard_layout_dirty) {
- layout = LATIN_KEYBOARD_QWERTY;
+ _update_keyboard_layouts();
+ }
+
+ ERR_FAIL_INDEX(p_index, kbd_layouts.size());
- 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);
+ NSString *cur_name = [NSString stringWithUTF8String:kbd_layouts[p_index].name.utf8().get_data()];
- 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;
+ 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];
- [test 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 DisplayServerOSX::keyboard_get_current_layout() const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
+ }
- keyboard_layout_dirty = false;
- return layout;
+ return current_layout;
+}
+
+String DisplayServerOSX::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 DisplayServerOSX::keyboard_get_layout_name(int p_index) const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
}
- return layout;
+ ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
+ return kbd_layouts[p_index].name;
}
void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) {
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 784fba75ec..c9b01ebbb4 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -55,6 +55,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
+ Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
@@ -66,6 +67,28 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
bool use_codesign() const { return false; }
bool use_dmg() const { return false; }
#endif
+ bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
+ String pname = p_package;
+
+ if (pname.length() == 0) {
+ if (r_error) {
+ *r_error = TTR("Identifier is missing.");
+ }
+ return false;
+ }
+
+ for (int i = 0; i < pname.length(); i++) {
+ CharType c = pname[i];
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) {
+ if (r_error) {
+ *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
+ }
+ return false;
+ }
+ }
+
+ return true;
+ }
protected:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
@@ -138,6 +161,11 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), ""));
#endif
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
@@ -363,6 +391,52 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
- and then wrap it up in a DMG
**/
+Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+#ifdef OSX_ENABLED
+ List<String> args;
+
+ args.push_back("altool");
+ args.push_back("--notarize-app");
+
+ args.push_back("--primary-bundle-id");
+ args.push_back(p_preset->get("application/identifier"));
+
+ args.push_back("--username");
+ args.push_back(p_preset->get("notarization/apple_id_name"));
+
+ args.push_back("--password");
+ args.push_back(p_preset->get("notarization/apple_id_password"));
+
+ args.push_back("--type");
+ args.push_back("osx");
+
+ if (p_preset->get("notarization/apple_team_id")) {
+ args.push_back("--asc-provider");
+ args.push_back(p_preset->get("notarization/apple_team_id"));
+ }
+
+ args.push_back("--file");
+ args.push_back(p_path);
+
+ String str;
+ Error err = OS::get_singleton()->execute("xcrun", args, true, nullptr, &str, nullptr, true);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ print_line("altool (" + p_path + "):\n" + str);
+ if (str.find("RequestUUID") == -1) {
+ EditorNode::add_io_error("altool: " + str);
+ return FAILED;
+ } else {
+ print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email.");
+ print_line(" You can check progress manually by opening a Terminal and running the following command:");
+ print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\"");
+ }
+
+#endif
+
+ return OK;
+}
+
Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
#ifdef OSX_ENABLED
List<String> args;
@@ -399,7 +473,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
Error err = OS::get_singleton()->execute("codesign", args, true, nullptr, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
- print_line("codesign (" + p_path + "): " + str);
+ print_line("codesign (" + p_path + "):\n" + str);
if (str.find("no identity found") != -1) {
EditorNode::add_io_error("codesign: no identity found");
return FAILED;
@@ -714,6 +788,14 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
}
+ bool noto_enabled = p_preset->get("notarization/enable");
+ if (err == OK && noto_enabled) {
+ if (ep.step("Sending archive for notarization", 4)) {
+ return ERR_SKIP;
+ }
+ err = _notarize(p_preset, p_path);
+ }
+
// Clean up temporary .app dir.
OS::get_singleton()->move_to_trash(tmp_app_path_name);
}
@@ -803,6 +885,41 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset
valid = dvalid || rvalid;
r_missing_templates = !valid;
+ String identifier = p_preset->get("application/identifier");
+ String pn_err;
+ if (!is_package_name_valid(identifier, &pn_err)) {
+ err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n";
+ valid = false;
+ }
+
+ bool sign_enabled = p_preset->get("codesign/enable");
+ if (sign_enabled) {
+ if (p_preset->get("codesign/identity") == "") {
+ err += TTR("Codesign: identity not specified.") + "\n";
+ valid = false;
+ }
+ }
+ bool noto_enabled = p_preset->get("notarization/enable");
+ if (noto_enabled) {
+ if (!sign_enabled) {
+ err += TTR("Notarization: code signing required.") + "\n";
+ valid = false;
+ }
+ bool hr_enabled = p_preset->get("codesign/hardened_runtime");
+ if (!hr_enabled) {
+ err += TTR("Notarization: hardened runtime required.") + "\n";
+ valid = false;
+ }
+ if (p_preset->get("notarization/apple_id_name") == "") {
+ err += TTR("Notarization: Apple ID name not specified.") + "\n";
+ valid = false;
+ }
+ if (p_preset->get("notarization/apple_id_password") == "") {
+ err += TTR("Notarization: Apple ID password not specified.") + "\n";
+ valid = false;
+ }
+ }
+
if (!err.empty()) {
r_error = err;
}