diff options
Diffstat (limited to 'platform')
35 files changed, 1290 insertions, 296 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub index 31bc7c25b0..7380511d6d 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -1,6 +1,7 @@ #!/usr/bin/env python import subprocess +from methods import print_warning Import("env") @@ -52,7 +53,7 @@ elif env["arch"] == "x86_32": elif env["arch"] == "x86_64": lib_arch_dir = "x86_64" else: - print("WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin") + print_warning("Architecture not suitable for embedding into APK; keeping .so at \\bin") if lib_arch_dir != "": if env.dev_build: diff --git a/platform/android/detect.py b/platform/android/detect.py index fea8ec3287..cbd6144182 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -2,7 +2,7 @@ import os import sys import platform import subprocess - +from methods import print_warning, print_error from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -76,7 +76,6 @@ def get_flags(): # Check if Android NDK version is installed # If not, install it. def install_ndk_if_needed(env: "SConsEnvironment"): - print("Checking for Android NDK...") sdk_root = env["ANDROID_HOME"] if not os.path.exists(get_android_ndk_root(env)): extension = ".bat" if os.name == "nt" else "" @@ -87,13 +86,11 @@ def install_ndk_if_needed(env: "SConsEnvironment"): ndk_download_args = "ndk;" + get_ndk_version() subprocess.check_call([sdkmanager, ndk_download_args]) else: - print("Cannot find " + sdkmanager) - print( - "Please ensure ANDROID_HOME is correct and cmdline-tools are installed, or install NDK version " - + get_ndk_version() - + " manually." + print_error( + f'Cannot find "{sdkmanager}". Please ensure ANDROID_HOME is correct and cmdline-tools' + f'are installed, or install NDK version "{get_ndk_version()}" manually.' ) - sys.exit() + sys.exit(255) env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env) @@ -101,15 +98,15 @@ def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64"] if env["arch"] not in supported_arches: - print( + print_error( 'Unsupported CPU architecture "%s" for Android. Supported architectures are: %s.' % (env["arch"], ", ".join(supported_arches)) ) - sys.exit() + sys.exit(255) if get_min_sdk_version(env["ndk_platform"]) < get_min_target_api(): - print( - "WARNING: minimum supported Android target api is %d. Forcing target api %d." + print_warning( + "Minimum supported Android target api is %d. Forcing target api %d." % (get_min_target_api(), get_min_target_api()) ) env["ndk_platform"] = "android-" + str(get_min_target_api()) diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index c6f2f82117..9869756be1 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -326,7 +326,7 @@ void DisplayServerAndroid::window_set_drop_files_callback(const Callable &p_call } void DisplayServerAndroid::_window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred) const { - if (!p_callable.is_null()) { + if (p_callable.is_valid()) { if (p_deferred) { p_callable.call_deferred(p_arg); } else { diff --git a/platform/ios/detect.py b/platform/ios/detect.py index 0c9b7b3204..e3bac4ec5c 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -1,6 +1,6 @@ import os import sys -from methods import detect_darwin_sdk_path +from methods import print_error, detect_darwin_sdk_path from typing import TYPE_CHECKING @@ -60,11 +60,11 @@ def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_64", "arm64"] if env["arch"] not in supported_arches: - print( + print_error( 'Unsupported CPU architecture "%s" for iOS. Supported architectures are: %s.' % (env["arch"], ", ".join(supported_arches)) ) - sys.exit() + sys.exit(255) ## LTO @@ -118,7 +118,7 @@ def configure(env: "SConsEnvironment"): if env["arch"] == "x86_64": if not env["ios_simulator"]: - print("ERROR: Building for iOS with 'arch=x86_64' requires 'ios_simulator=yes'.") + print_error("Building for iOS with 'arch=x86_64' requires 'ios_simulator=yes'.") sys.exit(255) env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = "10.9" diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index cd6f855d77..62bc55dce8 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -218,7 +218,7 @@ void DisplayServerIOS::send_window_event(DisplayServer::WindowEvent p_event) con } void DisplayServerIOS::_window_callback(const Callable &p_callable, const Variant &p_arg) const { - if (!p_callable.is_null()) { + if (p_callable.is_valid()) { p_callable.call(p_arg); } } diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 0c0ded5fea..20c1647843 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -29,6 +29,9 @@ <member name="application/code_sign_identity_release" type="String" setter="" getter=""> The "Full Name", "Common Name" or SHA-1 hash of the signing identity used for release export. </member> + <member name="application/delete_old_export_files_unconditionally" type="bool" setter="" getter=""> + If [code]true[/code], existing "project name" and "project name.xcodeproj" in the export destination directory will be unconditionally deleted during export. + </member> <member name="application/export_method_debug" type="int" setter="" getter=""> Application distribution target (debug export). </member> @@ -123,12 +126,441 @@ <member name="icons/spotlight_80x80" type="String" setter="" getter=""> Spotlight icon file on iPad and iPhone (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url]. </member> + <member name="privacy/active_keyboard_access_reasons" type="int" setter="" getter=""> + The reasons your app use active keyboard API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url]. + </member> <member name="privacy/camera_usage_description" type="String" setter="" getter=""> A message displayed when requesting access to the device's camera (in English). </member> <member name="privacy/camera_usage_description_localized" type="Dictionary" setter="" getter=""> A message displayed when requesting access to the device's camera (localized). </member> + <member name="privacy/collected_data/advertising_data/collected" type="bool" setter="" getter=""> + Indicates whether your app collects advertising data. + </member> + <member name="privacy/collected_data/advertising_data/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects advertising data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/advertising_data/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links advertising data to the user's identity. + </member> + <member name="privacy/collected_data/advertising_data/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses advertising data for tracking. + </member> + <member name="privacy/collected_data/audio_data/collected" type="bool" setter="" getter=""> + Indicates whether your app collects audio data data. + </member> + <member name="privacy/collected_data/audio_data/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects audio data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/audio_data/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links audio data data to the user's identity. + </member> + <member name="privacy/collected_data/audio_data/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses audio data data for tracking. + </member> + <member name="privacy/collected_data/browsing_history/collected" type="bool" setter="" getter=""> + Indicates whether your app collects browsing history. + </member> + <member name="privacy/collected_data/browsing_history/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects browsing history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/browsing_history/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links browsing history to the user's identity. + </member> + <member name="privacy/collected_data/browsing_history/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses browsing history for tracking. + </member> + <member name="privacy/collected_data/coarse_location/collected" type="bool" setter="" getter=""> + Indicates whether your app collects coarse location data. + </member> + <member name="privacy/collected_data/coarse_location/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects coarse location data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/coarse_location/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links coarse location data to the user's identity. + </member> + <member name="privacy/collected_data/coarse_location/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses coarse location data for tracking. + </member> + <member name="privacy/collected_data/contacts/collected" type="bool" setter="" getter=""> + Indicates whether your app collects contacts. + </member> + <member name="privacy/collected_data/contacts/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects contacts. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/contacts/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links contacts to the user's identity. + </member> + <member name="privacy/collected_data/contacts/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses contacts for tracking. + </member> + <member name="privacy/collected_data/crash_data/collected" type="bool" setter="" getter=""> + Indicates whether your app collects crash data. + </member> + <member name="privacy/collected_data/crash_data/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects crash data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/crash_data/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links crash data to the user's identity. + </member> + <member name="privacy/collected_data/crash_data/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses crash data for tracking. + </member> + <member name="privacy/collected_data/credit_info/collected" type="bool" setter="" getter=""> + Indicates whether your app collects credit information. + </member> + <member name="privacy/collected_data/credit_info/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects credit information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/credit_info/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links credit information to the user's identity. + </member> + <member name="privacy/collected_data/credit_info/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses credit information for tracking. + </member> + <member name="privacy/collected_data/customer_support/collected" type="bool" setter="" getter=""> + Indicates whether your app collects customer support data. + </member> + <member name="privacy/collected_data/customer_support/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects customer support data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/customer_support/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links customer support data to the user's identity. + </member> + <member name="privacy/collected_data/customer_support/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses customer support data for tracking. + </member> + <member name="privacy/collected_data/device_id/collected" type="bool" setter="" getter=""> + Indicates whether your app collects device IDs. + </member> + <member name="privacy/collected_data/device_id/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects device IDs. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/device_id/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links device IDs to the user's identity. + </member> + <member name="privacy/collected_data/device_id/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses device IDs for tracking. + </member> + <member name="privacy/collected_data/email_address/collected" type="bool" setter="" getter=""> + Indicates whether your app collects email address. + </member> + <member name="privacy/collected_data/email_address/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects email address. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/email_address/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links email address to the user's identity. + </member> + <member name="privacy/collected_data/email_address/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses email address for tracking. + </member> + <member name="privacy/collected_data/emails_or_text_messages/collected" type="bool" setter="" getter=""> + Indicates whether your app collects emails or text messages. + </member> + <member name="privacy/collected_data/emails_or_text_messages/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects emails or text messages. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/emails_or_text_messages/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links emails or text messages to the user's identity. + </member> + <member name="privacy/collected_data/emails_or_text_messages/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses emails or text messages for tracking. + </member> + <member name="privacy/collected_data/environment_scanning/collected" type="bool" setter="" getter=""> + Indicates whether your app collects environment scanning data. + </member> + <member name="privacy/collected_data/environment_scanning/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects environment scanning data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/environment_scanning/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links environment scanning data to the user's identity. + </member> + <member name="privacy/collected_data/environment_scanning/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses environment scanning data for tracking. + </member> + <member name="privacy/collected_data/fitness/collected" type="bool" setter="" getter=""> + Indicates whether your app collects fitness and exercise data. + </member> + <member name="privacy/collected_data/fitness/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects fitness and exercise data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/fitness/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links fitness and exercise data to the user's identity. + </member> + <member name="privacy/collected_data/fitness/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses fitness and exercise data for tracking. + </member> + <member name="privacy/collected_data/gameplay_content/collected" type="bool" setter="" getter=""> + Indicates whether your app collects gameplay content. + </member> + <member name="privacy/collected_data/gameplay_content/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects gameplay content. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/gameplay_content/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links gameplay content to the user's identity. + </member> + <member name="privacy/collected_data/gameplay_content/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses gameplay content for tracking. + </member> + <member name="privacy/collected_data/hands/collected" type="bool" setter="" getter=""> + Indicates whether your app collects user's hand structure and hand movements. + </member> + <member name="privacy/collected_data/hands/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects user's hand structure and hand movements. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/hands/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links user's hand structure and hand movements to the user's identity. + </member> + <member name="privacy/collected_data/hands/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses user's hand structure and hand movements for tracking. + </member> + <member name="privacy/collected_data/head/collected" type="bool" setter="" getter=""> + Indicates whether your app collects user's head movement. + </member> + <member name="privacy/collected_data/head/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects user's head movement. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/head/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links user's head movement to the user's identity. + </member> + <member name="privacy/collected_data/head/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses user's head movement for tracking. + </member> + <member name="privacy/collected_data/health/collected" type="bool" setter="" getter=""> + Indicates whether your app collects health and medical data. + </member> + <member name="privacy/collected_data/health/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects health and medical data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/health/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links health and medical data to the user's identity. + </member> + <member name="privacy/collected_data/health/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses health and medical data for tracking. + </member> + <member name="privacy/collected_data/name/collected" type="bool" setter="" getter=""> + Indicates whether your app collects user's name. + </member> + <member name="privacy/collected_data/name/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects user's name. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/name/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links user's name to the user's identity. + </member> + <member name="privacy/collected_data/name/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses user's name for tracking. + </member> + <member name="privacy/collected_data/other_contact_info/collected" type="bool" setter="" getter=""> + Indicates whether your app collects any other contact information. + </member> + <member name="privacy/collected_data/other_contact_info/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects any other contact information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/other_contact_info/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links any other contact information to the user's identity. + </member> + <member name="privacy/collected_data/other_contact_info/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses any other contact information for tracking. + </member> + <member name="privacy/collected_data/other_data_types/collected" type="bool" setter="" getter=""> + Indicates whether your app collects any other data. + </member> + <member name="privacy/collected_data/other_data_types/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects any other data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/other_data_types/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links any other data to the user's identity. + </member> + <member name="privacy/collected_data/other_data_types/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses any other data for tracking. + </member> + <member name="privacy/collected_data/other_diagnostic_data/collected" type="bool" setter="" getter=""> + Indicates whether your app collects any other diagnostic data. + </member> + <member name="privacy/collected_data/other_diagnostic_data/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects any other diagnostic data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/other_diagnostic_data/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links any other diagnostic data to the user's identity. + </member> + <member name="privacy/collected_data/other_diagnostic_data/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses any other diagnostic data for tracking. + </member> + <member name="privacy/collected_data/other_financial_info/collected" type="bool" setter="" getter=""> + Indicates whether your app collects any other financial information. + </member> + <member name="privacy/collected_data/other_financial_info/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects any other financial information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/other_financial_info/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links any other financial information to the user's identity. + </member> + <member name="privacy/collected_data/other_financial_info/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses any other financial information for tracking. + </member> + <member name="privacy/collected_data/other_usage_data/collected" type="bool" setter="" getter=""> + Indicates whether your app collects any other usage data. + </member> + <member name="privacy/collected_data/other_usage_data/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects any other usage data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/other_usage_data/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links any other usage data to the user's identity. + </member> + <member name="privacy/collected_data/other_usage_data/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses any other usage data for tracking. + </member> + <member name="privacy/collected_data/other_user_content/collected" type="bool" setter="" getter=""> + Indicates whether your app collects any other user generated content. + </member> + <member name="privacy/collected_data/other_user_content/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects any other user generated content. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/other_user_content/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links any other user generated content to the user's identity. + </member> + <member name="privacy/collected_data/other_user_content/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses any other user generated content for tracking. + </member> + <member name="privacy/collected_data/payment_info/collected" type="bool" setter="" getter=""> + Indicates whether your app collects payment information. + </member> + <member name="privacy/collected_data/payment_info/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects payment information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/payment_info/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links payment information to the user's identity. + </member> + <member name="privacy/collected_data/payment_info/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses payment information for tracking. + </member> + <member name="privacy/collected_data/performance_data/collected" type="bool" setter="" getter=""> + Indicates whether your app collects performance data. + </member> + <member name="privacy/collected_data/performance_data/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects performance data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/performance_data/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links performance data to the user's identity. + </member> + <member name="privacy/collected_data/performance_data/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses performance data for tracking. + </member> + <member name="privacy/collected_data/phone_number/collected" type="bool" setter="" getter=""> + Indicates whether your app collects phone number. + </member> + <member name="privacy/collected_data/phone_number/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects phone number. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/phone_number/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links phone number to the user's identity. + </member> + <member name="privacy/collected_data/phone_number/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses phone number for tracking. + </member> + <member name="privacy/collected_data/photos_or_videos/collected" type="bool" setter="" getter=""> + Indicates whether your app collects photos or videos. + </member> + <member name="privacy/collected_data/photos_or_videos/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects photos or videos. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/photos_or_videos/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links photos or videos to the user's identity. + </member> + <member name="privacy/collected_data/photos_or_videos/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses photos or videos for tracking. + </member> + <member name="privacy/collected_data/physical_address/collected" type="bool" setter="" getter=""> + Indicates whether your app collects physical address. + </member> + <member name="privacy/collected_data/physical_address/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects physical address. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/physical_address/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links physical address to the user's identity. + </member> + <member name="privacy/collected_data/physical_address/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses physical address for tracking. + </member> + <member name="privacy/collected_data/precise_location/collected" type="bool" setter="" getter=""> + Indicates whether your app collects precise location data. + </member> + <member name="privacy/collected_data/precise_location/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects precise location data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/precise_location/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links precise location data to the user's identity. + </member> + <member name="privacy/collected_data/precise_location/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses precise location data for tracking. + </member> + <member name="privacy/collected_data/product_interaction/collected" type="bool" setter="" getter=""> + Indicates whether your app collects product interaction data. + </member> + <member name="privacy/collected_data/product_interaction/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects product interaction data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/product_interaction/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links product interaction data to the user's identity. + </member> + <member name="privacy/collected_data/product_interaction/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses product interaction data for tracking. + </member> + <member name="privacy/collected_data/purchase_history/collected" type="bool" setter="" getter=""> + Indicates whether your app collects purchase history. + </member> + <member name="privacy/collected_data/purchase_history/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects purchase history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/purchase_history/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links purchase history to the user's identity. + </member> + <member name="privacy/collected_data/purchase_history/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses purchase history for tracking. + </member> + <member name="privacy/collected_data/search_hhistory/collected" type="bool" setter="" getter=""> + Indicates whether your app collects search history. + </member> + <member name="privacy/collected_data/search_hhistory/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects search history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/search_hhistory/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links search history to the user's identity. + </member> + <member name="privacy/collected_data/search_hhistory/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses search history for tracking. + </member> + <member name="privacy/collected_data/sensitive_info/collected" type="bool" setter="" getter=""> + Indicates whether your app collects sensitive user information. + </member> + <member name="privacy/collected_data/sensitive_info/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects sensitive user information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/sensitive_info/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links sensitive user information to the user's identity. + </member> + <member name="privacy/collected_data/sensitive_info/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses sensitive user information for tracking. + </member> + <member name="privacy/collected_data/user_id/collected" type="bool" setter="" getter=""> + Indicates whether your app collects user IDs. + </member> + <member name="privacy/collected_data/user_id/collection_purposes" type="int" setter="" getter=""> + The reasons your app collects user IDs. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url]. + </member> + <member name="privacy/collected_data/user_id/linked_to_user" type="bool" setter="" getter=""> + Indicates whether your app links user IDs to the user's identity. + </member> + <member name="privacy/collected_data/user_id/used_for_tracking" type="bool" setter="" getter=""> + Indicates whether your app uses user IDs for tracking. + </member> + <member name="privacy/disk_space_access_reasons" type="int" setter="" getter=""> + The reasons your app use free disk space API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url]. + </member> + <member name="privacy/file_timestamp_access_reasons" type="int" setter="" getter=""> + The reasons your app use file timestamp/metadata API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url]. + </member> <member name="privacy/microphone_usage_description" type="String" setter="" getter=""> A message displayed when requesting access to the device's microphone (in English). </member> @@ -141,6 +573,18 @@ <member name="privacy/photolibrary_usage_description_localized" type="Dictionary" setter="" getter=""> A message displayed when requesting access to the user's photo library (localized). </member> + <member name="privacy/system_boot_time_access_reasons" type="int" setter="" getter=""> + The reasons your app use system boot time / absolute time API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url]. + </member> + <member name="privacy/tracking_domains" type="PackedStringArray" setter="" getter=""> + The list of internet domains your app connects to that engage in tracking. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files]Privacy manifest files[/url]. + </member> + <member name="privacy/tracking_enabled" type="bool" setter="" getter=""> + Indicates whether your app uses data for tracking. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files]Privacy manifest files[/url]. + </member> + <member name="privacy/user_defaults_access_reasons" type="int" setter="" getter=""> + The reasons your app use user defaults API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url]. + </member> <member name="storyboard/custom_bg_color" type="Color" setter="" getter=""> A custom background color of the storyboard launch screen. </member> diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 33389129b7..c35c72d093 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -106,6 +106,94 @@ static const IconInfo icon_infos[] = { { PNAME("icons/notification_60x60"), "iphone", "Icon-60.png", "60", "3x", "20x20", false } }; +struct APIAccessInfo { + String prop_name; + String type_name; + Vector<String> prop_flag_value; + Vector<String> prop_flag_name; + int default_value; +}; + +static const APIAccessInfo api_info[] = { + { "file_timestamp", + "NSPrivacyAccessedAPICategoryFileTimestamp", + { "DDA9.1", "C617.1", "3B52.1" }, + { "Display to user on-device:", "Inside app or group container", "Files provided to app by user" }, + 3 }, + { "system_boot_time", + "NSPrivacyAccessedAPICategorySystemBootTime", + { "35F9.1", "8FFB.1", "3D61.1" }, + { "Measure time on-device", "Calculate absolute event timestamps", "User-initiated bug report" }, + 1 }, + { "disk_space", + "NSPrivacyAccessedAPICategoryDiskSpace", + { "E174.1", "85F4.1", "7D9E.1", "B728.1" }, + { "Write or delete file on-device", "Display to user on-device", "User-initiated bug report", "Health research app" }, + 3 }, + { "active_keyboard", + "NSPrivacyAccessedAPICategoryActiveKeyboards", + { "3EC4.1", "54BD.1" }, + { "Custom keyboard app on-device", "Customize UI on-device:2" }, + 0 }, + { "user_defaults", + "NSPrivacyAccessedAPICategoryUserDefaults", + { "1C8F.1", "AC6B.1", "CA92.1" }, + { "Access info from same App Group", "Access managed app configuration", "Access info from same app" }, + 0 } +}; + +struct DataCollectionInfo { + String prop_name; + String type_name; +}; + +static const DataCollectionInfo data_collect_type_info[] = { + { "name", "NSPrivacyCollectedDataTypeName" }, + { "email_address", "NSPrivacyCollectedDataTypeEmailAddress" }, + { "phone_number", "NSPrivacyCollectedDataTypePhoneNumber" }, + { "physical_address", "NSPrivacyCollectedDataTypePhysicalAddress" }, + { "other_contact_info", "NSPrivacyCollectedDataTypeOtherUserContactInfo" }, + { "health", "NSPrivacyCollectedDataTypeHealth" }, + { "fitness", "NSPrivacyCollectedDataTypeFitness" }, + { "payment_info", "NSPrivacyCollectedDataTypePaymentInfo" }, + { "credit_info", "NSPrivacyCollectedDataTypeCreditInfo" }, + { "other_financial_info", "NSPrivacyCollectedDataTypeOtherFinancialInfo" }, + { "precise_location", "NSPrivacyCollectedDataTypePreciseLocation" }, + { "coarse_location", "NSPrivacyCollectedDataTypeCoarseLocation" }, + { "sensitive_info", "NSPrivacyCollectedDataTypeSensitiveInfo" }, + { "contacts", "NSPrivacyCollectedDataTypeContacts" }, + { "emails_or_text_messages", "NSPrivacyCollectedDataTypeEmailsOrTextMessages" }, + { "photos_or_videos", "NSPrivacyCollectedDataTypePhotosorVideos" }, + { "audio_data", "NSPrivacyCollectedDataTypeAudioData" }, + { "gameplay_content", "NSPrivacyCollectedDataTypeGameplayContent" }, + { "customer_support", "NSPrivacyCollectedDataTypeCustomerSupport" }, + { "other_user_content", "NSPrivacyCollectedDataTypeOtherUserContent" }, + { "browsing_history", "NSPrivacyCollectedDataTypeBrowsingHistory" }, + { "search_hhistory", "NSPrivacyCollectedDataTypeSearchHistory" }, + { "user_id", "NSPrivacyCollectedDataTypeUserID" }, + { "device_id", "NSPrivacyCollectedDataTypeDeviceID" }, + { "purchase_history", "NSPrivacyCollectedDataTypePurchaseHistory" }, + { "product_interaction", "NSPrivacyCollectedDataTypeProductInteraction" }, + { "advertising_data", "NSPrivacyCollectedDataTypeAdvertisingData" }, + { "other_usage_data", "NSPrivacyCollectedDataTypeOtherUsageData" }, + { "crash_data", "NSPrivacyCollectedDataTypeCrashData" }, + { "performance_data", "NSPrivacyCollectedDataTypePerformanceData" }, + { "other_diagnostic_data", "NSPrivacyCollectedDataTypeOtherDiagnosticData" }, + { "environment_scanning", "NSPrivacyCollectedDataTypeEnvironmentScanning" }, + { "hands", "NSPrivacyCollectedDataTypeHands" }, + { "head", "NSPrivacyCollectedDataTypeHead" }, + { "other_data_types", "NSPrivacyCollectedDataTypeOtherDataTypes" }, +}; + +static const DataCollectionInfo data_collect_purpose_info[] = { + { "Analytics", "NSPrivacyCollectedDataTypePurposeAnalytics" }, + { "App Functionality", "NSPrivacyCollectedDataTypePurposeAppFunctionality" }, + { "Developer Advertising", "NSPrivacyCollectedDataTypePurposeDeveloperAdvertising" }, + { "Third-party Advertising", "NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising" }, + { "Product Personalization", "NSPrivacyCollectedDataTypePurposeProductPersonalization" }, + { "Other", "NSPrivacyCollectedDataTypePurposeOther" }, +}; + String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const { if (p_preset) { if (p_name == "application/app_store_team_id") { @@ -119,6 +207,21 @@ String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPres if (!is_package_name_valid(identifier, &pn_err)) { return TTR("Invalid Identifier:") + " " + pn_err; } + } else if (p_name == "privacy/file_timestamp_access_reasons") { + int access = p_preset->get("privacy/file_timestamp_access_reasons"); + if (access == 0) { + return TTR("At least one file timestamp access reason should be selected."); + } + } else if (p_name == "privacy/disk_space_access_reasons") { + int access = p_preset->get("privacy/disk_space_access_reasons"); + if (access == 0) { + return TTR("At least one disk space access reason should be selected."); + } + } else if (p_name == "privacy/system_boot_time_access_reasons") { + int access = p_preset->get("privacy/system_boot_time_access_reasons"); + if (access == 0) { + return TTR("At least one system boot time access reason should be selected."); + } } } return String(); @@ -140,6 +243,15 @@ bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPre return false; } + if (p_preset == nullptr) { + return true; + } + + bool advanced_options_enabled = p_preset->are_advanced_options_enabled(); + if (p_option.begins_with("privacy")) { + return advanced_options_enabled; + } + return true; } @@ -175,6 +287,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/delete_old_export_files_unconditionally"), false)); Vector<PluginConfigIOS> found_plugins = get_plugins(); for (int i = 0; i < found_plugins.size(); i++) { @@ -220,6 +333,37 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/photolibrary_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); + for (uint64_t i = 0; i < sizeof(api_info) / sizeof(api_info[0]); ++i) { + String prop_name = vformat("privacy/%s_access_reasons", api_info[i].prop_name); + String hint; + for (int j = 0; j < api_info[i].prop_flag_value.size(); j++) { + if (j != 0) { + hint += ","; + } + hint += vformat("%s - %s:%d", api_info[i].prop_flag_value[j], api_info[i].prop_flag_name[j], (1 << j)); + } + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, prop_name, PROPERTY_HINT_FLAGS, hint), api_info[i].default_value)); + } + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "privacy/tracking_enabled"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "privacy/tracking_domains"), Vector<String>())); + + { + String hint; + for (uint64_t i = 0; i < sizeof(data_collect_purpose_info) / sizeof(data_collect_purpose_info[0]); ++i) { + if (i != 0) { + hint += ","; + } + hint += vformat("%s:%d", data_collect_purpose_info[i].prop_name, (1 << i)); + } + for (uint64_t i = 0; i < sizeof(data_collect_type_info) / sizeof(data_collect_type_info[0]); ++i) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/collected", data_collect_type_info[i].prop_name)), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/linked_to_user", data_collect_type_info[i].prop_name)), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/used_for_tracking", data_collect_type_info[i].prop_name)), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, vformat("privacy/collected_data/%s/collection_purposes", data_collect_type_info[i].prop_name), PROPERTY_HINT_FLAGS, hint), 0)); + } + } + HashSet<String> used_names; for (uint64_t i = 0; i < sizeof(icon_infos) / sizeof(icon_infos[0]); ++i) { if (!used_names.has(icon_infos[i].preset_key)) { @@ -522,6 +666,87 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ } else if (lines[i].find("$swift_runtime_build_phase") != -1) { String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift */,"; strnew += lines[i].replace("$swift_runtime_build_phase", value) + "\n"; + } else if (lines[i].find("$priv_collection") != -1) { + bool section_opened = false; + for (uint64_t j = 0; j < sizeof(data_collect_type_info) / sizeof(data_collect_type_info[0]); ++j) { + bool data_collected = p_preset->get(vformat("privacy/collected_data/%s/collected", data_collect_type_info[j].prop_name)); + bool linked = p_preset->get(vformat("privacy/collected_data/%s/linked_to_user", data_collect_type_info[j].prop_name)); + bool tracking = p_preset->get(vformat("privacy/collected_data/%s/used_for_tracking", data_collect_type_info[j].prop_name)); + int purposes = p_preset->get(vformat("privacy/collected_data/%s/collection_purposes", data_collect_type_info[j].prop_name)); + if (data_collected) { + if (!section_opened) { + section_opened = true; + strnew += "\t<key>NSPrivacyCollectedDataTypes</key>\n"; + strnew += "\t<array>\n"; + } + strnew += "\t\t<dict>\n"; + strnew += "\t\t\t<key>NSPrivacyCollectedDataType</key>\n"; + strnew += vformat("\t\t\t<string>%s</string>\n", data_collect_type_info[j].type_name); + strnew += "\t\t\t\t<key>NSPrivacyCollectedDataTypeLinked</key>\n"; + if (linked) { + strnew += "\t\t\t\t<true/>\n"; + } else { + strnew += "\t\t\t\t<false/>\n"; + } + strnew += "\t\t\t\t<key>NSPrivacyCollectedDataTypeTracking</key>\n"; + if (tracking) { + strnew += "\t\t\t\t<true/>\n"; + } else { + strnew += "\t\t\t\t<false/>\n"; + } + if (purposes != 0) { + strnew += "\t\t\t\t<key>NSPrivacyCollectedDataTypePurposes</key>\n"; + strnew += "\t\t\t\t<array>\n"; + for (uint64_t k = 0; k < sizeof(data_collect_purpose_info) / sizeof(data_collect_purpose_info[0]); ++k) { + if (purposes & (1 << k)) { + strnew += vformat("\t\t\t\t\t<string>%s</string>\n", data_collect_purpose_info[k].type_name); + } + } + strnew += "\t\t\t\t</array>\n"; + } + strnew += "\t\t\t</dict>\n"; + } + } + if (section_opened) { + strnew += "\t</array>\n"; + } + } else if (lines[i].find("$priv_tracking") != -1) { + bool tracking = p_preset->get("privacy/tracking_enabled"); + strnew += "\t<key>NSPrivacyTracking</key>\n"; + if (tracking) { + strnew += "\t<true/>\n"; + } else { + strnew += "\t<false/>\n"; + } + Vector<String> tracking_domains = p_preset->get("privacy/tracking_domains"); + if (!tracking_domains.is_empty()) { + strnew += "\t<key>NSPrivacyTrackingDomains</key>\n"; + strnew += "\t<array>\n"; + for (const String &E : tracking_domains) { + strnew += "\t\t<string>" + E + "</string>\n"; + } + strnew += "\t</array>\n"; + } + } else if (lines[i].find("$priv_api_types") != -1) { + strnew += "\t<array>\n"; + for (uint64_t j = 0; j < sizeof(api_info) / sizeof(api_info[0]); ++j) { + int api_access = p_preset->get(vformat("privacy/%s_access_reasons", api_info[j].prop_name)); + if (api_access != 0) { + strnew += "\t\t<dict>\n"; + strnew += "\t\t\t<key>NSPrivacyAccessedAPITypeReasons</key>\n"; + strnew += "\t\t\t<array>\n"; + for (int k = 0; k < api_info[j].prop_flag_value.size(); k++) { + if (api_access & (1 << k)) { + strnew += vformat("\t\t\t\t<string>%s</string>\n", api_info[j].prop_flag_value[k]); + } + } + strnew += "\t\t\t</array>\n"; + strnew += "\t\t\t<key>NSPrivacyAccessedAPIType</key>\n"; + strnew += vformat("\t\t\t<string>%s</string>\n", api_info[j].type_name); + strnew += "\t\t</dict>\n"; + } + } + strnew += "\t</array>\n"; } else { strnew += lines[i] + "\n"; } @@ -1632,19 +1857,72 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres } { + bool delete_old = p_preset->get("application/delete_old_export_files_unconditionally"); Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (da.is_valid()) { String current_dir = da->get_current_dir(); - // remove leftovers from last export so they don't interfere - // in case some files are no longer needed + // Remove leftovers from last export so they don't interfere in case some files are no longer needed. if (da->change_dir(binary_dir + ".xcodeproj") == OK) { - da->erase_contents_recursive(); + // Check directory content before deleting. + int expected_files = 0; + int total_files = 0; + if (!delete_old) { + da->list_dir_begin(); + for (String n = da->get_next(); !n.is_empty(); n = da->get_next()) { + if (!n.begins_with(".")) { // Ignore ".", ".." and hidden files. + if (da->current_is_dir()) { + if (n == "xcshareddata" || n == "project.xcworkspace") { + expected_files++; + } + } else { + if (n == "project.pbxproj") { + expected_files++; + } + } + total_files++; + } + } + da->list_dir_end(); + } + if ((total_files == 0) || (expected_files >= Math::floor(total_files * 0.8))) { + da->erase_contents_recursive(); + } else { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Unexpected files found in the export destination directory \"%s.xcodeproj\", delete it manually or select another destination."), binary_dir)); + return ERR_CANT_CREATE; + } } + da->change_dir(current_dir); + if (da->change_dir(binary_dir) == OK) { - da->erase_contents_recursive(); + // Check directory content before deleting. + int expected_files = 0; + int total_files = 0; + if (!delete_old) { + da->list_dir_begin(); + for (String n = da->get_next(); !n.is_empty(); n = da->get_next()) { + if (!n.begins_with(".")) { // Ignore ".", ".." and hidden files. + if (da->current_is_dir()) { + if (n == "dylibs" || n == "Images.xcassets" || n.ends_with(".lproj") || n == "godot-publish-dotnet" || n.ends_with(".xcframework") || n.ends_with(".framework")) { + expected_files++; + } + } else { + if (n == binary_name + "-Info.plist" || n == binary_name + ".entitlements" || n == "Launch Screen.storyboard" || n == "export_options.plist" || n.begins_with("dummy.") || n.ends_with(".gdip")) { + expected_files++; + } + } + total_files++; + } + } + da->list_dir_end(); + } + if ((total_files == 0) || (expected_files >= Math::floor(total_files * 0.8))) { + da->erase_contents_recursive(); + } else { + add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Unexpected files found in the export destination directory \"%s\", delete it manually or select another destination."), binary_dir)); + return ERR_CANT_CREATE; + } } - da->change_dir(current_dir); if (!da->dir_exists(binary_dir)) { @@ -1694,6 +1972,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme"); files_to_parse.insert("godot_ios/godot_ios.entitlements"); files_to_parse.insert("godot_ios/Launch Screen.storyboard"); + files_to_parse.insert("PrivacyInfo.xcprivacy"); IOSConfigData config_data = { pkg_name, diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 27dec73b65..afc9d25a80 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -1,7 +1,7 @@ import os import platform import sys -from methods import get_compiler_version, using_gcc +from methods import print_warning, print_error, get_compiler_version, using_gcc from platform_methods import detect_arch from typing import TYPE_CHECKING @@ -20,7 +20,7 @@ def can_build(): pkgconf_error = os.system("pkg-config --version > /dev/null") if pkgconf_error: - print("Error: pkg-config not found. Aborting.") + print_error("pkg-config not found. Aborting.") return False return True @@ -75,7 +75,7 @@ def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64"] if env["arch"] not in supported_arches: - print( + print_error( 'Unsupported CPU architecture "%s" for Linux / *BSD. Supported architectures are: %s.' % (env["arch"], ", ".join(supported_arches)) ) @@ -128,7 +128,9 @@ def configure(env: "SConsEnvironment"): found_wrapper = True break if not found_wrapper: - print("Couldn't locate mold installation path. Make sure it's installed in /usr or /usr/local.") + print_error( + "Couldn't locate mold installation path. Make sure it's installed in /usr or /usr/local." + ) sys.exit(255) else: env.Append(LINKFLAGS=["-fuse-ld=mold"]) @@ -185,7 +187,7 @@ def configure(env: "SConsEnvironment"): if env["lto"] != "none": if env["lto"] == "thin": if not env["use_llvm"]: - print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") + print_error("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") sys.exit(255) env.Append(CCFLAGS=["-flto=thin"]) env.Append(LINKFLAGS=["-flto=thin"]) @@ -209,7 +211,7 @@ def configure(env: "SConsEnvironment"): if env["wayland"]: if os.system("wayland-scanner -v 2>/dev/null") != 0: - print("wayland-scanner not found. Disabling Wayland support.") + print_warning("wayland-scanner not found. Disabling Wayland support.") env["wayland"] = False if env["touch"]: @@ -227,7 +229,7 @@ def configure(env: "SConsEnvironment"): env["builtin_harfbuzz"], ] if (not all(ft_linked_deps)) and any(ft_linked_deps): # All or nothing. - print( + print_error( "These libraries should be either all builtin, or all system provided:\n" "freetype, libpng, zlib, graphite, harfbuzz.\n" "Please specify `builtin_<name>=no` for all of them, or none." @@ -318,7 +320,7 @@ def configure(env: "SConsEnvironment"): env.ParseConfig("pkg-config fontconfig --cflags --libs") env.Append(CPPDEFINES=["FONTCONFIG_ENABLED"]) else: - print("Warning: fontconfig development libraries not found. Disabling the system fonts support.") + print_warning("fontconfig development libraries not found. Disabling the system fonts support.") env["fontconfig"] = False else: env.Append(CPPDEFINES=["FONTCONFIG_ENABLED"]) @@ -329,7 +331,7 @@ def configure(env: "SConsEnvironment"): env.ParseConfig("pkg-config alsa --cflags --libs") env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"]) else: - print("Warning: ALSA development libraries not found. Disabling the ALSA audio driver.") + print_warning("ALSA development libraries not found. Disabling the ALSA audio driver.") env["alsa"] = False else: env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"]) @@ -340,7 +342,7 @@ def configure(env: "SConsEnvironment"): env.ParseConfig("pkg-config libpulse --cflags --libs") env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED"]) else: - print("Warning: PulseAudio development libraries not found. Disabling the PulseAudio audio driver.") + print_warning("PulseAudio development libraries not found. Disabling the PulseAudio audio driver.") env["pulseaudio"] = False else: env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED", "_REENTRANT"]) @@ -351,7 +353,7 @@ def configure(env: "SConsEnvironment"): env.ParseConfig("pkg-config dbus-1 --cflags --libs") env.Append(CPPDEFINES=["DBUS_ENABLED"]) else: - print("Warning: D-Bus development libraries not found. Disabling screensaver prevention.") + print_warning("D-Bus development libraries not found. Disabling screensaver prevention.") env["dbus"] = False else: env.Append(CPPDEFINES=["DBUS_ENABLED"]) @@ -362,7 +364,7 @@ def configure(env: "SConsEnvironment"): env.ParseConfig("pkg-config speech-dispatcher --cflags --libs") env.Append(CPPDEFINES=["SPEECHD_ENABLED"]) else: - print("Warning: speech-dispatcher development libraries not found. Disabling text to speech support.") + print_warning("speech-dispatcher development libraries not found. Disabling text to speech support.") env["speechd"] = False else: env.Append(CPPDEFINES=["SPEECHD_ENABLED"]) @@ -373,11 +375,11 @@ def configure(env: "SConsEnvironment"): env.Append(CPPDEFINES=["XKB_ENABLED"]) else: if env["wayland"]: - print("Error: libxkbcommon development libraries required by Wayland not found. Aborting.") + print_error("libxkbcommon development libraries required by Wayland not found. Aborting.") sys.exit(255) else: - print( - "Warning: libxkbcommon development libraries not found. Disabling dead key composition and key label support." + print_warning( + "libxkbcommon development libraries not found. Disabling dead key composition and key label support." ) else: env.Append(CPPDEFINES=["XKB_ENABLED"]) @@ -390,7 +392,7 @@ def configure(env: "SConsEnvironment"): env.ParseConfig("pkg-config libudev --cflags --libs") env.Append(CPPDEFINES=["UDEV_ENABLED"]) else: - print("Warning: libudev development libraries not found. Disabling controller hotplugging support.") + print_warning("libudev development libraries not found. Disabling controller hotplugging support.") env["udev"] = False else: env.Append(CPPDEFINES=["UDEV_ENABLED"]) @@ -416,31 +418,31 @@ def configure(env: "SConsEnvironment"): if env["x11"]: if not env["use_sowrap"]: if os.system("pkg-config --exists x11"): - print("Error: X11 libraries not found. Aborting.") + print_error("X11 libraries not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config x11 --cflags --libs") if os.system("pkg-config --exists xcursor"): - print("Error: Xcursor library not found. Aborting.") + print_error("Xcursor library not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config xcursor --cflags --libs") if os.system("pkg-config --exists xinerama"): - print("Error: Xinerama library not found. Aborting.") + print_error("Xinerama library not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config xinerama --cflags --libs") if os.system("pkg-config --exists xext"): - print("Error: Xext library not found. Aborting.") + print_error("Xext library not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config xext --cflags --libs") if os.system("pkg-config --exists xrandr"): - print("Error: XrandR library not found. Aborting.") + print_error("XrandR library not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config xrandr --cflags --libs") if os.system("pkg-config --exists xrender"): - print("Error: XRender library not found. Aborting.") + print_error("XRender library not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config xrender --cflags --libs") if os.system("pkg-config --exists xi"): - print("Error: Xi library not found. Aborting.") + print_error("Xi library not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config xi --cflags --libs") env.Append(CPPDEFINES=["X11_ENABLED"]) @@ -448,20 +450,20 @@ def configure(env: "SConsEnvironment"): if env["wayland"]: if not env["use_sowrap"]: if os.system("pkg-config --exists libdecor-0"): - print("Warning: libdecor development libraries not found. Disabling client-side decorations.") + print_warning("libdecor development libraries not found. Disabling client-side decorations.") env["libdecor"] = False else: env.ParseConfig("pkg-config libdecor-0 --cflags --libs") if os.system("pkg-config --exists wayland-client"): - print("Error: Wayland client library not found. Aborting.") + print_error("Wayland client library not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config wayland-client --cflags --libs") if os.system("pkg-config --exists wayland-cursor"): - print("Error: Wayland cursor library not found. Aborting.") + print_error("Wayland cursor library not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config wayland-cursor --cflags --libs") if os.system("pkg-config --exists wayland-egl"): - print("Error: Wayland EGL library not found. Aborting.") + print_error("Wayland EGL library not found. Aborting.") sys.exit(255) env.ParseConfig("pkg-config wayland-egl --cflags --libs") diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index 7f9008e952..3a63b87c7a 100644 --- a/platform/linuxbsd/wayland/wayland_thread.cpp +++ b/platform/linuxbsd/wayland/wayland_thread.cpp @@ -371,28 +371,22 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re } if (strcmp(interface, zxdg_exporter_v1_interface.name) == 0) { - registry->wl_exporter = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1); - registry->wl_exporter_name = name; + registry->xdg_exporter = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1); + registry->xdg_exporter_name = name; return; } if (strcmp(interface, wl_compositor_interface.name) == 0) { - registry->wl_compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4); + registry->wl_compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, CLAMP((int)version, 1, 6)); registry->wl_compositor_name = name; return; } - if (strcmp(interface, wl_subcompositor_interface.name) == 0) { - registry->wl_subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1); - registry->wl_subcompositor_name = name; - return; - } - if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { - registry->wl_data_device_manager = (struct wl_data_device_manager *)wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 3); + registry->wl_data_device_manager = (struct wl_data_device_manager *)wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, CLAMP((int)version, 1, 3)); registry->wl_data_device_manager_name = name; - // This global creates some seats data. Let's do that for the ones already available. + // This global creates some seat data. Let's do that for the ones already available. for (struct wl_seat *wl_seat : registry->wl_seats) { SeatState *ss = wl_seat_get_seat_state(wl_seat); ERR_FAIL_NULL(ss); @@ -406,7 +400,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re } if (strcmp(interface, wl_output_interface.name) == 0) { - struct wl_output *wl_output = (struct wl_output *)wl_registry_bind(wl_registry, name, &wl_output_interface, 2); + struct wl_output *wl_output = (struct wl_output *)wl_registry_bind(wl_registry, name, &wl_output_interface, CLAMP((int)version, 1, 4)); wl_proxy_tag_godot((struct wl_proxy *)wl_output); registry->wl_outputs.push_back(wl_output); @@ -421,7 +415,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re } if (strcmp(interface, wl_seat_interface.name) == 0) { - struct wl_seat *wl_seat = (struct wl_seat *)wl_registry_bind(wl_registry, name, &wl_seat_interface, 5); + struct wl_seat *wl_seat = (struct wl_seat *)wl_registry_bind(wl_registry, name, &wl_seat_interface, CLAMP((int)version, 1, 9)); wl_proxy_tag_godot((struct wl_proxy *)wl_seat); SeatState *ss = memnew(SeatState); @@ -466,7 +460,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re } if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, MAX(2, MIN(6, (int)version))); + registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, CLAMP((int)version, 1, 6)); registry->xdg_wm_base_name = name; xdg_wm_base_add_listener(registry->xdg_wm_base, &xdg_wm_base_listener, nullptr); @@ -502,7 +496,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) { registry->wp_primary_selection_device_manager = (struct zwp_primary_selection_device_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_primary_selection_device_manager_v1_interface, 1); - // This global creates some seats data. Let's do that for the ones already available. + // This global creates some seat data. Let's do that for the ones already available. for (struct wl_seat *wl_seat : registry->wl_seats) { SeatState *ss = wl_seat_get_seat_state(wl_seat); ERR_FAIL_NULL(ss); @@ -570,13 +564,13 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry return; } - if (name == registry->wl_exporter_name) { - if (registry->wl_exporter) { - zxdg_exporter_v1_destroy(registry->wl_exporter); - registry->wl_exporter = nullptr; + if (name == registry->xdg_exporter_name) { + if (registry->xdg_exporter) { + zxdg_exporter_v1_destroy(registry->xdg_exporter); + registry->xdg_exporter = nullptr; } - registry->wl_exporter_name = 0; + registry->xdg_exporter_name = 0; return; } @@ -592,17 +586,6 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry return; } - if (name == registry->wl_subcompositor_name) { - if (registry->wl_subcompositor) { - wl_subcompositor_destroy(registry->wl_subcompositor); - registry->wl_subcompositor = nullptr; - } - - registry->wl_subcompositor_name = 0; - - return; - } - if (name == registry->wl_data_device_manager_name) { if (registry->wl_data_device_manager) { wl_data_device_manager_destroy(registry->wl_data_device_manager); @@ -1000,6 +983,12 @@ void WaylandThread::_wl_output_on_geometry(void *data, struct wl_output *wl_outp ss->pending_data.make.parse_utf8(make); ss->pending_data.model.parse_utf8(model); + + // `wl_output::done` is a version 2 addition. We'll directly update the data + // for compatibility. + if (wl_output_get_version(wl_output) == 1) { + ss->data = ss->pending_data; + } } void WaylandThread::_wl_output_on_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { @@ -1010,8 +999,17 @@ void WaylandThread::_wl_output_on_mode(void *data, struct wl_output *wl_output, ss->pending_data.size.height = height; ss->pending_data.refresh_rate = refresh ? refresh / 1000.0f : -1; + + // `wl_output::done` is a version 2 addition. We'll directly update the data + // for compatibility. + if (wl_output_get_version(wl_output) == 1) { + ss->data = ss->pending_data; + } } +// NOTE: The following `wl_output` events are only for version 2 onwards, so we +// can assume that they're "atomic" (i.e. rely on the `wl_output::done` event). + void WaylandThread::_wl_output_on_done(void *data, struct wl_output *wl_output) { ScreenState *ss = (ScreenState *)data; ERR_FAIL_NULL(ss); @@ -1523,7 +1521,7 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point wayland_thread->push_message(msg); } - if (pd.discrete_scroll_vector - old_pd.discrete_scroll_vector != Vector2i()) { + if (pd.discrete_scroll_vector_120 - old_pd.discrete_scroll_vector_120 != Vector2i()) { // This is a discrete scroll (eg. from a scroll wheel), so we'll just emit // scroll wheel buttons. if (pd.scroll_vector.y != 0) { @@ -1596,13 +1594,13 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point if (test_button == MouseButton::WHEEL_UP || test_button == MouseButton::WHEEL_DOWN) { // If this is a discrete scroll, specify how many "clicks" it did for this // pointer frame. - mb->set_factor(abs(pd.discrete_scroll_vector.y)); + mb->set_factor(Math::abs(pd.discrete_scroll_vector_120.y / (float)120)); } if (test_button == MouseButton::WHEEL_RIGHT || test_button == MouseButton::WHEEL_LEFT) { // If this is a discrete scroll, specify how many "clicks" it did for this // pointer frame. - mb->set_factor(abs(pd.discrete_scroll_vector.x)); + mb->set_factor(fabs(pd.discrete_scroll_vector_120.x / (float)120)); } mb->set_button_mask(pd.pressed_button_mask); @@ -1661,7 +1659,7 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point // Reset the scroll vectors as we already handled them. pd.scroll_vector = Vector2(); - pd.discrete_scroll_vector = Vector2(); + pd.discrete_scroll_vector_120 = Vector2i(); // Update the data all getters read. Wayland's specification requires us to do // this, since all pointer actions are sent in individual events. @@ -1683,6 +1681,9 @@ void WaylandThread::_wl_pointer_on_axis_source(void *data, struct wl_pointer *wl void WaylandThread::_wl_pointer_on_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { } +// NOTE: This event is deprecated since version 8 and superseded by +// `wl_pointer::axis_value120`. This thus converts the data to its +// fraction-of-120 format. void WaylandThread::_wl_pointer_on_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { SeatState *ss = (SeatState *)data; ERR_FAIL_NULL(ss); @@ -1694,17 +1695,37 @@ void WaylandThread::_wl_pointer_on_axis_discrete(void *data, struct wl_pointer * PointerData &pd = ss->pointer_data_buffer; + // NOTE: We can allow ourselves to not accumulate this data (and thus just + // assign it) as the spec guarantees only one event per axis type. + if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - pd.discrete_scroll_vector.y = discrete; + pd.discrete_scroll_vector_120.y = discrete * 120; } if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { - pd.discrete_scroll_vector.x = discrete; + pd.discrete_scroll_vector_120.x = discrete * 120; } } -// TODO: Add support to this event. +// Supersedes `wl_pointer::axis_discrete` Since version 8. void WaylandThread::_wl_pointer_on_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) { + SeatState *ss = (SeatState *)data; + ERR_FAIL_NULL(ss); + + if (!ss->pointed_surface) { + // We're probably on a decoration or some other third-party thing. + return; + } + + PointerData &pd = ss->pointer_data_buffer; + + if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { + pd.discrete_scroll_vector_120.y += value120; + } + + if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { + pd.discrete_scroll_vector_120.x += value120; + } } // TODO: Add support to this event. @@ -1999,7 +2020,7 @@ void WaylandThread::_wp_relative_pointer_on_relative_motion(void *data, struct z pd.relative_motion_time = uptime_lo; } -void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) { +void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) { SeatState *ss = (SeatState *)data; ERR_FAIL_NULL(ss); @@ -2009,7 +2030,7 @@ void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_po } } -void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) { +void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) { SeatState *ss = (SeatState *)data; ERR_FAIL_NULL(ss); @@ -2068,7 +2089,7 @@ void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_p } } -void WaylandThread::_wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) { +void WaylandThread::_wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) { SeatState *ss = (SeatState *)data; ERR_FAIL_NULL(ss); @@ -2093,7 +2114,7 @@ void WaylandThread::_wp_primary_selection_device_on_selection(void *data, struct ss->wp_primary_selection_offer = id; } -void WaylandThread::_wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1, const char *mime_type) { +void WaylandThread::_wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer_v1, const char *mime_type) { OfferState *os = (OfferState *)data; ERR_FAIL_NULL(os); @@ -2147,10 +2168,10 @@ void WaylandThread::_wp_primary_selection_source_on_cancelled(void *data, struct } } -void WaylandThread::_wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id) { +void WaylandThread::_wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_v2 *id) { } -void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id) { +void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id) { SeatState *ss = (SeatState *)data; ERR_FAIL_NULL(ss); @@ -2163,31 +2184,31 @@ void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_ ss->tablet_tools.push_back(id); } -void WaylandThread::_wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) { +void WaylandThread::_wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) { } -void WaylandThread::_wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t tool_type) { - TabletToolState *state = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t tool_type) { + TabletToolState *state = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (state && tool_type == ZWP_TABLET_TOOL_V2_TYPE_ERASER) { state->is_eraser = true; } } -void WaylandThread::_wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) { +void WaylandThread::_wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) { } -void WaylandThread::_wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo) { +void WaylandThread::_wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo) { } -void WaylandThread::_wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t capability) { +void WaylandThread::_wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t capability) { } -void WaylandThread::_wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { +void WaylandThread::_wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) { } -void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -2199,7 +2220,7 @@ void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_too return; } - List<struct zwp_tablet_tool_v2 *>::Element *E = ss->tablet_tools.find(zwp_tablet_tool_v2); + List<struct zwp_tablet_tool_v2 *>::Element *E = ss->tablet_tools.find(wp_tablet_tool_v2); if (E && E->get()) { struct zwp_tablet_tool_v2 *tool = E->get(); @@ -2213,8 +2234,8 @@ void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_too } } -void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -2241,8 +2262,8 @@ void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_table DEBUG_LOG_WAYLAND_THREAD("Tablet tool entered window."); } -void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -2268,8 +2289,8 @@ void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tabl DEBUG_LOG_WAYLAND_THREAD("Tablet tool left window."); } -void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -2286,8 +2307,8 @@ void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v td.button_time = OS::get_singleton()->get_ticks_msec(); } -void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -2302,8 +2323,8 @@ void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 td.button_time = OS::get_singleton()->get_ticks_msec(); } -void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -2323,8 +2344,8 @@ void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool td.motion_time = OS::get_singleton()->get_ticks_msec(); } -void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t pressure) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t pressure) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -2333,12 +2354,12 @@ void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_to ts->data_pending.pressure = pressure; } -void WaylandThread::_wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t distance) { +void WaylandThread::_wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t distance) { // Unsupported } -void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -2350,20 +2371,20 @@ void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v td.tilt.y = wl_fixed_to_double(tilt_y); } -void WaylandThread::_wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees) { +void WaylandThread::_wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees) { // Unsupported. } -void WaylandThread::_wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, int32_t position) { +void WaylandThread::_wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, int32_t position) { // Unsupported. } -void WaylandThread::_wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks) { +void WaylandThread::_wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks) { // TODO } -void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -2398,8 +2419,8 @@ void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool } } -void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t time) { - TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2); +void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t time) { + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; @@ -3071,8 +3092,8 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid // "loop". wl_surface_commit(ws.wl_surface); - if (registry.wl_exporter) { - ws.xdg_exported = zxdg_exporter_v1_export(registry.wl_exporter, ws.wl_surface); + if (registry.xdg_exporter) { + ws.xdg_exported = zxdg_exporter_v1_export(registry.xdg_exporter, ws.wl_surface); zxdg_exported_v1_add_listener(ws.xdg_exported, &xdg_exported_listener, &ws); } @@ -3529,9 +3550,6 @@ Error WaylandThread::init() { ERR_FAIL_NULL_V_MSG(registry.wl_shm, ERR_UNAVAILABLE, "Can't obtain the Wayland shared memory global."); ERR_FAIL_NULL_V_MSG(registry.wl_compositor, ERR_UNAVAILABLE, "Can't obtain the Wayland compositor global."); - ERR_FAIL_NULL_V_MSG(registry.wl_subcompositor, ERR_UNAVAILABLE, "Can't obtain the Wayland subcompositor global."); - ERR_FAIL_NULL_V_MSG(registry.wl_data_device_manager, ERR_UNAVAILABLE, "Can't obtain the Wayland data device manager global."); - ERR_FAIL_NULL_V_MSG(registry.wp_pointer_constraints, ERR_UNAVAILABLE, "Can't obtain the Wayland pointer constraints global."); ERR_FAIL_NULL_V_MSG(registry.xdg_wm_base, ERR_UNAVAILABLE, "Can't obtain the Wayland XDG shell global."); if (!registry.xdg_decoration_manager) { @@ -3660,7 +3678,10 @@ void WaylandThread::cursor_shape_set_custom_image(DisplayServer::CursorShape p_c munmap(cursor.buffer_data, cursor.buffer_data_size); } - cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + // NOTE: From `wl_keyboard`s of version 7 or later, the spec requires the mmap + // operation to be done with MAP_PRIVATE, as "MAP_SHARED may fail". We'll do it + // regardless of global version. + cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (cursor.wl_buffer) { // Clean up the old Wayland buffer. @@ -4179,18 +4200,14 @@ void WaylandThread::destroy() { xdg_wm_base_destroy(registry.xdg_wm_base); } - if (registry.wl_exporter) { - zxdg_exporter_v1_destroy(registry.wl_exporter); + if (registry.xdg_exporter) { + zxdg_exporter_v1_destroy(registry.xdg_exporter); } if (registry.wl_shm) { wl_shm_destroy(registry.wl_shm); } - if (registry.wl_subcompositor) { - wl_subcompositor_destroy(registry.wl_subcompositor); - } - if (registry.wl_compositor) { wl_compositor_destroy(registry.wl_compositor); } diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h index d49f0c9d34..d35a5b7139 100644 --- a/platform/linuxbsd/wayland/wayland_thread.h +++ b/platform/linuxbsd/wayland/wayland_thread.h @@ -133,8 +133,8 @@ public: struct xdg_wm_base *xdg_wm_base = nullptr; uint32_t xdg_wm_base_name = 0; - struct zxdg_exporter_v1 *wl_exporter = nullptr; - uint32_t wl_exporter_name = 0; + struct zxdg_exporter_v1 *xdg_exporter = nullptr; + uint32_t xdg_exporter_name = 0; // wayland-protocols globals. @@ -300,8 +300,8 @@ public: // The amount "scrolled" in pixels, in each direction. Vector2 scroll_vector; - // The amount of scroll "clicks" in each direction. - Vector2i discrete_scroll_vector; + // The amount of scroll "clicks" in each direction, in fractions of 120. + Vector2i discrete_scroll_vector_120; uint32_t pinch_scale = 1; }; @@ -579,41 +579,41 @@ private: static void _wp_relative_pointer_on_relative_motion(void *data, struct zwp_relative_pointer_v1 *wp_relative_pointer_v1, uint32_t uptime_hi, uint32_t uptime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel); - static void _wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers); - static void _wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation); - static void _wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled); + static void _wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers); + static void _wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation); + static void _wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled); static void _wp_primary_selection_device_on_data_offer(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *offer); static void _wp_primary_selection_device_on_selection(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *id); - static void _wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1, const char *mime_type); + static void _wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer_v1, const char *mime_type); static void _wp_primary_selection_source_on_send(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1, const char *mime_type, int32_t fd); static void _wp_primary_selection_source_on_cancelled(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1); - static void _wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id); - static void _wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id); - static void _wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id); - - static void _wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t tool_type); - static void _wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo); - static void _wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo); - static void _wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t capability); - static void _wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2); - static void _wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2); - static void _wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface); - static void _wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2); - static void _wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial); - static void _wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2); - static void _wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y); - static void _wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t pressure); - static void _wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t distance); - static void _wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y); - static void _wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees); - static void _wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, int32_t position); - static void _wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks); - static void _wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state); - static void _wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t time); + static void _wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_v2 *id); + static void _wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id); + static void _wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id); + + static void _wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t tool_type); + static void _wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo); + static void _wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo); + static void _wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t capability); + static void _wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2); + static void _wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2); + static void _wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface); + static void _wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2); + static void _wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial); + static void _wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2); + static void _wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y); + static void _wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t pressure); + static void _wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t distance); + static void _wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y); + static void _wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees); + static void _wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, int32_t position); + static void _wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks); + static void _wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state); + static void _wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t time); static void _xdg_toplevel_decoration_on_configure(void *data, struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration, uint32_t mode); diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 0041b4c7f3..e854fe9274 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -4992,7 +4992,7 @@ void DisplayServerX11::process_events() { files.write[i] = files[i].replace("file://", "").uri_decode(); } - if (!windows[window_id].drop_files_callback.is_null()) { + if (windows[window_id].drop_files_callback.is_valid()) { windows[window_id].drop_files_callback.call(files); } diff --git a/platform/macos/detect.py b/platform/macos/detect.py index 3c8b1ebee1..a5ef29e34f 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -1,6 +1,6 @@ import os import sys -from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang +from methods import print_error, detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang from platform_methods import detect_arch, detect_mvk from typing import TYPE_CHECKING @@ -64,11 +64,11 @@ def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_64", "arm64"] if env["arch"] not in supported_arches: - print( + print_error( 'Unsupported CPU architecture "%s" for macOS. Supported architectures are: %s.' % (env["arch"], ", ".join(supported_arches)) ) - sys.exit() + sys.exit(255) ## Build type @@ -254,7 +254,7 @@ def configure(env: "SConsEnvironment"): if mvk_path != "": env.Append(LINKFLAGS=["-L" + mvk_path]) else: - print( + print_error( "MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path." ) sys.exit(255) diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index cfe925e79b..6461f50818 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -938,7 +938,7 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect button_pressed = int64_t(2 + (ret - NSAlertThirdButtonReturn)); } - if (!p_callback.is_null()) { + if (p_callback.is_valid()) { Variant ret; Callable::CallError ce; const Variant *args[1] = { &button_pressed }; @@ -1018,7 +1018,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title, String url; url.parse_utf8([[[panel URL] path] UTF8String]); files.push_back(url); - if (!callback.is_null()) { + if (callback.is_valid()) { if (p_options_in_cb) { Variant v_result = true; Variant v_files = files; @@ -1047,7 +1047,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title, } } } else { - if (!callback.is_null()) { + if (callback.is_valid()) { if (p_options_in_cb) { Variant v_result = false; Variant v_files = Vector<String>(); @@ -1134,7 +1134,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title, url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]); files.push_back(url); } - if (!callback.is_null()) { + if (callback.is_valid()) { if (p_options_in_cb) { Variant v_result = true; Variant v_files = files; @@ -1163,7 +1163,7 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title, } } } else { - if (!callback.is_null()) { + if (callback.is_valid()) { if (p_options_in_cb) { Variant v_result = false; Variant v_files = Vector<String>(); @@ -1222,7 +1222,7 @@ Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description String ret; ret.parse_utf8([[input stringValue] UTF8String]); - if (!p_callback.is_null()) { + if (p_callback.is_valid()) { Variant v_result = ret; Variant ret; Callable::CallError ce; diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index d75def9b50..5f52d33318 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -909,7 +909,7 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres return OK; } -Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn) { +Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn, bool p_set_id) { int codesign_tool = p_preset->get("codesign/codesign"); switch (codesign_tool) { case 1: { // built-in ad-hoc @@ -953,6 +953,12 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre args.push_back("--code-signature-flags"); args.push_back("runtime"); + if (p_set_id) { + String app_id = p_preset->get("application/bundle_identifier"); + args.push_back("--binary-identifier"); + args.push_back(app_id); + } + args.push_back("-v"); /* provide some more feedback */ args.push_back(p_path); @@ -1012,6 +1018,12 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre args.push_back(p_preset->get("codesign/identity")); } + if (p_set_id) { + String app_id = p_preset->get("application/bundle_identifier"); + args.push_back("-i"); + args.push_back(app_id); + } + args.push_back("-v"); /* provide some more feedback */ args.push_back("-f"); @@ -1043,7 +1055,7 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre } Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, - const String &p_ent_path, bool p_should_error_on_non_code) { + const String &p_ent_path, const String &p_helper_ent_path, bool p_should_error_on_non_code) { static Vector<String> extensions_to_sign; if (extensions_to_sign.is_empty()) { @@ -1070,7 +1082,8 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres } if (extensions_to_sign.find(current_file.get_extension()) > -1) { - Error code_sign_error{ _code_sign(p_preset, current_file_path, p_ent_path, false) }; + int ftype = MachO::get_filetype(current_file_path); + Error code_sign_error{ _code_sign(p_preset, current_file_path, (ftype == 2 || ftype == 5) ? p_helper_ent_path : p_ent_path, false, (ftype == 2 || ftype == 5)) }; if (code_sign_error != OK) { return code_sign_error; } @@ -1079,7 +1092,7 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres FileAccess::set_unix_permissions(current_file_path, 0755); } } else if (dir_access->current_is_dir()) { - Error code_sign_error{ _code_sign_directory(p_preset, current_file_path, p_ent_path, p_should_error_on_non_code) }; + Error code_sign_error{ _code_sign_directory(p_preset, current_file_path, p_ent_path, p_helper_ent_path, p_should_error_on_non_code) }; if (code_sign_error != OK) { return code_sign_error; } @@ -1097,6 +1110,7 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access, const String &p_src_path, const String &p_in_app_path, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, const String &p_ent_path, + const String &p_helper_ent_path, bool p_should_error_on_non_code_sign) { static Vector<String> extensions_to_sign; @@ -1186,10 +1200,11 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access if (err == OK && p_sign_enabled) { if (dir_access->dir_exists(p_src_path) && p_src_path.get_extension().is_empty()) { // If it is a directory, find and sign all dynamic libraries. - err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_should_error_on_non_code_sign); + err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_helper_ent_path, p_should_error_on_non_code_sign); } else { if (extensions_to_sign.find(p_in_app_path.get_extension()) > -1) { - err = _code_sign(p_preset, p_in_app_path, p_ent_path, false); + int ftype = MachO::get_filetype(p_in_app_path); + err = _code_sign(p_preset, p_in_app_path, (ftype == 2 || ftype == 5) ? p_helper_ent_path : p_ent_path, false, (ftype == 2 || ftype == 5)); } if (dir_access->file_exists(p_in_app_path) && is_executable(p_in_app_path)) { // chmod with 0755 if the file is executable. @@ -1203,13 +1218,13 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access Error EditorExportPlatformMacOS::_export_macos_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin, const String &p_app_path_name, Ref<DirAccess> &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, - const String &p_ent_path) { + const String &p_ent_path, const String &p_helper_ent_path) { Error error{ OK }; const Vector<String> &macos_plugins{ p_editor_export_plugin->get_macos_plugin_files() }; for (int i = 0; i < macos_plugins.size(); ++i) { String src_path{ ProjectSettings::get_singleton()->globalize_path(macos_plugins[i]) }; String path_in_app{ p_app_path_name + "/Contents/PlugIns/" + src_path.get_file() }; - error = _copy_and_sign_files(dir_access, src_path, path_in_app, p_sign_enabled, p_preset, p_ent_path, false); + error = _copy_and_sign_files(dir_access, src_path, path_in_app, p_sign_enabled, p_preset, p_ent_path, p_helper_ent_path, false); if (error != OK) { break; } @@ -1786,8 +1801,9 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("'rcodesign' doesn't support signing applications with embedded dynamic libraries.")); } + bool sandbox = p_preset->get("codesign/entitlements/app_sandbox/enabled"); String ent_path = p_preset->get("codesign/entitlements/custom_file"); - String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements"); + String hlp_ent_path = sandbox ? EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements") : ent_path; if (sign_enabled && (ent_path.is_empty())) { ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + ".entitlements"); @@ -1939,7 +1955,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p err = ERR_CANT_CREATE; } - if ((err == OK) && helpers.size() > 0) { + if ((err == OK) && sandbox && (helpers.size() > 0 || shared_objects.size() > 0)) { ent_f = FileAccess::open(hlp_ent_path, FileAccess::WRITE); if (ent_f.is_valid()) { ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); @@ -1965,7 +1981,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p String hlp_path = helpers[i]; err = da->copy(hlp_path, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file()); if (err == OK && sign_enabled) { - err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), hlp_ent_path, false); + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), hlp_ent_path, false, true); } FileAccess::set_unix_permissions(tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), 0755); } @@ -1977,11 +1993,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path); if (shared_objects[i].target.is_empty()) { String path_in_app = tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(); - err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, true); + err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, hlp_ent_path, true); } else { String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target); tmp_app_dir->make_dir_recursive(path_in_app); - err = _copy_and_sign_files(da, src_path, path_in_app.path_join(src_path.get_file()), sign_enabled, p_preset, ent_path, false); + err = _copy_and_sign_files(da, src_path, path_in_app.path_join(src_path.get_file()), sign_enabled, p_preset, ent_path, hlp_ent_path, false); } if (err != OK) { break; @@ -1990,7 +2006,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p Vector<Ref<EditorExportPlugin>> export_plugins{ EditorExport::get_singleton()->get_export_plugins() }; for (int i = 0; i < export_plugins.size(); ++i) { - err = _export_macos_plugins_for(export_plugins[i], tmp_app_path_name, da, sign_enabled, p_preset, ent_path); + err = _export_macos_plugins_for(export_plugins[i], tmp_app_path_name, da, sign_enabled, p_preset, ent_path, hlp_ent_path); if (err != OK) { break; } @@ -2010,7 +2026,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p if (ep.step(TTR("Code signing bundle"), 2)) { return ERR_SKIP; } - err = _code_sign(p_preset, tmp_app_path_name, ent_path); + err = _code_sign(p_preset, tmp_app_path_name, ent_path, true, false); } String noto_path = p_path; @@ -2028,7 +2044,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p if (ep.step(TTR("Code signing DMG"), 3)) { return ERR_SKIP; } - err = _code_sign(p_preset, p_path, ent_path, false); + err = _code_sign(p_preset, p_path, ent_path, false, false); } } else if (export_format == "pkg") { // Create a Installer. diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h index 0764b63e8c..2d615abede 100644 --- a/platform/macos/export/export_plugin.h +++ b/platform/macos/export/export_plugin.h @@ -89,14 +89,14 @@ class EditorExportPlatformMacOS : public EditorExportPlatform { void _make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data); Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path); - Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn = true); - Error _code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_should_error_on_non_code = true); + Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn = true, bool p_set_id = false); + Error _code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, const String &p_helper_ent_path, bool p_should_error_on_non_code = true); Error _copy_and_sign_files(Ref<DirAccess> &dir_access, const String &p_src_path, const String &p_in_app_path, - bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, const String &p_ent_path, + bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, const String &p_ent_path, const String &p_helper_ent_path, bool p_should_error_on_non_code_sign); Error _export_macos_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin, const String &p_app_path_name, Ref<DirAccess> &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, - const String &p_ent_path); + const String &p_ent_path, const String &p_helper_ent_path); Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name); Error _create_pkg(const Ref<EditorExportPreset> &p_preset, const String &p_pkg_path, const String &p_app_path_name); Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path); diff --git a/platform/macos/export/macho.cpp b/platform/macos/export/macho.cpp index c7556c1964..a829774a88 100644 --- a/platform/macos/export/macho.cpp +++ b/platform/macos/export/macho.cpp @@ -105,6 +105,26 @@ bool MachO::is_macho(const String &p_path) { return (magic == 0xcefaedfe || magic == 0xfeedface || magic == 0xcffaedfe || magic == 0xfeedfacf); } +uint32_t MachO::get_filetype(const String &p_path) { + Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("MachO: Can't open file: \"%s\".", p_path)); + uint32_t magic = fa->get_32(); + MachHeader mach_header; + + // Read MachO header. + if (magic == 0xcefaedfe || magic == 0xfeedface) { + // Thin 32-bit binary. + fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader)); + } else if (magic == 0xcffaedfe || magic == 0xfeedfacf) { + // Thin 64-bit binary. + fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader)); + fa->get_32(); // Skip extra reserved field. + } else { + ERR_FAIL_V_MSG(0, vformat("MachO: File is not a valid MachO binary: \"%s\".", p_path)); + } + return mach_header.filetype; +} + bool MachO::open_file(const String &p_path) { fa = FileAccess::open(p_path, FileAccess::READ_WRITE); ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path)); diff --git a/platform/macos/export/macho.h b/platform/macos/export/macho.h index 37975f0820..a84de7de60 100644 --- a/platform/macos/export/macho.h +++ b/platform/macos/export/macho.h @@ -181,6 +181,7 @@ class MachO : public RefCounted { public: static bool is_macho(const String &p_path); + static uint32_t get_filetype(const String &p_path); bool open_file(const String &p_path); diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm index 93bba84783..68a7288ad4 100644 --- a/platform/macos/godot_content_view.mm +++ b/platform/macos/godot_content_view.mm @@ -313,7 +313,7 @@ } DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); - if (!wd.drop_files_callback.is_null()) { + if (wd.drop_files_callback.is_valid()) { Vector<String> files; NSPasteboard *pboard = [sender draggingPasteboard]; diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm index 2d83b46007..7749debfd6 100644 --- a/platform/macos/godot_window_delegate.mm +++ b/platform/macos/godot_window_delegate.mm @@ -268,7 +268,7 @@ ds->window_resize(window_id, wd.size.width, wd.size.height); - if (!wd.rect_changed_callback.is_null()) { + if (wd.rect_changed_callback.is_valid()) { wd.rect_changed_callback.call(Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id))); } } @@ -291,7 +291,7 @@ DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); ds->release_pressed_events(); - if (!wd.rect_changed_callback.is_null()) { + if (wd.rect_changed_callback.is_valid()) { wd.rect_changed_callback.call(Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id))); } } diff --git a/platform/web/SCsub b/platform/web/SCsub index 3e0cc9ac4a..bc5893ab3a 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -1,5 +1,7 @@ #!/usr/bin/env python +from methods import print_error + Import("env") # The HTTP server "targets". Run with "scons p=web serve", or "scons p=web run" @@ -11,7 +13,7 @@ if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS: try: port = int(port) except Exception: - print("GODOT_WEB_TEST_PORT must be a valid integer") + print_error("GODOT_WEB_TEST_PORT must be a valid integer") sys.exit(255) serve(env.Dir(env.GetTemplateZipPath()).abspath, port, "run" in COMMAND_LINE_TARGETS) sys.exit(0) diff --git a/platform/web/api/web_tools_editor_plugin.h b/platform/web/api/web_tools_editor_plugin.h index ac0d5e20ec..2902f60f24 100644 --- a/platform/web/api/web_tools_editor_plugin.h +++ b/platform/web/api/web_tools_editor_plugin.h @@ -34,7 +34,7 @@ #if defined(TOOLS_ENABLED) && defined(WEB_ENABLED) #include "core/io/zip_io.h" -#include "editor/editor_plugin.h" +#include "editor/plugins/editor_plugin.h" class WebToolsEditorPlugin : public EditorPlugin { GDCLASS(WebToolsEditorPlugin, EditorPlugin); diff --git a/platform/web/detect.py b/platform/web/detect.py index 2d2cc288a1..ccd884b225 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -10,7 +10,7 @@ from emscripten_helpers import ( create_template_zip, get_template_zip_path, ) -from methods import get_compiler_version +from methods import print_warning, print_error, get_compiler_version from SCons.Util import WhereIs from typing import TYPE_CHECKING @@ -85,16 +85,16 @@ def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["wasm32"] if env["arch"] not in supported_arches: - print( + print_error( 'Unsupported CPU architecture "%s" for Web. Supported architectures are: %s.' % (env["arch"], ", ".join(supported_arches)) ) - sys.exit() + sys.exit(255) try: env["initial_memory"] = int(env["initial_memory"]) except Exception: - print("Initial memory must be a valid integer") + print_error("Initial memory must be a valid integer") sys.exit(255) ## Build type @@ -109,7 +109,7 @@ def configure(env: "SConsEnvironment"): env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"]) if env.editor_build and env["initial_memory"] < 64: - print('Note: Forcing "initial_memory=64" as it is required for the web editor.') + print("Note: Forcing `initial_memory=64` as it is required for the web editor.") env["initial_memory"] = 64 env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]]) @@ -227,7 +227,7 @@ def configure(env: "SConsEnvironment"): env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"]) env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"]) elif env["proxy_to_pthread"]: - print('"threads=no" support requires "proxy_to_pthread=no", disabling proxy to pthread.') + print_warning('"threads=no" support requires "proxy_to_pthread=no", disabling proxy to pthread.') env["proxy_to_pthread"] = False if env["lto"] != "none": @@ -240,11 +240,11 @@ def configure(env: "SConsEnvironment"): if env["dlink_enabled"]: if env["proxy_to_pthread"]: - print("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.") + print_warning("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.") env["proxy_to_pthread"] = False if cc_semver < (3, 1, 14): - print("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) + print_error("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver) sys.exit(255) env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"]) diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index 06f5eb82f7..a51c161b9c 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -58,7 +58,7 @@ DisplayServerWeb *DisplayServerWeb::get_singleton() { // Window (canvas) bool DisplayServerWeb::check_size_force_redraw() { bool size_changed = godot_js_display_size_update() != 0; - if (size_changed && !rect_changed_callback.is_null()) { + if (size_changed && rect_changed_callback.is_valid()) { Size2i window_size = window_get_size(); Variant size = Rect2i(Point2i(), window_size); // TODO use window_get_position if implemented. rect_changed_callback.call(size); @@ -109,7 +109,7 @@ void DisplayServerWeb::_drop_files_js_callback(const Vector<String> &p_files) { if (!ds) { ERR_FAIL_MSG("Unable to drop files because the DisplayServer is not active"); } - if (ds->drop_files_callback.is_null()) { + if (!ds->drop_files_callback.is_valid()) { return; } ds->drop_files_callback.call(p_files); @@ -129,7 +129,7 @@ void DisplayServerWeb::request_quit_callback() { void DisplayServerWeb::_request_quit_callback() { DisplayServerWeb *ds = get_singleton(); - if (ds && !ds->window_event_callback.is_null()) { + if (ds && ds->window_event_callback.is_valid()) { Variant event = int(DisplayServer::WINDOW_EVENT_CLOSE_REQUEST); ds->window_event_callback.call(event); } @@ -722,7 +722,7 @@ void DisplayServerWeb::vk_input_text_callback(const char *p_text, int p_cursor) void DisplayServerWeb::_vk_input_text_callback(const String &p_text, int p_cursor) { DisplayServerWeb *ds = DisplayServerWeb::get_singleton(); - if (!ds || ds->input_text_callback.is_null()) { + if (!ds || !ds->input_text_callback.is_valid()) { return; } // Call input_text @@ -972,7 +972,7 @@ void DisplayServerWeb::_send_window_event_callback(int p_notification) { if (godot_js_is_ime_focused() && (p_notification == DisplayServer::WINDOW_EVENT_FOCUS_IN || p_notification == DisplayServer::WINDOW_EVENT_FOCUS_OUT)) { return; } - if (!ds->window_event_callback.is_null()) { + if (ds->window_event_callback.is_valid()) { Variant event = int(p_notification); ds->window_event_callback.call(event); } diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp index d72ad8331b..a2c83d2f2b 100644 --- a/platform/web/javascript_bridge_singleton.cpp +++ b/platform/web/javascript_bridge_singleton.cpp @@ -248,7 +248,7 @@ Variant JavaScriptObjectImpl::callp(const StringName &p_method, const Variant ** void JavaScriptObjectImpl::callback(void *p_ref, int p_args_id, int p_argc) { const JavaScriptObjectImpl *obj = (JavaScriptObjectImpl *)p_ref; - ERR_FAIL_COND_MSG(obj->_callable.is_null(), "JavaScript callback failed."); + ERR_FAIL_COND_MSG(!obj->_callable.is_valid(), "JavaScript callback failed."); Vector<const Variant *> argp; Array arg_arr; diff --git a/platform/web/js/engine/features.js b/platform/web/js/engine/features.js index 81bc82f3c6..263ea6ac88 100644 --- a/platform/web/js/engine/features.js +++ b/platform/web/js/engine/features.js @@ -72,8 +72,7 @@ const Features = { // eslint-disable-line no-unused-vars * * @returns {Array<string>} A list of human-readable missing features. * @function Engine.getMissingFeatures - * @typedef {{ threads: boolean }} SupportedFeatures - * @param {SupportedFeatures} supportedFeatures + * @param {{threads: (boolean|undefined)}} supportedFeatures */ getMissingFeatures: function (supportedFeatures = {}) { const { diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 159a273e70..435c501956 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -10,7 +10,6 @@ sources = [] common_win = [ "godot_windows.cpp", - "crash_handler_windows.cpp", "os_windows.cpp", "display_server_windows.cpp", "key_mapping_windows.cpp", @@ -25,6 +24,11 @@ common_win = [ "rendering_context_driver_vulkan_windows.cpp", ] +if env.msvc: + common_win += ["crash_handler_windows_seh.cpp"] +else: + common_win += ["crash_handler_windows_signal.cpp"] + common_win_wrap = [ "console_wrapper_windows.cpp", ] diff --git a/platform/windows/console_wrapper_windows.cpp b/platform/windows/console_wrapper_windows.cpp index de751580b7..133711a9ea 100644 --- a/platform/windows/console_wrapper_windows.cpp +++ b/platform/windows/console_wrapper_windows.cpp @@ -136,6 +136,10 @@ int main(int argc, char *argv[]) { STARTUPINFOW si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); WCHAR new_command_line[32767]; _snwprintf_s(new_command_line, 32767, _TRUNCATE, L"%ls %ls", exe_name, PathGetArgsW(GetCommandLineW())); diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h index 3871210977..a0a0b610d0 100644 --- a/platform/windows/crash_handler_windows.h +++ b/platform/windows/crash_handler_windows.h @@ -35,12 +35,15 @@ #include <windows.h> // Crash handler exception only enabled with MSVC -#if defined(DEBUG_ENABLED) && defined(_MSC_VER) +#if defined(DEBUG_ENABLED) #define CRASH_HANDLER_EXCEPTION 1 +#ifdef _MSC_VER extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep); #endif +#endif + class CrashHandler { bool disabled; diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows_seh.cpp index 133d36aa0d..2abe285d31 100644 --- a/platform/windows/crash_handler_windows.cpp +++ b/platform/windows/crash_handler_windows_seh.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* crash_handler_windows.cpp */ +/* crash_handler_windows_seh.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/platform/windows/crash_handler_windows_signal.cpp b/platform/windows/crash_handler_windows_signal.cpp new file mode 100644 index 0000000000..e11a60bdc7 --- /dev/null +++ b/platform/windows/crash_handler_windows_signal.cpp @@ -0,0 +1,205 @@ +/**************************************************************************/ +/* crash_handler_windows_signal.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "crash_handler_windows.h" + +#include "core/config/project_settings.h" +#include "core/os/os.h" +#include "core/string/print_string.h" +#include "core/version.h" +#include "main/main.h" + +#ifdef CRASH_HANDLER_EXCEPTION + +#include <cxxabi.h> +#include <signal.h> +#include <algorithm> +#include <iterator> +#include <string> +#include <vector> + +#include <psapi.h> + +#include "thirdparty/libbacktrace/backtrace.h" + +struct CrashHandlerData { + int64_t index = 0; + backtrace_state *state = nullptr; + int64_t offset = 0; +}; + +int symbol_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) { + CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data); + if (!function) { + return 0; + } + + char fname[1024]; + snprintf(fname, 1024, "%s", function); + + if (function[0] == '_') { + int status; + char *demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status); + + if (status == 0 && demangled) { + snprintf(fname, 1024, "%s", demangled); + } + + if (demangled) { + free(demangled); + } + } + + print_error(vformat("[%d] %s (%s:%d)", ch_data->index++, String::utf8(fname), String::utf8(filename), lineno)); + return 0; +} + +void error_callback(void *data, const char *msg, int errnum) { + CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data); + if (ch_data->index == 0) { + print_error(vformat("Error(%d): %s", errnum, String::utf8(msg))); + } else { + print_error(vformat("[%d] error(%d): %s", ch_data->index++, errnum, String::utf8(msg))); + } +} + +int trace_callback(void *data, uintptr_t pc) { + CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data); + backtrace_pcinfo(ch_data->state, pc - ch_data->offset, &symbol_callback, &error_callback, data); + return 0; +} + +int64_t get_image_base(const String &p_path) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { + return 0; + } + { + f->seek(0x3c); + uint32_t pe_pos = f->get_32(); + + f->seek(pe_pos); + uint32_t magic = f->get_32(); + if (magic != 0x00004550) { + return 0; + } + } + int64_t opt_header_pos = f->get_position() + 0x14; + f->seek(opt_header_pos); + + uint16_t opt_header_magic = f->get_16(); + if (opt_header_magic == 0x10B) { + f->seek(opt_header_pos + 0x1C); + return f->get_32(); + } else if (opt_header_magic == 0x20B) { + f->seek(opt_header_pos + 0x18); + return f->get_64(); + } else { + return 0; + } +} + +extern void CrashHandlerException(int signal) { + CrashHandlerData data; + + if (OS::get_singleton() == nullptr || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) { + return; + } + + String msg; + const ProjectSettings *proj_settings = ProjectSettings::get_singleton(); + if (proj_settings) { + msg = proj_settings->get("debug/settings/crash_handler/message"); + } + + // Tell MainLoop about the crash. This can be handled by users too in Node. + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); + } + + print_error("\n================================================================"); + print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, signal)); + + // Print the engine version just before, so that people are reminded to include the version in backtrace reports. + if (String(VERSION_HASH).is_empty()) { + print_error(vformat("Engine version: %s", VERSION_FULL_NAME)); + } else { + print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, VERSION_HASH)); + } + print_error(vformat("Dumping the backtrace. %s", msg)); + + String _execpath = OS::get_singleton()->get_executable_path(); + + // Load process and image info to determine ASLR addresses offset. + MODULEINFO mi; + GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &mi, sizeof(mi)); + int64_t image_mem_base = reinterpret_cast<int64_t>(mi.lpBaseOfDll); + int64_t image_file_base = get_image_base(_execpath); + data.offset = image_mem_base - image_file_base; + + data.state = backtrace_create_state(_execpath.utf8().get_data(), 0, &error_callback, reinterpret_cast<void *>(&data)); + if (data.state != nullptr) { + data.index = 1; + backtrace_simple(data.state, 1, &trace_callback, &error_callback, reinterpret_cast<void *>(&data)); + } + + print_error("-- END OF BACKTRACE --"); + print_error("================================================================"); +} +#endif + +CrashHandler::CrashHandler() { + disabled = false; +} + +CrashHandler::~CrashHandler() { +} + +void CrashHandler::disable() { + if (disabled) { + return; + } + +#if defined(CRASH_HANDLER_EXCEPTION) + signal(SIGSEGV, nullptr); + signal(SIGFPE, nullptr); + signal(SIGILL, nullptr); +#endif + + disabled = true; +} + +void CrashHandler::initialize() { +#if defined(CRASH_HANDLER_EXCEPTION) + signal(SIGSEGV, CrashHandlerException); + signal(SIGFPE, CrashHandlerException); + signal(SIGILL, CrashHandlerException); +#endif +} diff --git a/platform/windows/detect.py b/platform/windows/detect.py index f34d479345..93eb34001e 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -2,6 +2,7 @@ import methods import os import subprocess import sys +from methods import print_warning, print_error from platform_methods import detect_arch from typing import TYPE_CHECKING @@ -293,16 +294,14 @@ def setup_msvc_manual(env: "SConsEnvironment"): env_arch = detect_build_env_arch() if env["arch"] != env_arch: - print( - """ - Arch argument (%s) is not matching Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings) that is being used to run SCons (%s). - Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you. - """ + print_error( + "Arch argument (%s) is not matching Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings) that is being used to run SCons (%s).\n" + "Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you." % (env["arch"], env_arch) ) - sys.exit(200) + sys.exit(255) - print("Found MSVC, arch %s" % (env_arch)) + print("Using VCVARS-determined MSVC, arch %s" % (env_arch)) def setup_msvc_auto(env: "SConsEnvironment"): @@ -338,7 +337,7 @@ def setup_msvc_auto(env: "SConsEnvironment"): env.Tool("mssdk") # we want the MS SDK # Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015 - print("Found MSVC version %s, arch %s" % (env["MSVC_VERSION"], env["arch"])) + print("Using SCons-detected MSVC version %s, arch %s" % (env["MSVC_VERSION"], env["arch"])) def setup_mingw(env: "SConsEnvironment"): @@ -346,32 +345,24 @@ def setup_mingw(env: "SConsEnvironment"): env_arch = detect_build_env_arch() if os.getenv("MSYSTEM") == "MSYS": - print( - """ - Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64). - """ + print_error( + "Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64)." ) - sys.exit(201) + sys.exit(255) if env_arch != "" and env["arch"] != env_arch: - print( - """ - Arch argument (%s) is not matching MSYS2 console/environment that is being used to run SCons (%s). - Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSYS2 compiler will be executed and inform you. - """ + print_error( + "Arch argument (%s) is not matching MSYS2 console/environment that is being used to run SCons (%s).\n" + "Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSYS2 compiler will be executed and inform you." % (env["arch"], env_arch) ) - sys.exit(202) + sys.exit(255) if not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]) and not try_cmd( "clang --version", env["mingw_prefix"], env["arch"] ): - print( - """ - No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path. - """ - ) - sys.exit(202) + print_error("No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.") + sys.exit(255) print("Using MinGW, arch %s" % (env["arch"])) @@ -454,10 +445,10 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): if os.getenv("WindowsSdkDir") is not None: env.Prepend(CPPPATH=[str(os.getenv("WindowsSdkDir")) + "/Include"]) else: - print("Missing environment variable: WindowsSdkDir") + print_warning("Missing environment variable: WindowsSdkDir") if int(env["target_win_version"], 16) < 0x0601: - print("`target_win_version` should be 0x0601 or higher (Windows 7).") + print_error("`target_win_version` should be 0x0601 or higher (Windows 7).") sys.exit(255) env.AppendUnique( @@ -515,10 +506,10 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): if env["d3d12"]: # Check whether we have d3d12 dependencies installed. if not os.path.exists(env["mesa_libs"]): - print("The Direct3D 12 rendering driver requires dependencies to be installed.") - print(r"You can install them by running `python misc\scripts\install_d3d12_sdk_windows.py`.") - print("See the documentation for more information:") - print( + print_error( + "The Direct3D 12 rendering driver requires dependencies to be installed.\n" + "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n" + "See the documentation for more information:\n\t" "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html" ) sys.exit(255) @@ -557,13 +548,16 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): LIBS += ["dxgi", "d3d9", "d3d11"] env.Prepend(CPPPATH=["#thirdparty/angle/include"]) + if env["target"] in ["editor", "template_debug"]: + LIBS += ["psapi", "dbghelp"] + env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) if vcvars_msvc_config: if os.getenv("WindowsSdkDir") is not None: env.Append(LIBPATH=[str(os.getenv("WindowsSdkDir")) + "/Lib"]) else: - print("Missing environment variable: WindowsSdkDir") + print_warning("Missing environment variable: WindowsSdkDir") ## LTO @@ -572,7 +566,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): if env["lto"] != "none": if env["lto"] == "thin": - print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") + print_error("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") sys.exit(255) env.AppendUnique(CCFLAGS=["/GL"]) env.AppendUnique(ARFLAGS=["/LTCG"]) @@ -690,7 +684,7 @@ def configure_mingw(env: "SConsEnvironment"): ## Compile flags if int(env["target_win_version"], 16) < 0x0601: - print("`target_win_version` should be 0x0601 or higher (Windows 7).") + print_error("`target_win_version` should be 0x0601 or higher (Windows 7).") sys.exit(255) if not env["use_llvm"]: @@ -744,10 +738,10 @@ def configure_mingw(env: "SConsEnvironment"): if env["d3d12"]: # Check whether we have d3d12 dependencies installed. if not os.path.exists(env["mesa_libs"]): - print("The Direct3D 12 rendering driver requires dependencies to be installed.") - print(r"You can install them by running `python misc\scripts\install_d3d12_sdk_windows.py`.") - print("See the documentation for more information:") - print( + print_error( + "The Direct3D 12 rendering driver requires dependencies to be installed.\n" + "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n" + "See the documentation for more information:\n\t" "https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html" ) sys.exit(255) @@ -794,11 +788,11 @@ def configure(env: "SConsEnvironment"): # Validate arch. supported_arches = ["x86_32", "x86_64", "arm32", "arm64"] if env["arch"] not in supported_arches: - print( + print_error( 'Unsupported CPU architecture "%s" for Windows. Supported architectures are: %s.' % (env["arch"], ", ".join(supported_arches)) ) - sys.exit() + sys.exit(255) # At this point the env has been set up with basic tools/compilers. env.Prepend(CPPPATH=["#platform/windows"]) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index b6b713687f..ebae00017b 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -545,7 +545,7 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title result->Release(); } } - if (!p_callback.is_null()) { + if (p_callback.is_valid()) { if (p_options_in_cb) { Variant v_result = true; Variant v_files = file_names; @@ -574,7 +574,7 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title } } } else { - if (!p_callback.is_null()) { + if (p_callback.is_valid()) { if (p_options_in_cb) { Variant v_result = false; Variant v_files = Vector<String>(); @@ -2556,7 +2556,7 @@ Error DisplayServerWindows::dialog_show(String p_title, String p_description, Ve int button_pressed; if (task_dialog_indirect && SUCCEEDED(task_dialog_indirect(&config, &button_pressed, nullptr, nullptr))) { - if (!p_callback.is_null()) { + if (p_callback.is_valid()) { Variant button = button_pressed; const Variant *args[1] = { &button }; Variant ret; @@ -4591,7 +4591,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } if (rect_changed) { - if (!window.rect_changed_callback.is_null()) { + if (window.rect_changed_callback.is_valid()) { window.rect_changed_callback.call(Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height)); } @@ -4803,7 +4803,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA files.push_back(file); } - if (files.size() && !windows[window_id].drop_files_callback.is_null()) { + if (files.size() && windows[window_id].drop_files_callback.is_valid()) { windows[window_id].drop_files_callback.call(files); } } break; diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp index 5f41b4e568..486c3120fc 100644 --- a/platform/windows/godot_windows.cpp +++ b/platform/windows/godot_windows.cpp @@ -215,7 +215,7 @@ int main(int argc, char **argv) { // _argc and _argv are ignored // we are going to use the WideChar version of them instead -#ifdef CRASH_HANDLER_EXCEPTION +#if defined(CRASH_HANDLER_EXCEPTION) && defined(_MSC_VER) __try { return _main(); } __except (CrashHandlerException(GetExceptionInformation())) { diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index abed93d414..157702655e 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -115,7 +115,24 @@ void RedirectStream(const char *p_file_name, const char *p_mode, FILE *p_cpp_str } void RedirectIOToConsole() { + // Save current handles. + HANDLE h_stdin = GetStdHandle(STD_INPUT_HANDLE); + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE h_stderr = GetStdHandle(STD_ERROR_HANDLE); + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + // Restore redirection (Note: if not redirected it's NULL handles not INVALID_HANDLE_VALUE). + if (h_stdin != 0) { + SetStdHandle(STD_INPUT_HANDLE, h_stdin); + } + if (h_stdout != 0) { + SetStdHandle(STD_OUTPUT_HANDLE, h_stdout); + } + if (h_stderr != 0) { + SetStdHandle(STD_ERROR_HANDLE, h_stderr); + } + + // Update file handles. RedirectStream("CONIN$", "r", stdin, STD_INPUT_HANDLE); RedirectStream("CONOUT$", "w", stdout, STD_OUTPUT_HANDLE); RedirectStream("CONOUT$", "w", stderr, STD_ERROR_HANDLE); @@ -173,10 +190,6 @@ void OS_Windows::initialize() { add_error_handler(&error_handlers); #endif -#ifndef WINDOWS_SUBSYSTEM_CONSOLE - RedirectIOToConsole(); -#endif - FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM); @@ -1521,10 +1534,10 @@ void OS_Windows::unset_environment(const String &p_var) const { } String OS_Windows::get_stdin_string() { - WCHAR buff[1024]; + char buff[1024]; DWORD count = 0; - if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), buff, 1024, &count, nullptr)) { - return String::utf16((const char16_t *)buff, count); + if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), buff, 1024, &count, nullptr)) { + return String::utf8((const char *)buff, count); } return String(); @@ -1908,6 +1921,13 @@ String OS_Windows::get_system_ca_certificates() { OS_Windows::OS_Windows(HINSTANCE _hInstance) { hInstance = _hInstance; +#ifndef WINDOWS_SUBSYSTEM_CONSOLE + RedirectIOToConsole(); +#endif + + SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); #ifdef WASAPI_ENABLED diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp index 6881a75596..6fc33afe99 100644 --- a/platform/windows/windows_terminal_logger.cpp +++ b/platform/windows/windows_terminal_logger.cpp @@ -53,26 +53,12 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er } buf[len] = 0; - int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, nullptr, 0); - if (wlen < 0) { - return; - } - - wchar_t *wbuf = (wchar_t *)memalloc((len + 1) * sizeof(wchar_t)); - ERR_FAIL_NULL_MSG(wbuf, "Out of memory."); - MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen); - wbuf[wlen] = 0; - - if (p_err) { - fwprintf(stderr, L"%ls", wbuf); - } else { - wprintf(L"%ls", wbuf); - } - - memfree(wbuf); + DWORD written = 0; + HANDLE h = p_err ? GetStdHandle(STD_ERROR_HANDLE) : GetStdHandle(STD_OUTPUT_HANDLE); + WriteFile(h, &buf[0], len, &written, nullptr); #ifdef DEBUG_ENABLED - fflush(stdout); + FlushFileBuffers(h); #endif } |
