diff options
Diffstat (limited to 'platform')
47 files changed, 485 insertions, 192 deletions
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index b2400d81dc..ad1cbddb08 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -39,6 +39,8 @@ class RenderingDeviceVulkan; #endif class DisplayServerAndroid : public DisplayServer { + // No need to register with GDCLASS, it's platform-specific and nothing is added. + String rendering_driver; // https://developer.android.com/reference/android/view/PointerIcon diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml index 570e8f01f1..11129ca149 100644 --- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml +++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml @@ -58,21 +58,27 @@ </member> <member name="keystore/debug" type="String" setter="" getter=""> Path of the debug keystore file. + Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PATH[/code]. </member> <member name="keystore/debug_password" type="String" setter="" getter=""> Password for the debug keystore file. + Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD[/code]. </member> <member name="keystore/debug_user" type="String" setter="" getter=""> User name for the debug keystore file. + Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_USER[/code]. </member> <member name="keystore/release" type="String" setter="" getter=""> Path of the release keystore file. + Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PATH[/code]. </member> <member name="keystore/release_password" type="String" setter="" getter=""> Password for the release keystore file. + Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD[/code]. </member> <member name="keystore/release_user" type="String" setter="" getter=""> User name for the release keystore file. + Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_USER[/code]. </member> <member name="launcher_icons/adaptive_background_432x432" type="String" setter="" getter=""> Background layer of the application adaptive icon file. @@ -153,7 +159,7 @@ Must be required by a HostApduService or OffHostApduService to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_NFC_SERVICE]BIND_NFC_SERVICE[/url]. </member> <member name="permissions/bind_notification_listener_service" type="bool" setter="" getter=""> - Must be required by an NotificationListenerService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE]BIND_NOTIFICATION_LISTENER_SERVICE[/url]. + Must be required by a NotificationListenerService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE]BIND_NOTIFICATION_LISTENER_SERVICE[/url]. </member> <member name="permissions/bind_print_service" type="bool" setter="" getter=""> Must be required by a PrintService, to ensure that only the system can bind to it. See [url=https://developer.android.com/reference/android/Manifest.permission#BIND_PRINT_SERVICE]BIND_PRINT_SERVICE[/url]. diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index f52edf2b61..d8dd453faf 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1825,12 +1825,12 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("architectures"), abi)), is_default)); } - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); @@ -2277,9 +2277,9 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito // Validate the rest of the export configuration. - String dk = p_preset->get("keystore/debug"); - String dk_user = p_preset->get("keystore/debug_user"); - String dk_password = p_preset->get("keystore/debug_password"); + String dk = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH); + String dk_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER); + String dk_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS); if ((dk.is_empty() || dk_user.is_empty() || dk_password.is_empty()) && (!dk.is_empty() || !dk_user.is_empty() || !dk_password.is_empty())) { valid = false; @@ -2294,9 +2294,9 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito } } - String rk = p_preset->get("keystore/release"); - String rk_user = p_preset->get("keystore/release_user"); - String rk_password = p_preset->get("keystore/release_password"); + String rk = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH); + String rk_user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER); + String rk_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS); if ((rk.is_empty() || rk_user.is_empty() || rk_password.is_empty()) && (!rk.is_empty() || !rk_user.is_empty() || !rk_password.is_empty())) { valid = false; @@ -2507,9 +2507,9 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) { int export_format = int(p_preset->get("gradle_build/export_format")); String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK"; - String release_keystore = p_preset->get("keystore/release"); - String release_username = p_preset->get("keystore/release_user"); - String release_password = p_preset->get("keystore/release_password"); + String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH); + String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER); + String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS); String target_sdk_version = p_preset->get("gradle_build/target_sdk"); if (!target_sdk_version.is_valid_int()) { target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION); @@ -2529,9 +2529,9 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre String password; String user; if (p_debug) { - keystore = p_preset->get("keystore/debug"); - password = p_preset->get("keystore/debug_password"); - user = p_preset->get("keystore/debug_user"); + keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH); + password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS); + user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER); if (keystore.is_empty()) { keystore = EDITOR_GET("export/android/debug_keystore"); @@ -2791,7 +2791,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP CustomExportData user_data; user_data.assets_directory = assets_directory; user_data.debug = p_debug; - err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so); + if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { + err = export_project_files(p_preset, p_debug, ignore_apk_file, &user_data, copy_gradle_so); + } else { + err = export_project_files(p_preset, p_debug, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so); + } if (err != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not export project files to gradle project.")); return err; @@ -2882,9 +2886,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP if (should_sign) { if (p_debug) { - String debug_keystore = p_preset->get("keystore/debug"); - String debug_password = p_preset->get("keystore/debug_password"); - String debug_user = p_preset->get("keystore/debug_user"); + String debug_keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH); + String debug_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS); + String debug_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER); if (debug_keystore.is_empty()) { debug_keystore = EDITOR_GET("export/android/debug_keystore"); @@ -2904,9 +2908,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password. } else { // Pass the release keystore info as well - String release_keystore = p_preset->get("keystore/release"); - String release_username = p_preset->get("keystore/release_user"); - String release_password = p_preset->get("keystore/release_password"); + String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH); + String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER); + String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS); if (release_keystore.is_relative_path()) { release_keystore = OS::get_singleton()->get_resource_dir().path_join(release_keystore).simplify_path(); } diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h index f9dad5ce5e..390b8c6465 100644 --- a/platform/android/export/export_plugin.h +++ b/platform/android/export/export_plugin.h @@ -49,6 +49,15 @@ const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding=" </layer-list> )SPLASH"; +// Optional environment variables for defining confidential information. If any +// of these is set, they will override the values set in the credentials file. +const String ENV_ANDROID_KEYSTORE_DEBUG_PATH = "GODOT_ANDROID_KEYSTORE_DEBUG_PATH"; +const String ENV_ANDROID_KEYSTORE_DEBUG_USER = "GODOT_ANDROID_KEYSTORE_DEBUG_USER"; +const String ENV_ANDROID_KEYSTORE_DEBUG_PASS = "GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD"; +const String ENV_ANDROID_KEYSTORE_RELEASE_PATH = "GODOT_ANDROID_KEYSTORE_RELEASE_PATH"; +const String ENV_ANDROID_KEYSTORE_RELEASE_USER = "GODOT_ANDROID_KEYSTORE_RELEASE_USER"; +const String ENV_ANDROID_KEYSTORE_RELEASE_PASS = "GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD"; + struct LauncherIcon { const char *export_path; int dimensions = 0; diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index e111bd18ca..99527ccf3a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -1044,6 +1044,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC return PermissionsUtil.getGrantedPermissions(getActivity()); } + @Keep + private String getCACertificates() { + return GodotNetUtils.getCACertificates(); + } + /** * The download state should trigger changes in the UI --- it may be useful * to show the state as being indeterminate at times. This sample can be diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt index af1f38f89c..7dc5fb6f83 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt @@ -197,7 +197,10 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi if (event.actionMasked == MotionEvent.ACTION_UP) { nextDownIsDoubleTap = false GodotInputHandler.handleMotionEvent(event) + } else if (event.actionMasked == MotionEvent.ACTION_MOVE && panningAndScalingEnabled == false) { + GodotInputHandler.handleMotionEvent(event) } + return true } diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java index 401c105cd7..c31d56a3e1 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java @@ -33,11 +33,17 @@ package org.godotengine.godot.utils; import android.app.Activity; import android.content.Context; import android.net.wifi.WifiManager; +import android.util.Base64; import android.util.Log; +import java.io.StringWriter; +import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.util.Enumeration; + /** * This class handles Android-specific networking functions. - * For now, it only provides access to WifiManager.MulticastLock, which is needed on some devices + * It provides access to the CA certificates KeyStore, and the WifiManager.MulticastLock, which is needed on some devices * to receive broadcast and multicast packets. */ public class GodotNetUtils { @@ -79,4 +85,34 @@ public class GodotNetUtils { Log.e("Godot", "Exception during multicast lock release: " + e); } } + + /** + * Retrieves the list of trusted CA certificates from the "AndroidCAStore" and returns them in PRM format. + * @see https://developer.android.com/reference/java/security/KeyStore . + * @return A string of concatenated X509 certificates in PEM format. + */ + public static String getCACertificates() { + try { + KeyStore ks = KeyStore.getInstance("AndroidCAStore"); + StringBuilder writer = new StringBuilder(); + + if (ks != null) { + ks.load(null, null); + Enumeration<String> aliases = ks.aliases(); + + while (aliases.hasMoreElements()) { + String alias = (String)aliases.nextElement(); + + X509Certificate cert = (X509Certificate)ks.getCertificate(alias); + writer.append("-----BEGIN CERTIFICATE-----\n"); + writer.append(Base64.encodeToString(cert.getEncoded(), Base64.DEFAULT)); + writer.append("-----END CERTIFICATE-----\n"); + } + } + return writer.toString(); + } catch (Exception e) { + Log.e("Godot", "Exception while reading CA certificates: " + e); + return ""; + } + } } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 71339c9443..18091649e3 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -446,39 +446,29 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en Object *obj = ObjectDB::get_instance(ObjectID(ID)); ERR_FAIL_NULL(obj); - int res = env->PushLocalFrame(16); - ERR_FAIL_COND(res != 0); - String str_method = jstring_to_string(method, env); int count = env->GetArrayLength(params); + Variant *vlist = (Variant *)alloca(sizeof(Variant) * count); - Variant **vptr = (Variant **)alloca(sizeof(Variant *) * count); + const Variant **vptr = (const Variant **)alloca(sizeof(Variant *) * count); + for (int i = 0; i < count; i++) { jobject jobj = env->GetObjectArrayElement(params, i); - Variant v; - if (jobj) { - v = _jobject_to_variant(env, jobj); - } - memnew_placement(&vlist[i], Variant); - vlist[i] = v; + ERR_FAIL_NULL(jobj); + memnew_placement(&vlist[i], Variant(_jobject_to_variant(env, jobj))); vptr[i] = &vlist[i]; env->DeleteLocalRef(jobj); } Callable::CallError err; - obj->callp(str_method, (const Variant **)vptr, count, err); - - env->PopLocalFrame(nullptr); + obj->callp(str_method, vptr, count, err); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) { Object *obj = ObjectDB::get_instance(ObjectID(ID)); ERR_FAIL_NULL(obj); - int res = env->PushLocalFrame(16); - ERR_FAIL_COND(res != 0); - String str_method = jstring_to_string(method, env); int count = env->GetArrayLength(params); @@ -488,16 +478,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * for (int i = 0; i < count; i++) { jobject jobj = env->GetObjectArrayElement(params, i); - if (jobj) { - args[i] = _jobject_to_variant(env, jobj); - } - env->DeleteLocalRef(jobj); + ERR_FAIL_NULL(jobj); + memnew_placement(&args[i], Variant(_jobject_to_variant(env, jobj))); argptrs[i] = &args[i]; + env->DeleteLocalRef(jobj); } - MessageQueue::get_singleton()->push_callp(obj, str_method, (const Variant **)argptrs, count); - - env->PopLocalFrame(nullptr); + MessageQueue::get_singleton()->push_callp(obj, str_method, argptrs, count); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result) { diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 9d9d087896..2b504ad69b 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -70,6 +70,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z"); _request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z"); _get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;"); + _get_ca_certificates = p_env->GetMethodID(godot_class, "getCACertificates", "()Ljava/lang/String;"); _init_input_devices = p_env->GetMethodID(godot_class, "initInputDevices", "()V"); _get_surface = p_env->GetMethodID(godot_class, "getSurface", "()Landroid/view/Surface;"); _is_activity_resumed = p_env->GetMethodID(godot_class, "isActivityResumed", "()Z"); @@ -310,6 +311,17 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const { return permissions_list; } +String GodotJavaWrapper::get_ca_certificates() const { + if (_get_ca_certificates) { + JNIEnv *env = get_jni_env(); + ERR_FAIL_NULL_V(env, String()); + jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_ca_certificates); + return jstring_to_string(s, env); + } else { + return String(); + } +} + void GodotJavaWrapper::init_input_devices() { if (_init_input_devices) { JNIEnv *env = get_jni_env(); diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 1bd79584d8..05144380e6 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -60,6 +60,7 @@ private: jmethodID _request_permission = nullptr; jmethodID _request_permissions = nullptr; jmethodID _get_granted_permissions = nullptr; + jmethodID _get_ca_certificates = nullptr; jmethodID _init_input_devices = nullptr; jmethodID _get_surface = nullptr; jmethodID _is_activity_resumed = nullptr; @@ -98,6 +99,7 @@ public: bool request_permission(const String &p_name); bool request_permissions(); Vector<String> get_granted_permissions() const; + String get_ca_certificates() const; void init_input_devices(); jobject get_surface(); bool is_activity_resumed(); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 725fea8d54..73081e35e7 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -311,7 +311,11 @@ String OS_Android::get_resource_dir() const { #ifdef TOOLS_ENABLED return OS_Unix::get_resource_dir(); #else - return "/"; //android has its own filesystem for resources inside the APK + if (remote_fs_dir.is_empty()) { + return "/"; // Android has its own filesystem for resources inside the APK + } else { + return remote_fs_dir; + } #endif } @@ -753,5 +757,19 @@ Error OS_Android::kill(const ProcessID &p_pid) { return OS_Unix::kill(p_pid); } +String OS_Android::get_system_ca_certificates() { + return godot_java->get_ca_certificates(); +} + +Error OS_Android::setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) { + r_project_path = get_user_data_dir(); + Error err = OS_Unix::setup_remote_filesystem(p_server_host, p_port, p_password, r_project_path); + if (err == OK) { + remote_fs_dir = r_project_path; + FileAccess::make_default<FileAccessFilesystemJAndroid>(FileAccess::ACCESS_RESOURCES); + } + return err; +} + OS_Android::~OS_Android() { } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 53910b1498..f1d08b7cfe 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -57,6 +57,7 @@ private: mutable String data_dir_cache; mutable String cache_dir_cache; + mutable String remote_fs_dir; AudioDriverOpenSL audio_driver_android; @@ -159,6 +160,9 @@ public: virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override; virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override; virtual Error kill(const ProcessID &p_pid) override; + virtual String get_system_ca_certificates() override; + + virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) override; virtual bool _check_internal_feature_support(const String &p_feature) override; OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion); diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp index 4bb90cb971..843c015d49 100644 --- a/platform/android/plugin/godot_plugin_jni.cpp +++ b/platform/android/plugin/godot_plugin_jni.cpp @@ -120,7 +120,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS for (int i = 0; i < count; i++) { jobject j_param = env->GetObjectArrayElement(j_signal_params, i); - variant_params[i] = _jobject_to_variant(env, j_param); + ERR_FAIL_NULL(j_param); + memnew_placement(&variant_params[i], Variant(_jobject_to_variant(env, j_param))); args[i] = &variant_params[i]; env->DeleteLocalRef(j_param); } diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index 6eaa7c8edc..57f601a858 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -55,7 +55,7 @@ #import <QuartzCore/CAMetalLayer.h> class DisplayServerIOS : public DisplayServer { - GDCLASS(DisplayServerIOS, DisplayServer) + // No need to register with GDCLASS, it's platform-specific and nothing is added. _THREAD_SAFE_CLASS_ diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index ef2b0a256d..381884067b 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -35,9 +35,11 @@ </member> <member name="application/provisioning_profile_uuid_debug" type="String" setter="" getter=""> UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url]. + Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG[/code]. </member> <member name="application/provisioning_profile_uuid_release" type="String" setter="" getter=""> UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url]. + Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE[/code]. </member> <member name="application/short_version" type="String" setter="" getter=""> Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). @@ -69,15 +71,15 @@ <member name="icons/app_store_1024x1024" type="String" setter="" getter=""> App Store application icon file. If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. </member> + <member name="icons/ipad_76x76" type="String" setter="" getter=""> + Home screen application icon file on iPad (1x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. + </member> <member name="icons/ipad_152x152" type="String" setter="" getter=""> Home screen application icon file on iPad (2x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. </member> <member name="icons/ipad_167x167" type="String" setter="" getter=""> Home screen application icon file on iPad (3x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. </member> - <member name="icons/ipad_76x76" type="String" setter="" getter=""> - Home screen application icon file on iPad (1x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. - </member> <member name="icons/iphone_120x120" type="String" setter="" getter=""> Home screen application icon file on iPhone (2x DPI). If left empty, project icon is used instead. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. </member> @@ -114,25 +116,25 @@ <member name="landscape_launch_screens/iphone_2436x1125" type="String" setter="" getter=""> Application launch screen image file, if left empty project splash screen is used instead. </member> - <member name="portrait_launch_screens/ipad_1536x2048" type="String" setter="" getter=""> - Application launch screen image file, if left empty project splash screen is used instead. - </member> <member name="portrait_launch_screens/ipad_768x1024" type="String" setter="" getter=""> Application launch screen image file, if left empty project splash screen is used instead. </member> - <member name="portrait_launch_screens/iphone_1125x2436" type="String" setter="" getter=""> + <member name="portrait_launch_screens/ipad_1536x2048" type="String" setter="" getter=""> Application launch screen image file, if left empty project splash screen is used instead. </member> - <member name="portrait_launch_screens/iphone_1242x2208" type="String" setter="" getter=""> + <member name="portrait_launch_screens/iphone_640x960" type="String" setter="" getter=""> Application launch screen image file, if left empty project splash screen is used instead. </member> <member name="portrait_launch_screens/iphone_640x1136" type="String" setter="" getter=""> Application launch screen image file, if left empty project splash screen is used instead. </member> - <member name="portrait_launch_screens/iphone_640x960" type="String" setter="" getter=""> + <member name="portrait_launch_screens/iphone_750x1334" type="String" setter="" getter=""> Application launch screen image file, if left empty project splash screen is used instead. </member> - <member name="portrait_launch_screens/iphone_750x1334" type="String" setter="" getter=""> + <member name="portrait_launch_screens/iphone_1125x2436" type="String" setter="" getter=""> + Application launch screen image file, if left empty project splash screen is used instead. + </member> + <member name="portrait_launch_screens/iphone_1242x2208" type="String" setter="" getter=""> Application launch screen image file, if left empty project splash screen is used instead. </member> <member name="privacy/camera_usage_description" type="String" setter="" getter=""> diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 1b925ff3e3..06741a12e4 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -160,10 +160,10 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "", false, true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0)); @@ -253,8 +253,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ }; String dbg_sign_id = p_preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "iPhone Developer" : p_preset->get("application/code_sign_identity_debug"); String rel_sign_id = p_preset->get("application/code_sign_identity_release").operator String().is_empty() ? "iPhone Distribution" : p_preset->get("application/code_sign_identity_release"); - bool dbg_manual = !p_preset->get("application/provisioning_profile_uuid_debug").operator String().is_empty() || (dbg_sign_id != "iPhone Developer"); - bool rel_manual = !p_preset->get("application/provisioning_profile_uuid_release").operator String().is_empty() || (rel_sign_id != "iPhone Distribution"); + bool dbg_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG).operator String().is_empty() || (dbg_sign_id != "iPhone Developer"); + bool rel_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE).operator String().is_empty() || (rel_sign_id != "iPhone Distribution"); String str; String strnew; str.parse_utf8((const char *)pfile.ptr(), pfile.size()); @@ -288,9 +288,9 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release"); strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n"; } else if (lines[i].find("$provisioning_profile_uuid_release") != -1) { - strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n"; + strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE)) + "\n"; } else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) { - strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n"; + strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG)) + "\n"; } else if (lines[i].find("$code_sign_style_debug") != -1) { if (dbg_manual) { strnew += lines[i].replace("$code_sign_style_debug", "Manual") + "\n"; @@ -304,7 +304,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$code_sign_style_release", "Automatic") + "\n"; } } else if (lines[i].find("$provisioning_profile_uuid") != -1) { - String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release"); + String uuid = p_debug ? p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG) : p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE); strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n"; } else if (lines[i].find("$code_sign_identity_debug") != -1) { strnew += lines[i].replace("$code_sign_identity_debug", dbg_sign_id) + "\n"; @@ -1984,6 +1984,8 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() { EditorExportPlatformIOS::~EditorExportPlatformIOS() { #ifndef ANDROID_ENABLED quit_request.set(); - check_for_changes_thread.wait_to_finish(); + if (check_for_changes_thread.is_started()) { + check_for_changes_thread.wait_to_finish(); + } #endif } diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h index 0fde3b7c0b..9afefef121 100644 --- a/platform/ios/export/export_plugin.h +++ b/platform/ios/export/export_plugin.h @@ -49,6 +49,11 @@ #include <sys/stat.h> +// Optional environment variables for defining confidential information. If any +// of these is set, they will override the values set in the credentials file. +const String ENV_IOS_PROFILE_UUID_DEBUG = "GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG"; +const String ENV_IOS_PROFILE_UUID_RELEASE = "GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE"; + class EditorExportPlatformIOS : public EditorExportPlatform { GDCLASS(EditorExportPlatformIOS, EditorExportPlatform); diff --git a/platform/ios/ios.mm b/platform/ios/ios.mm index 1065f5fd2a..c911a512a5 100644 --- a/platform/ios/ios.mm +++ b/platform/ios/ios.mm @@ -72,8 +72,8 @@ CHHapticEngine *iOS::get_haptic_engine_instance() API_AVAILABLE(ios(13)) { void iOS::vibrate_haptic_engine(float p_duration_seconds) API_AVAILABLE(ios(13)) { if (@available(iOS 13, *)) { // We need the @available check every time to make the compiler happy... if (supports_haptic_engine()) { - CHHapticEngine *haptic_engine = get_haptic_engine_instance(); - if (haptic_engine) { + CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance(); + if (cur_haptic_engine) { NSDictionary *hapticDict = @{ CHHapticPatternKeyPattern : @[ @{CHHapticPatternKeyEvent : @{ @@ -88,7 +88,7 @@ void iOS::vibrate_haptic_engine(float p_duration_seconds) API_AVAILABLE(ios(13)) NSError *error; CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error]; - [[haptic_engine createPlayerWithPattern:pattern error:&error] startAtTime:0 error:&error]; + [[cur_haptic_engine createPlayerWithPattern:pattern error:&error] startAtTime:0 error:&error]; NSLog(@"Could not vibrate using haptic engine: %@", error); } @@ -103,9 +103,9 @@ void iOS::vibrate_haptic_engine(float p_duration_seconds) API_AVAILABLE(ios(13)) void iOS::start_haptic_engine() { if (@available(iOS 13, *)) { if (supports_haptic_engine()) { - CHHapticEngine *haptic_engine = get_haptic_engine_instance(); - if (haptic_engine) { - [haptic_engine startWithCompletionHandler:^(NSError *returnedError) { + CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance(); + if (cur_haptic_engine) { + [cur_haptic_engine startWithCompletionHandler:^(NSError *returnedError) { if (returnedError) { NSLog(@"Could not start haptic engine: %@", returnedError); } @@ -122,9 +122,9 @@ void iOS::start_haptic_engine() { void iOS::stop_haptic_engine() { if (@available(iOS 13, *)) { if (supports_haptic_engine()) { - CHHapticEngine *haptic_engine = get_haptic_engine_instance(); - if (haptic_engine) { - [haptic_engine stopWithCompletionHandler:^(NSError *returnedError) { + CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance(); + if (cur_haptic_engine) { + [cur_haptic_engine stopWithCompletionHandler:^(NSError *returnedError) { if (returnedError) { NSLog(@"Could not stop haptic engine: %@", returnedError); } diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index b2f72c310f..739db419a3 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -240,10 +240,20 @@ Error OS_IOS::open_dynamic_library(const String p_path, void *&p_library_handle, } if (!FileAccess::exists(path)) { + // Load .dylib converted to framework from within the executable path. + path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file().get_basename() + ".framework")); + } + + if (!FileAccess::exists(path)) { // Load .dylib or framework from a standard iOS location. path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file())); } + if (!FileAccess::exists(path)) { + // Load .dylib converted to framework from a standard iOS location. + path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file().get_basename() + ".framework")); + } + p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + "."); diff --git a/platform/linuxbsd/freedesktop_portal_desktop.cpp b/platform/linuxbsd/freedesktop_portal_desktop.cpp index b45b7e676d..6dfa8ed93c 100644 --- a/platform/linuxbsd/freedesktop_portal_desktop.cpp +++ b/platform/linuxbsd/freedesktop_portal_desktop.cpp @@ -138,6 +138,11 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() { #else unsupported = false; #endif + + if (unsupported) { + return; + } + bool ver_ok = false; int version_major = 0; int version_minor = 0; diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp index 78f2337599..cf179b5735 100644 --- a/platform/linuxbsd/freedesktop_screensaver.cpp +++ b/platform/linuxbsd/freedesktop_screensaver.cpp @@ -141,6 +141,11 @@ FreeDesktopScreenSaver::FreeDesktopScreenSaver() { #else unsupported = false; #endif + + if (unsupported) { + return; + } + bool ver_ok = false; int version_major = 0; int version_minor = 0; diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp index 5c623b8ba2..a9725fff2e 100644 --- a/platform/linuxbsd/joypad_linux.cpp +++ b/platform/linuxbsd/joypad_linux.cpp @@ -32,6 +32,8 @@ #include "joypad_linux.h" +#include "core/os/os.h" + #include <dirent.h> #include <errno.h> #include <fcntl.h> @@ -72,6 +74,26 @@ void JoypadLinux::Joypad::reset() { events.clear(); } +// This function is derived from SDL: +// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45 +static bool detect_sandbox() { + if (access("/.flatpak-info", F_OK) == 0) { + return true; + } + + // For Snap, we check multiple variables because they might be set for + // unrelated reasons. This is the same thing WebKitGTK does. + if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) { + return true; + } + + if (access("/run/host/container-manager", F_OK) == 0) { + return true; + } + + return false; +} + JoypadLinux::JoypadLinux(Input *in) { #ifdef UDEV_ENABLED #ifdef SOWRAP_ENABLED @@ -80,17 +102,25 @@ JoypadLinux::JoypadLinux(Input *in) { #else int dylibloader_verbose = 0; #endif - use_udev = initialize_libudev(dylibloader_verbose) == 0; - if (use_udev) { - if (!udev_new || !udev_unref || !udev_enumerate_new || !udev_enumerate_add_match_subsystem || !udev_enumerate_scan_devices || !udev_enumerate_get_list_entry || !udev_list_entry_get_next || !udev_list_entry_get_name || !udev_device_new_from_syspath || !udev_device_get_devnode || !udev_device_get_action || !udev_device_unref || !udev_enumerate_unref || !udev_monitor_new_from_netlink || !udev_monitor_filter_add_match_subsystem_devtype || !udev_monitor_enable_receiving || !udev_monitor_get_fd || !udev_monitor_receive_device || !udev_monitor_unref) { - // There's no API to check version, check if functions are available instead. - use_udev = false; - print_verbose("JoypadLinux: Unsupported udev library version!"); + if (detect_sandbox()) { + // Linux binaries in sandboxes / containers need special handling because + // libudev doesn't work there. So we need to fallback to manual parsing + // of /dev/input in such case. + use_udev = false; + print_verbose("JoypadLinux: udev enabled, but detected incompatible sandboxed mode. Falling back to /dev/input to detect joypads."); + } else { + use_udev = initialize_libudev(dylibloader_verbose) == 0; + if (use_udev) { + if (!udev_new || !udev_unref || !udev_enumerate_new || !udev_enumerate_add_match_subsystem || !udev_enumerate_scan_devices || !udev_enumerate_get_list_entry || !udev_list_entry_get_next || !udev_list_entry_get_name || !udev_device_new_from_syspath || !udev_device_get_devnode || !udev_device_get_action || !udev_device_unref || !udev_enumerate_unref || !udev_monitor_new_from_netlink || !udev_monitor_filter_add_match_subsystem_devtype || !udev_monitor_enable_receiving || !udev_monitor_get_fd || !udev_monitor_receive_device || !udev_monitor_unref) { + // There's no API to check version, check if functions are available instead. + use_udev = false; + print_verbose("JoypadLinux: Unsupported udev library version!"); + } else { + print_verbose("JoypadLinux: udev enabled and loaded successfully."); + } } else { - print_verbose("JoypadLinux: udev enabled and loaded successfully."); + print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads."); } - } else { - print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads."); } #endif #else diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index 11c81be4a9..8d8c8ce27b 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -30,6 +30,7 @@ #include "os_linuxbsd.h" +#include "core/io/certs_compressed.gen.h" #include "core/io/dir_access.h" #include "main/main.h" #include "servers/display_server.h" @@ -195,6 +196,10 @@ void OS_LinuxBSD::set_main_loop(MainLoop *p_main_loop) { main_loop = p_main_loop; } +String OS_LinuxBSD::get_identifier() const { + return "linuxbsd"; +} + String OS_LinuxBSD::get_name() const { #ifdef __linux__ return "Linux"; @@ -490,6 +495,11 @@ bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) { return true; } + // Match against the specific OS (linux, freebsd, etc). + if (p_feature == get_name().to_lower()) { + return true; + } + return false; } @@ -1076,6 +1086,40 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) { return OK; } +String OS_LinuxBSD::get_system_ca_certificates() { + String certfile; + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + // Compile time preferred certificates path. + if (!String(_SYSTEM_CERTS_PATH).is_empty() && da->file_exists(_SYSTEM_CERTS_PATH)) { + certfile = _SYSTEM_CERTS_PATH; + } else if (da->file_exists("/etc/ssl/certs/ca-certificates.crt")) { + // Debian/Ubuntu + certfile = "/etc/ssl/certs/ca-certificates.crt"; + } else if (da->file_exists("/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem")) { + // Fedora + certfile = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"; + } else if (da->file_exists("/etc/ca-certificates/extracted/tls-ca-bundle.pem")) { + // Arch Linux + certfile = "/etc/ca-certificates/extracted/tls-ca-bundle.pem"; + } else if (da->file_exists("/var/lib/ca-certificates/ca-bundle.pem")) { + // openSUSE + certfile = "/var/lib/ca-certificates/ca-bundle.pem"; + } else if (da->file_exists("/etc/ssl/cert.pem")) { + // FreeBSD/OpenBSD + certfile = "/etc/ssl/cert.pem"; + } + + if (certfile.is_empty()) { + return ""; + } + + Ref<FileAccess> f = FileAccess::open(certfile, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), "", vformat("Failed to open system CA certificates file: '%s'", certfile)); + + return f->get_as_text(); +} + OS_LinuxBSD::OS_LinuxBSD() { main_loop = nullptr; diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h index 9423514944..c1e735b0d4 100644 --- a/platform/linuxbsd/os_linuxbsd.h +++ b/platform/linuxbsd/os_linuxbsd.h @@ -96,6 +96,7 @@ protected: virtual void set_main_loop(MainLoop *p_main_loop) override; public: + virtual String get_identifier() const override; virtual String get_name() const override; virtual String get_distribution_name() const override; virtual String get_version() const override; @@ -132,6 +133,8 @@ public: virtual Error move_to_trash(const String &p_path) override; + virtual String get_system_ca_certificates() override; + OS_LinuxBSD(); ~OS_LinuxBSD(); }; diff --git a/platform/linuxbsd/x11/detect_prime_x11.cpp b/platform/linuxbsd/x11/detect_prime_x11.cpp index 3d07be1c76..78778a8b56 100644 --- a/platform/linuxbsd/x11/detect_prime_x11.cpp +++ b/platform/linuxbsd/x11/detect_prime_x11.cpp @@ -60,6 +60,9 @@ typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *); +// To prevent shadowing warnings +#undef glGetString + struct vendor { const char *glxvendor = nullptr; int priority = 0; diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index ac4fbe2068..13f261aa43 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -186,6 +186,7 @@ bool DisplayServerX11::_refresh_device_info() { xi.absolute_devices.clear(); xi.touch_devices.clear(); xi.pen_inverted_devices.clear(); + xi.last_relative_time = 0; int dev_count; XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count); @@ -2724,8 +2725,8 @@ void DisplayServerX11::cursor_set_custom_image(const Ref<Resource> &p_cursor, Cu XcursorImageDestroy(cursor_image); } else { // Reset to default system cursor - if (img[p_shape]) { - cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]); + if (cursor_img[p_shape]) { + cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_img[p_shape]); } CursorShape c = current_cursor; @@ -3497,8 +3498,8 @@ void DisplayServerX11::_window_changed(XEvent *event) { // Query display server about a possible new window state. wd.fullscreen = _window_fullscreen_check(window_id); - wd.minimized = _window_minimize_check(window_id); - wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE"); + wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE") && !wd.fullscreen; + wd.minimized = _window_minimize_check(window_id) && !wd.fullscreen && !wd.maximized; // Readjusting the window position if the window is being reparented by the window manager for decoration Window root, parent, *children; @@ -5359,7 +5360,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode for (int i = 0; i < CURSOR_MAX; i++) { cursors[i] = None; - img[i] = nullptr; + cursor_img[i] = nullptr; } XInitThreads(); //always use threads @@ -5716,8 +5717,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode "question_arrow" }; - img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size); - if (!img[i]) { + cursor_img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size); + if (!cursor_img[i]) { const char *fallback = nullptr; switch (i) { @@ -5755,7 +5756,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode fallback = "bd_double_arrow"; break; case CURSOR_MOVE: - img[i] = img[CURSOR_DRAG]; + cursor_img[i] = cursor_img[CURSOR_DRAG]; break; case CURSOR_VSPLIT: fallback = "sb_v_double_arrow"; @@ -5768,11 +5769,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode break; } if (fallback != nullptr) { - img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size); + cursor_img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size); } } - if (img[i]) { - cursors[i] = XcursorImageLoadCursor(x11_display, img[i]); + if (cursor_img[i]) { + cursors[i] = XcursorImageLoadCursor(x11_display, cursor_img[i]); } else { print_verbose("Failed loading custom cursor: " + String(cursor_file[i])); } @@ -5911,8 +5912,8 @@ DisplayServerX11::~DisplayServerX11() { if (cursors[i] != None) { XFreeCursor(x11_display, cursors[i]); } - if (img[i] != nullptr) { - XcursorImageDestroy(img[i]); + if (cursor_img[i] != nullptr) { + XcursorImageDestroy(cursor_img[i]); } } diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index e8e0680c14..6d343be3ab 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -119,8 +119,7 @@ typedef struct _xrr_monitor_info { #undef CursorShape class DisplayServerX11 : public DisplayServer { - //No need to register, it's platform-specific and nothing is added - //GDCLASS(DisplayServerX11, DisplayServer) + // No need to register with GDCLASS, it's platform-specific and nothing is added. _THREAD_SAFE_CLASS_ @@ -307,7 +306,7 @@ class DisplayServerX11 : public DisplayServer { const char *cursor_theme = nullptr; int cursor_size = 0; - XcursorImage *img[CURSOR_MAX]; + XcursorImage *cursor_img[CURSOR_MAX]; Cursor cursors[CURSOR_MAX]; Cursor null_cursor; CursorShape current_cursor = CURSOR_ARROW; diff --git a/platform/linuxbsd/x11/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp index ee767dfa80..1e579c9f01 100644 --- a/platform/linuxbsd/x11/gl_manager_x11.cpp +++ b/platform/linuxbsd/x11/gl_manager_x11.cpp @@ -44,6 +44,9 @@ typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *); +// To prevent shadowing warnings +#undef glXCreateContextAttribsARB + struct GLManager_X11_Private { ::GLXContext glx_context; }; diff --git a/platform/macos/detect.py b/platform/macos/detect.py index 1fefdb3c68..7b8d3fd853 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -235,6 +235,8 @@ def configure(env: "Environment"): "CoreMedia", "-framework", "QuartzCore", + "-framework", + "Security", ] ) env.Append(LIBS=["pthread", "z"]) diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index a71ac3a310..a1cd83280d 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -56,7 +56,7 @@ #undef CursorShape class DisplayServerMacOS : public DisplayServer { - GDCLASS(DisplayServerMacOS, DisplayServer) + // No need to register with GDCLASS, it's platform-specific and nothing is added. _THREAD_SAFE_CLASS_ diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 5a7c309448..32f9441484 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -1840,9 +1840,9 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect if (!p_callback.is_null()) { Variant button = button_pressed; Variant *buttonp = &button; - Variant ret; + Variant fun_ret; Callable::CallError ce; - p_callback.callp((const Variant **)&buttonp, 1, ret, ce); + p_callback.callp((const Variant **)&buttonp, 1, fun_ret, ce); } return OK; @@ -1872,9 +1872,9 @@ Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description if (!p_callback.is_null()) { Variant text = ret; Variant *textp = &text; - Variant ret; + Variant fun_ret; Callable::CallError ce; - p_callback.callp((const Variant **)&textp, 1, ret, ce); + p_callback.callp((const Variant **)&textp, 1, fun_ret, ce); } return OK; @@ -1897,7 +1897,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) { bool previously_shown = (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED); if (show_cursor && !previously_shown) { - WindowID window_id = get_window_at_screen_position(mouse_get_position()); + window_id = get_window_at_screen_position(mouse_get_position()); if (window_id != INVALID_WINDOW_ID) { send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); } diff --git a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml index f57af3bbd7..64e1efde26 100644 --- a/platform/macos/doc_classes/EditorExportPlatformMacOS.xml +++ b/platform/macos/doc_classes/EditorExportPlatformMacOS.xml @@ -50,9 +50,11 @@ </member> <member name="codesign/certificate_file" type="String" setter="" getter=""> PKCS #12 certificate file used to sign [code].app[/code] bundle. + Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_CERTIFICATE_FILE[/code]. </member> <member name="codesign/certificate_password" type="String" setter="" getter=""> Password for the certificate file used to sign [code].app[/code] bundle. + Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_CERTIFICATE_PASSWORD[/code]. </member> <member name="codesign/codesign" type="int" setter="" getter=""> Tool to use for code signing. @@ -138,6 +140,7 @@ </member> <member name="codesign/provisioning_profile" type="String" setter="" getter=""> Provisioning profile file downloaded from Apple developer account dashboard. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url]. + Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_PROVISIONING_PROFILE[/code]. </member> <member name="custom_template/debug" type="String" setter="" getter=""> Path to the custom export template. If left empty, default template is used. @@ -156,18 +159,23 @@ </member> <member name="notarization/api_key" type="String" setter="" getter=""> Apple App Store Connect API issuer key file. + Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_KEY[/code]. </member> <member name="notarization/api_key_id" type="String" setter="" getter=""> Apple App Store Connect API issuer key ID. + Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_KEY_ID[/code]. </member> <member name="notarization/api_uuid" type="String" setter="" getter=""> Apple App Store Connect API issuer UUID. + Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_UUID[/code]. </member> <member name="notarization/apple_id_name" type="String" setter="" getter=""> Apple ID account name (email address). + Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_APPLE_ID_NAME[/code]. </member> <member name="notarization/apple_id_password" type="String" setter="" getter=""> Apple ID app-specific password. + Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_APPLE_ID_PASSWORD[/code]. </member> <member name="notarization/notarization" type="int" setter="" getter=""> Tool to use for notarization. diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index cd9d17dd4f..be677f4da2 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -73,7 +73,7 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr ad_hoc = true; } break; case 2: { // "rcodesign" - ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty(); + ad_hoc = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty() || p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty(); } break; #ifdef MACOS_ENABLED case 3: { // "codesign" @@ -114,7 +114,7 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr } if (p_name == "codesign/provisioning_profile" && dist_type == 2) { - String pprof = p_preset->get("codesign/provisioning_profile"); + String pprof = p_preset->get_or_env("codesign/provisioning_profile", ENV_MAC_CODESIGN_PROFILE); if (pprof.is_empty()) { return TTR("Provisioning profile is required for App Store distribution."); } @@ -154,8 +154,8 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr if (notary_tool == 2 || notary_tool == 3) { if (p_name == "notarization/apple_id_name" || p_name == "notarization/api_uuid") { - String apple_id = p_preset->get("notarization/apple_id_name"); - String api_uuid = p_preset->get("notarization/api_uuid"); + String apple_id = p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID); + String api_uuid = p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID); if (apple_id.is_empty() && api_uuid.is_empty()) { return TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified."); } @@ -164,28 +164,28 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr } } if (p_name == "notarization/apple_id_password") { - String apple_id = p_preset->get("notarization/apple_id_name"); - String apple_pass = p_preset->get("notarization/apple_id_password"); + String apple_id = p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID); + String apple_pass = p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS); if (!apple_id.is_empty() && apple_pass.is_empty()) { return TTR("Apple ID password not specified."); } } if (p_name == "notarization/api_key_id") { - String api_uuid = p_preset->get("notarization/api_uuid"); - String api_key = p_preset->get("notarization/api_key_id"); + String api_uuid = p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID); + String api_key = p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID); if (!api_uuid.is_empty() && api_key.is_empty()) { return TTR("App Store Connect API key ID not specified."); } } } else if (notary_tool == 1) { if (p_name == "notarization/api_uuid") { - String api_uuid = p_preset->get("notarization/api_uuid"); + String api_uuid = p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID); if (api_uuid.is_empty()) { return TTR("App Store Connect issuer ID name not specified."); } } if (p_name == "notarization/api_key_id") { - String api_key = p_preset->get("notarization/api_key_id"); + String api_key = p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID); if (api_key.is_empty()) { return TTR("App Store Connect API key ID not specified."); } @@ -398,10 +398,10 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options // "codesign" only options: r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), "")); // "rcodesign" only options: - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_password", PROPERTY_HINT_PASSWORD), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); // "codesign" and "rcodesign" only options: - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/provisioning_profile", PROPERTY_HINT_GLOBAL_FILE, "*.provisionprofile"), "", false, true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/provisioning_profile", PROPERTY_HINT_GLOBAL_FILE, "*.provisionprofile", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "", true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false)); @@ -434,12 +434,12 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "notarization/notarization", PROPERTY_HINT_ENUM, "Disabled,rcodesign"), 0, true)); #endif // "altool" and "notarytool" only options: - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), "", false, true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PASSWORD, "Enable two-factor authentication and provide app-specific password"), "", false, true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PASSWORD, "Enable two-factor authentication and provide app-specific password", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true)); // "altool", "notarytool" and "rcodesign" only options: - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_uuid", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect issuer ID UUID"), "", false, true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key", PROPERTY_HINT_GLOBAL_FILE, "*.p8"), "", false, true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect API key ID"), "", false, true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_uuid", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect issuer ID UUID", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key", PROPERTY_HINT_GLOBAL_FILE, "*.p8", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect API key ID", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "", false, true)); r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/microphone_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); @@ -776,24 +776,24 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres args.push_back("notary-submit"); - if (p_preset->get("notarization/api_uuid") == "") { + if (p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) == "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect issuer ID name not specified.")); return Error::FAILED; } - if (p_preset->get("notarization/api_key") == "") { + if (p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY) == "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified.")); return Error::FAILED; } args.push_back("--api-issuer"); - args.push_back(p_preset->get("notarization/api_uuid")); + args.push_back(p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID)); args.push_back("--api-key"); - args.push_back(p_preset->get("notarization/api_key_id")); + args.push_back(p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID)); - if (!p_preset->get("notarization/api_key").operator String().is_empty()) { + if (!p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY).operator String().is_empty()) { args.push_back("--api-key-path"); - args.push_back(p_preset->get("notarization/api_key")); + args.push_back(p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY)); } args.push_back(p_path); @@ -840,40 +840,40 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres args.push_back(p_path); - if (p_preset->get("notarization/apple_id_name") == "" && p_preset->get("notarization/api_uuid") == "") { + if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) == "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) == "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified.")); return Error::FAILED; } - if (p_preset->get("notarization/apple_id_name") != "" && p_preset->get("notarization/api_uuid") != "") { + if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) != "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Both Apple ID name and App Store Connect issuer ID name are specified, only one should be set at the same time.")); return Error::FAILED; } - if (p_preset->get("notarization/apple_id_name") != "") { - if (p_preset->get("notarization/apple_id_password") == "") { + if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "") { + if (p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS) == "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Apple ID password not specified.")); return Error::FAILED; } args.push_back("--apple-id"); - args.push_back(p_preset->get("notarization/apple_id_name")); + args.push_back(p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID)); args.push_back("--password"); - args.push_back(p_preset->get("notarization/apple_id_password")); + args.push_back(p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS)); } else { - if (p_preset->get("notarization/api_key_id") == "") { + if (p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID) == "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified.")); return Error::FAILED; } args.push_back("--issuer"); - args.push_back(p_preset->get("notarization/api_uuid")); + args.push_back(p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID)); - if (!p_preset->get("notarization/api_key").operator String().is_empty()) { + if (!p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY).operator String().is_empty()) { args.push_back("--key"); - args.push_back(p_preset->get("notarization/api_key")); + args.push_back(p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY)); } args.push_back("--key-id"); - args.push_back(p_preset->get("notarization/api_key_id")); + args.push_back(p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID)); } args.push_back("--no-progress"); @@ -925,35 +925,35 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres args.push_back("--primary-bundle-id"); args.push_back(p_preset->get("application/bundle_identifier")); - if (p_preset->get("notarization/apple_id_name") == "" && p_preset->get("notarization/api_uuid") == "") { + if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) == "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) == "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified.")); return Error::FAILED; } - if (p_preset->get("notarization/apple_id_name") != "" && p_preset->get("notarization/api_uuid") != "") { + if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) != "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Both Apple ID name and App Store Connect issuer ID name are specified, only one should be set at the same time.")); return Error::FAILED; } - if (p_preset->get("notarization/apple_id_name") != "") { - if (p_preset->get("notarization/apple_id_password") == "") { + if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "") { + if (p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS) == "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Apple ID password not specified.")); return Error::FAILED; } args.push_back("--username"); - args.push_back(p_preset->get("notarization/apple_id_name")); + args.push_back(p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID)); args.push_back("--password"); - args.push_back(p_preset->get("notarization/apple_id_password")); + args.push_back(p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS)); } else { - if (p_preset->get("notarization/api_key") == "") { + if (p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY) == "") { add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified.")); return Error::FAILED; } args.push_back("--apiIssuer"); - args.push_back(p_preset->get("notarization/api_uuid")); + args.push_back(p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID)); args.push_back("--apiKey"); - args.push_back(p_preset->get("notarization/api_key_id")); + args.push_back(p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID)); } args.push_back("--type"); @@ -1032,8 +1032,8 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre args.push_back(p_ent_path); } - String certificate_file = p_preset->get("codesign/certificate_file"); - String certificate_pass = p_preset->get("codesign/certificate_password"); + String certificate_file = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE); + String certificate_pass = p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_PASS); if (!certificate_file.is_empty() && !certificate_pass.is_empty()) { args.push_back("--p12-file"); args.push_back(certificate_file); @@ -1763,7 +1763,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p ad_hoc = true; } break; case 2: { // "rcodesign" - ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty(); + ad_hoc = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty() || p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_PASS).operator String().is_empty(); } break; #ifdef MACOS_ENABLED case 3: { // "codesign" @@ -1857,7 +1857,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p int dist_type = p_preset->get("export/distribution_type"); if (dist_type == 2) { - String pprof = p_preset->get("codesign/provisioning_profile"); + String pprof = p_preset->get_or_env("codesign/provisioning_profile", ENV_MAC_CODESIGN_PROFILE); String teamid = p_preset->get("codesign/apple_team_id"); String bid = p_preset->get("application/bundle_identifier"); if (!pprof.is_empty() && !teamid.is_empty()) { @@ -1990,7 +1990,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p if (err == OK && sign_enabled) { int dist_type = p_preset->get("export/distribution_type"); if (dist_type == 2) { - String pprof = p_preset->get("codesign/provisioning_profile").operator String(); + String pprof = p_preset->get_or_env("codesign/provisioning_profile", ENV_MAC_CODESIGN_PROFILE).operator String(); if (!pprof.is_empty()) { Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); err = da->copy(pprof, tmp_app_path_name + "/Contents/embedded.provisionprofile"); @@ -2147,7 +2147,7 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor ad_hoc = true; } break; case 2: { // "rcodesign" - ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty(); + ad_hoc = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty() || p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_PASS).operator String().is_empty(); } break; #ifdef MACOS_ENABLED case 3: { // "codesign" diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h index a8caf535c4..0477a8c0cc 100644 --- a/platform/macos/export/export_plugin.h +++ b/platform/macos/export/export_plugin.h @@ -43,6 +43,17 @@ #include <sys/stat.h> +// Optional environment variables for defining confidential information. If any +// of these is set, they will override the values set in the credentials file. +const String ENV_MAC_CODESIGN_CERT_FILE = "GODOT_MACOS_CODESIGN_CERTIFICATE_FILE"; +const String ENV_MAC_CODESIGN_CERT_PASS = "GODOT_MACOS_CODESIGN_CERTIFICATE_PASSWORD"; +const String ENV_MAC_CODESIGN_PROFILE = "GODOT_MACOS_CODESIGN_PROVISIONING_PROFILE"; +const String ENV_MAC_NOTARIZATION_UUID = "GODOT_MACOS_NOTARIZATION_API_UUID"; +const String ENV_MAC_NOTARIZATION_KEY = "GODOT_MACOS_NOTARIZATION_API_KEY"; +const String ENV_MAC_NOTARIZATION_KEY_ID = "GODOT_MACOS_NOTARIZATION_API_KEY_ID"; +const String ENV_MAC_NOTARIZATION_APPLE_ID = "GODOT_MACOS_NOTARIZATION_APPLE_ID_NAME"; +const String ENV_MAC_NOTARIZATION_APPLE_PASS = "GODOT_MACOS_NOTARIZATION_APPLE_ID_PASSWORD"; + class EditorExportPlatformMacOS : public EditorExportPlatform { GDCLASS(EditorExportPlatformMacOS, EditorExportPlatform); diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h index eb7a30203a..07bae479be 100644 --- a/platform/macos/os_macos.h +++ b/platform/macos/os_macos.h @@ -119,6 +119,8 @@ public: virtual Error move_to_trash(const String &p_path) override; + virtual String get_system_ca_certificates() override; + void run(); OS_MacOS(); diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index 74cdef6f25..838ae742fd 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -30,6 +30,7 @@ #include "os_macos.h" +#include "core/crypto/crypto_core.h" #include "core/version_generated.gen.h" #include "main/main.h" @@ -671,6 +672,34 @@ Error OS_MacOS::move_to_trash(const String &p_path) { return OK; } +String OS_MacOS::get_system_ca_certificates() { + CFArrayRef result; + SecCertificateRef item; + CFDataRef der; + + OSStatus ret = SecTrustCopyAnchorCertificates(&result); + ERR_FAIL_COND_V(ret != noErr, ""); + + CFIndex l = CFArrayGetCount(result); + String certs; + PackedByteArray pba; + for (CFIndex i = 0; i < l; i++) { + item = (SecCertificateRef)CFArrayGetValueAtIndex(result, i); + der = SecCertificateCopyData(item); + int derlen = CFDataGetLength(der); + if (pba.size() < derlen * 3) { + pba.resize(derlen * 3); + } + size_t b64len = 0; + Error err = CryptoCore::b64_encode(pba.ptrw(), pba.size(), &b64len, (unsigned char *)CFDataGetBytePtr(der), derlen); + CFRelease(der); + ERR_CONTINUE(err != OK); + certs += "-----BEGIN CERTIFICATE-----\n" + String((char *)pba.ptr(), b64len) + "\n-----END CERTIFICATE-----\n"; + } + CFRelease(result); + return certs; +} + void OS_MacOS::run() { if (!main_loop) { return; diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp index 163236e506..aac61184b1 100644 --- a/platform/uwp/export/export_plugin.cpp +++ b/platform/uwp/export/export_plugin.cpp @@ -80,8 +80,8 @@ void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "signing/algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256"), 2)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/major"), 1)); @@ -465,8 +465,8 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p int cert_alg = EDITOR_GET("export/uwp/debug_algorithm"); if (!p_debug) { - cert_path = p_preset->get("signing/certificate"); - cert_pass = p_preset->get("signing/password"); + cert_path = p_preset->get_or_env("signing/certificate", ENV_UWP_SIGNING_CERT); + cert_pass = p_preset->get_or_env("signing/password", ENV_UWP_SIGNING_PASS); cert_alg = p_preset->get("signing/algorithm"); } diff --git a/platform/uwp/export/export_plugin.h b/platform/uwp/export/export_plugin.h index 37a32b1f7f..b42a2ae6d9 100644 --- a/platform/uwp/export/export_plugin.h +++ b/platform/uwp/export/export_plugin.h @@ -85,6 +85,11 @@ static const char *uwp_device_capabilities[] = { nullptr }; +// Optional environment variables for defining confidential information. If any +// of these is set, they will override the values set in the credentials file. +const String ENV_UWP_SIGNING_CERT = "GODOT_UWP_SIGNING_CERTIFICATE"; +const String ENV_UWP_SIGNING_PASS = "GODOT_UWP_SIGNING_PASSWORD"; + class EditorExportPlatformUWP : public EditorExportPlatform { GDCLASS(EditorExportPlatformUWP, EditorExportPlatform); diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h index 2e50a6bbc8..a72977e3c3 100644 --- a/platform/web/display_server_web.h +++ b/platform/web/display_server_web.h @@ -37,6 +37,8 @@ #include <emscripten/html5.h> class DisplayServerWeb : public DisplayServer { + // No need to register with GDCLASS, it's platform-specific and nothing is added. + private: struct JSTouchEvent { uint32_t identifier[32] = { 0 }; diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 876efdf864..2fff628c85 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -687,5 +687,7 @@ EditorExportPlatformWeb::~EditorExportPlatformWeb() { server->stop(); } server_quit = true; - server_thread.wait_to_finish(); + if (server_thread.is_started()) { + server_thread.wait_to_finish(); + } } diff --git a/platform/windows/detect.py b/platform/windows/detect.py index cd6d461a93..963f533d67 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -413,6 +413,7 @@ def configure_msvc(env, vcvars_msvc_config): "dxguid", "imm32", "bcrypt", + "Crypt32", "Avrt", "dwmapi", "dwrite", @@ -592,6 +593,7 @@ def configure_mingw(env): "ksuser", "imm32", "bcrypt", + "crypt32", "avrt", "uuid", "dwmapi", diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 80c75c63b5..a8dcc6bad0 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -280,8 +280,7 @@ typedef struct { } ICONDIR, *LPICONDIR; class DisplayServerWindows : public DisplayServer { - //No need to register, it's platform-specific and nothing is added - //GDCLASS(DisplayServerWindows, DisplayServer) + // No need to register with GDCLASS, it's platform-specific and nothing is added. _THREAD_SAFE_CLASS_ diff --git a/platform/windows/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml index fee8a118bc..ec2b105f58 100644 --- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml +++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml @@ -64,12 +64,15 @@ </member> <member name="codesign/identity" type="String" setter="" getter=""> PKCS #12 certificate file used to sign executable or certificate SHA-1 hash (if [member codesign/identity_type] is set to "Use certificate store"). See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url]. + Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_IDENTITY[/code]. </member> <member name="codesign/identity_type" type="int" setter="" getter=""> Type of identity to use. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url]. + Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_IDENTITY_TYPE[/code]. </member> <member name="codesign/password" type="String" setter="" getter=""> Password for the certificate file used to sign executable. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url]. + Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_PASSWORD[/code]. </member> <member name="codesign/timestamp" type="bool" setter="" getter=""> If [code]true[/code], time-stamp is added to the signature. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url]. diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index 1863a3083b..ca390236fb 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -328,9 +328,9 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64"), "x86_64")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false, true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA-1 hash)"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password", PROPERTY_HINT_PASSWORD), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA-1 hash)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1)); @@ -518,21 +518,21 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p //identity #ifdef WINDOWS_ENABLED - int id_type = p_preset->get("codesign/identity_type"); + int id_type = p_preset->get_or_env("codesign/identity_type", ENV_WIN_CODESIGN_ID_TYPE); if (id_type == 0) { //auto select args.push_back("/a"); } else if (id_type == 1) { //pkcs12 - if (p_preset->get("codesign/identity") != "") { + if (p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID) != "") { args.push_back("/f"); - args.push_back(p_preset->get("codesign/identity")); + args.push_back(p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID)); } else { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found.")); return FAILED; } } else if (id_type == 2) { //Windows certificate store - if (p_preset->get("codesign/identity") != "") { + if (p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID) != "") { args.push_back("/sha1"); - args.push_back(p_preset->get("codesign/identity")); + args.push_back(p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID)); } else { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found.")); return FAILED; @@ -543,9 +543,9 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p } #else int id_type = 1; - if (p_preset->get("codesign/identity") != "") { + if (p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID) != "") { args.push_back("-pkcs12"); - args.push_back(p_preset->get("codesign/identity")); + args.push_back(p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID)); } else { add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found.")); return FAILED; @@ -553,13 +553,13 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p #endif //password - if ((id_type == 1) && (p_preset->get("codesign/password") != "")) { + if ((id_type == 1) && (p_preset->get_or_env("codesign/password", ENV_WIN_CODESIGN_PASS) != "")) { #ifdef WINDOWS_ENABLED args.push_back("/p"); #else args.push_back("-pass"); #endif - args.push_back(p_preset->get("codesign/password")); + args.push_back(p_preset->get_or_env("codesign/password", ENV_WIN_CODESIGN_PASS)); } //timestamp diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h index c466971202..184b2f96f8 100644 --- a/platform/windows/export/export_plugin.h +++ b/platform/windows/export/export_plugin.h @@ -36,6 +36,12 @@ #include "editor/editor_settings.h" #include "editor/export/editor_export_platform_pc.h" +// Optional environment variables for defining confidential information. If any +// of these is set, they will override the values set in the credentials file. +const String ENV_WIN_CODESIGN_ID_TYPE = "GODOT_WINDOWS_CODESIGN_IDENTITY_TYPE"; +const String ENV_WIN_CODESIGN_ID = "GODOT_WINDOWS_CODESIGN_IDENTITY"; +const String ENV_WIN_CODESIGN_PASS = "GODOT_WINDOWS_CODESIGN_PASSWORD"; + class EditorExportPlatformWindows : public EditorExportPlatformPC { GDCLASS(EditorExportPlatformWindows, EditorExportPlatformPC); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 5a749a9d6f..bd7d9f178a 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -55,6 +55,7 @@ #include <regstr.h> #include <shlobj.h> #include <wbemcli.h> +#include <wincrypt.h> #ifdef DEBUG_ENABLED #pragma pack(push, before_imagehlp, 8) @@ -435,8 +436,6 @@ String OS_Windows::get_distribution_name() const { } String OS_Windows::get_version() const { - typedef LONG NTSTATUS; - typedef NTSTATUS(WINAPI * RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); RtlGetVersionPtr version_ptr = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlGetVersion"); if (version_ptr != nullptr) { RTL_OSVERSIONINFOW fow; @@ -554,9 +553,9 @@ OS::DateTime OS_Windows::get_datetime(bool p_utc) const { //Get DST information from Windows, but only if p_utc is false. TIME_ZONE_INFORMATION info; - bool daylight = false; + bool is_daylight = false; if (!p_utc && GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) { - daylight = true; + is_daylight = true; } DateTime dt; @@ -567,20 +566,20 @@ OS::DateTime OS_Windows::get_datetime(bool p_utc) const { dt.hour = systemtime.wHour; dt.minute = systemtime.wMinute; dt.second = systemtime.wSecond; - dt.dst = daylight; + dt.dst = is_daylight; return dt; } OS::TimeZoneInfo OS_Windows::get_time_zone_info() const { TIME_ZONE_INFORMATION info; - bool daylight = false; + bool is_daylight = false; if (GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) { - daylight = true; + is_daylight = true; } // Daylight Bias needs to be added to the bias if DST is in effect, or else it will not properly update. TimeZoneInfo ret; - if (daylight) { + if (is_daylight) { ret.name = info.DaylightName; ret.bias = info.Bias + info.DaylightBias; } else { @@ -706,16 +705,16 @@ Dictionary OS_Windows::get_memory_info() const { } if (pref_info.PhysicalTotal * pref_info.PageSize != 0) { - meminfo["physical"] = pref_info.PhysicalTotal * pref_info.PageSize; + meminfo["physical"] = static_cast<int64_t>(pref_info.PhysicalTotal * pref_info.PageSize); } if (pref_info.PhysicalAvailable * pref_info.PageSize != 0) { - meminfo["free"] = pref_info.PhysicalAvailable * pref_info.PageSize; + meminfo["free"] = static_cast<int64_t>(pref_info.PhysicalAvailable * pref_info.PageSize); } if (pref_info.CommitLimit * pref_info.PageSize != 0) { - meminfo["available"] = pref_info.CommitLimit * pref_info.PageSize; + meminfo["available"] = static_cast<int64_t>(pref_info.CommitLimit * pref_info.PageSize); } if (HighLimit - LowLimit != 0) { - meminfo["stack"] = HighLimit - LowLimit; + meminfo["stack"] = static_cast<int64_t>(HighLimit - LowLimit); } return meminfo; @@ -1677,6 +1676,26 @@ Error OS_Windows::move_to_trash(const String &p_path) { return OK; } +String OS_Windows::get_system_ca_certificates() { + HCERTSTORE cert_store = CertOpenSystemStoreA(0, "ROOT"); + ERR_FAIL_COND_V_MSG(!cert_store, "", "Failed to read the root certificate store."); + + String certs; + PCCERT_CONTEXT curr = CertEnumCertificatesInStore(cert_store, nullptr); + while (curr) { + DWORD size = 0; + bool success = CryptBinaryToStringA(curr->pbCertEncoded, curr->cbCertEncoded, CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, nullptr, &size); + ERR_CONTINUE(!success); + PackedByteArray pba; + pba.resize(size); + CryptBinaryToStringA(curr->pbCertEncoded, curr->cbCertEncoded, CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR, (char *)pba.ptrw(), &size); + certs += String((char *)pba.ptr(), size); + curr = CertEnumCertificatesInStore(cert_store, curr); + } + CertCloseStore(cert_store, 0); + return certs; +} + OS_Windows::OS_Windows(HINSTANCE _hInstance) { hInstance = _hInstance; diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 960c3f30a9..c5f95870b3 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -226,6 +226,8 @@ public: virtual Error move_to_trash(const String &p_path) override; + virtual String get_system_ca_certificates() override; + void set_main_window(HWND p_main_window) { main_window = p_main_window; } HINSTANCE get_hinstance() { return hInstance; } |
