summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/SCsub8
-rw-r--r--platform/android/android_keys_utils.cpp4
-rw-r--r--platform/android/android_keys_utils.h131
-rw-r--r--platform/android/api/api.cpp6
-rw-r--r--platform/android/api/api.h4
-rw-r--r--platform/android/api/java_class_wrapper.h15
-rw-r--r--platform/android/api/jni_singleton.h12
-rw-r--r--platform/android/audio_driver_jandroid.cpp18
-rw-r--r--platform/android/audio_driver_jandroid.h4
-rw-r--r--platform/android/audio_driver_opensl.cpp6
-rw-r--r--platform/android/audio_driver_opensl.h17
-rw-r--r--platform/android/detect.py75
-rw-r--r--platform/android/dir_access_jandroid.cpp24
-rw-r--r--platform/android/dir_access_jandroid.h4
-rw-r--r--platform/android/display_server_android.cpp300
-rw-r--r--platform/android/display_server_android.h50
-rw-r--r--platform/android/export/export.cpp1469
-rw-r--r--platform/android/export/export.h4
-rw-r--r--platform/android/export/gradle_export_util.h315
-rw-r--r--platform/android/file_access_android.cpp12
-rw-r--r--platform/android/file_access_android.h13
-rw-r--r--platform/android/file_access_jandroid.cpp197
-rw-r--r--platform/android/file_access_jandroid.h83
-rw-r--r--platform/android/java/app/AndroidManifest.xml10
-rw-r--r--platform/android/java/app/build.gradle83
-rw-r--r--platform/android/java/app/config.gradle151
-rw-r--r--platform/android/java/app/res/drawable/splash.pngbin0 -> 14766 bytes
-rw-r--r--platform/android/java/app/res/drawable/splash_bg_color.pngbin0 -> 1360 bytes
-rw-r--r--platform/android/java/app/res/drawable/splash_drawable.xml12
-rw-r--r--platform/android/java/app/res/values-ar/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-ar/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-bg/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-bg/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-ca/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-ca/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-cs/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-cs/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-da/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-da/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-de/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-de/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-el/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-el/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-en/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-en/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-es-rES/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-es-rES/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-es/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-es/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-fa/godot_project_name_string.xml5
-rw-r--r--platform/android/java/app/res/values-fi/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-fi/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-fr/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-fr/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-hi/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-hi/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-hr/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-hr/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-hu/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-hu/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-in/godot_project_name_string.xml5
-rw-r--r--platform/android/java/app/res/values-it/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-it/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-iw/godot_project_name_string.xml5
-rw-r--r--platform/android/java/app/res/values-ja/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-ja/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-ko/godot_project_name_string.xml5
-rw-r--r--platform/android/java/app/res/values-lt/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-lt/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-lv/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-lv/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-nb/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-nb/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-nl/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-nl/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-pl/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-pl/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-pt/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-pt/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-ro/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-ro/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-ru/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-ru/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-sk/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-sk/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-sl/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-sl/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-sr/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-sr/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-sv/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-sv/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-th/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-th/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-tl/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-tl/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-tr/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-tr/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-uk/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-uk/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-vi/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-vi/strings.xml)3
-rw-r--r--platform/android/java/app/res/values-zh-rHK/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-zh-rHK/strings.xml)1
-rw-r--r--platform/android/java/app/res/values-zh-rTW/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-zh-rTW/strings.xml)1
-rw-r--r--platform/android/java/app/res/values-zh/godot_project_name_string.xml (renamed from platform/android/java/lib/res/values-zh-rCN/strings.xml)1
-rw-r--r--platform/android/java/app/res/values/godot_project_name_string.xml5
-rw-r--r--platform/android/java/app/res/values/themes.xml10
-rw-r--r--platform/android/java/app/src/com/godot/game/GodotApp.java15
-rw-r--r--platform/android/java/build.gradle18
-rw-r--r--platform/android/java/gradle.properties2
-rw-r--r--platform/android/java/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--platform/android/java/lib/AndroidManifest.xml5
-rw-r--r--platform/android/java/lib/build.gradle58
-rw-r--r--platform/android/java/lib/res/layout/downloading_expansion.xml2
-rw-r--r--platform/android/java/lib/res/layout/godot_app_layout.xml5
-rw-r--r--platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml2
-rw-r--r--platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml2
-rw-r--r--platform/android/java/lib/res/values-fa/strings.xml1
-rw-r--r--platform/android/java/lib/res/values-in/strings.xml4
-rw-r--r--platform/android/java/lib/res/values-iw/strings.xml4
-rw-r--r--platform/android/java/lib/res/values-ko/strings.xml3
-rw-r--r--platform/android/java/lib/res/values/dimens.xml4
-rw-r--r--platform/android/java/lib/res/values/strings.xml2
-rw-r--r--platform/android/java/lib/res/values/styles.xml2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Dictionary.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java (renamed from platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java)83
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java411
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java39
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java45
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotInstrumentation.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java37
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java36
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java53
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java351
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java17
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java121
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java67
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java11
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java9
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java19
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt18
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt5
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java4
-rw-r--r--platform/android/java/nativeSrcsConfigs/AndroidManifest.xml2
-rw-r--r--platform/android/java/nativeSrcsConfigs/CMakeLists.txt (renamed from platform/android/java/lib/CMakeLists.txt)1
-rw-r--r--platform/android/java/nativeSrcsConfigs/README.md4
-rw-r--r--platform/android/java/nativeSrcsConfigs/build.gradle56
-rw-r--r--platform/android/java/plugins/godotpayment/build.gradle32
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml11
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl281
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java116
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java258
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java93
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java70
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java403
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java118
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java140
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java143
-rw-r--r--platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java230
-rw-r--r--platform/android/java/settings.gradle2
-rw-r--r--platform/android/java_class_wrapper.cpp12
-rw-r--r--platform/android/java_godot_io_wrapper.cpp58
-rw-r--r--platform/android/java_godot_io_wrapper.h8
-rw-r--r--platform/android/java_godot_lib_jni.cpp94
-rw-r--r--platform/android/java_godot_lib_jni.h23
-rw-r--r--platform/android/java_godot_view_wrapper.cpp (renamed from platform/javascript/native/id_handler.js)55
-rw-r--r--platform/android/java_godot_view_wrapper.h (renamed from platform/haiku/context_gl_haiku.h)48
-rw-r--r--platform/android/java_godot_wrapper.cpp123
-rw-r--r--platform/android/java_godot_wrapper.h15
-rw-r--r--platform/android/jni_utils.cpp4
-rw-r--r--platform/android/jni_utils.h8
-rw-r--r--platform/android/logo.pngbin1459 -> 951 bytes
-rw-r--r--platform/android/net_socket_android.cpp10
-rw-r--r--platform/android/net_socket_android.h4
-rw-r--r--platform/android/os_android.cpp22
-rw-r--r--platform/android/os_android.h46
-rw-r--r--platform/android/platform_config.h4
-rw-r--r--platform/android/plugin/godot_plugin_config.h160
-rw-r--r--platform/android/plugin/godot_plugin_jni.cpp29
-rw-r--r--platform/android/plugin/godot_plugin_jni.h14
-rw-r--r--platform/android/string_android.h10
-rw-r--r--platform/android/thread_jandroid.cpp119
-rw-r--r--platform/android/thread_jandroid.h46
-rw-r--r--platform/android/vulkan/vulkan_context_android.cpp5
-rw-r--r--platform/android/vulkan/vulkan_context_android.h4
-rw-r--r--platform/haiku/SCsub25
-rw-r--r--platform/haiku/audio_driver_media_kit.cpp135
-rw-r--r--platform/haiku/context_gl_haiku.cpp83
-rw-r--r--platform/haiku/detect.py158
-rw-r--r--platform/haiku/godot.rdef60
-rw-r--r--platform/haiku/haiku_direct_window.cpp363
-rw-r--r--platform/haiku/haiku_direct_window.h89
-rw-r--r--platform/haiku/haiku_gl_view.cpp47
-rw-r--r--platform/haiku/key_mapping_haiku.cpp249
-rw-r--r--platform/haiku/logo.pngbin1265 -> 0 bytes
-rw-r--r--platform/haiku/os_haiku.cpp358
-rw-r--r--platform/haiku/os_haiku.h123
-rw-r--r--platform/iphone/SCsub22
-rw-r--r--platform/iphone/app_delegate.h23
-rw-r--r--platform/iphone/app_delegate.mm667
-rw-r--r--platform/iphone/detect.py112
-rw-r--r--platform/iphone/device_metrics.h (renamed from platform/haiku/platform_config.h)15
-rw-r--r--platform/iphone/device_metrics.m152
-rw-r--r--platform/iphone/display_layer.h58
-rw-r--r--platform/iphone/display_layer.mm183
-rw-r--r--platform/iphone/display_server_iphone.h202
-rw-r--r--platform/iphone/display_server_iphone.mm648
-rw-r--r--platform/iphone/export/export.cpp937
-rw-r--r--platform/iphone/export/export.h4
-rw-r--r--platform/iphone/game_center.mm387
-rw-r--r--platform/iphone/gl_view.h123
-rw-r--r--platform/iphone/gl_view.mm702
-rw-r--r--platform/iphone/godot_app_delegate.h41
-rw-r--r--platform/iphone/godot_app_delegate.m467
-rw-r--r--platform/iphone/godot_iphone.mm (renamed from platform/iphone/godot_iphone.cpp)61
-rw-r--r--platform/iphone/godot_view.h (renamed from platform/uwp/thread_uwp.h)45
-rw-r--r--platform/iphone/godot_view.mm467
-rw-r--r--platform/iphone/godot_view_gesture_recognizer.h (renamed from platform/iphone/icloud.h)46
-rw-r--r--platform/iphone/godot_view_gesture_recognizer.mm171
-rw-r--r--platform/iphone/godot_view_renderer.h (renamed from platform/haiku/godot_haiku.cpp)29
-rw-r--r--platform/iphone/godot_view_renderer.mm117
-rw-r--r--platform/iphone/icloud.mm359
-rw-r--r--platform/iphone/in_app_store.mm326
-rw-r--r--platform/iphone/ios.h6
-rw-r--r--platform/iphone/ios.mm43
-rw-r--r--platform/iphone/joypad_iphone.h (renamed from platform/haiku/haiku_gl_view.h)31
-rw-r--r--platform/iphone/joypad_iphone.mm345
-rw-r--r--platform/iphone/keyboard_input_view.h (renamed from platform/haiku/haiku_application.h)20
-rw-r--r--platform/iphone/keyboard_input_view.mm195
-rw-r--r--platform/iphone/logo.pngbin1489 -> 1297 bytes
-rw-r--r--platform/iphone/main.m22
-rw-r--r--platform/iphone/native_video_view.h (renamed from platform/haiku/key_mapping_haiku.h)23
-rw-r--r--platform/iphone/native_video_view.m266
-rw-r--r--platform/iphone/os_iphone.cpp632
-rw-r--r--platform/iphone/os_iphone.h172
-rw-r--r--platform/iphone/os_iphone.mm330
-rw-r--r--platform/iphone/platform_config.h13
-rw-r--r--platform/iphone/plugin/godot_plugin_config.h292
-rw-r--r--platform/iphone/view_controller.h25
-rw-r--r--platform/iphone/view_controller.mm228
-rw-r--r--platform/iphone/vulkan_context_iphone.h9
-rw-r--r--platform/iphone/vulkan_context_iphone.mm24
-rw-r--r--platform/javascript/.eslintrc.engine.js11
-rw-r--r--platform/javascript/.eslintrc.js43
-rw-r--r--platform/javascript/.eslintrc.libs.js25
-rw-r--r--platform/javascript/SCsub106
-rw-r--r--platform/javascript/api/api.cpp8
-rw-r--r--platform/javascript/api/api.h4
-rw-r--r--platform/javascript/api/javascript_eval.h6
-rw-r--r--platform/javascript/api/javascript_tools_editor_plugin.cpp135
-rw-r--r--platform/javascript/api/javascript_tools_editor_plugin.h (renamed from platform/iphone/in_app_store.h)53
-rw-r--r--platform/javascript/audio_driver_javascript.cpp394
-rw-r--r--platform/javascript/audio_driver_javascript.h77
-rw-r--r--platform/javascript/detect.py120
-rw-r--r--platform/javascript/display_server_javascript.cpp517
-rw-r--r--platform/javascript/display_server_javascript.h156
-rw-r--r--platform/javascript/dom_keys.inc591
-rw-r--r--platform/javascript/emscripten_helpers.py44
-rw-r--r--platform/javascript/engine/engine.js245
-rw-r--r--platform/javascript/engine/preloader.js139
-rw-r--r--platform/javascript/engine/utils.js51
-rw-r--r--platform/javascript/export/export.cpp216
-rw-r--r--platform/javascript/export/export.h4
-rw-r--r--platform/javascript/godot_audio.h (renamed from platform/iphone/game_center.h)66
-rw-r--r--platform/javascript/godot_js.h100
-rw-r--r--platform/javascript/http_client.h.inc4
-rw-r--r--platform/javascript/http_client_javascript.cpp31
-rw-r--r--platform/javascript/http_request.h10
-rw-r--r--platform/javascript/javascript_eval.cpp109
-rw-r--r--platform/javascript/javascript_main.cpp96
-rw-r--r--platform/javascript/javascript_runtime.cpp (renamed from platform/haiku/haiku_application.cpp)12
-rw-r--r--platform/javascript/js/engine/config.js100
-rw-r--r--platform/javascript/js/engine/engine.externs.js (renamed from platform/javascript/engine/externs.js)0
-rw-r--r--platform/javascript/js/engine/engine.js144
-rw-r--r--platform/javascript/js/engine/preloader.js127
-rw-r--r--platform/javascript/js/engine/utils.js58
-rw-r--r--platform/javascript/js/libs/audio.worklet.js186
-rw-r--r--platform/javascript/js/libs/library_godot_audio.js362
-rw-r--r--platform/javascript/js/libs/library_godot_display.js853
-rw-r--r--platform/javascript/js/libs/library_godot_editor_tools.js (renamed from platform/haiku/audio_driver_media_kit.h)73
-rw-r--r--platform/javascript/js/libs/library_godot_eval.js (renamed from platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java)100
-rw-r--r--platform/javascript/js/libs/library_godot_http_request.js (renamed from platform/javascript/native/http_request.js)118
-rw-r--r--platform/javascript/js/libs/library_godot_os.js304
-rw-r--r--platform/javascript/js/libs/library_godot_runtime.js120
-rw-r--r--platform/javascript/native/utils.js204
-rw-r--r--platform/javascript/os_javascript.cpp158
-rw-r--r--platform/javascript/os_javascript.h63
-rw-r--r--platform/javascript/package-lock.json1605
-rw-r--r--platform/javascript/package.json24
-rw-r--r--platform/javascript/platform_config.h4
-rw-r--r--platform/linuxbsd/SCsub5
-rw-r--r--platform/linuxbsd/context_gl_x11.cpp4
-rw-r--r--platform/linuxbsd/context_gl_x11.h4
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp12
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.h4
-rw-r--r--platform/linuxbsd/detect.py66
-rw-r--r--platform/linuxbsd/detect_prime_x11.cpp13
-rw-r--r--platform/linuxbsd/detect_prime_x11.h4
-rw-r--r--platform/linuxbsd/display_server_x11.cpp1572
-rw-r--r--platform/linuxbsd/display_server_x11.h85
-rw-r--r--platform/linuxbsd/export/export.cpp4
-rw-r--r--platform/linuxbsd/export/export.h4
-rw-r--r--platform/linuxbsd/godot_linuxbsd.cpp7
-rw-r--r--platform/linuxbsd/joypad_linux.cpp99
-rw-r--r--platform/linuxbsd/joypad_linux.h26
-rw-r--r--platform/linuxbsd/key_mapping_x11.cpp7
-rw-r--r--platform/linuxbsd/key_mapping_x11.h4
-rw-r--r--platform/linuxbsd/libudev-so_wrap.c1013
-rw-r--r--platform/linuxbsd/libudev-so_wrap.h378
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp210
-rw-r--r--platform/linuxbsd/os_linuxbsd.h42
-rw-r--r--platform/linuxbsd/platform_config.h18
-rw-r--r--platform/linuxbsd/vulkan_context_x11.cpp4
-rw-r--r--platform/linuxbsd/vulkan_context_x11.h4
-rw-r--r--platform/osx/SCsub2
-rw-r--r--platform/osx/context_gl_osx.h6
-rw-r--r--platform/osx/context_gl_osx.mm4
-rw-r--r--platform/osx/crash_handler_osx.h4
-rw-r--r--platform/osx/crash_handler_osx.mm12
-rw-r--r--platform/osx/detect.py57
-rw-r--r--platform/osx/dir_access_osx.h6
-rw-r--r--platform/osx/dir_access_osx.mm14
-rw-r--r--platform/osx/display_server_osx.h216
-rw-r--r--platform/osx/display_server_osx.mm943
-rw-r--r--platform/osx/export/export.cpp206
-rw-r--r--platform/osx/export/export.h4
-rw-r--r--platform/osx/godot_main_osx.mm9
-rw-r--r--platform/osx/joypad_osx.cpp21
-rw-r--r--platform/osx/joypad_osx.h22
-rw-r--r--platform/osx/logo.pngbin1751 -> 7195 bytes
-rw-r--r--platform/osx/os_osx.h52
-rw-r--r--platform/osx/os_osx.mm26
-rw-r--r--platform/osx/platform_config.h5
-rw-r--r--platform/osx/vulkan_context_osx.h4
-rw-r--r--platform/osx/vulkan_context_osx.mm4
-rw-r--r--platform/register_platform_apis.h4
-rw-r--r--platform/server/detect.py50
-rw-r--r--platform/server/godot_server.cpp7
-rw-r--r--platform/server/os_server.cpp22
-rw-r--r--platform/server/os_server.h22
-rw-r--r--platform/server/platform_config.h17
-rw-r--r--platform/uwp/app.cpp4
-rw-r--r--platform/uwp/app.h4
-rw-r--r--platform/uwp/context_egl_uwp.cpp7
-rw-r--r--platform/uwp/context_egl_uwp.h6
-rw-r--r--platform/uwp/detect.py18
-rw-r--r--platform/uwp/export/export.cpp85
-rw-r--r--platform/uwp/export/export.h4
-rw-r--r--platform/uwp/joypad_uwp.cpp4
-rw-r--r--platform/uwp/joypad_uwp.h25
-rw-r--r--platform/uwp/os_uwp.cpp58
-rw-r--r--platform/uwp/os_uwp.h30
-rw-r--r--platform/uwp/platform_config.h4
-rw-r--r--platform/uwp/thread_uwp.cpp61
-rw-r--r--platform/windows/SCsub8
-rw-r--r--platform/windows/context_gl_windows.cpp5
-rw-r--r--platform/windows/context_gl_windows.h6
-rw-r--r--platform/windows/crash_handler_windows.cpp10
-rw-r--r--platform/windows/crash_handler_windows.h4
-rw-r--r--platform/windows/detect.py97
-rw-r--r--platform/windows/display_server_windows.cpp578
-rw-r--r--platform/windows/display_server_windows.h57
-rw-r--r--platform/windows/export/export.cpp12
-rw-r--r--platform/windows/export/export.h4
-rw-r--r--platform/windows/godot.natvis64
-rw-r--r--platform/windows/godot_windows.cpp12
-rw-r--r--platform/windows/joypad_windows.cpp69
-rw-r--r--platform/windows/joypad_windows.h27
-rw-r--r--platform/windows/key_mapping_windows.cpp6
-rw-r--r--platform/windows/key_mapping_windows.h4
-rw-r--r--platform/windows/lang_table.h4
-rw-r--r--platform/windows/os_windows.cpp308
-rw-r--r--platform/windows/os_windows.h101
-rw-r--r--platform/windows/platform_config.h6
-rw-r--r--platform/windows/vulkan_context_win.cpp4
-rw-r--r--platform/windows/vulkan_context_win.h4
-rw-r--r--platform/windows/windows_terminal_logger.cpp4
-rw-r--r--platform/windows/windows_terminal_logger.h4
359 files changed, 19352 insertions, 13983 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub
index ec42bc42b5..7e9dac926c 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -6,7 +6,6 @@ android_files = [
"os_android.cpp",
"file_access_android.cpp",
"audio_driver_opensl.cpp",
- "file_access_jandroid.cpp",
"dir_access_jandroid.cpp",
"thread_jandroid.cpp",
"net_socket_android.cpp",
@@ -14,6 +13,7 @@ android_files = [
"java_godot_lib_jni.cpp",
"java_class_wrapper.cpp",
"java_godot_wrapper.cpp",
+ "java_godot_view_wrapper.cpp",
"java_godot_io_wrapper.cpp",
"jni_utils.cpp",
"android_keys_utils.cpp",
@@ -29,10 +29,14 @@ for x in android_files:
env_thirdparty = env_android.Clone()
env_thirdparty.disable_warnings()
-android_objects.append(env_thirdparty.SharedObject("#thirdparty/misc/ifaddrs-android.cc"))
+thirdparty_obj = env_thirdparty.SharedObject("#thirdparty/misc/ifaddrs-android.cc")
+android_objects.append(thirdparty_obj)
lib = env_android.add_shared_library("#bin/libgodot", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"])
+# Needed to force rebuilding the platform files when the thirdparty code is updated.
+env.Depends(lib, thirdparty_obj)
+
lib_arch_dir = ""
if env["android_arch"] == "armv7":
lib_arch_dir = "armeabi-v7a"
diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp
index b5b4fb9a4b..5aa546c17b 100644
--- a/platform/android/android_keys_utils.cpp
+++ b/platform/android/android_keys_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
index fb442f4c54..e0ee2888c0 100644
--- a/platform/android/android_keys_utils.h
+++ b/platform/android/android_keys_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,131 +31,12 @@
#ifndef ANDROID_KEYS_UTILS_H
#define ANDROID_KEYS_UTILS_H
+#include <android/input.h>
#include <core/os/keyboard.h>
-/*
- * Android Key codes.
- */
-enum {
- AKEYCODE_UNKNOWN = 0,
- AKEYCODE_SOFT_LEFT = 1,
- AKEYCODE_SOFT_RIGHT = 2,
- AKEYCODE_HOME = 3,
- AKEYCODE_BACK = 4,
- AKEYCODE_CALL = 5,
- AKEYCODE_ENDCALL = 6,
- AKEYCODE_0 = 7,
- AKEYCODE_1 = 8,
- AKEYCODE_2 = 9,
- AKEYCODE_3 = 10,
- AKEYCODE_4 = 11,
- AKEYCODE_5 = 12,
- AKEYCODE_6 = 13,
- AKEYCODE_7 = 14,
- AKEYCODE_8 = 15,
- AKEYCODE_9 = 16,
- AKEYCODE_STAR = 17,
- AKEYCODE_POUND = 18,
- AKEYCODE_DPAD_UP = 19,
- AKEYCODE_DPAD_DOWN = 20,
- AKEYCODE_DPAD_LEFT = 21,
- AKEYCODE_DPAD_RIGHT = 22,
- AKEYCODE_DPAD_CENTER = 23,
- AKEYCODE_VOLUME_UP = 24,
- AKEYCODE_VOLUME_DOWN = 25,
- AKEYCODE_POWER = 26,
- AKEYCODE_CAMERA = 27,
- AKEYCODE_CLEAR = 28,
- AKEYCODE_A = 29,
- AKEYCODE_B = 30,
- AKEYCODE_C = 31,
- AKEYCODE_D = 32,
- AKEYCODE_E = 33,
- AKEYCODE_F = 34,
- AKEYCODE_G = 35,
- AKEYCODE_H = 36,
- AKEYCODE_I = 37,
- AKEYCODE_J = 38,
- AKEYCODE_K = 39,
- AKEYCODE_L = 40,
- AKEYCODE_M = 41,
- AKEYCODE_N = 42,
- AKEYCODE_O = 43,
- AKEYCODE_P = 44,
- AKEYCODE_Q = 45,
- AKEYCODE_R = 46,
- AKEYCODE_S = 47,
- AKEYCODE_T = 48,
- AKEYCODE_U = 49,
- AKEYCODE_V = 50,
- AKEYCODE_W = 51,
- AKEYCODE_X = 52,
- AKEYCODE_Y = 53,
- AKEYCODE_Z = 54,
- AKEYCODE_COMMA = 55,
- AKEYCODE_PERIOD = 56,
- AKEYCODE_ALT_LEFT = 57,
- AKEYCODE_ALT_RIGHT = 58,
- AKEYCODE_SHIFT_LEFT = 59,
- AKEYCODE_SHIFT_RIGHT = 60,
- AKEYCODE_TAB = 61,
- AKEYCODE_SPACE = 62,
- AKEYCODE_SYM = 63,
- AKEYCODE_EXPLORER = 64,
- AKEYCODE_ENVELOPE = 65,
- AKEYCODE_ENTER = 66,
- AKEYCODE_DEL = 67,
- AKEYCODE_GRAVE = 68,
- AKEYCODE_MINUS = 69,
- AKEYCODE_EQUALS = 70,
- AKEYCODE_LEFT_BRACKET = 71,
- AKEYCODE_RIGHT_BRACKET = 72,
- AKEYCODE_BACKSLASH = 73,
- AKEYCODE_SEMICOLON = 74,
- AKEYCODE_APOSTROPHE = 75,
- AKEYCODE_SLASH = 76,
- AKEYCODE_AT = 77,
- AKEYCODE_NUM = 78,
- AKEYCODE_HEADSETHOOK = 79,
- AKEYCODE_FOCUS = 80, // *Camera* focus
- AKEYCODE_PLUS = 81,
- AKEYCODE_MENU = 82,
- AKEYCODE_NOTIFICATION = 83,
- AKEYCODE_SEARCH = 84,
- AKEYCODE_MEDIA_PLAY_PAUSE = 85,
- AKEYCODE_MEDIA_STOP = 86,
- AKEYCODE_MEDIA_NEXT = 87,
- AKEYCODE_MEDIA_PREVIOUS = 88,
- AKEYCODE_MEDIA_REWIND = 89,
- AKEYCODE_MEDIA_FAST_FORWARD = 90,
- AKEYCODE_MUTE = 91,
- AKEYCODE_PAGE_UP = 92,
- AKEYCODE_PAGE_DOWN = 93,
- AKEYCODE_PICTSYMBOLS = 94,
- AKEYCODE_SWITCH_CHARSET = 95,
- AKEYCODE_BUTTON_A = 96,
- AKEYCODE_BUTTON_B = 97,
- AKEYCODE_BUTTON_C = 98,
- AKEYCODE_BUTTON_X = 99,
- AKEYCODE_BUTTON_Y = 100,
- AKEYCODE_BUTTON_Z = 101,
- AKEYCODE_BUTTON_L1 = 102,
- AKEYCODE_BUTTON_R1 = 103,
- AKEYCODE_BUTTON_L2 = 104,
- AKEYCODE_BUTTON_R2 = 105,
- AKEYCODE_BUTTON_THUMBL = 106,
- AKEYCODE_BUTTON_THUMBR = 107,
- AKEYCODE_BUTTON_START = 108,
- AKEYCODE_BUTTON_SELECT = 109,
- AKEYCODE_BUTTON_MODE = 110,
-
- // NOTE: If you add a new keycode here you must also add it to several other files.
- // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
-};
-
struct _WinTranslatePair {
- unsigned int keysym;
- unsigned int keycode;
+ unsigned int keysym = 0;
+ unsigned int keycode = 0;
};
static _WinTranslatePair _ak_to_keycode[] = {
@@ -246,6 +127,8 @@ static _WinTranslatePair _ak_to_keycode[] = {
{ KEY_BACKSLASH, AKEYCODE_BACKSLASH },
{ KEY_BRACKETLEFT, AKEYCODE_LEFT_BRACKET },
{ KEY_BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET },
+ { KEY_CONTROL, AKEYCODE_CTRL_LEFT },
+ { KEY_CONTROL, AKEYCODE_CTRL_RIGHT },
{ KEY_UNKNOWN, 0 }
};
/*
diff --git a/platform/android/api/api.cpp b/platform/android/api/api.cpp
index 1f140f7119..d3c49c6eb7 100644
--- a/platform/android/api/api.cpp
+++ b/platform/android/api/api.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,7 +30,7 @@
#include "api.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "java_class_wrapper.h"
#include "jni_singleton.h"
diff --git a/platform/android/api/api.h b/platform/android/api/api.h
index 5e951b9c88..fe3a6734ac 100644
--- a/platform/android/api/api.h
+++ b/platform/android/api/api.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/api/java_class_wrapper.h b/platform/android/api/java_class_wrapper.h
index e34f2a9f69..d6c7a1abe5 100644
--- a/platform/android/api/java_class_wrapper.h
+++ b/platform/android/api/java_class_wrapper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,7 +31,7 @@
#ifndef JAVA_CLASS_WRAPPER_H
#define JAVA_CLASS_WRAPPER_H
-#include "core/reference.h"
+#include "core/object/reference.h"
#ifdef ANDROID_ENABLED
#include <android/log.h>
@@ -47,7 +47,6 @@ class JavaClass : public Reference {
#ifdef ANDROID_ENABLED
enum ArgumentType{
-
ARG_TYPE_VOID,
ARG_TYPE_BOOLEAN,
ARG_TYPE_BYTE,
@@ -67,10 +66,10 @@ class JavaClass : public Reference {
Map<StringName, Variant> constant_map;
struct MethodInfo {
- bool _static;
+ bool _static = false;
Vector<uint32_t> param_types;
Vector<StringName> param_sigs;
- uint32_t return_type;
+ uint32_t return_type = 0;
jmethodID method;
};
@@ -180,7 +179,7 @@ class JavaClass : public Reference {
#endif
public:
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
JavaClass();
};
@@ -196,7 +195,7 @@ class JavaObject : public Reference {
#endif
public:
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
#ifdef ANDROID_ENABLED
JavaObject(const Ref<JavaClass> &p_base, jobject *p_instance);
diff --git a/platform/android/api/jni_singleton.h b/platform/android/api/jni_singleton.h
index ed69f8d6e4..965eaabf81 100644
--- a/platform/android/api/jni_singleton.h
+++ b/platform/android/api/jni_singleton.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,8 +31,8 @@
#ifndef JNI_SINGLETON_H
#define JNI_SINGLETON_H
-#include <core/engine.h>
-#include <core/variant.h>
+#include <core/config/engine.h>
+#include <core/variant/variant.h>
#ifdef ANDROID_ENABLED
#include <platform/android/jni_utils.h>
#endif
@@ -52,7 +52,7 @@ class JNISingleton : public Object {
#endif
public:
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
#ifdef ANDROID_ENABLED
Map<StringName, MethodData>::Element *E = method_map.find(p_method);
@@ -83,7 +83,7 @@ public:
v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
}
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
int res = env->PushLocalFrame(16);
diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp
index 09c981b3fa..3a2ccac481 100644
--- a/platform/android/audio_driver_jandroid.cpp
+++ b/platform/android/audio_driver_jandroid.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,8 +30,8 @@
#include "audio_driver_jandroid.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "thread_jandroid.h"
AudioDriverAndroid *AudioDriverAndroid::s_ad = nullptr;
@@ -72,10 +72,10 @@ Error AudioDriverAndroid::init() {
// __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
- JNIEnv *env = ThreadAndroid::get_env();
- int mix_rate = GLOBAL_GET("audio/mix_rate");
+ JNIEnv *env = get_jni_env();
+ int mix_rate = GLOBAL_GET("audio/driver/mix_rate");
- int latency = GLOBAL_GET("audio/output_latency");
+ int latency = GLOBAL_GET("audio/driver/output_latency");
unsigned int buffer_size = next_power_of_2(latency * mix_rate / 1000);
print_verbose("Audio buffer size: " + itos(buffer_size));
@@ -98,7 +98,7 @@ void AudioDriverAndroid::start() {
}
void AudioDriverAndroid::setup(jobject p_io) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
io = p_io;
jclass c = env->GetObjectClass(io);
@@ -162,7 +162,7 @@ void AudioDriverAndroid::unlock() {
}
void AudioDriverAndroid::finish() {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(io, _quit);
if (audioBuffer) {
@@ -175,7 +175,7 @@ void AudioDriverAndroid::finish() {
}
void AudioDriverAndroid::set_pause(bool p_pause) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(io, _pause, p_pause);
}
diff --git a/platform/android/audio_driver_jandroid.h b/platform/android/audio_driver_jandroid.h
index 953ade9311..9007fd2f81 100644
--- a/platform/android/audio_driver_jandroid.h
+++ b/platform/android/audio_driver_jandroid.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index 740e9a3132..a1d8fb4810 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -339,6 +339,4 @@ void AudioDriverOpenSL::set_pause(bool p_pause) {
AudioDriverOpenSL::AudioDriverOpenSL() {
s_ad = this;
- pause = false;
- active = false;
}
diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h
index 9858a40822..e3efaddba2 100644
--- a/platform/android/audio_driver_opensl.h
+++ b/platform/android/audio_driver_opensl.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,20 +38,19 @@
#include <SLES/OpenSLES_Android.h>
class AudioDriverOpenSL : public AudioDriver {
- bool active;
+ bool active = false;
Mutex mutex;
enum {
-
BUFFER_COUNT = 2
};
- bool pause;
+ bool pause = false;
- uint32_t buffer_size;
- int16_t *buffers[BUFFER_COUNT];
- int32_t *mixdown_buffer;
- int last_free;
+ uint32_t buffer_size = 0;
+ int16_t *buffers[BUFFER_COUNT] = {};
+ int32_t *mixdown_buffer = nullptr;
+ int last_free = 0;
Vector<int16_t> rec_buffer;
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 6da1e5f3d6..5f0fcc9b77 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -13,7 +13,7 @@ def get_name():
def can_build():
- return "ANDROID_NDK_ROOT" in os.environ
+ return ("ANDROID_SDK_ROOT" in os.environ) or ("ANDROID_HOME" in os.environ)
def get_platform(platform):
@@ -24,13 +24,33 @@ def get_opts():
from SCons.Variables import BoolVariable, EnumVariable
return [
- ("ANDROID_NDK_ROOT", "Path to the Android NDK", os.environ.get("ANDROID_NDK_ROOT", 0)),
+ ("ANDROID_NDK_ROOT", "Path to the Android NDK", get_android_ndk_root()),
+ ("ANDROID_SDK_ROOT", "Path to the Android SDK", get_android_sdk_root()),
("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"),
EnumVariable("android_arch", "Target architecture", "armv7", ("armv7", "arm64v8", "x86", "x86_64")),
BoolVariable("android_neon", "Enable NEON support (armv7 only)", True),
]
+# Return the ANDROID_SDK_ROOT environment variable.
+# While ANDROID_HOME has been deprecated, it's used as a fallback for backward
+# compatibility purposes.
+def get_android_sdk_root():
+ if "ANDROID_SDK_ROOT" in os.environ:
+ return os.environ.get("ANDROID_SDK_ROOT", 0)
+ else:
+ return os.environ.get("ANDROID_HOME", 0)
+
+
+# Return the ANDROID_NDK_ROOT environment variable.
+# We generate one for this build using the ANDROID_SDK_ROOT env
+# variable and the project ndk version.
+# If the env variable is already defined, we override it with
+# our own to match what the project expects.
+def get_android_ndk_root():
+ return get_android_sdk_root() + "/ndk/" + get_project_ndk_version()
+
+
def get_flags():
return [
("tools", False),
@@ -47,7 +67,31 @@ def create(env):
return env.Clone(tools=tools)
+# Check if ANDROID_NDK_ROOT is valid.
+# If not, install the ndk using ANDROID_SDK_ROOT and sdkmanager.
+def install_ndk_if_needed(env):
+ print("Checking for Android NDK...")
+ env_ndk_version = get_env_ndk_version(env["ANDROID_NDK_ROOT"])
+ if env_ndk_version is None:
+ # Reinstall the ndk and update ANDROID_NDK_ROOT.
+ print("Installing Android NDK...")
+ if env["ANDROID_SDK_ROOT"] is None:
+ raise Exception("Invalid ANDROID_SDK_ROOT environment variable.")
+
+ import subprocess
+
+ extension = ".bat" if os.name == "nt" else ""
+ sdkmanager_path = env["ANDROID_SDK_ROOT"] + "/cmdline-tools/latest/bin/sdkmanager" + extension
+ ndk_download_args = "ndk;" + get_project_ndk_version()
+ subprocess.check_call([sdkmanager_path, ndk_download_args])
+
+ env["ANDROID_NDK_ROOT"] = env["ANDROID_SDK_ROOT"] + "/ndk/" + get_project_ndk_version()
+ print("ANDROID_NDK_ROOT: " + env["ANDROID_NDK_ROOT"])
+
+
def configure(env):
+ install_ndk_if_needed(env)
+
# Workaround for MinGW. See:
# http://www.scons.org/wiki/LongCmdLinesOnWin32
if os.name == "nt":
@@ -115,7 +159,8 @@ def configure(env):
if env["android_arch"] == "x86_64":
if get_platform(env["ndk_platform"]) < 21:
print(
- "WARNING: android_arch=x86_64 is not supported by ndk_platform lower than android-21; setting ndk_platform=android-21"
+ "WARNING: android_arch=x86_64 is not supported by ndk_platform lower than android-21; setting"
+ " ndk_platform=android-21"
)
env["ndk_platform"] = "android-21"
env["ARCH"] = "arch-x86_64"
@@ -136,7 +181,8 @@ def configure(env):
elif env["android_arch"] == "arm64v8":
if get_platform(env["ndk_platform"]) < 21:
print(
- "WARNING: android_arch=arm64v8 is not supported by ndk_platform lower than android-21; setting ndk_platform=android-21"
+ "WARNING: android_arch=arm64v8 is not supported by ndk_platform lower than android-21; setting"
+ " ndk_platform=android-21"
)
env["ndk_platform"] = "android-21"
env["ARCH"] = "arch-arm64"
@@ -164,7 +210,7 @@ def configure(env):
elif env["target"] == "debug":
env.Append(LINKFLAGS=["-O0"])
env.Append(CCFLAGS=["-O0", "-g", "-fno-limit-debug-info"])
- env.Append(CPPDEFINES=["_DEBUG", "DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Append(CPPDEFINES=["_DEBUG", "DEBUG_ENABLED"])
env.Append(CPPFLAGS=["-UNDEBUG"])
# Compiler configuration
@@ -213,7 +259,7 @@ def configure(env):
env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++abi/include"])
# Disable exceptions and rtti on non-tools (template) builds
- if env["tools"]:
+ if env["tools"] or env["builtin_icu"]:
env.Append(CXXFLAGS=["-frtti"])
else:
env.Append(CXXFLAGS=["-fno-rtti", "-fno-exceptions"])
@@ -231,7 +277,10 @@ def configure(env):
env.Append(CPPDEFINES=[("__ANDROID_API__", str(get_platform(env["ndk_platform"])))])
env.Append(
- CCFLAGS="-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
+ CCFLAGS=(
+ "-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden"
+ " -fno-strict-aliasing".split()
+ )
)
env.Append(CPPDEFINES=["NO_STATVFS", "GLES_ENABLED"])
@@ -265,7 +314,7 @@ def configure(env):
# Link flags
- ndk_version = get_ndk_version(env["ANDROID_NDK_ROOT"])
+ ndk_version = get_env_ndk_version(env["ANDROID_NDK_ROOT"])
if ndk_version != None and LooseVersion(ndk_version) >= LooseVersion("17.1.4828580"):
env.Append(LINKFLAGS=["-Wl,--exclude-libs,libgcc.a", "-Wl,--exclude-libs,libatomic.a", "-nostdlib++"])
else:
@@ -318,8 +367,14 @@ def configure(env):
env.Append(LIBS=["OpenSLES", "EGL", "GLESv2", "vulkan", "android", "log", "z", "dl"])
+# Return the project NDK version.
+# This is kept in sync with the value in 'platform/android/java/app/config.gradle'.
+def get_project_ndk_version():
+ return "21.4.7075529"
+
+
# Return NDK version string in source.properties (adapted from the Chromium project).
-def get_ndk_version(path):
+def get_env_ndk_version(path):
if path is None:
return None
prop_file_path = os.path.join(path, "source.properties")
@@ -329,6 +384,6 @@ def get_ndk_version(path):
key_value = list(map(lambda x: x.strip(), line.split("=")))
if key_value[0] == "Pkg.Revision":
return key_value[1]
- except:
+ except Exception:
print("Could not read source prop file '%s'" % prop_file_path)
return None
diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp
index ca312b427f..f8ac29c738 100644
--- a/platform/android/dir_access_jandroid.cpp
+++ b/platform/android/dir_access_jandroid.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,8 +29,8 @@
/*************************************************************************/
#include "dir_access_jandroid.h"
-#include "core/print_string.h"
-#include "file_access_jandroid.h"
+#include "core/string/print_string.h"
+#include "file_access_android.h"
#include "string_android.h"
#include "thread_jandroid.h"
@@ -47,7 +47,7 @@ DirAccess *DirAccessJAndroid::create_fs() {
Error DirAccessJAndroid::list_dir_begin() {
list_dir_end();
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring js = env->NewStringUTF(current_dir.utf8().get_data());
int res = env->CallIntMethod(io, _dir_open, js);
@@ -62,7 +62,7 @@ Error DirAccessJAndroid::list_dir_begin() {
String DirAccessJAndroid::get_next() {
ERR_FAIL_COND_V(id == 0, "");
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring str = (jstring)env->CallObjectMethod(io, _dir_next, id);
if (!str)
return "";
@@ -73,7 +73,7 @@ String DirAccessJAndroid::get_next() {
}
bool DirAccessJAndroid::current_is_dir() const {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
return env->CallBooleanMethod(io, _dir_is_dir, id);
}
@@ -86,7 +86,7 @@ void DirAccessJAndroid::list_dir_end() {
if (id == 0)
return;
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(io, _dir_close, id);
id = 0;
}
@@ -100,7 +100,7 @@ String DirAccessJAndroid::get_drive(int p_drive) {
}
Error DirAccessJAndroid::change_dir(String p_dir) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
if (p_dir == "" || p_dir == "." || (p_dir == ".." && current_dir == ""))
return OK;
@@ -146,7 +146,7 @@ bool DirAccessJAndroid::file_exists(String p_file) {
else
sd = current_dir.plus_file(p_file);
- FileAccessJAndroid *f = memnew(FileAccessJAndroid);
+ FileAccessAndroid *f = memnew(FileAccessAndroid);
bool exists = f->file_exists(sd);
memdelete(f);
@@ -154,7 +154,7 @@ bool DirAccessJAndroid::file_exists(String p_file) {
}
bool DirAccessJAndroid::dir_exists(String p_dir) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
String sd;
@@ -207,7 +207,7 @@ size_t DirAccessJAndroid::get_space_left() {
}
void DirAccessJAndroid::setup(jobject p_io) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
io = p_io;
jclass c = env->GetObjectClass(io);
diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h
index 7d0def137a..fed468d051 100644
--- a/platform/android/dir_access_jandroid.h
+++ b/platform/android/dir_access_jandroid.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index c5b06827b2..5f7e5eaa83 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,18 +31,17 @@
#include "display_server_android.h"
#include "android_keys_utils.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "java_godot_io_wrapper.h"
#include "java_godot_wrapper.h"
#include "os_android.h"
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
+#include <android/input.h>
+
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
#include "platform/android/vulkan/vulkan_context_android.h"
-#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#endif
DisplayServerAndroid *DisplayServerAndroid::get_singleton() {
@@ -58,7 +57,7 @@ bool DisplayServerAndroid::has_feature(Feature p_feature) const {
//case FEATURE_HIDPI:
//case FEATURE_ICON:
//case FEATURE_IME:
- //case FEATURE_MOUSE:
+ case FEATURE_MOUSE:
//case FEATURE_MOUSE_WARP:
//case FEATURE_NATIVE_DIALOG:
//case FEATURE_NATIVE_ICON:
@@ -140,8 +139,11 @@ Size2i DisplayServerAndroid::screen_get_size(int p_screen) const {
}
Rect2i DisplayServerAndroid::screen_get_usable_rect(int p_screen) const {
- Size2i display_size = OS_Android::get_singleton()->get_display_size();
- return Rect2i(0, 0, display_size.width, display_size.height);
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ ERR_FAIL_COND_V(!godot_io_java, Rect2i());
+ int xywh[4];
+ godot_io_java->screen_get_usable_rect(xywh);
+ return Rect2i(xywh[0], xywh[1], xywh[2], xywh[3]);
}
int DisplayServerAndroid::screen_get_dpi(int p_screen) const {
@@ -155,12 +157,12 @@ bool DisplayServerAndroid::screen_is_touchscreen(int p_screen) const {
return true;
}
-void DisplayServerAndroid::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_length, int p_cursor_start, int p_cursor_end) {
+void DisplayServerAndroid::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_length, int p_cursor_start, int p_cursor_end) {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
ERR_FAIL_COND(!godot_io_java);
if (godot_io_java->has_vk()) {
- godot_io_java->show_vk(p_existing_text, p_max_length, p_cursor_start, p_cursor_end);
+ godot_io_java->show_vk(p_existing_text, p_multiline, p_max_length, p_cursor_start, p_cursor_end);
} else {
ERR_PRINT("Virtual keyboard not available");
}
@@ -343,7 +345,7 @@ void DisplayServerAndroid::alert(const String &p_alert, const String &p_title) {
}
void DisplayServerAndroid::process_events() {
- // Nothing to do
+ Input::get_singleton()->flush_accumulated_events();
}
Vector<String> DisplayServerAndroid::get_rendering_drivers_func() {
@@ -360,13 +362,36 @@ Vector<String> DisplayServerAndroid::get_rendering_drivers_func() {
}
DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- return memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ if (r_error != OK) {
+ ds->alert("Your video card driver does not support any of the supported Vulkan versions.", "Unable to initialize Video driver");
+ }
+ return ds;
}
void DisplayServerAndroid::register_android_driver() {
register_create_function("android", create_func, get_rendering_drivers_func);
}
+void DisplayServerAndroid::reset_window() {
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ ANativeWindow *native_window = OS_Android::get_singleton()->get_native_window();
+ ERR_FAIL_COND(!native_window);
+
+ ERR_FAIL_COND(!context_vulkan);
+ context_vulkan->window_destroy(MAIN_WINDOW_ID);
+
+ Size2i display_size = OS_Android::get_singleton()->get_display_size();
+ if (context_vulkan->window_create(native_window, display_size.width, display_size.height) == -1) {
+ memdelete(context_vulkan);
+ context_vulkan = nullptr;
+ ERR_FAIL_MSG("Failed to reset Vulkan window.");
+ }
+ }
+#endif
+}
+
DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
rendering_driver = p_rendering_driver;
@@ -375,6 +400,8 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis
keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on");
+ buttons_state = 0;
+
#if defined(OPENGL_ENABLED)
if (rendering_driver == "opengl") {
bool gl_initialization_error = false;
@@ -420,11 +447,13 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis
rendering_device_vulkan = memnew(RenderingDeviceVulkan);
rendering_device_vulkan->initialize(context_vulkan);
- RasterizerRD::make_current();
+ RendererCompositorRD::make_current();
}
#endif
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
+
+ r_error = OK;
}
DisplayServerAndroid::~DisplayServerAndroid() {
@@ -461,17 +490,59 @@ void DisplayServerAndroid::process_joy_event(DisplayServerAndroid::JoypadEvent p
}
}
+void DisplayServerAndroid::_set_key_modifier_state(Ref<InputEventWithModifiers> ev) {
+ ev->set_shift(shift_mem);
+ ev->set_alt(alt_mem);
+ ev->set_metakey(meta_mem);
+ ev->set_control(control_mem);
+}
+
void DisplayServerAndroid::process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed) {
+ static char32_t prev_wc = 0;
+ char32_t unicode = p_unicode_char;
+ if ((p_unicode_char & 0xfffffc00) == 0xd800) {
+ if (prev_wc != 0) {
+ ERR_PRINT("invalid utf16 surrogate input");
+ }
+ prev_wc = unicode;
+ return; // Skip surrogate.
+ } else if ((unicode & 0xfffffc00) == 0xdc00) {
+ if (prev_wc == 0) {
+ ERR_PRINT("invalid utf16 surrogate input");
+ return; // Skip invalid surrogate.
+ }
+ unicode = (prev_wc << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev_wc = 0;
+ } else {
+ prev_wc = 0;
+ }
+
Ref<InputEventKey> ev;
ev.instance();
- int val = p_unicode_char;
+ int val = unicode;
int keycode = android_get_keysym(p_keycode);
int phy_keycode = android_get_keysym(p_scancode);
+
+ if (keycode == KEY_SHIFT) {
+ shift_mem = p_pressed;
+ }
+ if (keycode == KEY_ALT) {
+ alt_mem = p_pressed;
+ }
+ if (keycode == KEY_CONTROL) {
+ control_mem = p_pressed;
+ }
+ if (keycode == KEY_META) {
+ meta_mem = p_pressed;
+ }
+
ev->set_keycode(keycode);
ev->set_physical_keycode(phy_keycode);
ev->set_unicode(val);
ev->set_pressed(p_pressed);
+ _set_key_modifier_state(ev);
+
if (val == '\n') {
ev->set_keycode(KEY_ENTER);
} else if (val == 61448) {
@@ -484,12 +555,12 @@ void DisplayServerAndroid::process_key_event(int p_keycode, int p_scancode, int
OS_Android::get_singleton()->main_loop_request_go_back();
}
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
}
-void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector<DisplayServerAndroid::TouchPos> &p_points) {
- switch (p_what) {
- case 0: { //gesture begin
+void DisplayServerAndroid::process_touch(int p_event, int p_pointer, const Vector<DisplayServerAndroid::TouchPos> &p_points) {
+ switch (p_event) {
+ case AMOTION_EVENT_ACTION_DOWN: { //gesture begin
if (touch.size()) {
//end all if exist
for (int i = 0; i < touch.size(); i++) {
@@ -498,7 +569,7 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector
ev->set_index(touch[i].id);
ev->set_pressed(false);
ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
}
}
@@ -515,11 +586,11 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector
ev->set_index(touch[i].id);
ev->set_pressed(true);
ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
}
} break;
- case 1: { //motion
+ case AMOTION_EVENT_ACTION_MOVE: { //motion
ERR_FAIL_COND(touch.size() != p_points.size());
for (int i = 0; i < touch.size(); i++) {
@@ -541,12 +612,13 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector
ev->set_index(touch[i].id);
ev->set_position(p_points[idx].pos);
ev->set_relative(p_points[idx].pos - touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
touch.write[i].pos = p_points[idx].pos;
}
} break;
- case 2: { //release
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_UP: { //release
if (touch.size()) {
//end all if exist
for (int i = 0; i < touch.size(); i++) {
@@ -555,12 +627,12 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector
ev->set_index(touch[i].id);
ev->set_pressed(false);
ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
}
touch.clear();
}
} break;
- case 3: { // add touch
+ case AMOTION_EVENT_ACTION_POINTER_DOWN: { // add touch
for (int i = 0; i < p_points.size(); i++) {
if (p_points[i].id == p_pointer) {
TouchPos tp = p_points[i];
@@ -572,13 +644,13 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector
ev->set_index(tp.id);
ev->set_pressed(true);
ev->set_position(tp.pos);
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
break;
}
}
} break;
- case 4: { // remove touch
+ case AMOTION_EVENT_ACTION_POINTER_UP: { // remove touch
for (int i = 0; i < touch.size(); i++) {
if (touch[i].id == p_pointer) {
Ref<InputEventScreenTouch> ev;
@@ -586,7 +658,7 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector
ev->set_index(touch[i].id);
ev->set_pressed(false);
ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
touch.remove(i);
break;
@@ -599,36 +671,141 @@ void DisplayServerAndroid::process_touch(int p_what, int p_pointer, const Vector
void DisplayServerAndroid::process_hover(int p_type, Point2 p_pos) {
// https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
switch (p_type) {
- case 7: // hover move
- case 9: // hover enter
- case 10: { // hover exit
+ case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
+ case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
+ case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit
Ref<InputEventMouseMotion> ev;
ev.instance();
+ _set_key_modifier_state(ev);
ev->set_position(p_pos);
ev->set_global_position(p_pos);
ev->set_relative(p_pos - hover_prev_pos);
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
hover_prev_pos = p_pos;
} break;
}
}
-void DisplayServerAndroid::process_double_tap(Point2 p_pos) {
+void DisplayServerAndroid::process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) {
+ int event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask);
+ switch (event_action) {
+ case AMOTION_EVENT_ACTION_BUTTON_PRESS:
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
+ Ref<InputEventMouseButton> ev;
+ ev.instance();
+ _set_key_modifier_state(ev);
+ if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
+ ev->set_position(event_pos);
+ ev->set_global_position(event_pos);
+ } else {
+ ev->set_position(hover_prev_pos);
+ ev->set_global_position(hover_prev_pos);
+ }
+ ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS);
+ int changed_button_mask = buttons_state ^ event_buttons_mask;
+
+ buttons_state = event_buttons_mask;
+
+ ev->set_button_index(_button_index_from_mask(changed_button_mask));
+ ev->set_button_mask(event_buttons_mask);
+ Input::get_singleton()->accumulate_input_event(ev);
+ } break;
+
+ case AMOTION_EVENT_ACTION_MOVE: {
+ Ref<InputEventMouseMotion> ev;
+ ev.instance();
+ _set_key_modifier_state(ev);
+ if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
+ ev->set_position(event_pos);
+ ev->set_global_position(event_pos);
+ ev->set_relative(event_pos - hover_prev_pos);
+ hover_prev_pos = event_pos;
+ } else {
+ ev->set_position(hover_prev_pos);
+ ev->set_global_position(hover_prev_pos);
+ ev->set_relative(event_pos);
+ }
+ ev->set_button_mask(event_buttons_mask);
+ Input::get_singleton()->accumulate_input_event(ev);
+ } break;
+ case AMOTION_EVENT_ACTION_SCROLL: {
+ Ref<InputEventMouseButton> ev;
+ ev.instance();
+ if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
+ ev->set_position(event_pos);
+ ev->set_global_position(event_pos);
+ } else {
+ ev->set_position(hover_prev_pos);
+ ev->set_global_position(hover_prev_pos);
+ }
+ ev->set_pressed(true);
+ buttons_state = event_buttons_mask;
+ if (event_vertical_factor > 0) {
+ _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_UP, event_vertical_factor);
+ } else if (event_vertical_factor < 0) {
+ _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_DOWN, -event_vertical_factor);
+ }
+
+ if (event_horizontal_factor > 0) {
+ _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_RIGHT, event_horizontal_factor);
+ } else if (event_horizontal_factor < 0) {
+ _wheel_button_click(event_buttons_mask, ev, BUTTON_WHEEL_LEFT, -event_horizontal_factor);
+ }
+ } break;
+ }
+}
+
+void DisplayServerAndroid::_wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor) {
+ Ref<InputEventMouseButton> evd = ev->duplicate();
+ _set_key_modifier_state(evd);
+ evd->set_button_index(wheel_button);
+ evd->set_button_mask(event_buttons_mask ^ (1 << (wheel_button - 1)));
+ evd->set_factor(factor);
+ Input::get_singleton()->accumulate_input_event(evd);
+ Ref<InputEventMouseButton> evdd = evd->duplicate();
+ evdd->set_pressed(false);
+ evdd->set_button_mask(event_buttons_mask);
+ Input::get_singleton()->accumulate_input_event(evdd);
+}
+
+void DisplayServerAndroid::process_double_tap(int event_android_button_mask, Point2 p_pos) {
+ int event_button_mask = _android_button_mask_to_godot_button_mask(event_android_button_mask);
Ref<InputEventMouseButton> ev;
ev.instance();
+ _set_key_modifier_state(ev);
ev->set_position(p_pos);
ev->set_global_position(p_pos);
- ev->set_pressed(false);
+ ev->set_pressed(event_button_mask != 0);
+ ev->set_button_index(_button_index_from_mask(event_button_mask));
+ ev->set_button_mask(event_button_mask);
ev->set_doubleclick(true);
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
+}
+
+int DisplayServerAndroid::_button_index_from_mask(int button_mask) {
+ switch (button_mask) {
+ case BUTTON_MASK_LEFT:
+ return BUTTON_LEFT;
+ case BUTTON_MASK_RIGHT:
+ return BUTTON_RIGHT;
+ case BUTTON_MASK_MIDDLE:
+ return BUTTON_MIDDLE;
+ case BUTTON_MASK_XBUTTON1:
+ return BUTTON_XBUTTON1;
+ case BUTTON_MASK_XBUTTON2:
+ return BUTTON_XBUTTON2;
+ default:
+ return 0;
+ }
}
void DisplayServerAndroid::process_scroll(Point2 p_pos) {
Ref<InputEventPanGesture> ev;
ev.instance();
+ _set_key_modifier_state(ev);
ev->set_position(p_pos);
ev->set_delta(p_pos - scroll_prev_pos);
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->accumulate_input_event(ev);
scroll_prev_pos = p_pos;
}
@@ -647,3 +824,50 @@ void DisplayServerAndroid::process_magnetometer(const Vector3 &p_magnetometer) {
void DisplayServerAndroid::process_gyroscope(const Vector3 &p_gyroscope) {
Input::get_singleton()->set_gyroscope(p_gyroscope);
}
+
+void DisplayServerAndroid::mouse_set_mode(MouseMode p_mode) {
+ if (mouse_mode == p_mode) {
+ return;
+ }
+
+ if (p_mode == MouseMode::MOUSE_MODE_CAPTURED) {
+ OS_Android::get_singleton()->get_godot_java()->get_godot_view()->request_pointer_capture();
+ } else {
+ OS_Android::get_singleton()->get_godot_java()->get_godot_view()->release_pointer_capture();
+ }
+
+ mouse_mode = p_mode;
+}
+
+DisplayServer::MouseMode DisplayServerAndroid::mouse_get_mode() const {
+ return mouse_mode;
+}
+
+Point2i DisplayServerAndroid::mouse_get_position() const {
+ return hover_prev_pos;
+}
+
+int DisplayServerAndroid::mouse_get_button_state() const {
+ return buttons_state;
+}
+
+int DisplayServerAndroid::_android_button_mask_to_godot_button_mask(int android_button_mask) {
+ int godot_button_mask = 0;
+ if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) {
+ godot_button_mask |= BUTTON_MASK_LEFT;
+ }
+ if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
+ godot_button_mask |= BUTTON_MASK_RIGHT;
+ }
+ if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) {
+ godot_button_mask |= BUTTON_MASK_MIDDLE;
+ }
+ if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) {
+ godot_button_mask |= BUTTON_MASK_XBUTTON1;
+ }
+ if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
+ godot_button_mask |= BUTTON_MASK_XBUTTON2;
+ }
+
+ return godot_button_mask;
+}
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index 199371d08d..b9d1641656 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -41,7 +41,7 @@ class RenderingDeviceVulkan;
class DisplayServerAndroid : public DisplayServer {
public:
struct TouchPos {
- int id;
+ int id = 0;
Point2 pos;
};
@@ -52,17 +52,26 @@ public:
};
struct JoypadEvent {
- int device;
- int type;
- int index;
- bool pressed;
- float value;
- int hat;
+ int device = 0;
+ int type = 0;
+ int index = 0;
+ bool pressed = false;
+ float value = 0;
+ int hat = 0;
};
private:
String rendering_driver;
+ bool alt_mem = false;
+ bool shift_mem = false;
+ bool control_mem = false;
+ bool meta_mem = false;
+
+ int buttons_state;
+
+ MouseMode mouse_mode;
+
bool keep_screen_on;
Vector<TouchPos> touch;
@@ -84,6 +93,14 @@ private:
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
+
+ static int _button_index_from_mask(int button_mask);
+
+ static int _android_button_mask_to_godot_button_mask(int android_button_mask);
+
+ void _wheel_button_click(int event_buttons_mask, const Ref<InputEventMouseButton> &ev, int wheel_button, float factor);
+
public:
static DisplayServerAndroid *get_singleton();
@@ -106,7 +123,7 @@ public:
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
- virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1);
+ virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1);
virtual void virtual_keyboard_hide();
virtual int virtual_keyboard_get_height() const;
@@ -155,17 +172,26 @@ public:
void process_gravity(const Vector3 &p_gravity);
void process_magnetometer(const Vector3 &p_magnetometer);
void process_gyroscope(const Vector3 &p_gyroscope);
- void process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points);
+ void process_touch(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
void process_hover(int p_type, Point2 p_pos);
- void process_double_tap(Point2 p_pos);
+ void process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0);
+ void process_double_tap(int event_android_button_mask, Point2 p_pos);
void process_scroll(Point2 p_pos);
void process_joy_event(JoypadEvent p_event);
void process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed);
+ void mouse_set_mode(MouseMode p_mode);
+ MouseMode mouse_get_mode() const;
+
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
static void register_android_driver();
+ void reset_window();
+
+ virtual Point2i mouse_get_position() const;
+ virtual int mouse_get_button_state() const;
+
DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
~DisplayServerAndroid();
};
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index dfaaf68b69..088bb35f62 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,19 +30,22 @@
#include "export.h"
+#include "core/config/project_settings.h"
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/zip_io.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
+#include "core/templates/safe_refcount.h"
#include "core/version.h"
#include "drivers/png/png_driver_common.h"
#include "editor/editor_export.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "main/splash.gen.h"
+#include "platform/android/export/gradle_export_util.h"
#include "platform/android/logo.gen.h"
#include "platform/android/plugin/godot_plugin_config.h"
#include "platform/android/run_icon.gen.h"
@@ -198,9 +201,25 @@ static const char *android_perms[] = {
nullptr
};
+static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable/splash.png";
+static const char *SPLASH_BG_COLOR_PATH = "res/drawable/splash_bg_color.png";
+static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash_drawable.xml";
+
+const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/splash_bg_color" />
+ <item>
+ <bitmap
+ android:gravity="%s"
+ android:filter="%s"
+ android:src="@drawable/splash" />
+ </item>
+</layer-list>
+)SPLASH";
+
struct LauncherIcon {
const char *export_path;
- int dimensions;
+ int dimensions = 0;
};
static const int icon_densities_count = 6;
@@ -235,6 +254,9 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun
{ "res/mipmap/icon_background.png", 432 }
};
+static const int EXPORT_FORMAT_APK = 0;
+static const int EXPORT_FORMAT_AAB = 1;
+
class EditorExportPlatformAndroid : public EditorExportPlatform {
GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform);
@@ -245,60 +267,62 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String id;
String name;
String description;
- int api_level;
+ int api_level = 0;
};
struct APKExportData {
zipFile apk;
- EditorProgress *ep;
+ EditorProgress *ep = nullptr;
};
- Vector<PluginConfig> plugins;
- volatile bool plugins_changed;
+ Vector<PluginConfigAndroid> plugins;
+ String last_plugin_names;
+ uint64_t last_custom_build_time = 0;
+ SafeFlag plugins_changed;
Mutex plugins_lock;
Vector<Device> devices;
- volatile bool devices_changed;
+ SafeFlag devices_changed;
Mutex device_lock;
- Thread *check_for_changes_thread;
- volatile bool quit_request;
+ Thread check_for_changes_thread;
+ SafeFlag quit_request;
static void _check_for_changes_poll_thread(void *ud) {
EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud;
- while (!ea->quit_request) {
+ while (!ea->quit_request.is_set()) {
// Check for plugins updates
{
// Nothing to do if we already know the plugins have changed.
- if (!ea->plugins_changed) {
- Vector<PluginConfig> loaded_plugins = get_plugins();
+ if (!ea->plugins_changed.is_set()) {
+ Vector<PluginConfigAndroid> loaded_plugins = get_plugins();
MutexLock lock(ea->plugins_lock);
if (ea->plugins.size() != loaded_plugins.size()) {
- ea->plugins_changed = true;
+ ea->plugins_changed.set();
} else {
for (int i = 0; i < ea->plugins.size(); i++) {
if (ea->plugins[i].name != loaded_plugins[i].name) {
- ea->plugins_changed = true;
+ ea->plugins_changed.set();
break;
}
}
}
- if (ea->plugins_changed) {
+ if (ea->plugins_changed.is_set()) {
ea->plugins = loaded_plugins;
}
}
}
// Check for devices updates
- String adb = EditorSettings::get_singleton()->get("export/android/adb");
+ String adb = get_adb_path();
if (FileAccess::exists(adb)) {
String devices;
List<String> args;
args.push_back("devices");
int ec;
- OS::get_singleton()->execute(adb, args, true, nullptr, &devices, &ec);
+ OS::get_singleton()->execute(adb, args, &devices, &ec);
Vector<String> ds = devices.split("\n");
Vector<String> ldevices;
@@ -351,7 +375,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int ec2;
String dp;
- OS::get_singleton()->execute(adb, args, true, nullptr, &dp, &ec2);
+ OS::get_singleton()->execute(adb, args, &dp, &ec2);
Vector<String> props = dp.split("\n");
String vendor;
@@ -399,7 +423,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
ea->devices = ndevices;
- ea->devices_changed = true;
+ ea->devices_changed.set();
}
}
@@ -408,21 +432,21 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
uint64_t time = OS::get_singleton()->get_ticks_usec();
while (OS::get_singleton()->get_ticks_usec() - time < wait) {
OS::get_singleton()->delay_usec(1000 * sleep);
- if (ea->quit_request) {
+ if (ea->quit_request.is_set()) {
break;
}
}
}
if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) {
- String adb = EditorSettings::get_singleton()->get("export/android/adb");
+ String adb = get_adb_path();
if (!FileAccess::exists(adb)) {
return; //adb not configured
}
List<String> args;
args.push_back("kill-server");
- OS::get_singleton()->execute(adb, args, true);
+ OS::get_singleton()->execute(adb, args);
};
}
@@ -449,7 +473,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String name;
bool first = true;
for (int i = 0; i < basename.length(); i++) {
- CharType c = basename[i];
+ char32_t c = basename[i];
if (c >= '0' && c <= '9' && first) {
continue;
}
@@ -480,7 +504,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int segments = 0;
bool first = true;
for (int i = 0; i < pname.length(); i++) {
- CharType c = pname[i];
+ char32_t c = pname[i];
if (first && c == '.') {
if (r_error) {
*r_error = TTR("Package segments must be of non-zero length.");
@@ -584,7 +608,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
zipfi.tmz_date.tm_hour = time.hour;
zipfi.tmz_date.tm_mday = date.day;
zipfi.tmz_date.tm_min = time.min;
- zipfi.tmz_date.tm_mon = date.month;
+ zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed
zipfi.tmz_date.tm_sec = time.sec;
zipfi.tmz_date.tm_year = date.year;
zipfi.dosDate = 0;
@@ -619,7 +643,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
continue;
}
- if (file.ends_with(PLUGIN_CONFIG_EXT)) {
+ if (file.ends_with(PluginConfigAndroid::PLUGIN_CONFIG_EXT)) {
dir_files.push_back(file);
}
}
@@ -629,8 +653,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
return dir_files;
}
- static Vector<PluginConfig> get_plugins() {
- Vector<PluginConfig> loaded_plugins;
+ static Vector<PluginConfigAndroid> get_plugins() {
+ Vector<PluginConfigAndroid> loaded_plugins;
String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins");
@@ -640,10 +664,10 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
if (DirAccess::exists(plugins_dir)) {
Vector<String> plugins_filenames = list_gdap_files(plugins_dir);
- if (!plugins_filenames.empty()) {
+ if (!plugins_filenames.is_empty()) {
Ref<ConfigFile> config_file = memnew(ConfigFile);
for (int i = 0; i < plugins_filenames.size(); i++) {
- PluginConfig config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i]));
+ PluginConfigAndroid config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i]));
if (config.valid_config) {
loaded_plugins.push_back(config);
} else {
@@ -656,11 +680,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
return loaded_plugins;
}
- static Vector<PluginConfig> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
- Vector<PluginConfig> enabled_plugins;
- Vector<PluginConfig> all_plugins = get_plugins();
+ static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
+ Vector<PluginConfigAndroid> enabled_plugins;
+ Vector<PluginConfigAndroid> all_plugins = get_plugins();
for (int i = 0; i < all_plugins.size(); i++) {
- PluginConfig plugin = all_plugins[i];
+ PluginConfigAndroid plugin = all_plugins[i];
bool enabled = p_presets->get("plugins/" + plugin.name);
if (enabled) {
enabled_plugins.push_back(plugin);
@@ -719,7 +743,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
return OK;
}
- static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
+ static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
APKExportData *ed = (APKExportData *)p_userdata;
String dst_path = p_path.replace_first("res://", "assets/");
@@ -727,10 +751,70 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
return OK;
}
- static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
+ static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
return OK;
}
+ void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) {
+ const char **aperms = android_perms;
+ while (*aperms) {
+ bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower());
+ if (enabled) {
+ r_permissions.push_back("android.permission." + String(*aperms));
+ }
+ aperms++;
+ }
+ PackedStringArray user_perms = p_preset->get("permissions/custom_permissions");
+ for (int i = 0; i < user_perms.size(); i++) {
+ String user_perm = user_perms[i].strip_edges();
+ if (!user_perm.is_empty()) {
+ r_permissions.push_back(user_perm);
+ }
+ }
+ if (p_give_internet) {
+ if (r_permissions.find("android.permission.INTERNET") == -1) {
+ r_permissions.push_back("android.permission.INTERNET");
+ }
+ }
+
+ int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ if (xr_mode_index == 1 /* XRMode.OVR */) {
+ int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
+ if (hand_tracking_index > 0) {
+ if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) {
+ r_permissions.push_back("com.oculus.permission.HAND_TRACKING");
+ }
+ }
+ }
+ }
+
+ void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) {
+ print_verbose("Building temporary manifest..");
+ String manifest_text =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\">\n";
+
+ manifest_text += _get_screen_sizes_tag(p_preset);
+ manifest_text += _get_gles_tag();
+
+ Vector<String> perms;
+ _get_permissions(p_preset, p_give_internet, perms);
+ for (int i = 0; i < perms.size(); i++) {
+ manifest_text += vformat(" <uses-permission android:name=\"%s\" />\n", perms.get(i));
+ }
+
+ manifest_text += _get_xr_features_tag(p_preset);
+ manifest_text += _get_instrumentation_tag(p_preset);
+ String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
+ manifest_text += _get_application_tag(p_preset, plugins_names);
+ manifest_text += "</manifest>\n";
+ String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
+
+ print_verbose("Storing manifest into " + manifest_path + ": " + "\n" + manifest_text);
+ store_string_at_path(manifest_path, manifest_text);
+ }
+
void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) {
// Leaving the unused types commented because looking these constants up
// again later would be annoying
@@ -762,7 +846,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int version_code = p_preset->get("version/code");
String package_name = p_preset->get("package/unique_name");
- int orientation = p_preset->get("screen/orientation");
+ const int screen_orientation = _get_android_orientation_value(_get_screen_orientation());
bool screen_support_small = p_preset->get("screen/support_small");
bool screen_support_normal = p_preset->get("screen/support_normal");
@@ -770,34 +854,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
bool screen_support_xlarge = p_preset->get("screen/support_xlarge");
int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ bool focus_awareness = p_preset->get("xr_features/focus_awareness");
String plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
Vector<String> perms;
-
- const char **aperms = android_perms;
- while (*aperms) {
- bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower());
- if (enabled) {
- perms.push_back("android.permission." + String(*aperms));
- }
- aperms++;
- }
-
- PackedStringArray user_perms = p_preset->get("permissions/custom_permissions");
-
- for (int i = 0; i < user_perms.size(); i++) {
- String user_perm = user_perms[i].strip_edges();
- if (!user_perm.empty()) {
- perms.push_back(user_perm);
- }
- }
-
- if (p_give_internet) {
- if (perms.find("android.permission.INTERNET") == -1) {
- perms.push_back("android.permission.INTERNET");
- }
- }
+ // Write permissions into the perms variable.
+ _get_permissions(p_preset, p_give_internet, perms);
while (ofs < (uint32_t)p_manifest.size()) {
uint32_t chunk = decode_uint32(&p_manifest[ofs]);
@@ -833,7 +896,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
if (string_flags & UTF8_FLAG) {
} else {
uint32_t len = decode_uint16(&p_manifest[string_at]);
- Vector<CharType> ucstring;
+ Vector<char32_t> ucstring;
ucstring.resize(len + 1);
for (uint32_t j = 0; j < len; j++) {
uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]);
@@ -859,6 +922,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String tname = string_table[name];
uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]);
iofs += 28;
+ bool is_focus_aware_metadata = false;
for (uint32_t i = 0; i < attrcount; i++) {
uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]);
@@ -892,7 +956,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
if (tname == "activity" && attrname == "screenOrientation") {
- encode_uint32(orientation == 0 ? 0 : 1, &p_manifest.write[iofs + 16]);
+ encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]);
}
if (tname == "supports-screens") {
@@ -926,11 +990,17 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
}
- if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.empty()) {
+ if (tname == "meta-data" && attrname == "value" && is_focus_aware_metadata) {
+ // Update the focus awareness meta-data value
+ encode_uint32(xr_mode_index == /* XRMode.OVR */ 1 && focus_awareness ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
+ }
+
+ if (tname == "meta-data" && attrname == "value" && value == "plugins_value" && !plugins_names.is_empty()) {
// Update the meta-data 'android:value' attribute with the list of enabled plugins.
string_table.write[attr_value] = plugins_names;
}
+ is_focus_aware_metadata = tname == "meta-data" && attrname == "name" && value == "com.oculus.vr.focusaware";
iofs += 20;
}
@@ -961,10 +1031,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
feature_names.push_back("oculus.software.handtracking");
feature_required_list.push_back(hand_tracking_index == 2);
feature_versions.push_back(-1); // no version attribute should be added.
-
- if (perms.find("com.oculus.permission.HAND_TRACKING") == -1) {
- perms.push_back("com.oculus.permission.HAND_TRACKING");
- }
}
}
@@ -1291,7 +1357,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
} else {
String str;
for (uint32_t i = 0; i < len; i++) {
- CharType c = decode_uint16(&p_bytes[offset + i * 2]);
+ char32_t c = decode_uint16(&p_bytes[offset + i * 2]);
if (c == 0) {
break;
}
@@ -1300,12 +1366,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
return str;
}
}
- void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest) {
+
+ void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest) {
const int UTF8_FLAG = 0x00000100;
- uint32_t string_block_len = decode_uint32(&p_manifest[16]);
- uint32_t string_count = decode_uint32(&p_manifest[20]);
- uint32_t string_flags = decode_uint32(&p_manifest[28]);
+ uint32_t string_block_len = decode_uint32(&r_manifest[16]);
+ uint32_t string_count = decode_uint32(&r_manifest[20]);
+ uint32_t string_flags = decode_uint32(&r_manifest[28]);
const uint32_t string_table_begins = 40;
Vector<String> string_table;
@@ -1313,10 +1380,10 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String package_name = p_preset->get("package/name");
for (uint32_t i = 0; i < string_count; i++) {
- uint32_t offset = decode_uint32(&p_manifest[string_table_begins + i * 4]);
+ uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]);
offset += string_table_begins + string_count * 4;
- String str = _parse_string(&p_manifest[offset], string_flags & UTF8_FLAG);
+ String str = _parse_string(&r_manifest[offset], string_flags & UTF8_FLAG);
if (str.begins_with("godot-project-name")) {
if (str == "godot-project-name") {
@@ -1324,7 +1391,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
str = get_project_name(package_name);
} else {
- String lang = str.substr(str.find_last("-") + 1, str.length()).replace("-", "_");
+ String lang = str.substr(str.rfind("-") + 1, str.length()).replace("-", "_");
String prop = "application/config/name_" + lang;
if (ProjectSettings::get_singleton()->has_setting(prop)) {
str = ProjectSettings::get_singleton()->get(prop);
@@ -1342,7 +1409,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
ret.resize(string_table_begins + string_table.size() * 4);
for (uint32_t i = 0; i < string_table_begins; i++) {
- ret.write[i] = p_manifest[i];
+ ret.write[i] = r_manifest[i];
}
int ofs = 0;
@@ -1377,35 +1444,183 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
//append the rest...
int rest_from = 12 + string_block_len;
int rest_to = ret.size();
- int rest_len = (p_manifest.size() - rest_from);
- ret.resize(ret.size() + (p_manifest.size() - rest_from));
+ int rest_len = (r_manifest.size() - rest_from);
+ ret.resize(ret.size() + (r_manifest.size() - rest_from));
for (int i = 0; i < rest_len; i++) {
- ret.write[rest_to + i] = p_manifest[rest_from + i];
+ ret.write[rest_to + i] = r_manifest[rest_from + i];
}
//finally update the size
encode_uint32(ret.size(), &ret.write[4]);
- p_manifest = ret;
+ r_manifest = ret;
//printf("end\n");
}
- void _process_launcher_icons(const String &p_processing_file_name, const Ref<Image> &p_source_image, const LauncherIcon p_icon, Vector<uint8_t> &p_data) {
- if (p_processing_file_name == p_icon.export_path) {
- Ref<Image> working_image = p_source_image;
+ void _load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data) {
+ Vector<uint8_t> png_buffer;
+ Error err = PNGDriverCommon::image_to_png(p_splash_image, png_buffer);
+ if (err == OK) {
+ p_data.resize(png_buffer.size());
+ memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size());
+ } else {
+ String err_str = String("Failed to convert splash image to png.");
+ WARN_PRINT(err_str.utf8().get_data());
+ }
+ }
+
+ void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data) {
+ Ref<Image> working_image = p_source_image;
- if (p_source_image->get_width() != p_icon.dimensions || p_source_image->get_height() != p_icon.dimensions) {
- working_image = p_source_image->duplicate();
- working_image->resize(p_icon.dimensions, p_icon.dimensions, Image::Interpolation::INTERPOLATE_LANCZOS);
+ if (p_source_image->get_width() != dimension || p_source_image->get_height() != dimension) {
+ working_image = p_source_image->duplicate();
+ working_image->resize(dimension, dimension, Image::Interpolation::INTERPOLATE_LANCZOS);
+ }
+
+ Vector<uint8_t> png_buffer;
+ Error err = PNGDriverCommon::image_to_png(working_image, png_buffer);
+ if (err == OK) {
+ p_data.resize(png_buffer.size());
+ memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size());
+ } else {
+ String err_str = String("Failed to convert resized icon (") + p_file_name + ") to png.";
+ WARN_PRINT(err_str.utf8().get_data());
+ }
+ }
+
+ String load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image) {
+ bool scale_splash = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize");
+ bool apply_filter = ProjectSettings::get_singleton()->get("application/boot_splash/use_filter");
+ String project_splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
+
+ if (!project_splash_path.is_empty()) {
+ splash_image.instance();
+ print_verbose("Loading splash image: " + project_splash_path);
+ const Error err = ImageLoader::load_image(project_splash_path, splash_image);
+ if (err) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")");
+ }
+ splash_image.unref();
}
+ }
- Vector<uint8_t> png_buffer;
- Error err = PNGDriverCommon::image_to_png(working_image, png_buffer);
- if (err == OK) {
- p_data.resize(png_buffer.size());
- memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size());
- } else {
- String err_str = String("Failed to convert resized icon (") + p_processing_file_name + ") to png.";
- WARN_PRINT(err_str.utf8().get_data());
+ if (splash_image.is_null()) {
+ // Use the default
+ print_verbose("Using default splash image.");
+ splash_image = Ref<Image>(memnew(Image(boot_splash_png)));
+ }
+
+ // Setup the splash bg color
+ bool bg_color_valid;
+ Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid);
+ if (!bg_color_valid) {
+ bg_color = boot_splash_bg_color;
+ }
+
+ print_verbose("Creating splash background color image.");
+ splash_bg_color_image.instance();
+ splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format());
+ splash_bg_color_image->fill(bg_color);
+
+ String gravity = scale_splash ? "fill" : "center";
+ String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, gravity, bool_to_string(apply_filter));
+ return processed_splash_config_xml;
+ }
+
+ void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) {
+ String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
+
+ icon.instance();
+ foreground.instance();
+ background.instance();
+
+ // Regular icon: user selection -> project icon -> default.
+ String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges();
+ print_verbose("Loading regular icon from " + path);
+ if (path.is_empty() || ImageLoader::load_image(path, icon) != OK) {
+ print_verbose("- falling back to project icon: " + project_icon_path);
+ ImageLoader::load_image(project_icon_path, icon);
+ }
+
+ // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default).
+ path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges();
+ print_verbose("Loading adaptive foreground icon from " + path);
+ if (path.is_empty() || ImageLoader::load_image(path, foreground) != OK) {
+ print_verbose("- falling back to using the regular icon");
+ foreground = icon;
+ }
+
+ // Adaptive background: user selection -> default.
+ path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges();
+ if (!path.is_empty()) {
+ print_verbose("Loading adaptive background icon from " + path);
+ ImageLoader::load_image(path, background);
+ }
+ }
+
+ void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data) {
+ store_image(launcher_icon.export_path, data);
+ }
+
+ void store_image(const String &export_path, const Vector<uint8_t> &data) {
+ String img_path = export_path.insert(0, "res://android/build/");
+ store_file_at_path(img_path, data);
+ }
+
+ void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
+ const String &processed_splash_config_xml,
+ const Ref<Image> &splash_image,
+ const Ref<Image> &splash_bg_color_image,
+ const Ref<Image> &main_image,
+ const Ref<Image> &foreground,
+ const Ref<Image> &background) {
+ // Store the splash configuration
+ if (!processed_splash_config_xml.is_empty()) {
+ print_verbose("Storing processed splash configuration: " + String("\n") + processed_splash_config_xml);
+ store_string_at_path(SPLASH_CONFIG_PATH, processed_splash_config_xml);
+ }
+
+ // Store the splash image
+ if (splash_image.is_valid() && !splash_image->is_empty()) {
+ print_verbose("Storing splash image in " + String(SPLASH_IMAGE_EXPORT_PATH));
+ Vector<uint8_t> data;
+ _load_image_data(splash_image, data);
+ store_image(SPLASH_IMAGE_EXPORT_PATH, data);
+ }
+
+ // Store the splash bg color image
+ if (splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) {
+ print_verbose("Storing splash background image in " + String(SPLASH_BG_COLOR_PATH));
+ Vector<uint8_t> data;
+ _load_image_data(splash_bg_color_image, data);
+ store_image(SPLASH_BG_COLOR_PATH, data);
+ }
+
+ // Prepare images to be resized for the icons. If some image ends up being uninitialized,
+ // the default image from the export template will be used.
+
+ for (int i = 0; i < icon_densities_count; ++i) {
+ if (main_image.is_valid() && !main_image->is_empty()) {
+ print_verbose("Processing launcher icon for dimension " + itos(launcher_icons[i].dimensions) + " into " + launcher_icons[i].export_path);
+ Vector<uint8_t> data;
+ _process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data);
+ store_image(launcher_icons[i], data);
+ }
+
+ if (foreground.is_valid() && !foreground->is_empty()) {
+ print_verbose("Processing launcher adaptive icon foreground for dimension " + itos(launcher_adaptive_icon_foregrounds[i].dimensions) + " into " + launcher_adaptive_icon_foregrounds[i].export_path);
+ Vector<uint8_t> data;
+ _process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground,
+ launcher_adaptive_icon_foregrounds[i].dimensions, data);
+ store_image(launcher_adaptive_icon_foregrounds[i], data);
+ }
+
+ if (background.is_valid() && !background->is_empty()) {
+ print_verbose("Processing launcher adaptive icon background for dimension " + itos(launcher_adaptive_icon_backgrounds[i].dimensions) + " into " + launcher_adaptive_icon_backgrounds[i].export_path);
+ Vector<uint8_t> data;
+ _process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background,
+ launcher_adaptive_icon_backgrounds[i].dimensions, data);
+ store_image(launcher_adaptive_icon_backgrounds[i], data);
}
}
}
@@ -1423,11 +1638,11 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
public:
- typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total);
+ typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
public:
- virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
- String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name");
+ virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override {
+ String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
if (driver == "GLES2") {
r_features->push_back("etc");
}
@@ -1442,56 +1657,65 @@ public:
}
}
- virtual void get_export_options(List<ExportOption> *r_options) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false));
+ virtual void get_export_options(List<ExportOption> *r_options) override {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_template/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK));
- Vector<PluginConfig> plugins_configs = get_plugins();
+ Vector<PluginConfigAndroid> plugins_configs = get_plugins();
for (int i = 0; i < plugins_configs.size(); i++) {
print_verbose("Found Android plugin " + plugins_configs[i].name);
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false));
}
- plugins_changed = false;
+ plugins_changed.clear();
+
+ Vector<String> abis = get_abis();
+ for (int i = 0; i < abis.size(); ++i) {
+ String abi = abis[i];
+ bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a");
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
+ }
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "xr_features/focus_awareness"), false));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/opengl_debug"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), ""));
- Vector<String> abis = get_abis();
- for (int i = 0; i < abis.size(); ++i) {
- String abi = abis[i];
- bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a");
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
- }
-
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "permissions/custom_permissions"), PackedStringArray()));
const char **perms = android_perms;
@@ -1501,52 +1725,52 @@ public:
}
}
- virtual String get_name() const {
+ virtual String get_name() const override {
return "Android";
}
- virtual String get_os_name() const {
+ virtual String get_os_name() const override {
return "Android";
}
- virtual Ref<Texture2D> get_logo() const {
+ virtual Ref<Texture2D> get_logo() const override {
return logo;
}
- virtual bool should_update_export_options() {
- bool export_options_changed = plugins_changed;
+ virtual bool should_update_export_options() override {
+ bool export_options_changed = plugins_changed.is_set();
if (export_options_changed) {
// don't clear unless we're reporting true, to avoid race
- plugins_changed = false;
+ plugins_changed.clear();
}
return export_options_changed;
}
- virtual bool poll_export() {
- bool dc = devices_changed;
+ virtual bool poll_export() override {
+ bool dc = devices_changed.is_set();
if (dc) {
// don't clear unless we're reporting true, to avoid race
- devices_changed = false;
+ devices_changed.clear();
}
return dc;
}
- virtual int get_options_count() const {
+ virtual int get_options_count() const override {
MutexLock lock(device_lock);
return devices.size();
}
- virtual String get_options_tooltip() const {
+ virtual String get_options_tooltip() const override {
return TTR("Select device from the list");
}
- virtual String get_option_label(int p_index) const {
+ virtual String get_option_label(int p_index) const override {
ERR_FAIL_INDEX_V(p_index, devices.size(), "");
MutexLock lock(device_lock);
return devices[p_index].name;
}
- virtual String get_option_tooltip(int p_index) const {
+ virtual String get_option_tooltip(int p_index) const override {
ERR_FAIL_INDEX_V(p_index, devices.size(), "");
MutexLock lock(device_lock);
String s = devices[p_index].description;
@@ -1559,7 +1783,7 @@ public:
return s;
}
- virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override {
ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);
String can_export_error;
@@ -1573,7 +1797,7 @@ public:
EditorProgress ep("run", "Running on " + devices[p_device].name, 3);
- String adb = EditorSettings::get_singleton()->get("export/android/adb");
+ String adb = get_adb_path();
// Export_temp APK.
if (ep.step("Exporting APK...", 0)) {
@@ -1596,7 +1820,7 @@ public:
}
// Export to temporary APK before sending to device.
- Error err = export_project(p_preset, true, tmp_export_path, p_debug_flags);
+ Error err = export_project_helper(p_preset, true, tmp_export_path, EXPORT_FORMAT_APK, true, p_debug_flags);
if (err != OK) {
CLEANUP_AND_RETURN(err);
@@ -1621,7 +1845,7 @@ public:
args.push_back("uninstall");
args.push_back(get_package_name(package_name));
- err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+ err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
}
print_line("Installing to device (please wait...): " + devices[p_device].name);
@@ -1636,7 +1860,7 @@ public:
args.push_back("-r");
args.push_back(tmp_export_path);
- err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+ err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
if (err || rv != 0) {
EditorNode::add_io_error("Could not install to device.");
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
@@ -1653,7 +1877,7 @@ public:
args.push_back(devices[p_device].id);
args.push_back("reverse");
args.push_back("--remove-all");
- OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+ OS::get_singleton()->execute(adb, args, nullptr, &rv);
if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) {
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
@@ -1664,7 +1888,7 @@ public:
args.push_back("tcp:" + itos(dbg_port));
args.push_back("tcp:" + itos(dbg_port));
- OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+ OS::get_singleton()->execute(adb, args, nullptr, &rv);
print_line("Reverse result: " + itos(rv));
}
@@ -1678,7 +1902,7 @@ public:
args.push_back("tcp:" + itos(fs_port));
args.push_back("tcp:" + itos(fs_port));
- err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+ err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
print_line("Reverse result2: " + itos(rv));
}
} else {
@@ -1706,7 +1930,7 @@ public:
args.push_back("-n");
args.push_back(get_package_name(package_name) + "/com.godot.game.GodotApp");
- err = OS::get_singleton()->execute(adb, args, true, nullptr, nullptr, &rv);
+ err = OS::get_singleton()->execute(adb, args, nullptr, &rv);
if (err || rv != 0) {
EditorNode::add_io_error("Could not execute on device.");
CLEANUP_AND_RETURN(ERR_CANT_CREATE);
@@ -1716,81 +1940,160 @@ public:
#undef CLEANUP_AND_RETURN
}
- virtual Ref<Texture2D> get_run_icon() const {
+ virtual Ref<Texture2D> get_run_icon() const override {
return run_icon;
}
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+ static String get_adb_path() {
+ String exe_ext = "";
+ if (OS::get_singleton()->get_name() == "Windows") {
+ exe_ext = ".exe";
+ }
+ String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path");
+ return sdk_path.plus_file("platform-tools/adb" + exe_ext);
+ }
+
+ static String get_apksigner_path() {
+ String exe_ext = "";
+ if (OS::get_singleton()->get_name() == "Windows") {
+ exe_ext = ".bat";
+ }
+ String apksigner_command_name = "apksigner" + exe_ext;
+ String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path");
+ String apksigner_path = "";
+
+ Error errn;
+ String build_tools_dir = sdk_path.plus_file("build-tools");
+ DirAccessRef da = DirAccess::open(build_tools_dir, &errn);
+ if (errn != OK) {
+ print_error("Unable to open Android 'build-tools' directory.");
+ return apksigner_path;
+ }
+
+ // There are additional versions directories we need to go through.
+ da->list_dir_begin();
+ String sub_dir = da->get_next();
+ while (!sub_dir.is_empty()) {
+ if (!sub_dir.begins_with(".") && da->current_is_dir()) {
+ // Check if the tool is here.
+ String tool_path = build_tools_dir.plus_file(sub_dir).plus_file(apksigner_command_name);
+ if (FileAccess::exists(tool_path)) {
+ apksigner_path = tool_path;
+ break;
+ }
+ }
+ sub_dir = da->get_next();
+ }
+ da->list_dir_end();
+
+ if (apksigner_path.is_empty()) {
+ EditorNode::get_singleton()->show_warning(TTR("Unable to find the 'apksigner' tool."));
+ }
+
+ return apksigner_path;
+ }
+
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override {
String err;
bool valid = false;
// Look for export templates (first official, and if defined custom templates).
if (!bool(p_preset->get("custom_template/use_custom_build"))) {
- bool dvalid = exists_export_template("android_debug.apk", &err);
- bool rvalid = exists_export_template("android_release.apk", &err);
+ String template_err;
+ bool dvalid = false;
+ bool rvalid = false;
if (p_preset->get("custom_template/debug") != "") {
dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
if (!dvalid) {
- err += TTR("Custom debug template not found.") + "\n";
+ template_err += TTR("Custom debug template not found.") + "\n";
}
+ } else {
+ dvalid = exists_export_template("android_debug.apk", &template_err);
}
+
if (p_preset->get("custom_template/release") != "") {
rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
if (!rvalid) {
- err += TTR("Custom release template not found.") + "\n";
+ template_err += TTR("Custom release template not found.") + "\n";
}
+ } else {
+ rvalid = exists_export_template("android_release.apk", &template_err);
}
valid = dvalid || rvalid;
+ if (!valid) {
+ err += template_err;
+ }
} else {
valid = exists_export_template("android_source.zip", &err);
+
+ if (!FileAccess::exists("res://android/build/build.gradle")) {
+ err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n";
+ valid = false;
+ }
}
r_missing_templates = !valid;
// Validate the rest of the configuration.
- String adb = EditorSettings::get_singleton()->get("export/android/adb");
+ String dk = p_preset->get("keystore/debug");
- if (!FileAccess::exists(adb)) {
- valid = false;
- err += TTR("ADB executable not configured in the Editor Settings.") + "\n";
+ if (!FileAccess::exists(dk)) {
+ dk = EditorSettings::get_singleton()->get("export/android/debug_keystore");
+ if (!FileAccess::exists(dk)) {
+ valid = false;
+ err += TTR("Debug keystore not configured in the Editor Settings nor in the preset.") + "\n";
+ }
}
- String js = EditorSettings::get_singleton()->get("export/android/jarsigner");
+ String rk = p_preset->get("keystore/release");
- if (!FileAccess::exists(js)) {
+ if (!rk.is_empty() && !FileAccess::exists(rk)) {
valid = false;
- err += TTR("OpenJDK jarsigner not configured in the Editor Settings.") + "\n";
+ err += TTR("Release keystore incorrectly configured in the export preset.") + "\n";
}
- String dk = p_preset->get("keystore/debug");
+ String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path");
+ if (sdk_path == "") {
+ err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n";
+ valid = false;
+ } else {
+ Error errn;
+ // Check for the platform-tools directory.
+ DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn);
+ if (errn != OK) {
+ err += TTR("Invalid Android SDK path in Editor Settings.");
+ err += TTR("Missing 'platform-tools' directory!");
+ err += "\n";
+ valid = false;
+ }
- if (!FileAccess::exists(dk)) {
- dk = EditorSettings::get_singleton()->get("export/android/debug_keystore");
- if (!FileAccess::exists(dk)) {
+ // Validate that adb is available
+ String adb_path = get_adb_path();
+ if (!FileAccess::exists(adb_path)) {
+ err += TTR("Unable to find Android SDK platform-tools' adb command.");
+ err += TTR("Please check in the Android SDK directory specified in Editor Settings.");
+ err += "\n";
valid = false;
- err += TTR("Debug keystore not configured in the Editor Settings nor in the preset.") + "\n";
}
- }
- if (bool(p_preset->get("custom_template/use_custom_build"))) {
- String sdk_path = EditorSettings::get_singleton()->get("export/android/custom_build_sdk_path");
- if (sdk_path == "") {
- err += TTR("Custom build requires a valid Android SDK path in Editor Settings.") + "\n";
+ // Check for the build-tools directory.
+ DirAccessRef build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn);
+ if (errn != OK) {
+ err += TTR("Invalid Android SDK path in Editor Settings.");
+ err += TTR("Missing 'build-tools' directory!");
+ err += "\n";
valid = false;
- } else {
- Error errn;
- DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn);
- if (errn != OK) {
- err += TTR("Invalid Android SDK path for custom build in Editor Settings.") + "\n";
- valid = false;
- }
}
- if (!FileAccess::exists("res://android/build/build.gradle")) {
- err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n";
+ // Validate that apksigner is available
+ String apksigner_path = get_apksigner_path();
+ if (!FileAccess::exists(apksigner_path)) {
+ err += TTR("Unable to find Android SDK build-tools' apksigner command.");
+ err += TTR("Please check in the Android SDK directory specified in Editor Settings.");
+ err += "\n";
valid = false;
}
}
@@ -1821,46 +2124,391 @@ public:
err += etc_error;
}
+ // Ensure that `Use Custom Build` is enabled if a plugin is selected.
+ String enabled_plugins_names = get_plugins_names(get_enabled_plugins(p_preset));
+ bool custom_build_enabled = p_preset->get("custom_template/use_custom_build");
+ if (!enabled_plugins_names.is_empty() && !custom_build_enabled) {
+ valid = false;
+ err += TTR("\"Use Custom Build\" must be enabled to use the plugins.");
+ err += "\n";
+ }
+
+ // Validate the Xr features are properly populated
+ int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ int degrees_of_freedom = p_preset->get("xr_features/degrees_of_freedom");
+ int hand_tracking = p_preset->get("xr_features/hand_tracking");
+ bool focus_awareness = p_preset->get("xr_features/focus_awareness");
+ if (xr_mode_index != /* XRMode.OVR*/ 1) {
+ if (degrees_of_freedom > 0) {
+ valid = false;
+ err += TTR("\"Degrees Of Freedom\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+ err += "\n";
+ }
+
+ if (hand_tracking > 0) {
+ valid = false;
+ err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+ err += "\n";
+ }
+
+ if (focus_awareness) {
+ valid = false;
+ err += TTR("\"Focus Awareness\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\".");
+ err += "\n";
+ }
+ }
+
+ if (int(p_preset->get("custom_template/export_format")) == EXPORT_FORMAT_AAB &&
+ !bool(p_preset->get("custom_template/use_custom_build"))) {
+ valid = false;
+ err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled.");
+ err += "\n";
+ }
+
r_error = err;
return valid;
}
- virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
List<String> list;
list.push_back("apk");
+ list.push_back("aab");
return list;
}
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
+ inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) {
+ String plugin_names = get_plugins_names(enabled_plugins);
+ bool first_build = last_custom_build_time == 0;
+ bool have_plugins_changed = false;
+
+ if (!first_build) {
+ have_plugins_changed = plugin_names != last_plugin_names;
+ if (!have_plugins_changed) {
+ for (int i = 0; i < enabled_plugins.size(); i++) {
+ if (enabled_plugins.get(i).last_updated > last_custom_build_time) {
+ have_plugins_changed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ last_custom_build_time = OS::get_singleton()->get_unix_time();
+ last_plugin_names = plugin_names;
+
+ return have_plugins_changed || first_build;
+ }
+
+ String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+ int version_code = p_preset->get("version/code");
+ String package_name = p_preset->get("package/unique_name");
+ String apk_file_name = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb";
+ String fullpath = p_path.get_base_dir().plus_file(apk_file_name);
+ return fullpath;
+ }
+
+ Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+ String fullpath = get_apk_expansion_fullpath(p_preset, p_path);
+ Error err = save_pack(p_preset, fullpath);
+ return err;
+ }
+
+ void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) {
+ String cmdline = p_preset->get("command_line/extra_args");
+ Vector<String> command_line_strings = cmdline.strip_edges().split(" ");
+ for (int i = 0; i < command_line_strings.size(); i++) {
+ if (command_line_strings[i].strip_edges().length() == 0) {
+ command_line_strings.remove(i);
+ i--;
+ }
+ }
+
+ gen_export_flags(command_line_strings, p_flags);
+
+ bool apk_expansion = p_preset->get("apk_expansion/enable");
+ if (apk_expansion) {
+ String fullpath = get_apk_expansion_fullpath(p_preset, p_path);
+ String apk_expansion_public_key = p_preset->get("apk_expansion/public_key");
+
+ command_line_strings.push_back("--use_apk_expansion");
+ command_line_strings.push_back("--apk_expansion_md5");
+ command_line_strings.push_back(FileAccess::get_md5(fullpath));
+ command_line_strings.push_back("--apk_expansion_key");
+ command_line_strings.push_back(apk_expansion_public_key.strip_edges());
+ }
+
+ int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ if (xr_mode_index == 1) {
+ command_line_strings.push_back("--xr_mode_ovr");
+ } else { // XRMode.REGULAR is the default.
+ command_line_strings.push_back("--xr_mode_regular");
+ }
+
+ bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer");
+ if (use_32_bit_framebuffer) {
+ command_line_strings.push_back("--use_depth_32");
+ }
+
+ bool immersive = p_preset->get("screen/immersive_mode");
+ if (immersive) {
+ command_line_strings.push_back("--use_immersive");
+ }
+
+ bool debug_opengl = p_preset->get("graphics/opengl_debug");
+ if (debug_opengl) {
+ command_line_strings.push_back("--debug_opengl");
+ }
+
+ if (command_line_strings.size()) {
+ r_command_line_flags.resize(4);
+ encode_uint32(command_line_strings.size(), &r_command_line_flags.write[0]);
+ for (int i = 0; i < command_line_strings.size(); i++) {
+ print_line(itos(i) + " param: " + command_line_strings[i]);
+ CharString command_line_argument = command_line_strings[i].utf8();
+ int base = r_command_line_flags.size();
+ int length = command_line_argument.length();
+ if (length == 0)
+ continue;
+ r_command_line_flags.resize(base + 4 + length);
+ encode_uint32(length, &r_command_line_flags.write[base]);
+ copymem(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length);
+ }
+ }
+ }
+
+ Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
+ int export_format = int(p_preset->get("custom_template/export_format"));
+ String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
+ String release_keystore = p_preset->get("keystore/release");
+ String release_username = p_preset->get("keystore/release_user");
+ String release_password = p_preset->get("keystore/release_password");
+
+ String apksigner = get_apksigner_path();
+ print_verbose("Starting signing of the " + export_label + " binary using " + apksigner);
+ if (!FileAccess::exists(apksigner)) {
+ EditorNode::add_io_error("'apksigner' could not be found.\nPlease check the command is available in the Android SDK build-tools directory.\nThe resulting " + export_label + " is unsigned.");
+ return OK;
+ }
+
+ String keystore;
+ String password;
+ String user;
+ if (p_debug) {
+ keystore = p_preset->get("keystore/debug");
+ password = p_preset->get("keystore/debug_password");
+ user = p_preset->get("keystore/debug_user");
+
+ if (keystore.is_empty()) {
+ keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore");
+ password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass");
+ user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user");
+ }
+
+ if (ep.step("Signing debug " + export_label + "...", 104)) {
+ return ERR_SKIP;
+ }
+
+ } else {
+ keystore = release_keystore;
+ password = release_password;
+ user = release_username;
+
+ if (ep.step("Signing release " + export_label + "...", 104)) {
+ return ERR_SKIP;
+ }
+ }
+
+ if (!FileAccess::exists(keystore)) {
+ EditorNode::add_io_error("Could not find keystore, unable to export.");
+ return ERR_FILE_CANT_OPEN;
+ }
+
+ List<String> args;
+ args.push_back("sign");
+ args.push_back("--verbose");
+ args.push_back("--ks");
+ args.push_back(keystore);
+ args.push_back("--ks-pass");
+ args.push_back("pass:" + password);
+ args.push_back("--ks-key-alias");
+ args.push_back(user);
+ args.push_back(export_path);
+ if (p_debug) {
+ // We only print verbose logs for debug builds to avoid leaking release keystore credentials.
+ print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
+ }
+ int retval;
+ OS::get_singleton()->execute(apksigner, args, nullptr, &retval);
+ if (retval) {
+ EditorNode::add_io_error("'apksigner' returned with error #" + itos(retval));
+ return ERR_CANT_CREATE;
+ }
+
+ if (ep.step("Verifying " + export_label + "...", 105)) {
+ return ERR_SKIP;
+ }
+
+ args.clear();
+ args.push_back("verify");
+ args.push_back("--verbose");
+ args.push_back(export_path);
+ if (p_debug) {
+ print_verbose("Verifying signed build using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
+ }
+
+ OS::get_singleton()->execute(apksigner, args, nullptr, &retval);
+ if (retval) {
+ EditorNode::add_io_error("'apksigner' verification of " + export_label + " failed.");
+ return ERR_CANT_CREATE;
+ }
+
+ print_verbose("Successfully completed signing build.");
+ return OK;
+ }
+
+ void _clear_assets_directory() {
+ DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ if (da_res->dir_exists("res://android/build/assets")) {
+ print_verbose("Clearing assets directory..");
+ DirAccessRef da_assets = DirAccess::open("res://android/build/assets");
+ da_assets->erase_contents_recursive();
+ da_res->remove("res://android/build/assets");
+ }
+ }
+
+ String join_list(List<String> parts, const String &separator) const {
+ String ret;
+ for (int i = 0; i < parts.size(); ++i) {
+ if (i > 0) {
+ ret += separator;
+ }
+ ret += parts[i];
+ }
+ return ret;
+ }
+
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override {
+ int export_format = int(p_preset->get("custom_template/export_format"));
+ bool should_sign = p_preset->get("package/signed");
+ return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags);
+ }
+
+ Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
String src_apk;
+ Error err;
EditorProgress ep("export", "Exporting for Android", 105, true);
- if (bool(p_preset->get("custom_template/use_custom_build"))) { //custom build
- //re-generate build.gradle and AndroidManifest.xml
+ bool use_custom_build = bool(p_preset->get("custom_template/use_custom_build"));
+ bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG);
+ bool apk_expansion = p_preset->get("apk_expansion/enable");
+ Vector<String> enabled_abis = get_enabled_abis(p_preset);
+
+ print_verbose("Exporting for Android...");
+ print_verbose("- debug build: " + bool_to_string(p_debug));
+ print_verbose("- export path: " + p_path);
+ print_verbose("- export format: " + itos(export_format));
+ print_verbose("- sign build: " + bool_to_string(should_sign));
+ print_verbose("- custom build enabled: " + bool_to_string(use_custom_build));
+ print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion));
+ print_verbose("- enabled abis: " + String(",").join(enabled_abis));
+ print_verbose("- export filter: " + itos(p_preset->get_export_filter()));
+ print_verbose("- include filter: " + p_preset->get_include_filter());
+ print_verbose("- exclude filter: " + p_preset->get_exclude_filter());
+
+ Ref<Image> splash_image;
+ Ref<Image> splash_bg_color_image;
+ String processed_splash_config_xml = load_splash_refs(splash_image, splash_bg_color_image);
+
+ Ref<Image> main_image;
+ Ref<Image> foreground;
+ Ref<Image> background;
+
+ load_icon_refs(p_preset, main_image, foreground, background);
+
+ Vector<uint8_t> command_line_flags;
+ // Write command line flags into the command_line_flags variable.
+ get_command_line_flags(p_preset, p_path, p_flags, command_line_flags);
+
+ if (export_format == EXPORT_FORMAT_AAB) {
+ if (!p_path.ends_with(".aab")) {
+ EditorNode::get_singleton()->show_warning(TTR("Invalid filename! Android App Bundle requires the *.aab extension."));
+ return ERR_UNCONFIGURED;
+ }
+ if (apk_expansion) {
+ EditorNode::get_singleton()->show_warning(TTR("APK Expansion not compatible with Android App Bundle."));
+ return ERR_UNCONFIGURED;
+ }
+ }
+ if (export_format == EXPORT_FORMAT_APK && !p_path.ends_with(".apk")) {
+ EditorNode::get_singleton()->show_warning(
+ TTR("Invalid filename! Android APK requires the *.apk extension."));
+ return ERR_UNCONFIGURED;
+ }
+ if (export_format > EXPORT_FORMAT_AAB || export_format < EXPORT_FORMAT_APK) {
+ EditorNode::add_io_error("Unsupported export format!\n");
+ return ERR_UNCONFIGURED; //TODO: is this the right error?
+ }
- { //test that installed build version is alright
+ if (use_custom_build) {
+ print_verbose("Starting custom build..");
+ //test that installed build version is alright
+ {
+ print_verbose("Checking build version..");
FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ);
if (!f) {
EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu."));
return ERR_UNCONFIGURED;
}
String version = f->get_line().strip_edges();
+ print_verbose("- build version: " + version);
+ f->close();
if (version != VERSION_FULL_CONFIG) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG));
return ERR_UNCONFIGURED;
}
}
- //build project if custom build is enabled
- String sdk_path = EDITOR_GET("export/android/custom_build_sdk_path");
-
- ERR_FAIL_COND_V_MSG(sdk_path == "", ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/custom_build_sdk_path'.");
+ String sdk_path = EDITOR_GET("export/android/android_sdk_path");
+ ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'.");
+ print_verbose("Android sdk path: " + sdk_path);
+
+ // TODO: should we use "package/name" or "application/config/name"?
+ String project_name = get_project_name(p_preset->get("package/name"));
+ err = _create_project_name_strings_files(p_preset, project_name); //project name localization.
+ if (err != OK) {
+ EditorNode::add_io_error("Unable to overwrite res://android/build/res/*.xml files with project name");
+ }
+ // Copies the project icon files into the appropriate Gradle project directory.
+ _copy_icons_to_gradle_project(p_preset, processed_splash_config_xml, splash_image, splash_bg_color_image, main_image, foreground, background);
+ // Write an AndroidManifest.xml file into the Gradle project directory.
+ _write_tmp_manifest(p_preset, p_give_internet, p_debug);
+
+ //stores all the project files inside the Gradle project directory. Also includes all ABIs
+ _clear_assets_directory();
+ if (!apk_expansion) {
+ print_verbose("Exporting project files..");
+ err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, NULL, ignore_so_file);
+ if (err != OK) {
+ EditorNode::add_io_error("Could not export project files to gradle project\n");
+ return err;
+ }
+ } else {
+ print_verbose("Saving apk expansion file..");
+ err = save_apk_expansion_file(p_preset, p_path);
+ if (err != OK) {
+ EditorNode::add_io_error("Could not write expansion package file!");
+ return err;
+ }
+ }
+ print_verbose("Storing command line flags..");
+ store_file_at_path("res://android/build/assets/_cl_", command_line_flags);
+ print_verbose("Updating ANDROID_HOME environment to " + sdk_path);
OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required
-
String build_command;
+
#ifdef WINDOWS_ENABLED
build_command = "gradlew.bat";
#else
@@ -1868,65 +2516,124 @@ public:
#endif
String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build");
-
build_command = build_path.plus_file(build_command);
String package_name = get_package_name(p_preset->get("package/unique_name"));
-
- Vector<PluginConfig> enabled_plugins = get_enabled_plugins(p_preset);
- String local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins);
- String remote_plugins_binaries = get_plugins_binaries(BINARY_TYPE_REMOTE, enabled_plugins);
+ String version_code = itos(p_preset->get("version/code"));
+ String version_name = p_preset->get("version/name");
+ String enabled_abi_string = String("|").join(enabled_abis);
+ String sign_flag = should_sign ? "true" : "false";
+ String zipalign_flag = "true";
+
+ Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset);
+ String local_plugins_binaries = get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins);
+ String remote_plugins_binaries = get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins);
String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins);
+ bool clean_build_required = is_clean_build_required(enabled_plugins);
List<String> cmdline;
- cmdline.push_back("build");
+ if (clean_build_required) {
+ cmdline.push_back("clean");
+ }
+
+ String build_type = p_debug ? "Debug" : "Release";
+ if (export_format == EXPORT_FORMAT_AAB) {
+ String bundle_build_command = vformat("bundle%s", build_type);
+ cmdline.push_back(bundle_build_command);
+ } else if (export_format == EXPORT_FORMAT_APK) {
+ String apk_build_command = vformat("assemble%s", build_type);
+ cmdline.push_back(apk_build_command);
+ }
+
+ cmdline.push_back("-p"); // argument to specify the start directory.
+ cmdline.push_back(build_path); // start directory.
cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
+ cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code.
+ cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name.
+ cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs.
cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies.
cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies.
cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies.
- cmdline.push_back("-p"); // argument to specify the start directory.
- cmdline.push_back(build_path); // start directory.
- /*{ used for debug
- int ec;
- String pipe;
- OS::get_singleton()->execute(build_command, cmdline, true, nullptr, nullptr, &ec);
- print_line("exit code: " + itos(ec));
+ cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
+ cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
+ cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG));
+
+ // NOTE: The release keystore is not included in the verbose logging
+ // to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting.
+ // Any non-sensitive additions to the command line arguments must be done above this section.
+ // Sensitive additions must be done below the logging statement.
+ print_verbose("Build Android project using gradle command: " + String("\n") + build_command + " " + join_list(cmdline, String(" ")));
+
+ if (should_sign && !p_debug) {
+ // Pass the release keystore info as well
+ String release_keystore = p_preset->get("keystore/release");
+ String release_username = p_preset->get("keystore/release_user");
+ String release_password = p_preset->get("keystore/release_password");
+ if (!FileAccess::exists(release_keystore)) {
+ EditorNode::add_io_error("Could not find keystore, unable to export.");
+ return ERR_FILE_CANT_OPEN;
+ }
+
+ cmdline.push_back("-Prelease_keystore_file=" + release_keystore); // argument to specify the release keystore file.
+ cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias.
+ cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specity the release keystore password.
}
- */
+
int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
if (result != 0) {
EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation."));
return ERR_CANT_CREATE;
}
- if (p_debug) {
- src_apk = build_path.plus_file("build/outputs/apk/debug/android_debug.apk");
- } else {
- src_apk = build_path.plus_file("build/outputs/apk/release/android_release.apk");
+
+ List<String> copy_args;
+ String copy_command;
+ if (export_format == EXPORT_FORMAT_AAB) {
+ copy_command = vformat("copyAndRename%sAab", build_type);
+ } else if (export_format == EXPORT_FORMAT_APK) {
+ copy_command = vformat("copyAndRename%sApk", build_type);
+ }
+
+ copy_args.push_back(copy_command);
+
+ copy_args.push_back("-p"); // argument to specify the start directory.
+ copy_args.push_back(build_path); // start directory.
+
+ String export_filename = p_path.get_file();
+ String export_path = p_path.get_base_dir();
+ if (export_path.is_rel_path()) {
+ export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path);
}
+ export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path();
+
+ copy_args.push_back("-Pexport_path=file:" + export_path);
+ copy_args.push_back("-Pexport_filename=" + export_filename);
- if (!FileAccess::exists(src_apk)) {
- EditorNode::get_singleton()->show_warning(TTR("No build apk generated at: ") + "\n" + src_apk);
+ print_verbose("Copying Android binary using gradle command: " + String("\n") + build_command + " " + join_list(copy_args, String(" ")));
+ int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args);
+ if (copy_result != 0) {
+ EditorNode::get_singleton()->show_warning(TTR("Unable to copy and rename export file, check gradle project directory for outputs."));
return ERR_CANT_CREATE;
}
- } else {
+ print_verbose("Successfully completed Android custom build.");
+ return OK;
+ }
+ // This is the start of the Legacy build system
+ print_verbose("Starting legacy build system..");
+ if (p_debug)
+ src_apk = p_preset->get("custom_template/debug");
+ else
+ src_apk = p_preset->get("custom_template/release");
+ src_apk = src_apk.strip_edges();
+ if (src_apk == "") {
if (p_debug) {
- src_apk = p_preset->get("custom_template/debug");
+ src_apk = find_export_template("android_debug.apk");
} else {
- src_apk = p_preset->get("custom_template/release");
+ src_apk = find_export_template("android_release.apk");
}
-
- src_apk = src_apk.strip_edges();
if (src_apk == "") {
- if (p_debug) {
- src_apk = find_export_template("android_debug.apk");
- } else {
- src_apk = find_export_template("android_release.apk");
- }
- if (src_apk == "") {
- EditorNode::add_io_error("Package not found: " + src_apk);
- return ERR_FILE_NOT_FOUND;
- }
+ EditorNode::add_io_error("Package not found: " + src_apk);
+ return ERR_FILE_NOT_FOUND;
}
}
@@ -1963,57 +2670,13 @@ public:
zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2);
- bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer");
- bool immersive = p_preset->get("screen/immersive_mode");
- bool debug_opengl = p_preset->get("screen/opengl_debug");
-
- bool _signed = p_preset->get("package/signed");
-
- bool apk_expansion = p_preset->get("apk_expansion/enable");
-
String cmdline = p_preset->get("command_line/extra_args");
- int version_code = p_preset->get("version/code");
String version_name = p_preset->get("version/name");
String package_name = p_preset->get("package/unique_name");
String apk_expansion_pkey = p_preset->get("apk_expansion/public_key");
- String release_keystore = p_preset->get("keystore/release");
- String release_username = p_preset->get("keystore/release_user");
- String release_password = p_preset->get("keystore/release_password");
-
- Vector<String> enabled_abis = get_enabled_abis(p_preset);
-
- String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon");
-
- // Prepare images to be resized for the icons. If some image ends up being uninitialized, the default image from the export template will be used.
- Ref<Image> launcher_icon_image;
- Ref<Image> launcher_adaptive_icon_foreground_image;
- Ref<Image> launcher_adaptive_icon_background_image;
-
- launcher_icon_image.instance();
- launcher_adaptive_icon_foreground_image.instance();
- launcher_adaptive_icon_background_image.instance();
-
- // Regular icon: user selection -> project icon -> default.
- String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges();
- if (path.empty() || ImageLoader::load_image(path, launcher_icon_image) != OK) {
- ImageLoader::load_image(project_icon_path, launcher_icon_image);
- }
-
- // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default).
- path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges();
- if (path.empty() || ImageLoader::load_image(path, launcher_adaptive_icon_foreground_image) != OK) {
- launcher_adaptive_icon_foreground_image = launcher_icon_image;
- }
-
- // Adaptive background: user selection -> default.
- path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges();
- if (!path.empty()) {
- ImageLoader::load_image(path, launcher_adaptive_icon_background_image);
- }
-
Vector<String> invalid_abis(enabled_abis);
while (ret == UNZ_OK) {
//get filename
@@ -2034,24 +2697,38 @@ public:
unzCloseCurrentFile(pkg);
//write
-
if (file == "AndroidManifest.xml") {
- _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG));
+ _fix_manifest(p_preset, data, p_give_internet);
}
-
if (file == "resources.arsc") {
_fix_resources(p_preset, data);
}
+ // Process the splash image
+ if (file == SPLASH_IMAGE_EXPORT_PATH && splash_image.is_valid() && !splash_image->is_empty()) {
+ _load_image_data(splash_image, data);
+ }
+
+ // Process the splash bg color image
+ if (file == SPLASH_BG_COLOR_PATH && splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) {
+ _load_image_data(splash_bg_color_image, data);
+ }
+
for (int i = 0; i < icon_densities_count; ++i) {
- if (launcher_icon_image.is_valid() && !launcher_icon_image->empty()) {
- _process_launcher_icons(file, launcher_icon_image, launcher_icons[i], data);
+ if (main_image.is_valid() && !main_image->is_empty()) {
+ if (file == launcher_icons[i].export_path) {
+ _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data);
+ }
}
- if (launcher_adaptive_icon_foreground_image.is_valid() && !launcher_adaptive_icon_foreground_image->empty()) {
- _process_launcher_icons(file, launcher_adaptive_icon_foreground_image, launcher_adaptive_icon_foregrounds[i], data);
+ if (foreground.is_valid() && !foreground->is_empty()) {
+ if (file == launcher_adaptive_icon_foregrounds[i].export_path) {
+ _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data);
+ }
}
- if (launcher_adaptive_icon_background_image.is_valid() && !launcher_adaptive_icon_background_image->empty()) {
- _process_launcher_icons(file, launcher_adaptive_icon_background_image, launcher_adaptive_icon_backgrounds[i], data);
+ if (background.is_valid() && !background->is_empty()) {
+ if (file == launcher_adaptive_icon_backgrounds[i].export_path) {
+ _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data);
+ }
}
}
@@ -2069,7 +2746,7 @@ public:
}
}
- if (file.begins_with("META-INF") && _signed) {
+ if (file.begins_with("META-INF") && should_sign) {
skip = true;
}
@@ -2099,7 +2776,7 @@ public:
ret = unzGoToNextFile(pkg);
}
- if (!invalid_abis.empty()) {
+ if (!invalid_abis.is_empty()) {
String unsupported_arch = String(", ").join(invalid_abis);
EditorNode::add_io_error("Missing libraries in the export template for the selected architectures: " + unsupported_arch + ".\n" +
"Please build a template with all required libraries, or uncheck the missing architectures in the export preset.");
@@ -2109,16 +2786,7 @@ public:
if (ep.step("Adding files...", 1)) {
CLEANUP_AND_RETURN(ERR_SKIP);
}
- Error err = OK;
- Vector<String> cl = cmdline.strip_edges().split(" ");
- for (int i = 0; i < cl.size(); i++) {
- if (cl[i].strip_edges().length() == 0) {
- cl.remove(i);
- i--;
- }
- }
-
- gen_export_flags(cl, p_flags);
+ err = OK;
if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
APKExportData ed;
@@ -2126,90 +2794,39 @@ public:
ed.apk = unaligned_apk;
err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so);
} else {
- //all files
-
if (apk_expansion) {
- String apkfname = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb";
- String fullpath = p_path.get_base_dir().plus_file(apkfname);
- err = save_pack(p_preset, fullpath);
-
+ err = save_apk_expansion_file(p_preset, p_path);
if (err != OK) {
- unzClose(pkg);
- EditorNode::add_io_error("Could not write expansion package file: " + apkfname);
-
- CLEANUP_AND_RETURN(ERR_SKIP);
+ EditorNode::add_io_error("Could not write expansion package file!");
+ return err;
}
-
- cl.push_back("--use_apk_expansion");
- cl.push_back("--apk_expansion_md5");
- cl.push_back(FileAccess::get_md5(fullpath));
- cl.push_back("--apk_expansion_key");
- cl.push_back(apk_expansion_pkey.strip_edges());
-
} else {
APKExportData ed;
ed.ep = &ep;
ed.apk = unaligned_apk;
-
err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so);
}
}
- int xr_mode_index = p_preset->get("xr_features/xr_mode");
- if (xr_mode_index == 1 /* XRMode.OVR */) {
- cl.push_back("--xr_mode_ovr");
- } else {
- // XRMode.REGULAR is the default.
- cl.push_back("--xr_mode_regular");
- }
-
- if (use_32_fb) {
- cl.push_back("--use_depth_32");
- }
-
- if (immersive) {
- cl.push_back("--use_immersive");
- }
-
- if (debug_opengl) {
- cl.push_back("--debug_opengl");
- }
-
- if (cl.size()) {
- //add comandline
- Vector<uint8_t> clf;
- clf.resize(4);
- encode_uint32(cl.size(), &clf.write[0]);
- for (int i = 0; i < cl.size(); i++) {
- print_line(itos(i) + " param: " + cl[i]);
- CharString txt = cl[i].utf8();
- int base = clf.size();
- int length = txt.length();
- if (!length) {
- continue;
- }
- clf.resize(base + 4 + length);
- encode_uint32(length, &clf.write[base]);
- copymem(&clf.write[base + 4], txt.ptr(), length);
- }
-
- zip_fileinfo zipfi = get_zip_fileinfo();
-
- zipOpenNewFileInZip(unaligned_apk,
- "assets/_cl_",
- &zipfi,
- nullptr,
- 0,
- nullptr,
- 0,
- nullptr,
- 0, // No compress (little size gain and potentially slower startup)
- Z_DEFAULT_COMPRESSION);
-
- zipWriteInFileInZip(unaligned_apk, clf.ptr(), clf.size());
- zipCloseFileInZip(unaligned_apk);
+ if (err != OK) {
+ unzClose(pkg);
+ EditorNode::add_io_error("Could not export project files");
+ CLEANUP_AND_RETURN(ERR_SKIP);
}
+ zip_fileinfo zipfi = get_zip_fileinfo();
+ zipOpenNewFileInZip(unaligned_apk,
+ "assets/_cl_",
+ &zipfi,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ 0, // No compress (little size gain and potentially slower startup)
+ Z_DEFAULT_COMPRESSION);
+ zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size());
+ zipCloseFileInZip(unaligned_apk);
zipClose(unaligned_apk, nullptr);
unzClose(pkg);
@@ -2217,93 +2834,13 @@ public:
CLEANUP_AND_RETURN(err);
}
- if (_signed) {
- String jarsigner = EditorSettings::get_singleton()->get("export/android/jarsigner");
- if (!FileAccess::exists(jarsigner)) {
- EditorNode::add_io_error("'jarsigner' could not be found.\nPlease supply a path in the Editor Settings.\nThe resulting APK is unsigned.");
- CLEANUP_AND_RETURN(OK);
- }
-
- String keystore;
- String password;
- String user;
- if (p_debug) {
- keystore = p_preset->get("keystore/debug");
- password = p_preset->get("keystore/debug_password");
- user = p_preset->get("keystore/debug_user");
-
- if (keystore.empty()) {
- keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore");
- password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass");
- user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user");
- }
-
- if (ep.step("Signing debug APK...", 103)) {
- CLEANUP_AND_RETURN(ERR_SKIP);
- }
-
- } else {
- keystore = release_keystore;
- password = release_password;
- user = release_username;
-
- if (ep.step("Signing release APK...", 103)) {
- CLEANUP_AND_RETURN(ERR_SKIP);
- }
- }
-
- if (!FileAccess::exists(keystore)) {
- EditorNode::add_io_error("Could not find keystore, unable to export.");
- CLEANUP_AND_RETURN(ERR_FILE_CANT_OPEN);
- }
-
- List<String> args;
- args.push_back("-digestalg");
- args.push_back("SHA-256");
- args.push_back("-sigalg");
- args.push_back("SHA256withRSA");
- String tsa_url = EditorSettings::get_singleton()->get("export/android/timestamping_authority_url");
- if (tsa_url != "") {
- args.push_back("-tsa");
- args.push_back(tsa_url);
- }
- args.push_back("-verbose");
- args.push_back("-keystore");
- args.push_back(keystore);
- args.push_back("-storepass");
- args.push_back(password);
- args.push_back(tmp_unaligned_path);
- args.push_back(user);
- int retval;
- OS::get_singleton()->execute(jarsigner, args, true, nullptr, nullptr, &retval);
- if (retval) {
- EditorNode::add_io_error("'jarsigner' returned with error #" + itos(retval));
- CLEANUP_AND_RETURN(ERR_CANT_CREATE);
- }
-
- if (ep.step("Verifying APK...", 104)) {
- CLEANUP_AND_RETURN(ERR_SKIP);
- }
-
- args.clear();
- args.push_back("-verify");
- args.push_back("-keystore");
- args.push_back(keystore);
- args.push_back(tmp_unaligned_path);
- args.push_back("-verbose");
-
- OS::get_singleton()->execute(jarsigner, args, true, nullptr, nullptr, &retval);
- if (retval) {
- EditorNode::add_io_error("'jarsigner' verification of APK failed. Make sure to use a jarsigner from OpenJDK 8.");
- CLEANUP_AND_RETURN(ERR_CANT_CREATE);
- }
- }
-
- // Let's zip-align (must be done after signing)
+ // Let's zip-align (must be done before signing)
static const int ZIP_ALIGNMENT = 4;
- if (ep.step("Aligning APK...", 105)) {
+ // If we're not signing the apk, then the next step should be the last.
+ const int next_step = should_sign ? 103 : 105;
+ if (ep.step("Aligning APK...", next_step)) {
CLEANUP_AND_RETURN(ERR_SKIP);
}
@@ -2354,12 +2891,10 @@ public:
memset(extra + info.size_file_extra, 0, padding);
- // write
- zip_fileinfo zipfi = get_zip_fileinfo();
-
+ zip_fileinfo fileinfo = get_zip_fileinfo();
zipOpenNewFileInZip2(final_apk,
file.utf8().get_data(),
- &zipfi,
+ &fileinfo,
extra,
info.size_file_extra + padding,
nullptr,
@@ -2379,15 +2914,24 @@ public:
zipClose(final_apk, nullptr);
unzClose(tmp_unaligned);
+ if (should_sign) {
+ // Signing must be done last as any additional modifications to the
+ // file will invalidate the signature.
+ err = sign_apk(p_preset, p_debug, p_path, ep);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ }
+
CLEANUP_AND_RETURN(OK);
}
- virtual void get_platform_features(List<String> *r_features) {
+ virtual void get_platform_features(List<String> *r_features) override {
r_features->push_back("mobile");
r_features->push_back("Android");
}
- virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
}
EditorExportPlatformAndroid() {
@@ -2399,16 +2943,14 @@ public:
run_icon.instance();
run_icon->create_from_image(img);
- devices_changed = true;
- plugins_changed = true;
- quit_request = false;
- check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this);
+ devices_changed.set();
+ plugins_changed.set();
+ check_for_changes_thread.start(_check_for_changes_poll_thread, this);
}
~EditorExportPlatformAndroid() {
- quit_request = true;
- Thread::wait_to_finish(check_for_changes_thread);
- memdelete(check_for_changes_thread);
+ quit_request.set();
+ check_for_changes_thread.wait_to_finish();
}
};
@@ -2418,19 +2960,14 @@ void register_android_exporter() {
exe_ext = "*.exe";
}
- EDITOR_DEF("export/android/adb", "");
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/adb", PROPERTY_HINT_GLOBAL_FILE, exe_ext));
- EDITOR_DEF("export/android/jarsigner", "");
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/jarsigner", PROPERTY_HINT_GLOBAL_FILE, exe_ext));
+ EDITOR_DEF("export/android/android_sdk_path", "");
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/android_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
EDITOR_DEF("export/android/debug_keystore", "");
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore"));
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"));
EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey");
EDITOR_DEF("export/android/debug_keystore_pass", "android");
EDITOR_DEF("export/android/force_system_user", false);
- EDITOR_DEF("export/android/custom_build_sdk_path", "");
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/custom_build_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
- EDITOR_DEF("export/android/timestamping_authority_url", "");
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid));
diff --git a/platform/android/export/export.h b/platform/android/export/export.h
index d11ab9f49e..28e09f41db 100644
--- a/platform/android/export/export.h
+++ b/platform/android/export/export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
new file mode 100644
index 0000000000..ce6a3c96db
--- /dev/null
+++ b/platform/android/export/gradle_export_util.h
@@ -0,0 +1,315 @@
+/*************************************************************************/
+/* gradle_export_util.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifndef GODOT_GRADLE_EXPORT_UTIL_H
+#define GODOT_GRADLE_EXPORT_UTIL_H
+
+#include "core/io/zip_io.h"
+#include "core/os/dir_access.h"
+#include "core/os/file_access.h"
+#include "core/os/os.h"
+#include "editor/editor_export.h"
+
+const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="utf-8"?>
+<!--WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
+<resources>
+ <string name="godot_project_name_string">%s</string>
+</resources>
+)";
+
+DisplayServer::ScreenOrientation _get_screen_orientation() {
+ String orientation_settings = ProjectSettings::get_singleton()->get("display/window/handheld/orientation");
+ DisplayServer::ScreenOrientation screen_orientation;
+ if (orientation_settings == "portrait")
+ screen_orientation = DisplayServer::SCREEN_PORTRAIT;
+ else if (orientation_settings == "reverse_landscape")
+ screen_orientation = DisplayServer::SCREEN_REVERSE_LANDSCAPE;
+ else if (orientation_settings == "reverse_portrait")
+ screen_orientation = DisplayServer::SCREEN_REVERSE_PORTRAIT;
+ else if (orientation_settings == "sensor_landscape")
+ screen_orientation = DisplayServer::SCREEN_SENSOR_LANDSCAPE;
+ else if (orientation_settings == "sensor_portrait")
+ screen_orientation = DisplayServer::SCREEN_SENSOR_PORTRAIT;
+ else if (orientation_settings == "sensor")
+ screen_orientation = DisplayServer::SCREEN_SENSOR;
+ else
+ screen_orientation = DisplayServer::SCREEN_LANDSCAPE;
+
+ return screen_orientation;
+}
+
+int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) {
+ switch (screen_orientation) {
+ case DisplayServer::SCREEN_PORTRAIT:
+ return 1;
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
+ return 8;
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
+ return 9;
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ return 11;
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
+ return 12;
+ case DisplayServer::SCREEN_SENSOR:
+ return 13;
+ case DisplayServer::SCREEN_LANDSCAPE:
+ default:
+ return 0;
+ }
+}
+
+String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation) {
+ switch (screen_orientation) {
+ case DisplayServer::SCREEN_PORTRAIT:
+ return "portrait";
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
+ return "reverseLandscape";
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
+ return "reversePortrait";
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ return "userLandscape";
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
+ return "userPortrait";
+ case DisplayServer::SCREEN_SENSOR:
+ return "fullUser";
+ case DisplayServer::SCREEN_LANDSCAPE:
+ default:
+ return "landscape";
+ }
+}
+
+// Utility method used to create a directory.
+Error create_directory(const String &p_dir) {
+ if (!DirAccess::exists(p_dir)) {
+ DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
+ Error err = filesystem_da->make_dir_recursive(p_dir);
+ ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'.");
+ memdelete(filesystem_da);
+ }
+ return OK;
+}
+
+// Implementation of EditorExportSaveSharedObject.
+// This method will only be called as an input to export_project_files.
+// This method lets the .so files for all ABIs to be copied
+// into the gradle project from the .AAR file
+Error ignore_so_file(void *p_userdata, const SharedObject &p_so) {
+ return OK;
+}
+
+// Writes p_data into a file at p_path, creating directories if necessary.
+// Note: this will overwrite the file at p_path if it already exists.
+Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) {
+ String dir = p_path.get_base_dir();
+ Error err = create_directory(dir);
+ if (err != OK) {
+ return err;
+ }
+ FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
+ fa->store_buffer(p_data.ptr(), p_data.size());
+ memdelete(fa);
+ return OK;
+}
+
+// Writes string p_data into a file at p_path, creating directories if necessary.
+// Note: this will overwrite the file at p_path if it already exists.
+Error store_string_at_path(const String &p_path, const String &p_data) {
+ String dir = p_path.get_base_dir();
+ Error err = create_directory(dir);
+ if (err != OK) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ print_error("Unable to write data into " + p_path);
+ }
+ return err;
+ }
+ FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
+ fa->store_string(p_data);
+ memdelete(fa);
+ return OK;
+}
+
+// Implementation of EditorExportSaveFunction.
+// This method will only be called as an input to export_project_files.
+// It is used by the export_project_files method to save all the asset files into the gradle project.
+// It's functionality mirrors that of the method save_apk_file.
+// This method will be called ONLY when custom build is enabled.
+Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
+ String dst_path = p_path.replace_first("res://", "res://android/build/assets/");
+ print_verbose("Saving project files from " + p_path + " into " + dst_path);
+ Error err = store_file_at_path(dst_path, p_data);
+ return err;
+}
+
+// Creates strings.xml files inside the gradle project for different locales.
+Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) {
+ print_verbose("Creating strings resources for supported locales for project " + project_name);
+ // Stores the string into the default values directory.
+ String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true));
+ store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string);
+
+ // Searches the Gradle project res/ directory to find all supported locales
+ DirAccessRef da = DirAccess::open("res://android/build/res");
+ if (!da) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ print_error("Unable to open Android resources directory.");
+ }
+ return ERR_CANT_OPEN;
+ }
+ da->list_dir_begin();
+ while (true) {
+ String file = da->get_next();
+ if (file == "") {
+ break;
+ }
+ if (!file.begins_with("values-")) {
+ // NOTE: This assumes all directories that start with "values-" are for localization.
+ continue;
+ }
+ String locale = file.replace("values-", "").replace("-r", "_");
+ String property_name = "application/config/name_" + locale;
+ String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml";
+ if (ProjectSettings::get_singleton()->has_setting(property_name)) {
+ String locale_project_name = ProjectSettings::get_singleton()->get(property_name);
+ String processed_xml_string = vformat(godot_project_name_xml_string, locale_project_name.xml_escape(true));
+ print_verbose("Storing project name for locale " + locale + " under " + locale_directory);
+ store_string_at_path(locale_directory, processed_xml_string);
+ } else {
+ // TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch
+ store_string_at_path(locale_directory, processed_default_xml_string);
+ }
+ }
+ da->list_dir_end();
+ return OK;
+}
+
+String bool_to_string(bool v) {
+ return v ? "true" : "false";
+}
+
+String _get_gles_tag() {
+ bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES3" &&
+ !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2");
+ return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : "";
+}
+
+String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) {
+ String manifest_screen_sizes = " <supports-screens \n tools:node=\"replace\"";
+ String sizes[] = { "small", "normal", "large", "xlarge" };
+ size_t num_sizes = sizeof(sizes) / sizeof(sizes[0]);
+ for (size_t i = 0; i < num_sizes; i++) {
+ String feature_name = vformat("screen/support_%s", sizes[i]);
+ String feature_support = bool_to_string(p_preset->get(feature_name));
+ String xml_entry = vformat("\n android:%sScreens=\"%s\"", sizes[i], feature_support);
+ manifest_screen_sizes += xml_entry;
+ }
+ manifest_screen_sizes += " />\n";
+ return manifest_screen_sizes;
+}
+
+String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) {
+ String manifest_xr_features;
+ bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
+ if (uses_xr) {
+ int dof_index = p_preset->get("xr_features/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof
+ if (dof_index == 1) {
+ manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"false\" android:version=\"1\" />\n";
+ } else if (dof_index == 2) {
+ manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"true\" android:version=\"1\" />\n";
+ }
+ int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required
+ if (hand_tracking_index == 1) {
+ manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n";
+ } else if (hand_tracking_index == 2) {
+ manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"true\" />\n";
+ }
+ }
+ return manifest_xr_features;
+}
+
+String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) {
+ String package_name = p_preset->get("package/unique_name");
+ String manifest_instrumentation_text = vformat(
+ " <instrumentation\n"
+ " tools:node=\"replace\"\n"
+ " android:name=\".GodotInstrumentation\"\n"
+ " android:icon=\"@mipmap/icon\"\n"
+ " android:label=\"@string/godot_project_name_string\"\n"
+ " android:targetPackage=\"%s\" />\n",
+ package_name);
+ return manifest_instrumentation_text;
+}
+
+String _get_plugins_tag(const String &plugins_names) {
+ if (!plugins_names.is_empty()) {
+ return vformat(" <meta-data tools:node=\"replace\" android:name=\"plugins\" android:value=\"%s\" />\n", plugins_names);
+ } else {
+ return " <meta-data tools:node=\"remove\" android:name=\"plugins\" />\n";
+ }
+}
+
+String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
+ bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
+ String orientation = _get_android_orientation_label(_get_screen_orientation());
+ String manifest_activity_text = vformat(
+ " <activity android:name=\"com.godot.game.GodotApp\" "
+ "tools:replace=\"android:screenOrientation\" "
+ "android:screenOrientation=\"%s\">\n",
+ orientation);
+ if (uses_xr) {
+ String focus_awareness = bool_to_string(p_preset->get("xr_features/focus_awareness"));
+ manifest_activity_text += vformat(" <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"%s\" />\n", focus_awareness);
+ } else {
+ manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n";
+ }
+ manifest_activity_text += " </activity>\n";
+ return manifest_activity_text;
+}
+
+String _get_application_tag(const Ref<EditorExportPreset> &p_preset, const String &plugins_names) {
+ bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1;
+ String manifest_application_text =
+ " <application android:label=\"@string/godot_project_name_string\"\n"
+ " android:allowBackup=\"false\" tools:ignore=\"GoogleAppIndexingWarning\"\n"
+ " android:icon=\"@mipmap/icon\">\n\n"
+ " <meta-data tools:node=\"remove\" android:name=\"xr_mode_metadata_name\" />\n";
+
+ manifest_application_text += _get_plugins_tag(plugins_names);
+ if (uses_xr) {
+ manifest_application_text += " <meta-data tools:node=\"replace\" android:name=\"com.samsung.android.vr.application.mode\" android:value=\"vr_only\" />\n";
+ }
+ manifest_application_text += _get_activity_tag(p_preset);
+ manifest_application_text += " </application>\n";
+ return manifest_application_text;
+}
+
+#endif //GODOT_GRADLE_EXPORT_UTIL_H
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index 05d5fb576d..165d5da3ae 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,12 +29,11 @@
/*************************************************************************/
#include "file_access_android.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
AAssetManager *FileAccessAndroid::asset_manager = nullptr;
/*void FileAccessAndroid::make_default() {
-
create_func=create_android;
}*/
@@ -158,11 +157,6 @@ bool FileAccessAndroid::file_exists(const String &p_path) {
return true;
}
-FileAccessAndroid::FileAccessAndroid() {
- a = nullptr;
- eof = false;
-}
-
FileAccessAndroid::~FileAccessAndroid() {
close();
}
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index a347c63ffb..56010c918a 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,10 +39,10 @@
class FileAccessAndroid : public FileAccess {
static FileAccess *create_android();
- mutable AAsset *a;
- mutable size_t len;
- mutable size_t pos;
- mutable bool eof;
+ mutable AAsset *a = nullptr;
+ mutable size_t len = 0;
+ mutable size_t pos = 0;
+ mutable bool eof = false;
public:
static AAssetManager *asset_manager;
@@ -74,7 +74,6 @@ public:
//static void make_default();
- FileAccessAndroid();
~FileAccessAndroid();
};
diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp
deleted file mode 100644
index df8b57fd3a..0000000000
--- a/platform/android/file_access_jandroid.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-/*************************************************************************/
-/* file_access_jandroid.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "file_access_jandroid.h"
-#include "core/os/os.h"
-#include "thread_jandroid.h"
-#include <unistd.h>
-
-jobject FileAccessJAndroid::io = nullptr;
-jclass FileAccessJAndroid::cls;
-jmethodID FileAccessJAndroid::_file_open = 0;
-jmethodID FileAccessJAndroid::_file_get_size = 0;
-jmethodID FileAccessJAndroid::_file_seek = 0;
-jmethodID FileAccessJAndroid::_file_read = 0;
-jmethodID FileAccessJAndroid::_file_tell = 0;
-jmethodID FileAccessJAndroid::_file_eof = 0;
-jmethodID FileAccessJAndroid::_file_close = 0;
-
-FileAccess *FileAccessJAndroid::create_jandroid() {
- return memnew(FileAccessJAndroid);
-}
-
-Error FileAccessJAndroid::_open(const String &p_path, int p_mode_flags) {
- if (is_open())
- close();
-
- String path = fix_path(p_path).simplify_path();
- if (path.begins_with("/"))
- path = path.substr(1, path.length());
- else if (path.begins_with("res://"))
- path = path.substr(6, path.length());
-
- JNIEnv *env = ThreadAndroid::get_env();
-
- jstring js = env->NewStringUTF(path.utf8().get_data());
- int res = env->CallIntMethod(io, _file_open, js, (p_mode_flags & WRITE) ? true : false);
- env->DeleteLocalRef(js);
-
- OS::get_singleton()->print("fopen: '%s' ret %i\n", path.utf8().get_data(), res);
-
- if (res <= 0)
- return ERR_FILE_CANT_OPEN;
- id = res;
-
- return OK;
-}
-
-void FileAccessJAndroid::close() {
- if (!is_open())
- return;
-
- JNIEnv *env = ThreadAndroid::get_env();
-
- env->CallVoidMethod(io, _file_close, id);
- id = 0;
-}
-
-bool FileAccessJAndroid::is_open() const {
- return id != 0;
-}
-
-void FileAccessJAndroid::seek(size_t p_position) {
- JNIEnv *env = ThreadAndroid::get_env();
-
- ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
- env->CallVoidMethod(io, _file_seek, id, p_position);
-}
-
-void FileAccessJAndroid::seek_end(int64_t p_position) {
- ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use.");
-
- seek(get_len());
-}
-
-size_t FileAccessJAndroid::get_position() const {
- JNIEnv *env = ThreadAndroid::get_env();
- ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
- return env->CallIntMethod(io, _file_tell, id);
-}
-
-size_t FileAccessJAndroid::get_len() const {
- JNIEnv *env = ThreadAndroid::get_env();
- ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
- return env->CallIntMethod(io, _file_get_size, id);
-}
-
-bool FileAccessJAndroid::eof_reached() const {
- JNIEnv *env = ThreadAndroid::get_env();
- ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
- return env->CallIntMethod(io, _file_eof, id);
-}
-
-uint8_t FileAccessJAndroid::get_8() const {
- ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
- uint8_t byte;
- get_buffer(&byte, 1);
- return byte;
-}
-
-int FileAccessJAndroid::get_buffer(uint8_t *p_dst, int p_length) const {
- ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use.");
- if (p_length == 0)
- return 0;
- JNIEnv *env = ThreadAndroid::get_env();
-
- jbyteArray jca = (jbyteArray)env->CallObjectMethod(io, _file_read, id, p_length);
-
- int len = env->GetArrayLength(jca);
- env->GetByteArrayRegion(jca, 0, len, (jbyte *)p_dst);
- env->DeleteLocalRef((jobject)jca);
-
- return len;
-}
-
-Error FileAccessJAndroid::get_error() const {
- if (eof_reached())
- return ERR_FILE_EOF;
- return OK;
-}
-
-void FileAccessJAndroid::flush() {
-}
-
-void FileAccessJAndroid::store_8(uint8_t p_dest) {
-}
-
-bool FileAccessJAndroid::file_exists(const String &p_path) {
- JNIEnv *env = ThreadAndroid::get_env();
-
- String path = fix_path(p_path).simplify_path();
- if (path.begins_with("/"))
- path = path.substr(1, path.length());
- else if (path.begins_with("res://"))
- path = path.substr(6, path.length());
-
- jstring js = env->NewStringUTF(path.utf8().get_data());
- int res = env->CallIntMethod(io, _file_open, js, false);
- if (res <= 0) {
- env->DeleteLocalRef(js);
- return false;
- }
- env->CallVoidMethod(io, _file_close, res);
- env->DeleteLocalRef(js);
- return true;
-}
-
-void FileAccessJAndroid::setup(jobject p_io) {
- io = p_io;
- JNIEnv *env = ThreadAndroid::get_env();
-
- jclass c = env->GetObjectClass(io);
- cls = (jclass)env->NewGlobalRef(c);
-
- _file_open = env->GetMethodID(cls, "file_open", "(Ljava/lang/String;Z)I");
- _file_get_size = env->GetMethodID(cls, "file_get_size", "(I)I");
- _file_tell = env->GetMethodID(cls, "file_tell", "(I)I");
- _file_eof = env->GetMethodID(cls, "file_eof", "(I)Z");
- _file_seek = env->GetMethodID(cls, "file_seek", "(II)V");
- _file_read = env->GetMethodID(cls, "file_read", "(II)[B");
- _file_close = env->GetMethodID(cls, "file_close", "(I)V");
-}
-
-FileAccessJAndroid::FileAccessJAndroid() {
- id = 0;
-}
-
-FileAccessJAndroid::~FileAccessJAndroid() {
- if (is_open())
- close();
-}
diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h
deleted file mode 100644
index e252a4d3ac..0000000000
--- a/platform/android/file_access_jandroid.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*************************************************************************/
-/* file_access_jandroid.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#ifndef FILE_ACCESS_JANDROID_H
-#define FILE_ACCESS_JANDROID_H
-
-#include "core/os/file_access.h"
-#include "java_godot_lib_jni.h"
-class FileAccessJAndroid : public FileAccess {
- static jobject io;
- static jclass cls;
-
- static jmethodID _file_open;
- static jmethodID _file_get_size;
- static jmethodID _file_seek;
- static jmethodID _file_tell;
- static jmethodID _file_eof;
- static jmethodID _file_read;
- static jmethodID _file_close;
-
- int id;
- static FileAccess *create_jandroid();
-
-public:
- virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file
- virtual void close(); ///< close a file
- virtual bool is_open() const; ///< true when file is open
-
- virtual void seek(size_t p_position); ///< seek to a given position
- virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
- virtual size_t get_position() const; ///< get position in the file
- virtual size_t get_len() const; ///< get size of the file
-
- virtual bool eof_reached() const; ///< reading passed EOF
-
- virtual uint8_t get_8() const; ///< get a byte
- virtual int get_buffer(uint8_t *p_dst, int p_length) const;
-
- virtual Error get_error() const; ///< get last error
-
- virtual void flush();
- virtual void store_8(uint8_t p_dest); ///< store a byte
-
- virtual bool file_exists(const String &p_path); ///< return true if a file exists
-
- static void setup(jobject p_io);
-
- virtual uint64_t _get_modified_time(const String &p_file) { return 0; }
- virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; }
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; }
-
- FileAccessJAndroid();
- ~FileAccessJAndroid();
-};
-
-#endif // FILE_ACCESS_JANDROID_H
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index f5b1d29f22..cd2f1d367e 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/platform/android/java/app/AndroidManifest.xml
@@ -22,6 +22,11 @@
tools:ignore="GoogleAppIndexingWarning"
android:icon="@mipmap/icon" >
+ <!-- Records the version of the Godot editor used for building -->
+ <meta-data
+ android:name="org.godotengine.editor.version"
+ android:value="${godotEditorVersion}" />
+
<!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. -->
<!-- Do these changes in the export preset. Adding new ones is fine. -->
@@ -38,13 +43,16 @@
<activity
android:name=".GodotApp"
android:label="@string/godot_project_name_string"
- android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
+ android:theme="@style/GodotAppSplashTheme"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:resizeableActivity="false"
tools:ignore="UnusedAttribute" >
+ <!-- Focus awareness metadata is updated at export time if the user enables it in the 'Xr Features' section. -->
+ <meta-data android:name="com.oculus.vr.focusaware" android:value="false" />
+
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index ea341b37b1..934c4bf441 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -70,13 +70,27 @@ android {
buildToolsVersion versions.buildTools
compileOptions {
- sourceCompatibility 1.8
- targetCompatibility 1.8
+ sourceCompatibility versions.javaVersion
+ targetCompatibility versions.javaVersion
}
defaultConfig {
+ // The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Godot projects.
+ aaptOptions {
+ ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
+ }
+
+ ndk {
+ String[] export_abi_list = getExportEnabledABIs()
+ abiFilters export_abi_list
+ }
+
+ manifestPlaceholders = [godotEditorVersion: getGodotEditorVersion()]
+
// Feel free to modify the application id to your own.
applicationId getExportPackageName()
+ versionCode getExportVersionCode()
+ versionName getExportVersionName()
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
}
@@ -86,6 +100,8 @@ android {
disable 'MissingTranslation', 'UnusedResources'
}
+ ndkVersion versions.ndkVersion
+
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
@@ -94,10 +110,41 @@ android {
// doNotStrip '**/*.so'
}
- // Both signing and zip-aligning will be done at export time
- buildTypes.all { buildType ->
- buildType.zipAlignEnabled false
- buildType.signingConfig null
+ signingConfigs {
+ release {
+ File keystoreFile = new File(getReleaseKeystoreFile())
+ if (keystoreFile.isFile()) {
+ storeFile keystoreFile
+ storePassword getReleaseKeystorePassword()
+ keyAlias getReleaseKeyAlias()
+ keyPassword getReleaseKeystorePassword()
+ }
+ }
+ }
+
+ buildTypes {
+
+ debug {
+ // Signing and zip-aligning are skipped for prebuilt builds, but
+ // performed for custom builds.
+ zipAlignEnabled shouldZipAlign()
+ if (shouldSign()) {
+ signingConfig signingConfigs.debug
+ } else {
+ signingConfig null
+ }
+ }
+
+ release {
+ // Signing and zip-aligning are skipped for prebuilt builds, but
+ // performed for custom builds.
+ zipAlignEnabled shouldZipAlign()
+ if (shouldSign()) {
+ signingConfig signingConfigs.release
+ } else {
+ signingConfig null
+ }
+ }
}
sourceSets {
@@ -118,3 +165,27 @@ android {
}
}
}
+
+task copyAndRenameDebugApk(type: Copy) {
+ from "$buildDir/outputs/apk/debug/android_debug.apk"
+ into getExportPath()
+ rename "android_debug.apk", getExportFilename()
+}
+
+task copyAndRenameReleaseApk(type: Copy) {
+ from "$buildDir/outputs/apk/release/android_release.apk"
+ into getExportPath()
+ rename "android_release.apk", getExportFilename()
+}
+
+task copyAndRenameDebugAab(type: Copy) {
+ from "$buildDir/outputs/bundle/debug/build-debug.aab"
+ into getExportPath()
+ rename "build-debug.aab", getExportFilename()
+}
+
+task copyAndRenameReleaseAab(type: Copy) {
+ from "$buildDir/outputs/bundle/release/build-release.aab"
+ into getExportPath()
+ rename "build-release.aab", getExportFilename()
+}
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 5251bc3066..06d1f4064e 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -1,12 +1,14 @@
ext.versions = [
- androidGradlePlugin: '3.5.3',
+ androidGradlePlugin: '4.0.1',
compileSdk : 29,
minSdk : 18,
targetSdk : 29,
- buildTools : '29.0.3',
+ buildTools : '30.0.1',
supportCoreUtils : '1.0.0',
- kotlinVersion : '1.3.61',
- v4Support : '1.0.0'
+ kotlinVersion : '1.4.10',
+ v4Support : '1.0.0',
+ javaVersion : 1.8,
+ ndkVersion : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated.
]
@@ -28,8 +30,108 @@ ext.getExportPackageName = { ->
return appId
}
+ext.getExportVersionCode = { ->
+ String versionCode = project.hasProperty("export_version_code") ? project.property("export_version_code") : ""
+ if (versionCode == null || versionCode.isEmpty()) {
+ versionCode = "1"
+ }
+ try {
+ return Integer.parseInt(versionCode)
+ } catch (NumberFormatException ignored) {
+ return 1
+ }
+}
+
+ext.getExportVersionName = { ->
+ String versionName = project.hasProperty("export_version_name") ? project.property("export_version_name") : ""
+ if (versionName == null || versionName.isEmpty()) {
+ versionName = "1.0"
+ }
+ return versionName
+}
+
+ext.getGodotEditorVersion = { ->
+ String editorVersion = project.hasProperty("godot_editor_version") ? project.property("godot_editor_version") : ""
+ if (editorVersion == null || editorVersion.isEmpty()) {
+ // Try the library version first
+ editorVersion = getGodotLibraryVersion()
+
+ if (editorVersion.isEmpty()) {
+ // Fallback value.
+ editorVersion = "custom_build"
+ }
+ }
+ return editorVersion
+}
+
+ext.getGodotLibraryVersion = { ->
+ // Attempt to read the version from the `version.py` file.
+ String libraryVersion = ""
+
+ File versionFile = new File("../../../version.py")
+ if (versionFile.isFile()) {
+ List<String> requiredKeys = ["major", "minor", "patch", "status", "module_config"]
+ def map = [:]
+
+ List<String> lines = versionFile.readLines()
+ for (String line in lines) {
+ String[] keyValue = line.split("=")
+ String key = keyValue[0].trim()
+ String value = keyValue[1].trim().replaceAll("\"", "")
+
+ if (requiredKeys.contains(key)) {
+ if (!value.isEmpty()) {
+ map[key] = value
+ }
+ requiredKeys.remove(key)
+ }
+ }
+
+ if (requiredKeys.empty) {
+ libraryVersion = map.values().join(".")
+ }
+ }
+
+ if (libraryVersion.isEmpty()) {
+ // Fallback value in case we're unable to read the file.
+ libraryVersion = "custom_build"
+ }
+ return libraryVersion
+}
+
final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|"
+// get the list of ABIs the project should be exported to
+ext.getExportEnabledABIs = { ->
+ String enabledABIs = project.hasProperty("export_enabled_abis") ? project.property("export_enabled_abis") : "";
+ if (enabledABIs == null || enabledABIs.isEmpty()) {
+ enabledABIs = "armeabi-v7a|arm64-v8a|x86|x86_64|"
+ }
+ Set<String> exportAbiFilter = [];
+ for (String abi_name : enabledABIs.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
+ if (!abi_name.trim().isEmpty()){
+ exportAbiFilter.add(abi_name);
+ }
+ }
+ return exportAbiFilter;
+}
+
+ext.getExportPath = {
+ String exportPath = project.hasProperty("export_path") ? project.property("export_path") : ""
+ if (exportPath == null || exportPath.isEmpty()) {
+ exportPath = "."
+ }
+ return exportPath
+}
+
+ext.getExportFilename = {
+ String exportFilename = project.hasProperty("export_filename") ? project.property("export_filename") : ""
+ if (exportFilename == null || exportFilename.isEmpty()) {
+ exportFilename = "godot_android"
+ }
+ return exportFilename
+}
+
/**
* Parse the project properties for the 'plugins_maven_repos' property and return the list
* of maven repos.
@@ -72,17 +174,12 @@ ext.getGodotPluginsRemoteBinaries = { ->
/**
* Parse the project properties for the 'plugins_local_binaries' property and return
* their binaries for inclusion in the build dependencies.
- *
- * Returns the prebuilt plugins if the 'plugins_local_binaries' property is unavailable.
*/
ext.getGodotPluginsLocalBinaries = { ->
- // Set the prebuilt plugins as default. If custom build is enabled,
- // the 'plugins_local_binaries' will be defined so we can use it instead.
- Set<String> binDeps = ["libs/plugins/GodotPayment.release.aar"]
+ Set<String> binDeps = []
// Retrieve the list of local plugins binaries.
if (project.hasProperty("plugins_local_binaries")) {
- binDeps.clear()
String pluginsList = project.property("plugins_local_binaries")
if (pluginsList != null && !pluginsList.trim().isEmpty()) {
for (String plugin : pluginsList.split(PLUGIN_VALUE_SEPARATOR_REGEX)) {
@@ -93,3 +190,37 @@ ext.getGodotPluginsLocalBinaries = { ->
return binDeps
}
+
+ext.getReleaseKeystoreFile = { ->
+ String keystoreFile = project.hasProperty("release_keystore_file") ? project.property("release_keystore_file") : ""
+ if (keystoreFile == null || keystoreFile.isEmpty()) {
+ keystoreFile = "."
+ }
+ return keystoreFile
+}
+
+ext.getReleaseKeystorePassword = { ->
+ String keystorePassword = project.hasProperty("release_keystore_password") ? project.property("release_keystore_password") : ""
+ return keystorePassword
+}
+
+ext.getReleaseKeyAlias = { ->
+ String keyAlias = project.hasProperty("release_keystore_alias") ? project.property("release_keystore_alias") : ""
+ return keyAlias
+}
+
+ext.shouldZipAlign = { ->
+ String zipAlignFlag = project.hasProperty("perform_zipalign") ? project.property("perform_zipalign") : ""
+ if (zipAlignFlag == null || zipAlignFlag.isEmpty()) {
+ zipAlignFlag = "false"
+ }
+ return Boolean.parseBoolean(zipAlignFlag)
+}
+
+ext.shouldSign = { ->
+ String signFlag = project.hasProperty("perform_signing") ? project.property("perform_signing") : ""
+ if (signFlag == null || signFlag.isEmpty()) {
+ signFlag = "false"
+ }
+ return Boolean.parseBoolean(signFlag)
+}
diff --git a/platform/android/java/app/res/drawable/splash.png b/platform/android/java/app/res/drawable/splash.png
new file mode 100644
index 0000000000..7bddd4325a
--- /dev/null
+++ b/platform/android/java/app/res/drawable/splash.png
Binary files differ
diff --git a/platform/android/java/app/res/drawable/splash_bg_color.png b/platform/android/java/app/res/drawable/splash_bg_color.png
new file mode 100644
index 0000000000..004b6fd508
--- /dev/null
+++ b/platform/android/java/app/res/drawable/splash_bg_color.png
Binary files differ
diff --git a/platform/android/java/app/res/drawable/splash_drawable.xml b/platform/android/java/app/res/drawable/splash_drawable.xml
new file mode 100644
index 0000000000..30627b998c
--- /dev/null
+++ b/platform/android/java/app/res/drawable/splash_drawable.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:drawable="@drawable/splash_bg_color" />
+
+ <item>
+ <bitmap
+ android:gravity="center"
+ android:filter="false"
+ android:src="@drawable/splash" />
+ </item>
+</layer-list>
diff --git a/platform/android/java/lib/res/values-ar/strings.xml b/platform/android/java/app/res/values-ar/godot_project_name_string.xml
index 9f3dc6d6ac..23aa5cf3e1 100644
--- a/platform/android/java/lib/res/values-ar/strings.xml
+++ b/platform/android/java/app/res/values-ar/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-ar</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-bg/strings.xml b/platform/android/java/app/res/values-bg/godot_project_name_string.xml
index bd8109277e..dbb7e04ae5 100644
--- a/platform/android/java/lib/res/values-bg/strings.xml
+++ b/platform/android/java/app/res/values-bg/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-bg</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-ca/strings.xml b/platform/android/java/app/res/values-ca/godot_project_name_string.xml
index 494cb88468..709d0961e6 100644
--- a/platform/android/java/lib/res/values-ca/strings.xml
+++ b/platform/android/java/app/res/values-ca/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-ca</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-cs/strings.xml b/platform/android/java/app/res/values-cs/godot_project_name_string.xml
index 30ce00f895..ab248a8032 100644
--- a/platform/android/java/lib/res/values-cs/strings.xml
+++ b/platform/android/java/app/res/values-cs/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-cs</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-da/strings.xml b/platform/android/java/app/res/values-da/godot_project_name_string.xml
index 4c2a1cf0f4..906bf44f57 100644
--- a/platform/android/java/lib/res/values-da/strings.xml
+++ b/platform/android/java/app/res/values-da/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-da</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-de/strings.xml b/platform/android/java/app/res/values-de/godot_project_name_string.xml
index 52946d4cce..0cacb0175f 100644
--- a/platform/android/java/lib/res/values-de/strings.xml
+++ b/platform/android/java/app/res/values-de/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-de</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-el/strings.xml b/platform/android/java/app/res/values-el/godot_project_name_string.xml
index 181dc51762..047de616a5 100644
--- a/platform/android/java/lib/res/values-el/strings.xml
+++ b/platform/android/java/app/res/values-el/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-el</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-en/strings.xml b/platform/android/java/app/res/values-en/godot_project_name_string.xml
index 976a565013..bb3a5dbef3 100644
--- a/platform/android/java/lib/res/values-en/strings.xml
+++ b/platform/android/java/app/res/values-en/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-en</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-es-rES/strings.xml b/platform/android/java/app/res/values-es-rES/godot_project_name_string.xml
index 73f63a08f8..d4537f3496 100644
--- a/platform/android/java/lib/res/values-es-rES/strings.xml
+++ b/platform/android/java/app/res/values-es-rES/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-es_ES</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-es/strings.xml b/platform/android/java/app/res/values-es/godot_project_name_string.xml
index 07b718a641..d63a16022e 100644
--- a/platform/android/java/lib/res/values-es/strings.xml
+++ b/platform/android/java/app/res/values-es/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-es</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/app/res/values-fa/godot_project_name_string.xml b/platform/android/java/app/res/values-fa/godot_project_name_string.xml
new file mode 100644
index 0000000000..c303f13d5f
--- /dev/null
+++ b/platform/android/java/app/res/values-fa/godot_project_name_string.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
+<resources>
+ <string name="godot_project_name_string">godot-project-name-fa</string>
+</resources>
diff --git a/platform/android/java/lib/res/values-fi/strings.xml b/platform/android/java/app/res/values-fi/godot_project_name_string.xml
index 323d82aff1..bd6005574a 100644
--- a/platform/android/java/lib/res/values-fi/strings.xml
+++ b/platform/android/java/app/res/values-fi/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-fi</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-fr/strings.xml b/platform/android/java/app/res/values-fr/godot_project_name_string.xml
index 32bead2661..2e94b65a20 100644
--- a/platform/android/java/lib/res/values-fr/strings.xml
+++ b/platform/android/java/app/res/values-fr/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-fr</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-hi/strings.xml b/platform/android/java/app/res/values-hi/godot_project_name_string.xml
index 8aab2a8c63..0bf75dcd56 100644
--- a/platform/android/java/lib/res/values-hi/strings.xml
+++ b/platform/android/java/app/res/values-hi/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-hi</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-hr/strings.xml b/platform/android/java/app/res/values-hr/godot_project_name_string.xml
index caf55e2241..d3f75910f9 100644
--- a/platform/android/java/lib/res/values-hr/strings.xml
+++ b/platform/android/java/app/res/values-hr/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-hr</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-hu/strings.xml b/platform/android/java/app/res/values-hu/godot_project_name_string.xml
index e7f9e51226..012b613af3 100644
--- a/platform/android/java/lib/res/values-hu/strings.xml
+++ b/platform/android/java/app/res/values-hu/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-hu</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/app/res/values-in/godot_project_name_string.xml b/platform/android/java/app/res/values-in/godot_project_name_string.xml
new file mode 100644
index 0000000000..eedecff7a1
--- /dev/null
+++ b/platform/android/java/app/res/values-in/godot_project_name_string.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
+<resources>
+ <string name="godot_project_name_string">godot-project-name-in</string>
+</resources>
diff --git a/platform/android/java/lib/res/values-it/strings.xml b/platform/android/java/app/res/values-it/godot_project_name_string.xml
index 1f5e5a049e..7e734047c4 100644
--- a/platform/android/java/lib/res/values-it/strings.xml
+++ b/platform/android/java/app/res/values-it/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-it</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/app/res/values-iw/godot_project_name_string.xml b/platform/android/java/app/res/values-iw/godot_project_name_string.xml
new file mode 100644
index 0000000000..03893f0cbb
--- /dev/null
+++ b/platform/android/java/app/res/values-iw/godot_project_name_string.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
+<resources>
+ <string name="godot_project_name_string">godot-project-name-iw</string>
+</resources>
diff --git a/platform/android/java/lib/res/values-ja/strings.xml b/platform/android/java/app/res/values-ja/godot_project_name_string.xml
index 7f85f57df7..f9dd4fab0d 100644
--- a/platform/android/java/lib/res/values-ja/strings.xml
+++ b/platform/android/java/app/res/values-ja/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-ja</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/app/res/values-ko/godot_project_name_string.xml b/platform/android/java/app/res/values-ko/godot_project_name_string.xml
new file mode 100644
index 0000000000..26f5dac176
--- /dev/null
+++ b/platform/android/java/app/res/values-ko/godot_project_name_string.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
+<resources>
+ <string name="godot_project_name_string">godot-project-name-ko</string>
+</resources>
diff --git a/platform/android/java/lib/res/values-lt/strings.xml b/platform/android/java/app/res/values-lt/godot_project_name_string.xml
index 6e3677fde7..1c2e976cc5 100644
--- a/platform/android/java/lib/res/values-lt/strings.xml
+++ b/platform/android/java/app/res/values-lt/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-lt</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-lv/strings.xml b/platform/android/java/app/res/values-lv/godot_project_name_string.xml
index 701fc271ac..b5e638ed73 100644
--- a/platform/android/java/lib/res/values-lv/strings.xml
+++ b/platform/android/java/app/res/values-lv/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-lv</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-nb/strings.xml b/platform/android/java/app/res/values-nb/godot_project_name_string.xml
index 73147ca1af..e6d89d6a3f 100644
--- a/platform/android/java/lib/res/values-nb/strings.xml
+++ b/platform/android/java/app/res/values-nb/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-nb</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-nl/strings.xml b/platform/android/java/app/res/values-nl/godot_project_name_string.xml
index e501928a35..93cb3a3878 100644
--- a/platform/android/java/lib/res/values-nl/strings.xml
+++ b/platform/android/java/app/res/values-nl/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-nl</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-pl/strings.xml b/platform/android/java/app/res/values-pl/godot_project_name_string.xml
index ea5da73b6f..e5d6ac74fb 100644
--- a/platform/android/java/lib/res/values-pl/strings.xml
+++ b/platform/android/java/app/res/values-pl/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-pl</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-pt/strings.xml b/platform/android/java/app/res/values-pt/godot_project_name_string.xml
index bdda7cd2c7..a4624655c5 100644
--- a/platform/android/java/lib/res/values-pt/strings.xml
+++ b/platform/android/java/app/res/values-pt/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-pt</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-ro/strings.xml b/platform/android/java/app/res/values-ro/godot_project_name_string.xml
index 3686da4c19..19e026637e 100644
--- a/platform/android/java/lib/res/values-ro/strings.xml
+++ b/platform/android/java/app/res/values-ro/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-ro</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-ru/strings.xml b/platform/android/java/app/res/values-ru/godot_project_name_string.xml
index 954067658b..284845241f 100644
--- a/platform/android/java/lib/res/values-ru/strings.xml
+++ b/platform/android/java/app/res/values-ru/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-ru</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-sk/strings.xml b/platform/android/java/app/res/values-sk/godot_project_name_string.xml
index 37d1283124..f8ab4a5b59 100644
--- a/platform/android/java/lib/res/values-sk/strings.xml
+++ b/platform/android/java/app/res/values-sk/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-sk</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-sl/strings.xml b/platform/android/java/app/res/values-sl/godot_project_name_string.xml
index 0bb249c375..98bd53e8d2 100644
--- a/platform/android/java/lib/res/values-sl/strings.xml
+++ b/platform/android/java/app/res/values-sl/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-sl</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-sr/strings.xml b/platform/android/java/app/res/values-sr/godot_project_name_string.xml
index 0e83cab1a1..3f400f2a4d 100644
--- a/platform/android/java/lib/res/values-sr/strings.xml
+++ b/platform/android/java/app/res/values-sr/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-sr</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-sv/strings.xml b/platform/android/java/app/res/values-sv/godot_project_name_string.xml
index e3a04ac2ec..8670b7c9aa 100644
--- a/platform/android/java/lib/res/values-sv/strings.xml
+++ b/platform/android/java/app/res/values-sv/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-sv</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-th/strings.xml b/platform/android/java/app/res/values-th/godot_project_name_string.xml
index 0aa893b8bf..a1cc1bcd49 100644
--- a/platform/android/java/lib/res/values-th/strings.xml
+++ b/platform/android/java/app/res/values-th/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-th</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-tl/strings.xml b/platform/android/java/app/res/values-tl/godot_project_name_string.xml
index e7e2af4909..6d66d114cf 100644
--- a/platform/android/java/lib/res/values-tl/strings.xml
+++ b/platform/android/java/app/res/values-tl/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-tl</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-tr/strings.xml b/platform/android/java/app/res/values-tr/godot_project_name_string.xml
index 97af1243a6..ba3bd7de36 100644
--- a/platform/android/java/lib/res/values-tr/strings.xml
+++ b/platform/android/java/app/res/values-tr/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-tr</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-uk/strings.xml b/platform/android/java/app/res/values-uk/godot_project_name_string.xml
index 3dea6908a9..5f14ab25a0 100644
--- a/platform/android/java/lib/res/values-uk/strings.xml
+++ b/platform/android/java/app/res/values-uk/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-uk</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-vi/strings.xml b/platform/android/java/app/res/values-vi/godot_project_name_string.xml
index a6552130b0..295378e111 100644
--- a/platform/android/java/lib/res/values-vi/strings.xml
+++ b/platform/android/java/app/res/values-vi/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-vi</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values-zh-rHK/strings.xml b/platform/android/java/app/res/values-zh-rHK/godot_project_name_string.xml
index 8a6269da0f..40ab0f285a 100644
--- a/platform/android/java/lib/res/values-zh-rHK/strings.xml
+++ b/platform/android/java/app/res/values-zh-rHK/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-zh_HK</string>
</resources>
diff --git a/platform/android/java/lib/res/values-zh-rTW/strings.xml b/platform/android/java/app/res/values-zh-rTW/godot_project_name_string.xml
index b1bb39d5d6..095bd564e2 100644
--- a/platform/android/java/lib/res/values-zh-rTW/strings.xml
+++ b/platform/android/java/app/res/values-zh-rTW/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-zh_TW</string>
</resources>
diff --git a/platform/android/java/lib/res/values-zh-rCN/strings.xml b/platform/android/java/app/res/values-zh/godot_project_name_string.xml
index 6668c56bd9..31aa8c273a 100644
--- a/platform/android/java/lib/res/values-zh-rCN/strings.xml
+++ b/platform/android/java/app/res/values-zh/godot_project_name_string.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
<resources>
<string name="godot_project_name_string">godot-project-name-zh</string>
</resources>
diff --git a/platform/android/java/app/res/values/godot_project_name_string.xml b/platform/android/java/app/res/values/godot_project_name_string.xml
new file mode 100644
index 0000000000..7ec2738896
--- /dev/null
+++ b/platform/android/java/app/res/values/godot_project_name_string.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- WARNING: THIS FILE WILL BE OVERWRITTEN AT BUILD TIME-->
+<resources>
+ <string name="godot_project_name_string">godot-project-name</string>
+</resources>
diff --git a/platform/android/java/app/res/values/themes.xml b/platform/android/java/app/res/values/themes.xml
new file mode 100644
index 0000000000..99f723f5ba
--- /dev/null
+++ b/platform/android/java/app/res/values/themes.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="GodotAppMainTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen"/>
+
+ <style name="GodotAppSplashTheme" parent="@style/GodotAppMainTheme">
+ <item name="android:windowBackground">@drawable/splash_drawable</item>
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+</resources>
diff --git a/platform/android/java/app/src/com/godot/game/GodotApp.java b/platform/android/java/app/src/com/godot/game/GodotApp.java
index eb884404cd..955446b0c2 100644
--- a/platform/android/java/app/src/com/godot/game/GodotApp.java
+++ b/platform/android/java/app/src/com/godot/game/GodotApp.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,11 +30,18 @@
package com.godot.game;
-import org.godotengine.godot.Godot;
+import org.godotengine.godot.FullScreenGodotApp;
+
+import android.os.Bundle;
/**
* Template activity for Godot Android custom builds.
* Feel free to extend and modify this class for your custom logic.
*/
-public class GodotApp extends Godot {
+public class GodotApp extends FullScreenGodotApp {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ setTheme(R.style.GodotAppMainTheme);
+ super.onCreate(savedInstanceState);
+ }
}
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 01a3607b20..ec02b0fc7a 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -22,8 +22,6 @@ allprojects {
}
ext {
- sconsExt = org.gradle.internal.os.OperatingSystem.current().isWindows() ? ".bat" : ""
-
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
supportedTargets = ["release", "debug"]
@@ -97,13 +95,6 @@ task copyReleaseAARToAppModule(type: Copy) {
include('godot-lib.release.aar')
}
-task copyGodotPaymentPluginToAppModule(type: Copy) {
- dependsOn ':plugins:godotpayment:assembleRelease'
- from('plugins/godotpayment/build/outputs/aar')
- into('app/libs/plugins')
- include('GodotPayment.release.aar')
-}
-
/**
* Copy the Godot android library archive release file into the root bin directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
@@ -161,7 +152,6 @@ task generateGodotTemplates(type: GradleBuild) {
}
}
- dependsOn 'copyGodotPaymentPluginToAppModule'
finalizedBy 'zipCustomBuild'
}
@@ -175,12 +165,6 @@ task cleanGodotTemplates(type: Delete) {
// Delete the library generated AAR files
delete("lib/build/outputs/aar")
- // Delete the godotpayment libs directory contents
- delete("plugins/godotpayment/libs")
-
- // Delete the generated godotpayment aar
- delete("plugins/godotpayment/build/outputs/aar")
-
// Delete the app libs directory contents
delete("app/libs")
@@ -193,4 +177,6 @@ task cleanGodotTemplates(type: Delete) {
delete("$binDir/android_source.zip")
delete("$binDir/godot-lib.debug.aar")
delete("$binDir/godot-lib.release.aar")
+
+ finalizedBy getTasksByName("clean", true)
}
diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties
index e14cd5ba5c..2dc069ad2f 100644
--- a/platform/android/java/gradle.properties
+++ b/platform/android/java/gradle.properties
@@ -18,3 +18,5 @@ org.gradle.jvmargs=-Xmx1536m
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
+
+org.gradle.warning.mode=all
diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
index f56b0f6a5e..a7d8a0f310 100644
--- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties
+++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml
index fa39bc0f1d..3034794d69 100644
--- a/platform/android/java/lib/AndroidManifest.xml
+++ b/platform/android/java/lib/AndroidManifest.xml
@@ -6,6 +6,11 @@
<application>
+ <!-- Records the version of the Godot library -->
+ <meta-data
+ android:name="org.godotengine.library.version"
+ android:value="${godotLibraryVersion}" />
+
<service android:name=".GodotDownloaderService" />
</application>
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index 19eee5a315..6fc9a11a08 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -13,9 +13,18 @@ android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
+
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
+
+ manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersion()]
+ }
+
+ compileOptions {
+ sourceCompatibility versions.javaVersion
+ targetCompatibility versions.javaVersion
}
lintOptions {
@@ -50,15 +59,6 @@ android {
def buildType = variant.buildType.name.capitalize()
- def taskPrefix = ""
- if (project.path != ":") {
- taskPrefix = project.path + ":"
- }
-
- // Disable the externalNativeBuild* task as it would cause build failures since the cmake build
- // files is only setup for editing support.
- gradle.startParameter.excludedTaskNames += taskPrefix + "externalNativeBuild" + buildType
-
def releaseTarget = buildType.toLowerCase()
if (releaseTarget == null || releaseTarget == "") {
throw new GradleException("Invalid build type: " + buildType)
@@ -68,20 +68,46 @@ android {
throw new GradleException("Invalid default abi: " + defaultAbi)
}
+ // Find scons' executable path
+ File sconsExecutableFile = null
+ def sconsName = "scons"
+ def sconsExts = (org.gradle.internal.os.OperatingSystem.current().isWindows()
+ ? [".bat", ".cmd", ".ps1", ".exe"]
+ : [""])
+ logger.lifecycle("Looking for $sconsName executable path")
+ for (ext in sconsExts) {
+ String sconsNameExt = sconsName + ext
+ logger.lifecycle("Checking $sconsNameExt")
+
+ sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt)
+ if (sconsExecutableFile != null) {
+ // We're done!
+ break
+ }
+
+ // Check all the options in path
+ List<File> allOptions = org.gradle.internal.os.OperatingSystem.current().findAllInPath(sconsNameExt)
+ if (!allOptions.isEmpty()) {
+ // Pick the first option and we're done!
+ sconsExecutableFile = allOptions.get(0)
+ break
+ }
+ }
+
+ if (sconsExecutableFile == null) {
+ throw new GradleException("Unable to find executable path for the '$sconsName' command.")
+ } else {
+ logger.lifecycle("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}")
+ }
+
// Creating gradle task to generate the native libraries for the default abi.
def taskName = getSconsTaskName(buildType)
tasks.create(name: taskName, type: Exec) {
- executable "scons" + sconsExt
+ executable sconsExecutableFile.absolutePath
args "--directory=${pathToRootDir}", "platform=android", "target=${releaseTarget}", "android_arch=${defaultAbi}", "-j" + Runtime.runtime.availableProcessors()
}
// Schedule the tasks so the generated libs are present before the aar file is packaged.
tasks["merge${buildType}JniLibFolders"].dependsOn taskName
}
-
- externalNativeBuild {
- cmake {
- path "CMakeLists.txt"
- }
- }
}
diff --git a/platform/android/java/lib/res/layout/downloading_expansion.xml b/platform/android/java/lib/res/layout/downloading_expansion.xml
index 4a9700965f..34c2757598 100644
--- a/platform/android/java/lib/res/layout/downloading_expansion.xml
+++ b/platform/android/java/lib/res/layout/downloading_expansion.xml
@@ -162,4 +162,4 @@
</LinearLayout>
</LinearLayout>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/platform/android/java/lib/res/layout/godot_app_layout.xml b/platform/android/java/lib/res/layout/godot_app_layout.xml
new file mode 100644
index 0000000000..386ded1c5d
--- /dev/null
+++ b/platform/android/java/lib/res/layout/godot_app_layout.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/godot_fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml b/platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml
index fae1faeb60..426e1bd841 100644
--- a/platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml
+++ b/platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml
@@ -105,4 +105,4 @@
</RelativeLayout>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
index 1ed4037035..cfdcca2ab5 100644
--- a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
+++ b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
@@ -2,4 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/icon_background"/>
<foreground android:drawable="@mipmap/icon_foreground"/>
-</adaptive-icon> \ No newline at end of file
+</adaptive-icon>
diff --git a/platform/android/java/lib/res/values-fa/strings.xml b/platform/android/java/lib/res/values-fa/strings.xml
index f1e29013c4..60b01accf1 100644
--- a/platform/android/java/lib/res/values-fa/strings.xml
+++ b/platform/android/java/lib/res/values-fa/strings.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="godot_project_name_string">godot-project-name-fa</string>
<string name="text_paused_cellular">آیا می خواهید بر روی اتصال داده همراه دانلود را شروع کنید؟ بر اساس نوع سطح داده شما این ممکن است برای شما هزینه مالی داشته باشد.</string>
<string name="text_paused_cellular_2">اگر نمی خواهید بر روی اتصال داده همراه دانلود را شروع کنید ، دانلود به صورت خودکار در زمان دسترسی به وای-فای شروع می شود.</string>
<string name="text_button_resume_cellular">ادامه دانلود</string>
diff --git a/platform/android/java/lib/res/values-in/strings.xml b/platform/android/java/lib/res/values-in/strings.xml
deleted file mode 100644
index 9e9a8b0c03..0000000000
--- a/platform/android/java/lib/res/values-in/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="godot_project_name_string">godot-project-name-id</string>
-</resources> \ No newline at end of file
diff --git a/platform/android/java/lib/res/values-iw/strings.xml b/platform/android/java/lib/res/values-iw/strings.xml
deleted file mode 100644
index f52ede2085..0000000000
--- a/platform/android/java/lib/res/values-iw/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="godot_project_name_string">godot-project-name-he</string>
-</resources> \ No newline at end of file
diff --git a/platform/android/java/lib/res/values-ko/strings.xml b/platform/android/java/lib/res/values-ko/strings.xml
index fab0bdd753..7b62345977 100644
--- a/platform/android/java/lib/res/values-ko/strings.xml
+++ b/platform/android/java/lib/res/values-ko/strings.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="godot_project_name_string">godot-project-name-ko</string>
<string name="text_paused_cellular">모바일 네트워크를 사용하여 다운로드 하시겠습니까? 남은 데이터 사용량에 따라, 요금이 부과될 수 있습니다.</string>
<string name="text_paused_cellular_2">모바일 네트워크를 사용하여 다운로드 하지 않을 경우, 와이파이 연결이 가능할 때 자동적으로 다운로드가 이루어집니다.</string>
<string name="text_button_resume_cellular">다운로드 계속하기</string>
@@ -52,4 +51,4 @@
<string name="kilobytes_per_second">%1$s KB/s</string>
<string name="time_remaining">남은 시간: %1$s</string>
<string name="time_remaining_notification">%1$s 남음</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values/dimens.xml b/platform/android/java/lib/res/values/dimens.xml
new file mode 100644
index 0000000000..9034dbbcc1
--- /dev/null
+++ b/platform/android/java/lib/res/values/dimens.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="text_edit_height">48dp</dimen>
+</resources>
diff --git a/platform/android/java/lib/res/values/strings.xml b/platform/android/java/lib/res/values/strings.xml
index a1b81a6186..590b066d8a 100644
--- a/platform/android/java/lib/res/values/strings.xml
+++ b/platform/android/java/lib/res/values/strings.xml
@@ -52,4 +52,4 @@
<string name="kilobytes_per_second">%1$s KB/s</string>
<string name="time_remaining">Time remaining: %1$s</string>
<string name="time_remaining_notification">%1$s left</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/res/values/styles.xml b/platform/android/java/lib/res/values/styles.xml
index a442f61e7e..b798373bc6 100644
--- a/platform/android/java/lib/res/values/styles.xml
+++ b/platform/android/java/lib/res/values/styles.xml
@@ -22,4 +22,4 @@
<item name="android:background">@android:color/background_dark</item>
</style>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
index 8b7a9c6c74..0572cf3589 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Dictionary.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
index 55b87b49e5..4e67402c63 100644
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/CustomSSLSocketFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* CustomSSLSocketFactory.java */
+/* FullScreenGodotApp.java */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,45 +28,72 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.plugin.payment.utils;
+package org.godotengine.godot;
-import java.io.IOException;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.KeyEvent;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManagerFactory;
-
-import org.apache.http.conn.ssl.SSLSocketFactory;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
/**
+ * Base activity for Android apps intending to use Godot as the primary and only screen.
*
- * @author Luis Linietsky <luis.linietsky@gmail.com>
+ * It's also a reference implementation for how to setup and use the {@link Godot} fragment
+ * within an Android app.
*/
-public class CustomSSLSocketFactory extends SSLSocketFactory {
- SSLContext sslContext = SSLContext.getInstance("TLS");
+public abstract class FullScreenGodotApp extends FragmentActivity {
+ @Nullable
+ private Godot godotFragment;
- public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- super(truststore);
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.godot_app_layout);
+ godotFragment = initGodotInstance();
+ if (godotFragment == null) {
+ throw new IllegalStateException("Godot instance must be non-null.");
+ }
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
- tmf.init(truststore);
+ getSupportFragmentManager().beginTransaction().replace(R.id.godot_fragment_container, godotFragment).setPrimaryNavigationFragment(godotFragment).commitNowAllowingStateLoss();
+ }
- sslContext.init(null, tmf.getTrustManagers(), null);
+ @Override
+ public void onNewIntent(Intent intent) {
+ if (godotFragment != null) {
+ godotFragment.onNewIntent(intent);
+ }
}
@Override
- public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
- return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
+ public void onBackPressed() {
+ if (godotFragment != null) {
+ godotFragment.onBackPressed();
+ } else {
+ super.onBackPressed();
+ }
}
@Override
- public Socket createSocket() throws IOException {
- return sslContext.getSocketFactory().createSocket();
+ public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) {
+ if (godotFragment != null && godotFragment.onKeyMultiple(inKeyCode, repeatCount, event)) {
+ return true;
+ }
+ return super.onKeyMultiple(inKeyCode, repeatCount, event);
+ }
+
+ /**
+ * Used to initialize the Godot fragment instance in {@link FullScreenGodotApp#onCreate(Bundle)}.
+ */
+ @NonNull
+ protected Godot initGodotInstance() {
+ return new Godot();
+ }
+
+ @Nullable
+ protected final Godot getGodotFragment() {
+ return godotFragment;
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index f27d8620ec..0891904dff 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,6 +30,9 @@
package org.godotengine.godot;
+import static android.content.Context.MODE_PRIVATE;
+import static android.content.Context.WINDOW_SERVICE;
+
import org.godotengine.godot.input.GodotEditText;
import org.godotengine.godot.plugin.GodotPlugin;
import org.godotengine.godot.plugin.GodotPluginRegistry;
@@ -67,7 +70,9 @@ import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings.Secure;
import android.view.Display;
+import android.view.InputDevice;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
@@ -84,7 +89,7 @@ import android.widget.TextView;
import androidx.annotation.CallSuper;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
-import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.Fragment;
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
@@ -102,7 +107,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
-public abstract class Godot extends FragmentActivity implements SensorEventListener, IDownloaderClient {
+public class Godot extends Fragment implements SensorEventListener, IDownloaderClient {
private IStub mDownloaderClientStub;
private TextView mStatusText;
private TextView mProgressFraction;
@@ -130,7 +135,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
static private Intent mCurrentIntent;
- @Override
public void onNewIntent(Intent intent) {
mCurrentIntent = intent;
}
@@ -148,14 +152,14 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
private void setButtonPausedState(boolean paused) {
mStatePaused = paused;
- int stringResourceID = paused ? R.string.text_button_resume :
- R.string.text_button_pause;
+ int stringResourceID = paused ? R.string.text_button_resume : R.string.text_button_pause;
mPauseButton.setText(stringResourceID);
}
private String[] command_line;
private boolean use_apk_expansion;
+ private ViewGroup containerLayout;
public GodotRenderView mRenderView;
private boolean godot_initialized = false;
@@ -174,7 +178,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
public ResultCallback result_callback;
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (result_callback != null) {
result_callback.callback(requestCode, resultCode, data);
result_callback = null;
@@ -211,27 +215,29 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
*/
@Keep
private void onVideoInit() {
- final FrameLayout layout = new FrameLayout(this);
- layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- setContentView(layout);
+ final Activity activity = getActivity();
+ containerLayout = new FrameLayout(activity);
+ containerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// GodotEditText layout
- GodotEditText editText = new GodotEditText(this);
- editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ GodotEditText editText = new GodotEditText(activity);
+ editText.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
+ (int)getResources().getDimension(R.dimen.text_edit_height)));
// ...add to FrameLayout
- layout.addView(editText);
+ containerLayout.addView(editText);
GodotLib.setup(command_line);
final String videoDriver = GodotLib.getGlobal("rendering/quality/driver/driver_name");
if (videoDriver.equals("Vulkan")) {
- mRenderView = new GodotVulkanRenderView(this);
+ mRenderView = new GodotVulkanRenderView(activity, this);
} else {
- mRenderView = new GodotGLRenderView(this, xrMode, use_32_bits, use_debug_opengl);
+ mRenderView = new GodotGLRenderView(activity, this, xrMode, use_32_bits,
+ use_debug_opengl);
}
View view = mRenderView.getView();
- layout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ containerLayout.addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
editText.setView(mRenderView);
io.setEdit(editText);
@@ -239,7 +245,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Override
public void onGlobalLayout() {
Point fullSize = new Point();
- getWindowManager().getDefaultDisplay().getSize(fullSize);
+ activity.getWindowManager().getDefaultDisplay().getSize(fullSize);
Rect gameSize = new Rect();
mRenderView.getView().getWindowVisibleDisplayFrame(gameSize);
@@ -262,9 +268,9 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
// Include the returned non-null views in the Godot view hierarchy.
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
- View pluginView = plugin.onMainCreateView(this);
+ View pluginView = plugin.onMainCreate(activity);
if (pluginView != null) {
- layout.addView(pluginView);
+ containerLayout.addView(pluginView);
}
}
}
@@ -274,9 +280,9 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Override
public void run() {
if (p_enabled) {
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
});
@@ -290,7 +296,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Keep
private void vibrate(int durationMs) {
if (requestPermission("VIBRATE")) {
- Vibrator v = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
+ Vibrator v = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (v != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE));
@@ -314,13 +320,16 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
// Using instrumentation is a way of making the whole app process restart, because Android
// will kill any process of the same package which was already running.
//
- Bundle args = new Bundle();
- args.putParcelable("intent", mCurrentIntent);
- startInstrumentation(new ComponentName(this, GodotInstrumentation.class), null, args);
+ final Activity activity = getActivity();
+ if (activity != null) {
+ Bundle args = new Bundle();
+ args.putParcelable("intent", mCurrentIntent);
+ activity.startInstrumentation(new ComponentName(activity, GodotInstrumentation.class), null, args);
+ }
}
public void alert(final String message, final String title) {
- final Activity activity = this;
+ final Activity activity = getActivity();
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -340,15 +349,16 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
public int getGLESVersionCode() {
- ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager am = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo deviceInfo = am.getDeviceConfigurationInfo();
return deviceInfo.reqGlEsVersion;
}
- private String[] getCommandLine() {
+ @CallSuper
+ protected String[] getCommandLine() {
InputStream is;
try {
- is = getAssets().open("_cl_");
+ is = getActivity().getAssets().open("_cl_");
byte[] len = new byte[4];
int r = is.read(len);
if (r < 4) {
@@ -425,11 +435,12 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
command_line = new_cmdline;
}
- io = new GodotIO(this);
- io.unique_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
+ final Activity activity = getActivity();
+ io = new GodotIO(activity);
+ io.unique_id = Secure.getString(activity.getContentResolver(), Secure.ANDROID_ID);
GodotLib.io = io;
- netUtils = new GodotNetUtils(this);
- mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
+ netUtils = new GodotNetUtils(activity);
+ mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
mGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
@@ -439,7 +450,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
- GodotLib.initialize(this, getAssets(), use_apk_expansion);
+ GodotLib.initialize(activity, this, activity.getAssets(), use_apk_expansion);
result_callback = null;
@@ -453,151 +464,162 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
@Override
- protected void onCreate(Bundle icicle) {
+ public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- Window window = getWindow();
+
+ final Activity activity = getActivity();
+ Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
- mClipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
+ mClipboard = (ClipboardManager)activity.getSystemService(Context.CLIPBOARD_SERVICE);
pluginRegistry = GodotPluginRegistry.initializePluginRegistry(this);
//check for apk expansion API
- if (true) {
- boolean md5mismatch = false;
- command_line = getCommandLine();
- String main_pack_md5 = null;
- String main_pack_key = null;
-
- List<String> new_args = new LinkedList<String>();
-
- for (int i = 0; i < command_line.length; i++) {
- boolean has_extra = i < command_line.length - 1;
- if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) {
- xrMode = XRMode.REGULAR;
- } else if (command_line[i].equals(XRMode.OVR.cmdLineArg)) {
- xrMode = XRMode.OVR;
- } else if (command_line[i].equals("--use_depth_32")) {
- use_32_bits = true;
- } else if (command_line[i].equals("--debug_opengl")) {
- use_debug_opengl = true;
- } else if (command_line[i].equals("--use_immersive")) {
- use_immersive = true;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+
- window.getDecorView().setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar
- View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
-
- UiChangeListener();
- }
- } else if (command_line[i].equals("--use_apk_expansion")) {
- use_apk_expansion = true;
- } else if (has_extra && command_line[i].equals("--apk_expansion_md5")) {
- main_pack_md5 = command_line[i + 1];
- i++;
- } else if (has_extra && command_line[i].equals("--apk_expansion_key")) {
- main_pack_key = command_line[i + 1];
- SharedPreferences prefs = getSharedPreferences("app_data_keys", MODE_PRIVATE);
- Editor editor = prefs.edit();
- editor.putString("store_public_key", main_pack_key);
-
- editor.apply();
- i++;
- } else if (command_line[i].trim().length() != 0) {
- new_args.add(command_line[i]);
+ boolean md5mismatch = false;
+ command_line = getCommandLine();
+ String main_pack_md5 = null;
+ String main_pack_key = null;
+
+ List<String> new_args = new LinkedList<String>();
+
+ for (int i = 0; i < command_line.length; i++) {
+ boolean has_extra = i < command_line.length - 1;
+ if (command_line[i].equals(XRMode.REGULAR.cmdLineArg)) {
+ xrMode = XRMode.REGULAR;
+ } else if (command_line[i].equals(XRMode.OVR.cmdLineArg)) {
+ xrMode = XRMode.OVR;
+ } else if (command_line[i].equals("--use_depth_32")) {
+ use_32_bits = true;
+ } else if (command_line[i].equals("--debug_opengl")) {
+ use_debug_opengl = true;
+ } else if (command_line[i].equals("--use_immersive")) {
+ use_immersive = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+
+ window.getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar
+ View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+
+ UiChangeListener();
}
+ } else if (command_line[i].equals("--use_apk_expansion")) {
+ use_apk_expansion = true;
+ } else if (has_extra && command_line[i].equals("--apk_expansion_md5")) {
+ main_pack_md5 = command_line[i + 1];
+ i++;
+ } else if (has_extra && command_line[i].equals("--apk_expansion_key")) {
+ main_pack_key = command_line[i + 1];
+ SharedPreferences prefs = activity.getSharedPreferences("app_data_keys",
+ MODE_PRIVATE);
+ Editor editor = prefs.edit();
+ editor.putString("store_public_key", main_pack_key);
+
+ editor.apply();
+ i++;
+ } else if (command_line[i].trim().length() != 0) {
+ new_args.add(command_line[i]);
}
+ }
- if (new_args.isEmpty()) {
- command_line = null;
- } else {
- command_line = new_args.toArray(new String[new_args.size()]);
+ if (new_args.isEmpty()) {
+ command_line = null;
+ } else {
+ command_line = new_args.toArray(new String[new_args.size()]);
+ }
+ if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) {
+ //check that environment is ok!
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ //show popup and die
}
- if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) {
- //check that environment is ok!
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- //show popup and die
- }
- // Build the full path to the app's expansion files
- try {
- expansion_pack_path = Helpers.getSaveFilePath(getApplicationContext());
- expansion_pack_path += "/main." + getPackageManager().getPackageInfo(getPackageName(), 0).versionCode + "." + this.getPackageName() + ".obb";
- } catch (Exception e) {
- e.printStackTrace();
- }
+ // Build the full path to the app's expansion files
+ try {
+ expansion_pack_path = Helpers.getSaveFilePath(getContext());
+ expansion_pack_path += "/main." + activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionCode + "." + activity.getPackageName() + ".obb";
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
- File f = new File(expansion_pack_path);
+ File f = new File(expansion_pack_path);
- boolean pack_valid = true;
+ boolean pack_valid = true;
- if (!f.exists()) {
- pack_valid = false;
+ if (!f.exists()) {
+ pack_valid = false;
- } else if (obbIsCorrupted(expansion_pack_path, main_pack_md5)) {
- pack_valid = false;
- try {
- f.delete();
- } catch (Exception e) {
- }
+ } else if (obbIsCorrupted(expansion_pack_path, main_pack_md5)) {
+ pack_valid = false;
+ try {
+ f.delete();
+ } catch (Exception e) {
}
+ }
- if (!pack_valid) {
- Intent notifierIntent = new Intent(this, this.getClass());
- notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if (!pack_valid) {
+ Intent notifierIntent = new Intent(activity, activity.getClass());
+ notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_CLEAR_TOP);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
- notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent pendingIntent = PendingIntent.getActivity(activity, 0,
+ notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- int startResult;
- try {
- startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
- getApplicationContext(),
- pendingIntent,
+ int startResult;
+ try {
+ startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(
+ getContext(),
+ pendingIntent,
+ GodotDownloaderService.class);
+
+ if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
+ // This is where you do set up to display the download
+ // progress (next step in onCreateView)
+ mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
GodotDownloaderService.class);
- if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
- // This is where you do set up to display the download
- // progress (next step)
- mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
- GodotDownloaderService.class);
-
- setContentView(R.layout.downloading_expansion);
- mPB = (ProgressBar)findViewById(R.id.progressBar);
- mStatusText = (TextView)findViewById(R.id.statusText);
- mProgressFraction = (TextView)findViewById(R.id.progressAsFraction);
- mProgressPercent = (TextView)findViewById(R.id.progressAsPercentage);
- mAverageSpeed = (TextView)findViewById(R.id.progressAverageSpeed);
- mTimeRemaining = (TextView)findViewById(R.id.progressTimeRemaining);
- mDashboard = findViewById(R.id.downloaderDashboard);
- mCellMessage = findViewById(R.id.approveCellular);
- mPauseButton = (Button)findViewById(R.id.pauseButton);
- mWiFiSettingsButton = (Button)findViewById(R.id.wifiSettingsButton);
-
- return;
- }
- } catch (NameNotFoundException e) {
- // TODO Auto-generated catch block
+ return;
}
+ } catch (NameNotFoundException e) {
+ // TODO Auto-generated catch block
}
}
}
- mCurrentIntent = getIntent();
+ mCurrentIntent = activity.getIntent();
initializeGodot();
}
@Override
- protected void onDestroy() {
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) {
+ if (mDownloaderClientStub != null) {
+ View downloadingExpansionView =
+ inflater.inflate(R.layout.downloading_expansion, container, false);
+ mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar);
+ mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText);
+ mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction);
+ mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage);
+ mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed);
+ mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining);
+ mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard);
+ mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular);
+ mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton);
+ mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton);
+
+ return downloadingExpansionView;
+ }
+
+ return containerLayout;
+ }
+
+ @Override
+ public void onDestroy() {
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onMainDestroy();
}
- GodotLib.ondestroy(this);
+ GodotLib.ondestroy();
super.onDestroy();
@@ -607,13 +629,13 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
@Override
- protected void onPause() {
+ public void onPause() {
super.onPause();
activityResumed = false;
if (!godot_initialized) {
if (null != mDownloaderClientStub) {
- mDownloaderClientStub.disconnect(this);
+ mDownloaderClientStub.disconnect(getActivity());
}
return;
}
@@ -643,12 +665,12 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
@Override
- protected void onResume() {
+ public void onResume() {
super.onResume();
activityResumed = true;
if (!godot_initialized) {
if (null != mDownloaderClientStub) {
- mDownloaderClientStub.connect(this);
+ mDownloaderClientStub.connect(getActivity());
}
return;
}
@@ -661,7 +683,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME);
if (use_immersive && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // check if the application runs on an android 4.4+
- Window window = getWindow();
+ Window window = getActivity().getWindow();
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
@@ -677,7 +699,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
public void UiChangeListener() {
- final View decorView = getWindow().getDecorView();
+ final View decorView = getActivity().getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
@@ -698,7 +720,8 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
@Override
public void onSensorChanged(SensorEvent event) {
- Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
+ Display display =
+ ((WindowManager)getActivity().getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
int displayRotation = display.getRotation();
float[] adjustedValues = new float[3];
@@ -747,9 +770,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
/*
@Override public boolean dispatchKeyEvent(KeyEvent event) {
-
if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
-
System.out.printf("** BACK REQUEST!\n");
GodotLib.quit();
@@ -761,7 +782,6 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
*/
- @Override
public void onBackPressed() {
boolean shouldQuit = true;
@@ -792,6 +812,12 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
}
+ public final void runOnUiThread(@NonNull Runnable action) {
+ if (getActivity() != null) {
+ getActivity().runOnUiThread(action);
+ }
+ }
+
private void forceQuit() {
System.exit(0);
}
@@ -837,75 +863,17 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
}
- public boolean gotTouchEvent(final MotionEvent event) {
- final int evcount = event.getPointerCount();
- if (evcount == 0)
- return true;
-
- if (mRenderView != null) {
- final int[] arr = new int[event.getPointerCount() * 3];
-
- for (int i = 0; i < event.getPointerCount(); i++) {
- arr[i * 3 + 0] = (int)event.getPointerId(i);
- arr[i * 3 + 1] = (int)event.getX(i);
- arr[i * 3 + 2] = (int)event.getY(i);
- }
- final int pointer_idx = event.getPointerId(event.getActionIndex());
-
- //System.out.printf("gaction: %d\n",event.getAction());
- final int action = event.getAction() & MotionEvent.ACTION_MASK;
- mRenderView.queueOnRenderThread(new Runnable() {
- @Override
- public void run() {
- switch (action) {
- case MotionEvent.ACTION_DOWN: {
- GodotLib.touch(0, 0, evcount, arr);
- //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY());
- } break;
- case MotionEvent.ACTION_MOVE: {
- GodotLib.touch(1, 0, evcount, arr);
- /*
- for(int i=0;i<event.getPointerCount();i++) {
- System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i));
- }
- */
- } break;
- case MotionEvent.ACTION_POINTER_UP: {
- GodotLib.touch(4, pointer_idx, evcount, arr);
- //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
- } break;
- case MotionEvent.ACTION_POINTER_DOWN: {
- GodotLib.touch(3, pointer_idx, evcount, arr);
- //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx));
- } break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- GodotLib.touch(2, 0, evcount, arr);
- /*
- for(int i=0;i<event.getPointerCount();i++) {
- System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i));
- }
- */
- } break;
- }
- }
- });
- }
- return true;
- }
-
- @Override
public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) {
String s = event.getCharacters();
if (s == null || s.length() == 0)
- return super.onKeyMultiple(inKeyCode, repeatCount, event);
+ return false;
final char[] cc = s.toCharArray();
int cnt = 0;
for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0)
;
if (cnt == 0)
- return super.onKeyMultiple(inKeyCode, repeatCount, event);
+ return false;
mRenderView.queueOnRenderThread(new Runnable() {
// This method will be called on the rendering thread:
public void run() {
@@ -923,15 +891,15 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
}
public boolean requestPermission(String p_name) {
- return PermissionsUtil.requestPermission(p_name, this);
+ return PermissionsUtil.requestPermission(p_name, getActivity());
}
public boolean requestPermissions() {
- return PermissionsUtil.requestManifestPermissions(this);
+ return PermissionsUtil.requestManifestPermissions(getActivity());
}
public String[] getGrantedPermissions() {
- return PermissionsUtil.getGrantedPermissions(this);
+ return PermissionsUtil.getGrantedPermissions(getActivity());
}
/**
@@ -1030,4 +998,9 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe
public void initInputDevices() {
mRenderView.initInputDevices();
}
+
+ @Keep
+ private GodotRenderView getRenderView() { // used by native side to get renderView
+ return mRenderView;
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java
index a3dae15980..9784d51182 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java
index 434da95bc0..d33faab641 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 14dd893faa..63c91561ff 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,7 +29,6 @@
/*************************************************************************/
package org.godotengine.godot;
-
import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.utils.GLUtils;
@@ -42,6 +41,7 @@ import org.godotengine.godot.xr.regular.RegularContextFactory;
import org.godotengine.godot.xr.regular.RegularFallbackConfigChooser;
import android.annotation.SuppressLint;
+import android.content.Context;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.view.GestureDetector;
@@ -68,19 +68,20 @@ import android.view.SurfaceView;
* bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
*/
public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
- private final Godot activity;
+ private final Godot godot;
private final GodotInputHandler inputHandler;
private final GestureDetector detector;
private final GodotRenderer godotRenderer;
- public GodotGLRenderView(Godot activity, XRMode xrMode, boolean p_use_32_bits, boolean p_use_debug_opengl) {
- super(activity);
+ public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits,
+ boolean p_use_debug_opengl) {
+ super(context);
GLUtils.use_32 = p_use_32_bits;
GLUtils.use_debug_opengl = p_use_debug_opengl;
- this.activity = activity;
+ this.godot = godot;
this.inputHandler = new GodotInputHandler(this);
- this.detector = new GestureDetector(activity, new GodotGestureHandler(this));
+ this.detector = new GestureDetector(context, new GodotGestureHandler(this));
this.godotRenderer = new GodotRenderer();
init(xrMode, false, 16, 0);
}
@@ -112,7 +113,12 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override
public void onBackPressed() {
- activity.onBackPressed();
+ godot.onBackPressed();
+ }
+
+ @Override
+ public GodotInputHandler getInputHandler() {
+ return inputHandler;
}
@SuppressLint("ClickableViewAccessibility")
@@ -120,7 +126,7 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
this.detector.onTouchEvent(event);
- return activity.gotTouchEvent(event);
+ return inputHandler.onTouchEvent(event);
}
@Override
@@ -138,6 +144,11 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
return inputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
}
+ @Override
+ public boolean onCapturedPointerEvent(MotionEvent event) {
+ return inputHandler.onGenericMotionEvent(event);
+ }
+
private void init(XRMode xrMode, boolean translucent, int depth, int stencil) {
setPreserveEGLContextOnPause(true);
setFocusableInTouchMode(true);
@@ -177,15 +188,15 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
if (GLUtils.use_32) {
setEGLConfigChooser(translucent ?
- new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
+ new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
new RegularConfigChooser(8, 8, 8, 8, 16, stencil)) :
- new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
+ new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
new RegularConfigChooser(5, 6, 5, 0, 16, stencil)));
} else {
setEGLConfigChooser(translucent ?
- new RegularConfigChooser(8, 8, 8, 8, 16, stencil) :
- new RegularConfigChooser(5, 6, 5, 0, 16, stencil));
+ new RegularConfigChooser(8, 8, 8, 8, 16, stencil) :
+ new RegularConfigChooser(5, 6, 5, 0, 16, stencil));
}
break;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
index 93f4786e83..c7c7c1b40c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,16 +32,21 @@ package org.godotengine.godot;
import org.godotengine.godot.input.*;
+import android.app.Activity;
import android.content.*;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
+import android.graphics.Point;
import android.media.*;
import android.net.Uri;
import android.os.*;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayCutout;
+import android.view.WindowInsets;
import java.io.IOException;
import java.io.InputStream;
@@ -51,7 +56,7 @@ import java.util.Locale;
public class GodotIO {
AssetManager am;
- Godot activity;
+ final Activity activity;
GodotEditText edit;
final int SCREEN_LANDSCAPE = 0;
@@ -314,7 +319,7 @@ public class GodotIO {
dirs.remove(id);
}
- GodotIO(Godot p_activity) {
+ GodotIO(Activity p_activity) {
am = p_activity.getAssets();
activity = p_activity;
//streams = new HashMap<Integer, AssetData>();
@@ -460,9 +465,31 @@ public class GodotIO {
return (int)(metrics.density * 160f);
}
- public void showKeyboard(String p_existing_text, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
+ public int[] screenGetUsableRect() {
+ DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
+ Display display = activity.getWindowManager().getDefaultDisplay();
+ Point size = new Point();
+ display.getRealSize(size);
+
+ int result[] = { 0, 0, size.x, size.y };
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ WindowInsets insets = activity.getWindow().getDecorView().getRootWindowInsets();
+ DisplayCutout cutout = insets.getDisplayCutout();
+ if (cutout != null) {
+ int insetLeft = cutout.getSafeInsetLeft();
+ int insetTop = cutout.getSafeInsetTop();
+ result[0] = insetLeft;
+ result[1] = insetTop;
+ result[2] -= insetLeft + cutout.getSafeInsetRight();
+ result[3] -= insetTop + cutout.getSafeInsetBottom();
+ }
+ }
+ return result;
+ }
+
+ public void showKeyboard(String p_existing_text, boolean p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
if (edit != null)
- edit.showKeyboard(p_existing_text, p_max_input_length, p_cursor_start, p_cursor_end);
+ edit.showKeyboard(p_existing_text, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end);
//InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
//inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
@@ -488,13 +515,13 @@ public class GodotIO {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
} break;
case SCREEN_SENSOR_LANDSCAPE: {
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
} break;
case SCREEN_SENSOR_PORTRAIT: {
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
} break;
case SCREEN_SENSOR: {
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
} break;
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotInstrumentation.java b/platform/android/java/lib/src/org/godotengine/godot/GodotInstrumentation.java
index 965e616ef3..7f5fd8627c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotInstrumentation.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotInstrumentation.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index 72198ba123..534a50e9ed 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -50,13 +50,13 @@ public class GodotLib {
/**
* Invoked on the main thread to initialize Godot native layer.
*/
- public static native void initialize(Godot p_instance, Object p_asset_manager, boolean use_apk_expansion);
+ public static native void initialize(Activity activity, Godot p_instance, Object p_asset_manager, boolean use_apk_expansion);
/**
* Invoked on the main thread to clean up Godot native layer.
- * @see Activity#onDestroy()
+ * @see androidx.fragment.app.Fragment#onDestroy()
*/
- public static native void ondestroy(Godot p_instance);
+ public static native void ondestroy();
/**
* Invoked on the GL thread to complete setup for the Godot native layer logic.
@@ -66,11 +66,12 @@ public class GodotLib {
/**
* Invoked on the GL thread when the underlying Android surface has changed size.
- * @param width
- * @param height
+ * @param p_surface
+ * @param p_width
+ * @param p_height
* @see android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(GL10, int, int)
*/
- public static native void resize(int width, int height);
+ public static native void resize(Surface p_surface, int p_width, int p_height);
/**
* Invoked on the render thread when the underlying Android surface is created or recreated.
@@ -93,17 +94,19 @@ public class GodotLib {
/**
* Forward touch events from the main thread to the GL thread.
*/
- public static native void touch(int what, int pointer, int howmany, int[] arr);
+ public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions);
+ public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask);
+ public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask, float verticalFactor, float horizontalFactor);
/**
* Forward hover events from the main thread to the GL thread.
*/
- public static native void hover(int type, int x, int y);
+ public static native void hover(int type, float x, float y);
/**
* Forward double_tap events from the main thread to the GL thread.
*/
- public static native void doubletap(int x, int y);
+ public static native void doubleTap(int buttonMask, int x, int y);
/**
* Forward scroll events from the main thread to the GL thread.
@@ -160,14 +163,14 @@ public class GodotLib {
public static native void joyconnectionchanged(int p_device, boolean p_connected, String p_name);
/**
- * Invoked when the Android activity resumes.
- * @see Activity#onResume()
+ * Invoked when the Android app resumes.
+ * @see androidx.fragment.app.Fragment#onResume()
*/
public static native void focusin();
/**
- * Invoked when the Android activity pauses.
- * @see Activity#onPause()
+ * Invoked when the Android app pauses.
+ * @see androidx.fragment.app.Fragment#onPause()
*/
public static native void focusout();
@@ -189,7 +192,7 @@ public class GodotLib {
* @param p_method Name of the method to invoke
* @param p_params Parameters to use for method invocation
*/
- public static native void callobject(int p_id, String p_method, Object[] p_params);
+ public static native void callobject(long p_id, String p_method, Object[] p_params);
/**
* Invoke method |p_method| on the Godot object specified by |p_id| during idle time.
@@ -197,7 +200,7 @@ public class GodotLib {
* @param p_method Name of the method to invoke
* @param p_params Parameters to use for method invocation
*/
- public static native void calldeferred(int p_id, String p_method, Object[] p_params);
+ public static native void calldeferred(long p_id, String p_method, Object[] p_params);
/**
* Forward the results from a permission request.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
index 27e63f3a66..2047c88070 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,6 +30,8 @@
package org.godotengine.godot;
+import org.godotengine.godot.input.GodotInputHandler;
+
import android.view.SurfaceView;
public interface GodotRenderView {
@@ -43,4 +45,6 @@ public interface GodotRenderView {
abstract public void onActivityResumed();
abstract public void onBackPressed();
+
+ abstract public GodotInputHandler getInputHandler();
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
index 99d3662317..59bdbf7f8d 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -64,7 +64,7 @@ class GodotRenderer implements GLSurfaceView.Renderer {
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
- GodotLib.resize(width, height);
+ GodotLib.resize(null, width, height);
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onGLSurfaceChanged(gl, width, height);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
index e9872b58ff..2e59dbc0d0 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -36,23 +36,25 @@ import org.godotengine.godot.vulkan.VkRenderer;
import org.godotengine.godot.vulkan.VkSurfaceView;
import android.annotation.SuppressLint;
+import android.content.Context;
import android.view.GestureDetector;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceView;
public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
- private final Godot mActivity;
+ private final Godot godot;
private final GodotInputHandler mInputHandler;
private final GestureDetector mGestureDetector;
private final VkRenderer mRenderer;
- public GodotVulkanRenderView(Godot activity) {
- super(activity);
+ public GodotVulkanRenderView(Context context, Godot godot) {
+ super(context);
- mActivity = activity;
+ this.godot = godot;
mInputHandler = new GodotInputHandler(this);
- mGestureDetector = new GestureDetector(mActivity, new GodotGestureHandler(this));
+ mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));
mRenderer = new VkRenderer();
setFocusableInTouchMode(true);
@@ -86,7 +88,12 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override
public void onBackPressed() {
- mActivity.onBackPressed();
+ godot.onBackPressed();
+ }
+
+ @Override
+ public GodotInputHandler getInputHandler() {
+ return mInputHandler;
}
@SuppressLint("ClickableViewAccessibility")
@@ -94,22 +101,27 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
mGestureDetector.onTouchEvent(event);
- return mActivity.gotTouchEvent(event);
+ return mInputHandler.onTouchEvent(event);
}
@Override
public boolean onKeyUp(final int keyCode, KeyEvent event) {
- return mInputHandler.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
+ return mInputHandler.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyDown(final int keyCode, KeyEvent event) {
- return mInputHandler.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
+ return mInputHandler.onKeyDown(keyCode, event);
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
- return mInputHandler.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
+ return mInputHandler.onGenericMotionEvent(event);
+ }
+
+ @Override
+ public boolean onCapturedPointerEvent(MotionEvent event) {
+ return mInputHandler.onGenericMotionEvent(event);
}
@Override
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java
index 7f596575a8..d1e8ae5ca9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -36,6 +36,7 @@ import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.text.InputFilter;
+import android.text.InputType;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
@@ -58,7 +59,8 @@ public class GodotEditText extends EditText {
private GodotTextInputWrapper mInputWrapper;
private EditHandler sHandler = new EditHandler(this);
private String mOriginText;
- private int mMaxInputLength;
+ private int mMaxInputLength = Integer.MAX_VALUE;
+ private boolean mMultiline = false;
private static class EditHandler extends Handler {
private final WeakReference<GodotEditText> mEdit;
@@ -95,7 +97,11 @@ public class GodotEditText extends EditText {
protected void initView() {
setPadding(0, 0, 0, 0);
- setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
+ setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_ACTION_DONE);
+ }
+
+ public boolean isMultiline() {
+ return mMultiline;
}
private void handleMessage(final Message msg) {
@@ -115,6 +121,12 @@ public class GodotEditText extends EditText {
edit.mInputWrapper.setSelection(false);
}
+ int inputType = InputType.TYPE_CLASS_TEXT;
+ if (edit.isMultiline()) {
+ inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
+ }
+ edit.setInputType(inputType);
+
edit.mInputWrapper.setOriginText(text);
edit.addTextChangedListener(edit.mInputWrapper);
final InputMethodManager imm = (InputMethodManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -155,20 +167,41 @@ public class GodotEditText extends EditText {
// ===========================================================
@Override
public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
- super.onKeyDown(keyCode, keyEvent);
-
- /* Let GlSurfaceView get focus if back key is input. */
+ /* Let SurfaceView get focus if back key is input. */
if (keyCode == KeyEvent.KEYCODE_BACK) {
mRenderView.getView().requestFocus();
}
- return true;
+ // pass event to godot in special cases
+ if (needHandlingInGodot(keyCode, keyEvent) && mRenderView.getInputHandler().onKeyDown(keyCode, keyEvent)) {
+ return true;
+ } else {
+ return super.onKeyDown(keyCode, keyEvent);
+ }
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent keyEvent) {
+ if (needHandlingInGodot(keyCode, keyEvent) && mRenderView.getInputHandler().onKeyUp(keyCode, keyEvent)) {
+ return true;
+ } else {
+ return super.onKeyUp(keyCode, keyEvent);
+ }
+ }
+
+ private boolean needHandlingInGodot(int keyCode, KeyEvent keyEvent) {
+ boolean isArrowKey = keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN ||
+ keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT;
+ boolean isModifiedKey = keyEvent.isAltPressed() || keyEvent.isCtrlPressed() || keyEvent.isSymPressed() ||
+ keyEvent.isFunctionPressed() || keyEvent.isMetaPressed();
+ return isArrowKey || keyCode == KeyEvent.KEYCODE_TAB || KeyEvent.isModifierKey(keyCode) ||
+ isModifiedKey;
}
// ===========================================================
// Methods
// ===========================================================
- public void showKeyboard(String p_existing_text, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
+ public void showKeyboard(String p_existing_text, boolean p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
int maxInputLength = (p_max_input_length <= 0) ? Integer.MAX_VALUE : p_max_input_length;
if (p_cursor_start == -1) { // cursor position not given
this.mOriginText = p_existing_text;
@@ -181,6 +214,8 @@ public class GodotEditText extends EditText {
this.mMaxInputLength = maxInputLength - (p_existing_text.length() - p_cursor_end);
}
+ this.mMultiline = p_multiline;
+
final Message msg = new Message();
msg.what = HANDLER_OPEN_IME_KEYBOARD;
msg.obj = this;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
index 1c9a683bbd..2c39d06832 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,7 +33,6 @@ package org.godotengine.godot.input;
import org.godotengine.godot.GodotLib;
import org.godotengine.godot.GodotRenderView;
-import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
@@ -75,10 +74,11 @@ public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener
//Log.i("GodotGesture", "onDoubleTap");
final int x = Math.round(event.getX());
final int y = Math.round(event.getY());
+ final int buttonMask = event.getButtonState();
queueEvent(new Runnable() {
@Override
public void run() {
- GodotLib.doubletap(x, y);
+ GodotLib.doubleTap(buttonMask, x, y);
}
});
return true;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 9abd65cc67..435b8b325f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -36,7 +36,10 @@ import org.godotengine.godot.GodotLib;
import org.godotengine.godot.GodotRenderView;
import org.godotengine.godot.input.InputManagerCompat.InputDeviceListener;
+import android.os.Build;
import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.InputDevice;
import android.view.InputDevice.MotionRange;
import android.view.KeyEvent;
@@ -45,17 +48,24 @@ import android.view.MotionEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Handles input related events for the {@link GodotRenderView} view.
*/
public class GodotInputHandler implements InputDeviceListener {
- private final ArrayList<Joystick> mJoysticksDevices = new ArrayList<Joystick>();
-
private final GodotRenderView mRenderView;
private final InputManagerCompat mInputManager;
+ private final String tag = this.getClass().getSimpleName();
+
+ private final SparseIntArray mJoystickIds = new SparseIntArray(4);
+ private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<Joystick>(4);
+
public GodotInputHandler(GodotRenderView godotView) {
mRenderView = godotView;
mInputManager = InputManagerCompat.Factory.getInputManager(mRenderView.getView().getContext());
@@ -81,19 +91,20 @@ public class GodotInputHandler implements InputDeviceListener {
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
return false;
- };
+ }
int source = event.getSource();
if (isKeyEvent_GameDevice(source)) {
- final int button = getGodotButton(keyCode);
- final int device_id = findJoystickDevice(event.getDeviceId());
-
// Check if the device exists
- if (device_id > -1) {
+ final int deviceId = event.getDeviceId();
+ if (mJoystickIds.indexOfKey(deviceId) >= 0) {
+ final int button = getGodotButton(keyCode);
+ final int godotJoyId = mJoystickIds.get(deviceId);
+
queueEvent(new Runnable() {
@Override
public void run() {
- GodotLib.joybutton(device_id, button, false);
+ GodotLib.joybutton(godotJoyId, button, false);
}
});
}
@@ -106,7 +117,7 @@ public class GodotInputHandler implements InputDeviceListener {
GodotLib.key(keyCode, scanCode, chr, false);
}
});
- };
+ }
return true;
}
@@ -121,24 +132,25 @@ public class GodotInputHandler implements InputDeviceListener {
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
return false;
- };
+ }
int source = event.getSource();
//Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD)));
+ final int deviceId = event.getDeviceId();
+ // Check if source is a game device and that the device is a registered gamepad
if (isKeyEvent_GameDevice(source)) {
if (event.getRepeatCount() > 0) // ignore key echo
return true;
- final int button = getGodotButton(keyCode);
- final int device_id = findJoystickDevice(event.getDeviceId());
+ if (mJoystickIds.indexOfKey(deviceId) >= 0) {
+ final int button = getGodotButton(keyCode);
+ final int godotJoyId = mJoystickIds.get(deviceId);
- // Check if the device exists
- if (device_id > -1) {
queueEvent(new Runnable() {
@Override
public void run() {
- GodotLib.joybutton(device_id, button, true);
+ GodotLib.joybutton(godotJoyId, button, true);
}
});
}
@@ -151,46 +163,105 @@ public class GodotInputHandler implements InputDeviceListener {
GodotLib.key(keyCode, scanCode, chr, true);
}
});
- };
+ }
return true;
}
- public boolean onGenericMotionEvent(MotionEvent event) {
- if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) {
- final int device_id = findJoystickDevice(event.getDeviceId());
+ public boolean onTouchEvent(final MotionEvent event) {
+ // Mouse drag (mouse pressed and move) doesn't fire onGenericMotionEvent so this is needed
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ if (event.getAction() != MotionEvent.ACTION_MOVE) {
+ // we return true because every time a mouse event is fired, the event is already handled
+ // in onGenericMotionEvent, so by touch event we can say that the event is also handled
+ return true;
+ }
+ return handleMouseEvent(event);
+ }
+
+ final int evcount = event.getPointerCount();
+ if (evcount == 0)
+ return true;
+
+ if (mRenderView != null) {
+ final float[] arr = new float[event.getPointerCount() * 3]; // pointerId1, x1, y1, pointerId2, etc...
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ arr[i * 3 + 0] = event.getPointerId(i);
+ arr[i * 3 + 1] = event.getX(i);
+ arr[i * 3 + 2] = event.getY(i);
+ }
+ final int action = event.getActionMasked();
+ final int pointer_idx = event.getPointerId(event.getActionIndex());
+
+ mRenderView.queueOnRenderThread(new Runnable() {
+ @Override
+ public void run() {
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_POINTER_UP:
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr);
+ } break;
+ }
+ }
+ });
+ }
+ return true;
+ }
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getAction() == MotionEvent.ACTION_MOVE) {
// Check if the device exists
- if (device_id > -1) {
- Joystick joy = mJoysticksDevices.get(device_id);
-
- for (int i = 0; i < joy.axes.size(); i++) {
- InputDevice.MotionRange range = joy.axes.get(i);
- final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
- final int idx = i;
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyaxis(device_id, idx, value);
- }
- });
+ final int deviceId = event.getDeviceId();
+ if (mJoystickIds.indexOfKey(deviceId) >= 0) {
+ final int godotJoyId = mJoystickIds.get(deviceId);
+ Joystick joystick = mJoysticksDevices.get(deviceId);
+
+ for (int i = 0; i < joystick.axes.size(); i++) {
+ final int axis = joystick.axes.get(i);
+ final float value = event.getAxisValue(axis);
+ /**
+ * As all axes are polled for each event, only fire an axis event if the value has actually changed.
+ * Prevents flooding Godot with repeated events.
+ */
+ if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) {
+ // save value to prevent repeats
+ joystick.axesValues.put(axis, value);
+ final int godotAxisIdx = i;
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joyaxis(godotJoyId, godotAxisIdx, value);
+ //Log.i(tag, "GodotLib.joyaxis("+godotJoyId+", "+godotAxisIdx+", "+value+");");
+ }
+ });
+ }
}
- for (int i = 0; i < joy.hats.size(); i += 2) {
- final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis()));
- final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis()));
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyhat(device_id, hatX, hatY);
- }
- });
+ if (joystick.hasAxisHat) {
+ final int hatX = Math.round(event.getAxisValue(MotionEvent.AXIS_HAT_X));
+ final int hatY = Math.round(event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+ if (joystick.hatX != hatX || joystick.hatY != hatY) {
+ joystick.hatX = hatX;
+ joystick.hatY = hatY;
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joyhat(godotJoyId, hatX, hatY);
+ //Log.i(tag, "GodotLib.joyhat("+godotJoyId+", "+hatX+", "+hatY+");");
+ }
+ });
+ }
}
return true;
}
- } else if ((event.getSource() & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS) {
- final int x = Math.round(event.getX());
- final int y = Math.round(event.getY());
+ } else if (event.isFromSource(InputDevice.SOURCE_STYLUS)) {
+ final float x = event.getX();
+ final float y = event.getY();
final int type = event.getAction();
queueEvent(new Runnable() {
@Override
@@ -199,6 +270,11 @@ public class GodotInputHandler implements InputDeviceListener {
}
});
return true;
+
+ } else if (event.isFromSource(InputDevice.SOURCE_MOUSE) || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ return handleMouseEvent(event);
+ }
}
return false;
@@ -216,67 +292,98 @@ public class GodotInputHandler implements InputDeviceListener {
}
}
+ private int assignJoystickIdNumber(int deviceId) {
+ int godotJoyId = 0;
+ while (mJoystickIds.indexOfValue(godotJoyId) >= 0) {
+ godotJoyId++;
+ }
+ mJoystickIds.put(deviceId, godotJoyId);
+ return godotJoyId;
+ }
+
@Override
public void onInputDeviceAdded(int deviceId) {
- int id = findJoystickDevice(deviceId);
-
// Check if the device has not been already added
- if (id < 0) {
- InputDevice device = mInputManager.getInputDevice(deviceId);
- //device can be null if deviceId is not found
- if (device != null) {
- int sources = device.getSources();
- if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ||
- ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) {
- id = mJoysticksDevices.size();
-
- Joystick joy = new Joystick();
- joy.device_id = deviceId;
- joy.name = device.getName();
- joy.axes = new ArrayList<InputDevice.MotionRange>();
- joy.hats = new ArrayList<InputDevice.MotionRange>();
-
- List<InputDevice.MotionRange> ranges = device.getMotionRanges();
- Collections.sort(ranges, new RangeComparator());
-
- for (InputDevice.MotionRange range : ranges) {
- if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
- joy.hats.add(range);
- } else {
- joy.axes.add(range);
- }
- }
- mJoysticksDevices.add(joy);
+ if (mJoystickIds.indexOfKey(deviceId) >= 0) {
+ return;
+ }
+
+ InputDevice device = mInputManager.getInputDevice(deviceId);
+ //device can be null if deviceId is not found
+ if (device == null) {
+ return;
+ }
- final int device_id = id;
- final String name = joy.name;
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyconnectionchanged(device_id, true, name);
- }
- });
+ int sources = device.getSources();
+
+ // Device may not be a joystick or gamepad
+ if ((sources & InputDevice.SOURCE_GAMEPAD) != InputDevice.SOURCE_GAMEPAD &&
+ (sources & InputDevice.SOURCE_JOYSTICK) != InputDevice.SOURCE_JOYSTICK) {
+ return;
+ }
+
+ // Assign first available number. Re-use numbers where possible.
+ final int id = assignJoystickIdNumber(deviceId);
+
+ final Joystick joystick = new Joystick();
+ joystick.device_id = deviceId;
+ joystick.name = device.getName();
+
+ //Helps with creating new joypad mappings.
+ Log.i(tag, "=== New Input Device: " + joystick.name);
+
+ Set<Integer> already = new HashSet<Integer>();
+ for (InputDevice.MotionRange range : device.getMotionRanges()) {
+ boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK);
+ boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD);
+ //Log.i(tag, "axis: "+range.getAxis()+ ", isJoystick: "+isJoystick+", isGamepad: "+isGamepad);
+ if (!isJoystick && !isGamepad) {
+ continue;
+ }
+ final int axis = range.getAxis();
+ if (axis == MotionEvent.AXIS_HAT_X || axis == MotionEvent.AXIS_HAT_Y) {
+ joystick.hasAxisHat = true;
+ } else {
+ if (!already.contains(axis)) {
+ already.add(axis);
+ joystick.axes.add(axis);
+ } else {
+ Log.w(tag, " - DUPLICATE AXIS VALUE IN LIST: " + axis);
}
}
}
+ Collections.sort(joystick.axes);
+ for (int idx = 0; idx < joystick.axes.size(); idx++) {
+ //Helps with creating new joypad mappings.
+ Log.i(tag, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx);
+ }
+ mJoysticksDevices.put(deviceId, joystick);
+
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joyconnectionchanged(id, true, joystick.name);
+ }
+ });
}
@Override
public void onInputDeviceRemoved(int deviceId) {
- final int device_id = findJoystickDevice(deviceId);
-
- // Check if the evice has not been already removed
- if (device_id > -1) {
- mJoysticksDevices.remove(device_id);
-
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.joyconnectionchanged(device_id, false, "");
- }
- });
+ // Check if the device has not been already removed
+ if (mJoystickIds.indexOfKey(deviceId) < 0) {
+ return;
}
+ final int godotJoyId = mJoystickIds.get(deviceId);
+ mJoystickIds.delete(deviceId);
+ mJoysticksDevices.delete(deviceId);
+
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.joyconnectionchanged(godotJoyId, false, "");
+ }
+ });
}
@Override
@@ -357,13 +464,57 @@ public class GodotInputHandler implements InputDeviceListener {
return button;
}
- private int findJoystickDevice(int device_id) {
- for (int i = 0; i < mJoysticksDevices.size(); i++) {
- if (mJoysticksDevices.get(i).device_id == device_id) {
- return i;
+ private boolean handleMouseEvent(final MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_HOVER_EXIT: {
+ final float x = event.getX();
+ final float y = event.getY();
+ final int type = event.getAction();
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.hover(type, x, y);
+ }
+ });
+ return true;
+ }
+ case MotionEvent.ACTION_BUTTON_PRESS:
+ case MotionEvent.ACTION_BUTTON_RELEASE:
+ case MotionEvent.ACTION_MOVE: {
+ final float x = event.getX();
+ final float y = event.getY();
+ final int buttonsMask = event.getButtonState();
+ final int action = event.getAction();
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask);
+ }
+ });
+ return true;
+ }
+ case MotionEvent.ACTION_SCROLL: {
+ final float x = event.getX();
+ final float y = event.getY();
+ final int buttonsMask = event.getButtonState();
+ final int action = event.getAction();
+ final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor);
+ }
+ });
+ }
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_UP: {
+ // we can safely ignore these cases because they are always come beside ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE
+ return true;
}
}
-
- return -1;
+ return false;
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index 9c7cf9f341..3e0e6a65fd 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -123,7 +123,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
public void run() {
for (int i = 0; i < count; ++i) {
int key = newChars[i];
- if (key == '\n') {
+ if ((key == '\n') && !mEdit.isMultiline()) {
// Return keys are handled through action events
continue;
}
@@ -151,7 +151,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
});
}
- if (pActionID == EditorInfo.IME_NULL) {
+ if (pActionID == EditorInfo.IME_ACTION_DONE) {
// Enter key has been pressed
GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true);
GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java
index 1f3fe1e527..4b7318c718 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,9 +30,10 @@
package org.godotengine.godot.input;
-import android.view.InputDevice.MotionRange;
+import android.util.SparseArray;
import java.util.ArrayList;
+import java.util.List;
/**
* POJO class to represent a Joystick input device.
@@ -40,6 +41,12 @@ import java.util.ArrayList;
class Joystick {
int device_id;
String name;
- ArrayList<MotionRange> axes;
- ArrayList<MotionRange> hats;
+ List<Integer> axes = new ArrayList<Integer>();
+ protected boolean hasAxisHat = false;
+ /*
+ * Keep track of values so we can prevent flooding the engine with useless events.
+ */
+ protected final SparseArray axesValues = new SparseArray<Float>(4);
+ protected int hatX;
+ protected int hatY;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
index 431bd4f5f9..993f0e9127 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,6 +35,7 @@ import org.godotengine.godot.Godot;
import android.app.Activity;
import android.content.Intent;
+import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.View;
@@ -45,7 +46,9 @@ import androidx.annotation.Nullable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -93,19 +96,60 @@ public abstract class GodotPlugin {
}
/**
+ * Provides access to the underlying {@link Activity}.
+ */
+ @Nullable
+ protected Activity getActivity() {
+ return godot.getActivity();
+ }
+
+ /**
* Register the plugin with Godot native code.
*
* This method is invoked on the render thread.
*/
public final void onRegisterPluginWithGodotNative() {
- nativeRegisterSingleton(getPluginName());
+ registeredSignals.putAll(registerPluginWithGodotNative(this, new GodotPluginInfoProvider() {
+ @NonNull
+ @Override
+ public String getPluginName() {
+ return GodotPlugin.this.getPluginName();
+ }
+
+ @NonNull
+ @Override
+ public List<String> getPluginMethods() {
+ return GodotPlugin.this.getPluginMethods();
+ }
+
+ @NonNull
+ @Override
+ public Set<SignalInfo> getPluginSignals() {
+ return GodotPlugin.this.getPluginSignals();
+ }
+
+ @NonNull
+ @Override
+ public Set<String> getPluginGDNativeLibrariesPaths() {
+ return GodotPlugin.this.getPluginGDNativeLibrariesPaths();
+ }
+ }));
+ }
+
+ /**
+ * Register the plugin with Godot native code.
+ *
+ * This method must be invoked on the render thread.
+ */
+ public static Map<String, SignalInfo> registerPluginWithGodotNative(Object pluginObject, GodotPluginInfoProvider pluginInfoProvider) {
+ nativeRegisterSingleton(pluginInfoProvider.getPluginName(), pluginObject);
- Class clazz = getClass();
+ Class clazz = pluginObject.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
boolean found = false;
- for (String s : getPluginMethods()) {
+ for (String s : pluginInfoProvider.getPluginMethods()) {
if (s.equals(method.getName())) {
found = true;
break;
@@ -114,7 +158,7 @@ public abstract class GodotPlugin {
if (!found)
continue;
- List<String> ptr = new ArrayList<String>();
+ List<String> ptr = new ArrayList<>();
Class[] paramTypes = method.getParameterTypes();
for (Class c : paramTypes) {
@@ -124,34 +168,38 @@ public abstract class GodotPlugin {
String[] pt = new String[ptr.size()];
ptr.toArray(pt);
- nativeRegisterMethod(getPluginName(), method.getName(), method.getReturnType().getName(), pt);
+ nativeRegisterMethod(pluginInfoProvider.getPluginName(), method.getName(), method.getReturnType().getName(), pt);
}
// Register the signals for this plugin.
- for (SignalInfo signalInfo : getPluginSignals()) {
+ Map<String, SignalInfo> registeredSignals = new HashMap<>();
+ for (SignalInfo signalInfo : pluginInfoProvider.getPluginSignals()) {
String signalName = signalInfo.getName();
- nativeRegisterSignal(getPluginName(), signalName, signalInfo.getParamTypesNames());
+ nativeRegisterSignal(pluginInfoProvider.getPluginName(), signalName, signalInfo.getParamTypesNames());
registeredSignals.put(signalName, signalInfo);
}
// Get the list of gdnative libraries to register.
- Set<String> gdnativeLibrariesPaths = getPluginGDNativeLibrariesPaths();
+ Set<String> gdnativeLibrariesPaths = pluginInfoProvider.getPluginGDNativeLibrariesPaths();
if (!gdnativeLibrariesPaths.isEmpty()) {
nativeRegisterGDNativeLibraries(gdnativeLibrariesPaths.toArray(new String[0]));
}
+
+ return registeredSignals;
}
/**
* Invoked once during the Godot Android initialization process after creation of the
- * {@link org.godotengine.godot.GodotView} view.
+ * {@link org.godotengine.godot.GodotRenderView} view.
* <p>
- * This method should be overridden by descendants of this class that would like to add
- * their view/layout to the Godot view hierarchy.
+ * The plugin can return a non-null {@link View} layout in order to add it to the Godot view
+ * hierarchy.
*
- * @return the view to be included; null if no views should be included.
+ * @see Activity#onCreate(Bundle)
+ * @return the plugin's view to be included; null if no views should be included.
*/
@Nullable
- public View onMainCreateView(Activity activity) {
+ public View onMainCreate(Activity activity) {
return null;
}
@@ -280,8 +328,8 @@ public abstract class GodotPlugin {
/**
* Emit a registered Godot signal.
- * @param signalName
- * @param signalArgs
+ * @param signalName Name of the signal to emit. It will be validated against the set of registered signals.
+ * @param signalArgs Arguments used to populate the emitted signal. The arguments will be validated against the {@link SignalInfo} matching the registered signalName parameter.
*/
protected void emitSignal(final String signalName, final Object... signalArgs) {
try {
@@ -291,6 +339,27 @@ public abstract class GodotPlugin {
throw new IllegalArgumentException(
"Signal " + signalName + " is not registered for this plugin.");
}
+ emitSignal(getGodot(), getPluginName(), signalInfo, signalArgs);
+ } catch (IllegalArgumentException exception) {
+ Log.w(TAG, exception.getMessage());
+ if (BuildConfig.DEBUG) {
+ throw exception;
+ }
+ }
+ }
+
+ /**
+ * Emit a Godot signal.
+ * @param godot
+ * @param pluginName Name of the Godot plugin the signal will be emitted from. The plugin must already be registered with the Godot engine.
+ * @param signalInfo Information about the signal to emit.
+ * @param signalArgs Arguments used to populate the emitted signal. The arguments will be validated against the given {@link SignalInfo} parameter.
+ */
+ public static void emitSignal(Godot godot, String pluginName, SignalInfo signalInfo, final Object... signalArgs) {
+ try {
+ if (signalInfo == null) {
+ throw new IllegalArgumentException("Signal must be non null.");
+ }
// Validate the arguments count.
Class<?>[] signalParamTypes = signalInfo.getParamTypes();
@@ -307,12 +376,8 @@ public abstract class GodotPlugin {
}
}
- runOnRenderThread(new Runnable() {
- @Override
- public void run() {
- nativeEmitSignal(getPluginName(), signalName, signalArgs);
- }
- });
+ godot.runOnRenderThread(() -> nativeEmitSignal(pluginName, signalInfo.getName(), signalArgs));
+
} catch (IllegalArgumentException exception) {
Log.w(TAG, exception.getMessage());
if (BuildConfig.DEBUG) {
@@ -325,7 +390,7 @@ public abstract class GodotPlugin {
* Used to setup a {@link GodotPlugin} instance.
* @param p_name Name of the instance.
*/
- private native void nativeRegisterSingleton(String p_name);
+ private static native void nativeRegisterSingleton(String p_name, Object object);
/**
* Used to complete registration of the {@link GodotPlugin} instance's methods.
@@ -334,13 +399,13 @@ public abstract class GodotPlugin {
* @param p_ret Return type of the registered method
* @param p_params Method parameters types
*/
- private native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params);
+ private static native void nativeRegisterMethod(String p_sname, String p_name, String p_ret, String[] p_params);
/**
* Used to register gdnative libraries bundled by the plugin.
* @param gdnlibPaths Paths to the libraries relative to the 'assets' directory.
*/
- private native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths);
+ private static native void nativeRegisterGDNativeLibraries(String[] gdnlibPaths);
/**
* Used to complete registration of the {@link GodotPlugin} instance's methods.
@@ -348,7 +413,7 @@ public abstract class GodotPlugin {
* @param signalName Name of the signal to register
* @param signalParamTypes Signal parameters types
*/
- private native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes);
+ private static native void nativeRegisterSignal(String pluginName, String signalName, String[] signalParamTypes);
/**
* Used to emit signal by {@link GodotPlugin} instance.
@@ -356,5 +421,5 @@ public abstract class GodotPlugin {
* @param signalName Name of the signal to emit
* @param signalParams Signal parameters
*/
- private native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams);
+ private static native void nativeEmitSignal(String pluginName, String signalName, Object[] signalParams);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java
new file mode 100644
index 0000000000..c3084b036e
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginInfoProvider.java
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* GodotPluginInfoProvider.java */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+package org.godotengine.godot.plugin;
+
+import androidx.annotation.NonNull;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Provides the set of information expected from a Godot plugin.
+ */
+public interface GodotPluginInfoProvider {
+ /**
+ * Returns the name of the plugin.
+ */
+ @NonNull
+ String getPluginName();
+
+ /**
+ * Returns the list of methods to be exposed to Godot.
+ */
+ @NonNull
+ List<String> getPluginMethods();
+
+ /**
+ * Returns the list of signals to be exposed to Godot.
+ */
+ @NonNull
+ Set<SignalInfo> getPluginSignals();
+
+ /**
+ * Returns the paths for the plugin's gdnative libraries (if any).
+ *
+ * The paths must be relative to the 'assets' directory and point to a '*.gdnlib' file.
+ */
+ @NonNull
+ Set<String> getPluginGDNativeLibrariesPaths();
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
index 12d2ed09fb..99811f72ed 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,6 +32,7 @@ package org.godotengine.godot.plugin;
import org.godotengine.godot.Godot;
+import android.app.Activity;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -121,9 +122,11 @@ public final class GodotPluginRegistry {
private void loadPlugins(Godot godot) {
try {
- ApplicationInfo appInfo = godot
+ final Activity activity = godot.getActivity();
+ ApplicationInfo appInfo = activity
.getPackageManager()
- .getApplicationInfo(godot.getPackageName(), PackageManager.GET_META_DATA);
+ .getApplicationInfo(activity.getPackageName(),
+ PackageManager.GET_META_DATA);
Bundle metaData = appInfo.metaData;
if (metaData == null || metaData.isEmpty()) {
return;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java
index f82c4d3fa0..6428422641 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/SignalInfo.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java
index acc9c4981b..d6e49bb635 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/Crypt.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
index 82420eda79..19588f8465 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java
index 0832a9b965..721d7c65c6 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GodotNetUtils.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,8 +30,7 @@
package org.godotengine.godot.utils;
-import org.godotengine.godot.Godot;
-
+import android.app.Activity;
import android.content.Context;
import android.net.wifi.WifiManager;
import android.util.Log;
@@ -45,7 +44,7 @@ public class GodotNetUtils {
/* A single, reference counted, multicast lock, or null if permission CHANGE_WIFI_MULTICAST_STATE is missing */
private WifiManager.MulticastLock multicastLock;
- public GodotNetUtils(Godot p_activity) {
+ public GodotNetUtils(Activity p_activity) {
if (PermissionsUtil.hasManifestPermission(p_activity, "android.permission.CHANGE_WIFI_MULTICAST_STATE")) {
WifiManager wifi = (WifiManager)p_activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
multicastLock = wifi.createMulticastLock("GodotMulticastLock");
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
index 6837e4f147..b0ca96271e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,9 +30,8 @@
package org.godotengine.godot.utils;
-import org.godotengine.godot.Godot;
-
import android.Manifest;
+import android.app.Activity;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
@@ -65,7 +64,7 @@ public final class PermissionsUtil {
* @param activity the caller activity for this method.
* @return true/false. "true" if permission was granted otherwise returns "false".
*/
- public static boolean requestPermission(String name, Godot activity) {
+ public static boolean requestPermission(String name, Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Not necessary, asked on install already
return true;
@@ -93,7 +92,7 @@ public final class PermissionsUtil {
* @param activity the caller activity for this method.
* @return true/false. "true" if all permissions were granted otherwise returns "false".
*/
- public static boolean requestManifestPermissions(Godot activity) {
+ public static boolean requestManifestPermissions(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
@@ -138,7 +137,7 @@ public final class PermissionsUtil {
* @param activity the caller activity for this method.
* @return granted permissions list
*/
- public static String[] getGrantedPermissions(Godot activity) {
+ public static String[] getGrantedPermissions(Activity activity) {
String[] manifestPermissions;
try {
manifestPermissions = getManifestPermissions(activity);
@@ -172,7 +171,7 @@ public final class PermissionsUtil {
* @param permission the permession to look for in the manifest file.
* @return "true" if the permission is in the manifest file of the activity, "false" otherwise.
*/
- public static boolean hasManifestPermission(Godot activity, String permission) {
+ public static boolean hasManifestPermission(Activity activity, String permission) {
try {
for (String p : getManifestPermissions(activity)) {
if (permission.equals(p))
@@ -190,7 +189,7 @@ public final class PermissionsUtil {
* @return manifest permissions list
* @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found.
*/
- private static String[] getManifestPermissions(Godot activity) throws PackageManager.NameNotFoundException {
+ private static String[] getManifestPermissions(Activity activity) throws PackageManager.NameNotFoundException {
PackageManager packageManager = activity.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);
if (packageInfo.requestedPermissions == null)
@@ -205,7 +204,7 @@ public final class PermissionsUtil {
* @return permission info object
* @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found.
*/
- private static PermissionInfo getPermissionInfo(Godot activity, String permission) throws PackageManager.NameNotFoundException {
+ private static PermissionInfo getPermissionInfo(Activity activity, String permission) throws PackageManager.NameNotFoundException {
PackageManager packageManager = activity.getPackageManager();
return packageManager.getPermissionInfo(permission, 0);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
index 608ad48df9..a35f6ec5a7 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -52,16 +52,13 @@ import org.godotengine.godot.plugin.GodotPluginRegistry
* @see [VkSurfaceView.startRenderer]
*/
internal class VkRenderer {
-
private val pluginRegistry: GodotPluginRegistry = GodotPluginRegistry.getPluginRegistry()
/**
* Called when the surface is created and signals the beginning of rendering.
*/
fun onVkSurfaceCreated(surface: Surface) {
- // TODO: properly implement surface re-creation:
- // GodotLib.newcontext should be called here once it's done.
- //GodotLib.newcontext(surface, false)
+ GodotLib.newcontext(surface, false)
for (plugin in pluginRegistry.getAllPlugins()) {
plugin.onVkSurfaceCreated(surface)
@@ -72,13 +69,8 @@ internal class VkRenderer {
* Called after the surface is created and whenever its size changes.
*/
fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) {
- GodotLib.resize(width, height)
-
- // TODO: properly implement surface re-creation:
- // Update the native renderer instead of restarting the app.
- // GodotLib.newcontext should not be called here once it's done.
- GodotLib.newcontext(surface, false)
-
+ GodotLib.resize(surface, width, height)
+
for (plugin in pluginRegistry.getAllPlugins()) {
plugin.onVkSurfaceChanged(surface, width, height)
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
index 6b0e12b21a..f0e37d80b8 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -50,7 +50,6 @@ import android.view.SurfaceView
* </ul>
*/
open internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
-
companion object {
fun checkState(expression: Boolean, errorMessage: Any) {
check(expression) { errorMessage.toString() }
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
index 7557c8aa22..b967fd5f24 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -41,7 +41,6 @@ import kotlin.concurrent.withLock
* The implementation is modeled after [android.opengl.GLSurfaceView]'s GLThread.
*/
internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vkRenderer: VkRenderer) : Thread(TAG) {
-
companion object {
private val TAG = VkThread::class.java.simpleName
}
@@ -226,5 +225,4 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
threadExiting()
}
}
-
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java b/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java
index 982e43f9d1..0995477baf 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
index 819bcccdf1..245d573bfc 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrConfigChooser.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
index 2d9b921466..d3aca267ef 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrContextFactory.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
index 43c7f0f966..83aa458064 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/ovr/OvrWindowSurfaceFactory.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
index 54672db282..341427209b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularConfigChooser.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
index 126f3ad5f5..71610d2d00 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
index c83c47bed7..e690c5b695 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/java/nativeSrcsConfigs/AndroidManifest.xml b/platform/android/java/nativeSrcsConfigs/AndroidManifest.xml
new file mode 100644
index 0000000000..dc180375d5
--- /dev/null
+++ b/platform/android/java/nativeSrcsConfigs/AndroidManifest.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest package="org.godotengine.godot" />
diff --git a/platform/android/java/lib/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
index d3bdf6a5f2..34925684da 100644
--- a/platform/android/java/lib/CMakeLists.txt
+++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
@@ -1,3 +1,4 @@
+# Non functional cmake build file used to provide Android Studio editor support to the project.
cmake_minimum_required(VERSION 3.6)
project(godot)
diff --git a/platform/android/java/nativeSrcsConfigs/README.md b/platform/android/java/nativeSrcsConfigs/README.md
new file mode 100644
index 0000000000..e48505ccda
--- /dev/null
+++ b/platform/android/java/nativeSrcsConfigs/README.md
@@ -0,0 +1,4 @@
+## Native sources configs
+
+This is a non functional Android library used to provide Android Studio editor support to the Godot project native files.
+Nothing else should be added to this library.
diff --git a/platform/android/java/nativeSrcsConfigs/build.gradle b/platform/android/java/nativeSrcsConfigs/build.gradle
new file mode 100644
index 0000000000..66077060ea
--- /dev/null
+++ b/platform/android/java/nativeSrcsConfigs/build.gradle
@@ -0,0 +1,56 @@
+// Non functional android library used to provide Android Studio editor support to the project.
+plugins {
+ id 'com.android.library'
+}
+
+android {
+ compileSdkVersion versions.compileSdk
+ buildToolsVersion versions.buildTools
+
+ defaultConfig {
+ minSdkVersion versions.minSdk
+ targetSdkVersion versions.targetSdk
+ }
+
+ compileOptions {
+ sourceCompatibility versions.javaVersion
+ targetCompatibility versions.javaVersion
+ }
+
+ packagingOptions {
+ exclude 'META-INF/LICENSE'
+ exclude 'META-INF/NOTICE'
+
+ // Should be uncommented for development purpose within Android Studio
+ // doNotStrip '**/*.so'
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ }
+ }
+
+ ndkVersion versions.ndkVersion
+
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+
+ libraryVariants.all { variant ->
+ def buildType = variant.buildType.name.capitalize()
+
+ def taskPrefix = ""
+ if (project.path != ":") {
+ taskPrefix = project.path + ":"
+ }
+
+ // Disable the externalNativeBuild* task as it would cause build failures since the cmake build
+ // files is only setup for editing support.
+ gradle.startParameter.excludedTaskNames += taskPrefix + "externalNativeBuild" + buildType
+ }
+}
+
+dependencies {}
diff --git a/platform/android/java/plugins/godotpayment/build.gradle b/platform/android/java/plugins/godotpayment/build.gradle
deleted file mode 100644
index ffab86e26e..0000000000
--- a/platform/android/java/plugins/godotpayment/build.gradle
+++ /dev/null
@@ -1,32 +0,0 @@
-apply plugin: 'com.android.library'
-
-android {
- compileSdkVersion versions.compileSdk
- buildToolsVersion versions.buildTools
- useLibrary 'org.apache.http.legacy'
-
- defaultConfig {
- minSdkVersion versions.minSdk
- targetSdkVersion versions.targetSdk
- }
-
- libraryVariants.all { variant ->
- variant.outputs.all { output ->
- output.outputFileName = "GodotPayment.${variant.name}.aar"
- }
- }
-
-}
-
-dependencies {
- implementation libraries.supportCoreUtils
- implementation libraries.v4Support
-
- if (rootProject.findProject(":lib")) {
- compileOnly project(":lib")
- } else if (rootProject.findProject(":godot:lib")) {
- compileOnly project(":godot:lib")
- } else {
- compileOnly fileTree(dir: 'libs', include: ['godot-lib*.aar'])
- }
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml b/platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml
deleted file mode 100644
index 61afa03799..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.godotengine.godot.plugin.payment">
-
- <application>
-
- <meta-data
- android:name="org.godotengine.plugin.v1.GodotPayment"
- android:value="org.godotengine.godot.plugin.payment.GodotPayment" />
-
- </application>
-</manifest>
diff --git a/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl b/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl
deleted file mode 100644
index 0f2bcae338..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.vending.billing;
-
-import android.os.Bundle;
-
-/**
- * InAppBillingService is the service that provides in-app billing version 3 and beyond.
- * This service provides the following features:
- * 1. Provides a new API to get details of in-app items published for the app including
- * price, type, title and description.
- * 2. The purchase flow is synchronous and purchase information is available immediately
- * after it completes.
- * 3. Purchase information of in-app purchases is maintained within the Google Play system
- * till the purchase is consumed.
- * 4. An API to consume a purchase of an inapp item. All purchases of one-time
- * in-app items are consumable and thereafter can be purchased again.
- * 5. An API to get current purchases of the user immediately. This will not contain any
- * consumed purchases.
- *
- * All calls will give a response code with the following possible values
- * RESULT_OK = 0 - success
- * RESULT_USER_CANCELED = 1 - User pressed back or canceled a dialog
- * RESULT_SERVICE_UNAVAILABLE = 2 - The network connection is down
- * RESULT_BILLING_UNAVAILABLE = 3 - This billing API version is not supported for the type requested
- * RESULT_ITEM_UNAVAILABLE = 4 - Requested SKU is not available for purchase
- * RESULT_DEVELOPER_ERROR = 5 - Invalid arguments provided to the API
- * RESULT_ERROR = 6 - Fatal error during the API action
- * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
- * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
- */
-interface IInAppBillingService {
- /**
- * Checks support for the requested billing API version, package and in-app type.
- * Minimum API version supported by this interface is 3.
- * @param apiVersion billing API version that the app is using
- * @param packageName the package name of the calling app
- * @param type type of the in-app item being purchased ("inapp" for one-time purchases
- * and "subs" for subscriptions)
- * @return RESULT_OK(0) on success and appropriate response code on failures.
- */
- int isBillingSupported(int apiVersion, String packageName, String type);
-
- /**
- * Provides details of a list of SKUs
- * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
- * with a list JSON strings containing the productId, price, title and description.
- * This API can be called with a maximum of 20 SKUs.
- * @param apiVersion billing API version that the app is using
- * @param packageName the package name of the calling app
- * @param type of the in-app items ("inapp" for one-time purchases
- * and "subs" for subscriptions)
- * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
- * @return Bundle containing the following key-value pairs
- * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
- * on failures.
- * "DETAILS_LIST" with a StringArrayList containing purchase information
- * in JSON format similar to:
- * '{ "productId" : "exampleSku",
- * "type" : "inapp",
- * "price" : "$5.00",
- * "price_currency": "USD",
- * "price_amount_micros": 5000000,
- * "title : "Example Title",
- * "description" : "This is an example description" }'
- */
- Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
-
- /**
- * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
- * the type, a unique purchase token and an optional developer payload.
- * @param apiVersion billing API version that the app is using
- * @param packageName package name of the calling app
- * @param sku the SKU of the in-app item as published in the developer console
- * @param type of the in-app item being purchased ("inapp" for one-time purchases
- * and "subs" for subscriptions)
- * @param developerPayload optional argument to be sent back with the purchase information
- * @return Bundle containing the following key-value pairs
- * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
- * on failures.
- * "BUY_INTENT" - PendingIntent to start the purchase flow
- *
- * The Pending intent should be launched with startIntentSenderForResult. When purchase flow
- * has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
- * If the purchase is successful, the result data will contain the following key-value pairs
- * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
- * codes on failures.
- * "INAPP_PURCHASE_DATA" - String in JSON format similar to
- * '{"orderId":"12999763169054705758.1371079406387615",
- * "packageName":"com.example.app",
- * "productId":"exampleSku",
- * "purchaseTime":1345678900000,
- * "purchaseToken" : "122333444455555",
- * "developerPayload":"example developer payload" }'
- * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
- * was signed with the private key of the developer
- */
- Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
- String developerPayload);
-
- /**
- * Returns the current SKUs owned by the user of the type and package name specified along with
- * purchase information and a signature of the data to be validated.
- * This will return all SKUs that have been purchased in V3 and managed items purchased using
- * V1 and V2 that have not been consumed.
- * @param apiVersion billing API version that the app is using
- * @param packageName package name of the calling app
- * @param type of the in-app items being requested ("inapp" for one-time purchases
- * and "subs" for subscriptions)
- * @param continuationToken to be set as null for the first call, if the number of owned
- * skus are too many, a continuationToken is returned in the response bundle.
- * This method can be called again with the continuation token to get the next set of
- * owned skus.
- * @return Bundle containing the following key-value pairs
- * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
- on failures.
- * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
- * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
- * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
- * of the purchase information
- * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
- * next set of in-app purchases. Only set if the
- * user has more owned skus than the current list.
- */
- Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
-
- /**
- * Consume the last purchase of the given SKU. This will result in this item being removed
- * from all subsequent responses to getPurchases() and allow re-purchase of this item.
- * @param apiVersion billing API version that the app is using
- * @param packageName package name of the calling app
- * @param purchaseToken token in the purchase information JSON that identifies the purchase
- * to be consumed
- * @return RESULT_OK(0) if consumption succeeded, appropriate response codes on failures.
- */
- int consumePurchase(int apiVersion, String packageName, String purchaseToken);
-
- /**
- * This API is currently under development.
- */
- int stub(int apiVersion, String packageName, String type);
-
- /**
- * Returns a pending intent to launch the purchase flow for upgrading or downgrading a
- * subscription. The existing owned SKU(s) should be provided along with the new SKU that
- * the user is upgrading or downgrading to.
- * @param apiVersion billing API version that the app is using, must be 5 or later
- * @param packageName package name of the calling app
- * @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
- * if null or empty this method will behave like {@link #getBuyIntent}
- * @param newSku the SKU that the user is upgrading or downgrading to
- * @param type of the item being purchased, currently must be "subs"
- * @param developerPayload optional argument to be sent back with the purchase information
- * @return Bundle containing the following key-value pairs
- * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
- * on failures.
- * "BUY_INTENT" - PendingIntent to start the purchase flow
- *
- * The Pending intent should be launched with startIntentSenderForResult. When purchase flow
- * has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
- * If the purchase is successful, the result data will contain the following key-value pairs
- * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
- * codes on failures.
- * "INAPP_PURCHASE_DATA" - String in JSON format similar to
- * '{"orderId":"12999763169054705758.1371079406387615",
- * "packageName":"com.example.app",
- * "productId":"exampleSku",
- * "purchaseTime":1345678900000,
- * "purchaseToken" : "122333444455555",
- * "developerPayload":"example developer payload" }'
- * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
- * was signed with the private key of the developer
- */
- Bundle getBuyIntentToReplaceSkus(int apiVersion, String packageName,
- in List<String> oldSkus, String newSku, String type, String developerPayload);
-
- /**
- * Returns a pending intent to launch the purchase flow for an in-app item. This method is
- * a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
- * parameter. This parameter is a Bundle of optional keys and values that affect the
- * operation of the method.
- * @param apiVersion billing API version that the app is using, must be 6 or later
- * @param packageName package name of the calling app
- * @param sku the SKU of the in-app item as published in the developer console
- * @param type of the in-app item being purchased ("inapp" for one-time purchases
- * and "subs" for subscriptions)
- * @param developerPayload optional argument to be sent back with the purchase information
- * @extraParams a Bundle with the following optional keys:
- * "skusToReplace" - List<String> - an optional list of SKUs that the user is
- * upgrading or downgrading from.
- * Pass this field if the purchase is upgrading or downgrading
- * existing subscriptions.
- * The specified SKUs are replaced with the SKUs that the user is
- * purchasing. Google Play replaces the specified SKUs at the start of
- * the next billing cycle.
- * "replaceSkusProration" - Boolean - whether the user should be credited for any unused
- * subscription time on the SKUs they are upgrading or downgrading.
- * If you set this field to true, Google Play swaps out the old SKUs
- * and credits the user with the unused value of their subscription
- * time on a pro-rated basis.
- * Google Play applies this credit to the new subscription, and does
- * not begin billing the user for the new subscription until after
- * the credit is used up.
- * If you set this field to false, the user does not receive credit for
- * any unused subscription time and the recurrence date does not
- * change.
- * Default value is true. Ignored if you do not pass skusToReplace.
- * "accountId" - String - an optional obfuscated string that is uniquely
- * associated with the user's account in your app.
- * If you pass this value, Google Play can use it to detect irregular
- * activity, such as many devices making purchases on the same
- * account in a short period of time.
- * Do not use the developer ID or the user's Google ID for this field.
- * In addition, this field should not contain the user's ID in
- * cleartext.
- * We recommend that you use a one-way hash to generate a string from
- * the user's ID, and store the hashed string in this field.
- * "vr" - Boolean - an optional flag indicating whether the returned intent
- * should start a VR purchase flow. The apiVersion must also be 7 or
- * later to use this flag.
- */
- Bundle getBuyIntentExtraParams(int apiVersion, String packageName, String sku,
- String type, String developerPayload, in Bundle extraParams);
-
- /**
- * Returns the most recent purchase made by the user for each SKU, even if that purchase is
- * expired, canceled, or consumed.
- * @param apiVersion billing API version that the app is using, must be 6 or later
- * @param packageName package name of the calling app
- * @param type of the in-app items being requested ("inapp" for one-time purchases
- * and "subs" for subscriptions)
- * @param continuationToken to be set as null for the first call, if the number of owned
- * skus is too large, a continuationToken is returned in the response bundle.
- * This method can be called again with the continuation token to get the next set of
- * owned skus.
- * @param extraParams a Bundle with extra params that would be appended into http request
- * query string. Not used at this moment. Reserved for future functionality.
- * @return Bundle containing the following key-value pairs
- * "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
- * {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
- *
- * "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
- * "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
- * "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
- * of the purchase information
- * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
- * next set of in-app purchases. Only set if the
- * user has more owned skus than the current list.
- */
- Bundle getPurchaseHistory(int apiVersion, String packageName, String type,
- String continuationToken, in Bundle extraParams);
-
- /**
- * This method is a variant of {@link #isBillingSupported}} that takes an additional
- * {@code extraParams} parameter.
- * @param apiVersion billing API version that the app is using, must be 7 or later
- * @param packageName package name of the calling app
- * @param type of the in-app item being purchased ("inapp" for one-time purchases and "subs"
- * for subscriptions)
- * @param extraParams a Bundle with the following optional keys:
- * "vr" - Boolean - an optional flag to indicate whether {link #getBuyIntentExtraParams}
- * supports returning a VR purchase flow.
- * @return RESULT_OK(0) on success and appropriate response code on failures.
- */
- int isBillingSupportedExtraParams(int apiVersion, String packageName, String type,
- in Bundle extraParams);
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java
deleted file mode 100644
index de009f6d16..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ConsumeTask.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*************************************************************************/
-/* ConsumeTask.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-package org.godotengine.godot.plugin.payment;
-
-import android.content.Context;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-
-import com.android.vending.billing.IInAppBillingService;
-
-import java.lang.ref.WeakReference;
-
-abstract public class ConsumeTask {
- private Context context;
- private IInAppBillingService mService;
-
- private String mSku;
- private String mToken;
-
- private static class ConsumeAsyncTask extends AsyncTask<String, String, String> {
- private WeakReference<ConsumeTask> mTask;
-
- ConsumeAsyncTask(ConsumeTask consume) {
- mTask = new WeakReference<>(consume);
- }
-
- @Override
- protected String doInBackground(String... strings) {
- ConsumeTask consume = mTask.get();
- if (consume != null) {
- return consume.doInBackground(strings);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(String param) {
- ConsumeTask consume = mTask.get();
- if (consume != null) {
- consume.onPostExecute(param);
- }
- }
- }
-
- public ConsumeTask(IInAppBillingService mService, Context context) {
- this.context = context;
- this.mService = mService;
- }
-
- public void consume(final String sku) {
- mSku = sku;
- PaymentsCache pc = new PaymentsCache(context);
- Boolean isBlocked = pc.getConsumableFlag("block", sku);
- mToken = pc.getConsumableValue("token", sku);
- if (!isBlocked && mToken == null) {
- // Consuming task is processing
- } else if (!isBlocked) {
- return;
- } else if (mToken == null) {
- this.error("No token for sku:" + sku);
- return;
- }
- new ConsumeAsyncTask(this).execute();
- }
-
- private String doInBackground(String... params) {
- try {
- int response = mService.consumePurchase(3, context.getPackageName(), mToken);
- if (response == 0 || response == 8) {
- return null;
- }
- } catch (RemoteException e) {
- return e.getMessage();
- }
- return "Some error";
- }
-
- private void onPostExecute(String param) {
- if (param == null) {
- success(new PaymentsCache(context).getConsumableValue("ticket", mSku));
- } else {
- error(param);
- }
- }
-
- abstract protected void success(String ticket);
- abstract protected void error(String message);
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java
deleted file mode 100644
index ded7f0a9aa..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/GodotPayment.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*************************************************************************/
-/* GodotPayment.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-package org.godotengine.godot.plugin.payment;
-
-import org.godotengine.godot.Dictionary;
-import org.godotengine.godot.Godot;
-import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.plugin.GodotPlugin;
-
-import android.content.Intent;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-public class GodotPayment extends GodotPlugin {
- private Integer purchaseCallbackId = 0;
- private String accessToken;
- private String purchaseValidationUrlPrefix;
- private String transactionId;
- private final PaymentsManager mPaymentManager;
- private final Dictionary mSkuDetails = new Dictionary();
-
- public GodotPayment(Godot godot) {
- super(godot);
- mPaymentManager = new PaymentsManager(godot, this);
- mPaymentManager.initService();
- }
-
- @Override
- public void onMainActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) {
- mPaymentManager.processPurchaseResponse(resultCode, data);
- }
- }
-
- @Override
- public void onMainDestroy() {
- super.onMainDestroy();
- if (mPaymentManager != null) {
- mPaymentManager.destroy();
- }
- }
-
- public void purchase(final String sku, final String transactionId) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mPaymentManager.requestPurchase(sku, transactionId);
- }
- });
- }
-
- public void consumeUnconsumedPurchases() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mPaymentManager.consumeUnconsumedPurchases();
- }
- });
- }
-
- private String signature;
-
- public String getSignature() {
- return this.signature;
- }
-
- public void callbackSuccess(String ticket, String signature, String sku) {
- GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[] { ticket, signature, sku });
- }
-
- public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) {
- Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku);
- GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[] { ticket, signature, sku });
- }
-
- public void callbackSuccessNoUnconsumedPurchases() {
- GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {});
- }
-
- public void callbackFailConsume(String message) {
- GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] { message });
- }
-
- public void callbackFail(String message) {
- GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] { message });
- }
-
- public void callbackCancel() {
- GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[] {});
- }
-
- public void callbackAlreadyOwned(String sku) {
- GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku });
- }
-
- public int getPurchaseCallbackId() {
- return purchaseCallbackId;
- }
-
- public void setPurchaseCallbackId(int purchaseCallbackId) {
- this.purchaseCallbackId = purchaseCallbackId;
- }
-
- public String getPurchaseValidationUrlPrefix() {
- return this.purchaseValidationUrlPrefix;
- }
-
- public void setPurchaseValidationUrlPrefix(String url) {
- this.purchaseValidationUrlPrefix = url;
- }
-
- public String getAccessToken() {
- return accessToken;
- }
-
- public void setAccessToken(String accessToken) {
- this.accessToken = accessToken;
- }
-
- public void setTransactionId(String transactionId) {
- this.transactionId = transactionId;
- }
-
- public String getTransactionId() {
- return this.transactionId;
- }
-
- // request purchased items are not consumed
- public void requestPurchased() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mPaymentManager.requestPurchased();
- }
- });
- }
-
- // callback for requestPurchased()
- public void callbackPurchased(String receipt, String signature, String sku) {
- GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[] { receipt, signature, sku });
- }
-
- public void callbackDisconnected() {
- GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[] {});
- }
-
- public void callbackConnected() {
- GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[] {});
- }
-
- // true if connected, false otherwise
- public boolean isConnected() {
- return mPaymentManager.isConnected();
- }
-
- // consume item automatically after purchase. default is true.
- public void setAutoConsume(boolean autoConsume) {
- mPaymentManager.setAutoConsume(autoConsume);
- }
-
- // consume a specific item
- public void consume(String sku) {
- mPaymentManager.consume(sku);
- }
-
- // query in app item detail info
- public void querySkuDetails(String[] list) {
- List<String> nKeys = Arrays.asList(list);
- List<String> cKeys = Arrays.asList(mSkuDetails.get_keys());
- ArrayList<String> fKeys = new ArrayList<String>();
- for (String key : nKeys) {
- if (!cKeys.contains(key)) {
- fKeys.add(key);
- }
- }
- if (fKeys.size() > 0) {
- mPaymentManager.querySkuDetails(fKeys.toArray(new String[0]));
- } else {
- completeSkuDetail();
- }
- }
-
- public void addSkuDetail(String itemJson) {
- JSONObject o = null;
- try {
- o = new JSONObject(itemJson);
- Dictionary item = new Dictionary();
- item.put("type", o.optString("type"));
- item.put("product_id", o.optString("productId"));
- item.put("title", o.optString("title"));
- item.put("description", o.optString("description"));
- item.put("price", o.optString("price"));
- item.put("price_currency_code", o.optString("price_currency_code"));
- item.put("price_amount", 0.000001d * o.optLong("price_amount_micros"));
- mSkuDetails.put(item.get("product_id").toString(), item);
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
-
- public void completeSkuDetail() {
- GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[] { mSkuDetails });
- }
-
- public void errorSkuDetail(String errorMessage) {
- GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[] { errorMessage });
- }
-
- @NonNull
- @Override
- public String getPluginName() {
- return "GodotPayment";
- }
-
- @NonNull
- @Override
- public List<String> getPluginMethods() {
- return Arrays.asList("purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix",
- "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased",
- "setAutoConsume", "consume", "querySkuDetails", "isConnected");
- }
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java
deleted file mode 100644
index 00e216e8c0..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/HandlePurchaseTask.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*************************************************************************/
-/* HandlePurchaseTask.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-package org.godotengine.godot.plugin.payment;
-
-import android.app.Activity;
-import android.content.Intent;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-abstract public class HandlePurchaseTask {
- private Activity context;
-
- public HandlePurchaseTask(Activity context) {
- this.context = context;
- }
-
- public void handlePurchaseRequest(int resultCode, Intent data) {
- //Log.d("XXX", "Handling purchase response");
- if (resultCode == Activity.RESULT_OK) {
- try {
- //int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
- PaymentsCache pc = new PaymentsCache(context);
-
- String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
- //Log.d("XXX", "Purchase data:" + purchaseData);
- String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
- //Log.d("XXX", "Purchase signature:" + dataSignature);
- //Log.d("SARLANGA", purchaseData);
-
- JSONObject jo = new JSONObject(purchaseData);
- //String sku = jo.getString("productId");
- //alert("You have bought the " + sku + ". Excellent choice, aventurer!");
- //String orderId = jo.getString("orderId");
- //String packageName = jo.getString("packageName");
- String productId = jo.getString("productId");
- //Long purchaseTime = jo.getLong("purchaseTime");
- //Integer state = jo.getInt("purchaseState");
- String developerPayload = jo.getString("developerPayload");
- String purchaseToken = jo.getString("purchaseToken");
-
- if (!pc.getConsumableValue("validation_hash", productId).equals(developerPayload)) {
- error("Untrusted callback");
- return;
- }
- //Log.d("XXX", "Este es el product ID:" + productId);
- pc.setConsumableValue("ticket_signautre", productId, dataSignature);
- pc.setConsumableValue("ticket", productId, purchaseData);
- pc.setConsumableFlag("block", productId, true);
- pc.setConsumableValue("token", productId, purchaseToken);
-
- success(productId, dataSignature, purchaseData);
- return;
- } catch (JSONException e) {
- error(e.getMessage());
- }
- } else if (resultCode == Activity.RESULT_CANCELED) {
- canceled();
- }
- }
-
- abstract protected void success(String sku, String signature, String ticket);
- abstract protected void error(String message);
- abstract protected void canceled();
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java
deleted file mode 100644
index 435f43c49d..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsCache.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*************************************************************************/
-/* PaymentsCache.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-package org.godotengine.godot.plugin.payment;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-public class PaymentsCache {
- public Context context;
-
- public PaymentsCache(Context context) {
- this.context = context;
- }
-
- public void setConsumableFlag(String set, String sku, Boolean flag) {
- SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPref.edit();
- editor.putBoolean(sku, flag);
- editor.apply();
- }
-
- public boolean getConsumableFlag(String set, String sku) {
- SharedPreferences sharedPref = context.getSharedPreferences(
- "consumables_" + set, Context.MODE_PRIVATE);
- return sharedPref.getBoolean(sku, false);
- }
-
- public void setConsumableValue(String set, String sku, String value) {
- SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPref.edit();
- editor.putString(sku, value);
- //Log.d("XXX", "Setting asset: consumables_" + set + ":" + sku);
- editor.apply();
- }
-
- public String getConsumableValue(String set, String sku) {
- SharedPreferences sharedPref = context.getSharedPreferences(
- "consumables_" + set, Context.MODE_PRIVATE);
- //Log.d("XXX", "Getting asset: consumables_" + set + ":" + sku);
- return sharedPref.getString(sku, null);
- }
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java
deleted file mode 100644
index 9b3a338866..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PaymentsManager.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*************************************************************************/
-/* PaymentsManager.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-package org.godotengine.godot.plugin.payment;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.vending.billing.IInAppBillingService;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-public class PaymentsManager {
- public static final int BILLING_RESPONSE_RESULT_OK = 0;
- public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001;
- private static boolean auto_consume = true;
-
- private final Activity activity;
- private final GodotPayment godotPayment;
- IInAppBillingService mService;
-
- PaymentsManager(Activity activity, GodotPayment godotPayment) {
- this.activity = activity;
- this.godotPayment = godotPayment;
- }
-
- public PaymentsManager initService() {
- Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
- intent.setPackage("com.android.vending");
- activity.bindService(
- intent,
- mServiceConn,
- Context.BIND_AUTO_CREATE);
- return this;
- }
-
- public void destroy() {
- if (mService != null) {
- activity.unbindService(mServiceConn);
- }
- }
-
- ServiceConnection mServiceConn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mService = null;
-
- // At this stage, godotPayment might not have been initialized yet.
- if (godotPayment != null) {
- godotPayment.callbackDisconnected();
- }
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mService = IInAppBillingService.Stub.asInterface(service);
-
- // At this stage, godotPayment might not have been initialized yet.
- if (godotPayment != null) {
- godotPayment.callbackConnected();
- }
- }
- };
-
- public void requestPurchase(final String sku, String transactionId) {
- new PurchaseTask(mService, activity) {
- @Override
- protected void error(String message) {
- godotPayment.callbackFail(message);
- }
-
- @Override
- protected void canceled() {
- godotPayment.callbackCancel();
- }
-
- @Override
- protected void alreadyOwned() {
- godotPayment.callbackAlreadyOwned(sku);
- }
- }
- .purchase(sku, transactionId);
- }
-
- public boolean isConnected() {
- return mService != null;
- }
-
- public void consumeUnconsumedPurchases() {
- new ReleaseAllConsumablesTask(mService, activity) {
- @Override
- protected void success(String sku, String receipt, String signature, String token) {
- godotPayment.callbackSuccessProductMassConsumed(receipt, signature, sku);
- }
-
- @Override
- protected void error(String message) {
- Log.d("godot", "consumeUnconsumedPurchases :" + message);
- godotPayment.callbackFailConsume(message);
- }
-
- @Override
- protected void notRequired() {
- Log.d("godot", "callbackSuccessNoUnconsumedPurchases :");
- godotPayment.callbackSuccessNoUnconsumedPurchases();
- }
- }
- .consumeItAll();
- }
-
- public void requestPurchased() {
- try {
- PaymentsCache pc = new PaymentsCache(activity);
-
- String continueToken = null;
-
- do {
- Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp", continueToken);
-
- if (bundle.getInt("RESPONSE_CODE") == 0) {
- final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
- final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
-
- if (myPurchases == null || myPurchases.size() == 0) {
- godotPayment.callbackPurchased("", "", "");
- return;
- }
-
- for (int i = 0; i < myPurchases.size(); i++) {
- try {
- String receipt = myPurchases.get(i);
- JSONObject inappPurchaseData = new JSONObject(receipt);
- String sku = inappPurchaseData.getString("productId");
- String token = inappPurchaseData.getString("purchaseToken");
- String signature = mySignatures.get(i);
-
- pc.setConsumableValue("ticket_signautre", sku, signature);
- pc.setConsumableValue("ticket", sku, receipt);
- pc.setConsumableFlag("block", sku, true);
- pc.setConsumableValue("token", sku, token);
-
- godotPayment.callbackPurchased(receipt, signature, sku);
- } catch (JSONException e) {
- }
- }
- }
- continueToken = bundle.getString("INAPP_CONTINUATION_TOKEN");
- Log.d("godot", "continue token = " + continueToken);
- } while (!TextUtils.isEmpty(continueToken));
- } catch (Exception e) {
- Log.d("godot", "Error requesting purchased products:" + e.getClass().getName() + ":" + e.getMessage());
- }
- }
-
- public void processPurchaseResponse(int resultCode, Intent data) {
- new HandlePurchaseTask(activity) {
- @Override
- protected void success(final String sku, final String signature, final String ticket) {
- godotPayment.callbackSuccess(ticket, signature, sku);
-
- if (auto_consume) {
- new ConsumeTask(mService, activity) {
- @Override
- protected void success(String ticket) {
- }
-
- @Override
- protected void error(String message) {
- godotPayment.callbackFail(message);
- }
- }
- .consume(sku);
- }
- }
-
- @Override
- protected void error(String message) {
- godotPayment.callbackFail(message);
- }
-
- @Override
- protected void canceled() {
- godotPayment.callbackCancel();
- }
- }
- .handlePurchaseRequest(resultCode, data);
- }
-
- public void validatePurchase(String purchaseToken, final String sku) {
- new ValidateTask(activity, godotPayment) {
- @Override
- protected void success() {
- new ConsumeTask(mService, activity) {
- @Override
- protected void success(String ticket) {
- godotPayment.callbackSuccess(ticket, null, sku);
- }
-
- @Override
- protected void error(String message) {
- godotPayment.callbackFail(message);
- }
- }
- .consume(sku);
- }
-
- @Override
- protected void error(String message) {
- godotPayment.callbackFail(message);
- }
-
- @Override
- protected void canceled() {
- godotPayment.callbackCancel();
- }
- }
- .validatePurchase(sku);
- }
-
- public void setAutoConsume(boolean autoConsume) {
- auto_consume = autoConsume;
- }
-
- public void consume(final String sku) {
- new ConsumeTask(mService, activity) {
- @Override
- protected void success(String ticket) {
- godotPayment.callbackSuccessProductMassConsumed(ticket, "", sku);
- }
-
- @Override
- protected void error(String message) {
- godotPayment.callbackFailConsume(message);
- }
- }
- .consume(sku);
- }
-
- // Workaround to bug where sometimes response codes come as Long instead of Integer
- int getResponseCodeFromBundle(Bundle b) {
- Object o = b.get("RESPONSE_CODE");
- if (o == null) {
- //logDebug("Bundle with null response code, assuming OK (known issue)");
- return BILLING_RESPONSE_RESULT_OK;
- } else if (o instanceof Integer)
- return ((Integer)o).intValue();
- else if (o instanceof Long)
- return (int)((Long)o).longValue();
- else {
- //logError("Unexpected type for bundle response code.");
- //logError(o.getClass().getName());
- throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName());
- }
- }
-
- /**
- * Returns a human-readable description for the given response code.
- *
- * @param code The response code
- * @return A human-readable string explaining the result code.
- * It also includes the result code numerically.
- */
- public static String getResponseDesc(int code) {
- String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/"
- +
- "3:Billing Unavailable/4:Item unavailable/"
- +
- "5:Developer Error/6:Error/7:Item Already Owned/"
- +
- "8:Item not owned")
- .split("/");
- String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/"
- +
- "-1002:Bad response received/"
- +
- "-1003:Purchase signature verification failed/"
- +
- "-1004:Send intent failed/"
- +
- "-1005:User cancelled/"
- +
- "-1006:Unknown purchase response/"
- +
- "-1007:Missing token/"
- +
- "-1008:Unknown error/"
- +
- "-1009:Subscriptions not available/"
- +
- "-1010:Invalid consumption attempt")
- .split("/");
-
- if (code <= -1000) {
- int index = -1000 - code;
- if (index >= 0 && index < iabhelper_msgs.length)
- return iabhelper_msgs[index];
- else
- return String.valueOf(code) + ":Unknown IAB Helper Error";
- } else if (code < 0 || code >= iab_msgs.length)
- return String.valueOf(code) + ":Unknown";
- else
- return iab_msgs[code];
- }
-
- public void querySkuDetails(final String[] list) {
- (new Thread(new Runnable() {
- @Override
- public void run() {
- ArrayList<String> skuList = new ArrayList<String>(Arrays.asList(list));
- if (skuList.size() == 0) {
- return;
- }
- // Split the sku list in blocks of no more than 20 elements.
- ArrayList<ArrayList<String>> packs = new ArrayList<ArrayList<String>>();
- ArrayList<String> tempList;
- int n = skuList.size() / 20;
- int mod = skuList.size() % 20;
- for (int i = 0; i < n; i++) {
- tempList = new ArrayList<String>();
- for (String s : skuList.subList(i * 20, i * 20 + 20)) {
- tempList.add(s);
- }
- packs.add(tempList);
- }
- if (mod != 0) {
- tempList = new ArrayList<String>();
- for (String s : skuList.subList(n * 20, n * 20 + mod)) {
- tempList.add(s);
- }
- packs.add(tempList);
- }
- for (ArrayList<String> skuPartList : packs) {
- Bundle querySkus = new Bundle();
- querySkus.putStringArrayList("ITEM_ID_LIST", skuPartList);
- Bundle skuDetails = null;
- try {
- skuDetails = mService.getSkuDetails(3, activity.getPackageName(), "inapp", querySkus);
- if (!skuDetails.containsKey("DETAILS_LIST")) {
- int response = getResponseCodeFromBundle(skuDetails);
- if (response != BILLING_RESPONSE_RESULT_OK) {
- godotPayment.errorSkuDetail(getResponseDesc(response));
- } else {
- godotPayment.errorSkuDetail("No error but no detail list.");
- }
- return;
- }
-
- ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
-
- for (String thisResponse : responseList) {
- Log.d("godot", "response = " + thisResponse);
- godotPayment.addSkuDetail(thisResponse);
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- godotPayment.errorSkuDetail("RemoteException error!");
- }
- }
- godotPayment.completeSkuDetail();
- }
- }))
- .start();
- }
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java
deleted file mode 100644
index f894bd5132..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/PurchaseTask.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*************************************************************************/
-/* PurchaseTask.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-package org.godotengine.godot.plugin.payment;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.content.IntentSender.SendIntentException;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.vending.billing.IInAppBillingService;
-
-abstract public class PurchaseTask {
- private Activity context;
-
- private IInAppBillingService mService;
- public PurchaseTask(IInAppBillingService mService, Activity context) {
- this.context = context;
- this.mService = mService;
- }
-
- private boolean isLooping = false;
-
- public void purchase(final String sku, final String transactionId) {
- Log.d("XXX", "Starting purchase for: " + sku);
- PaymentsCache pc = new PaymentsCache(context);
- Boolean isBlocked = pc.getConsumableFlag("block", sku);
- /*
- if(isBlocked) {
- Log.d("XXX", "Is awaiting payment confirmation");
- error("Awaiting payment confirmation");
- return;
- }
- */
- final String hash = transactionId;
-
- Bundle buyIntentBundle;
- try {
- buyIntentBundle = mService.getBuyIntent(3, context.getApplicationContext().getPackageName(), sku, "inapp", hash);
- } catch (RemoteException e) {
- //Log.d("XXX", "Error: " + e.getMessage());
- error(e.getMessage());
- return;
- }
- Object rc = buyIntentBundle.get("RESPONSE_CODE");
- int responseCode = 0;
- if (rc == null) {
- responseCode = PaymentsManager.BILLING_RESPONSE_RESULT_OK;
- } else if (rc instanceof Integer) {
- responseCode = ((Integer)rc).intValue();
- } else if (rc instanceof Long) {
- responseCode = (int)((Long)rc).longValue();
- }
- //Log.d("XXX", "Buy intent response code: " + responseCode);
- if (responseCode == 1 || responseCode == 3 || responseCode == 4) {
- canceled();
- return;
- }
- if (responseCode == 7) {
- alreadyOwned();
- return;
- }
-
- PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
- pc.setConsumableValue("validation_hash", sku, hash);
- try {
- if (context == null) {
- //Log.d("XXX", "No context!");
- }
- if (pendingIntent == null) {
- //Log.d("XXX", "No pending intent");
- }
- //Log.d("XXX", "Starting activity for purchase!");
- context.startIntentSenderForResult(
- pendingIntent.getIntentSender(),
- PaymentsManager.REQUEST_CODE_FOR_PURCHASE,
- new Intent(),
- Integer.valueOf(0), Integer.valueOf(0),
- Integer.valueOf(0));
- } catch (SendIntentException e) {
- error(e.getMessage());
- }
- }
-
- abstract protected void error(String message);
- abstract protected void canceled();
- abstract protected void alreadyOwned();
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java
deleted file mode 100644
index 1d52cf0fa5..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ReleaseAllConsumablesTask.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*************************************************************************/
-/* ReleaseAllConsumablesTask.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-package org.godotengine.godot.plugin.payment;
-
-import android.content.Context;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.vending.billing.IInAppBillingService;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-abstract public class ReleaseAllConsumablesTask {
- private Context context;
- private IInAppBillingService mService;
-
- private static class ReleaseAllConsumablesAsyncTask extends AsyncTask<String, String, String> {
- private WeakReference<ReleaseAllConsumablesTask> mTask;
- private String mSku;
- private String mReceipt;
- private String mSignature;
- private String mToken;
-
- ReleaseAllConsumablesAsyncTask(ReleaseAllConsumablesTask task, String sku, String receipt, String signature, String token) {
- mTask = new WeakReference<ReleaseAllConsumablesTask>(task);
-
- mSku = sku;
- mReceipt = receipt;
- mSignature = signature;
- mToken = token;
- }
-
- @Override
- protected String doInBackground(String... params) {
- ReleaseAllConsumablesTask consume = mTask.get();
- if (consume != null) {
- return consume.doInBackground(mToken);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(String param) {
- ReleaseAllConsumablesTask consume = mTask.get();
- if (consume != null) {
- consume.success(mSku, mReceipt, mSignature, mToken);
- }
- }
- }
-
- public ReleaseAllConsumablesTask(IInAppBillingService mService, Context context) {
- this.context = context;
- this.mService = mService;
- }
-
- public void consumeItAll() {
- try {
- //Log.d("godot", "consumeItall for " + context.getPackageName());
- Bundle bundle = mService.getPurchases(3, context.getPackageName(), "inapp", null);
-
- if (bundle.getInt("RESPONSE_CODE") == 0) {
- final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
- final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
-
- if (myPurchases == null || myPurchases.size() == 0) {
- //Log.d("godot", "No purchases!");
- notRequired();
- return;
- }
-
- //Log.d("godot", "# products to be consumed:" + myPurchases.size());
- for (int i = 0; i < myPurchases.size(); i++) {
- try {
- String receipt = myPurchases.get(i);
- JSONObject inappPurchaseData = new JSONObject(receipt);
- String sku = inappPurchaseData.getString("productId");
- String token = inappPurchaseData.getString("purchaseToken");
- String signature = mySignatures.get(i);
- //Log.d("godot", "A punto de consumir un item con token:" + token + "\n" + receipt);
- new ReleaseAllConsumablesAsyncTask(this, sku, receipt, signature, token).execute();
- } catch (JSONException e) {
- }
- }
- }
- } catch (Exception e) {
- Log.d("godot", "Error releasing products:" + e.getClass().getName() + ":" + e.getMessage());
- }
- }
-
- private String doInBackground(String token) {
- try {
- //Log.d("godot", "Requesting to consume an item with token ." + token);
- int response = mService.consumePurchase(3, context.getPackageName(), token);
- //Log.d("godot", "consumePurchase response: " + response);
- if (response == 0 || response == 8) {
- return null;
- }
- } catch (Exception e) {
- Log.d("godot", "Error " + e.getClass().getName() + ":" + e.getMessage());
- }
- return null;
- }
-
- abstract protected void success(String sku, String receipt, String signature, String token);
- abstract protected void error(String message);
- abstract protected void notRequired();
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java
deleted file mode 100644
index a7156152ce..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/ValidateTask.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*************************************************************************/
-/* ValidateTask.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-package org.godotengine.godot.plugin.payment;
-
-import org.godotengine.godot.plugin.payment.utils.HttpRequester;
-import org.godotengine.godot.plugin.payment.utils.RequestParams;
-
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.os.AsyncTask;
-
-import java.lang.ref.WeakReference;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-abstract public class ValidateTask {
- private Activity context;
- private GodotPayment godotPayments;
- private ProgressDialog dialog;
- private String mSku;
-
- private static class ValidateAsyncTask extends AsyncTask<String, String, String> {
- private WeakReference<ValidateTask> mTask;
-
- ValidateAsyncTask(ValidateTask task) {
- mTask = new WeakReference<>(task);
- }
-
- @Override
- protected void onPreExecute() {
- ValidateTask task = mTask.get();
- if (task != null) {
- task.onPreExecute();
- }
- }
-
- @Override
- protected String doInBackground(String... params) {
- ValidateTask task = mTask.get();
- if (task != null) {
- return task.doInBackground(params);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(String response) {
- ValidateTask task = mTask.get();
- if (task != null) {
- task.onPostExecute(response);
- }
- }
- }
-
- public ValidateTask(Activity context, GodotPayment godotPayments) {
- this.context = context;
- this.godotPayments = godotPayments;
- }
-
- public void validatePurchase(final String sku) {
- mSku = sku;
- new ValidateAsyncTask(this).execute();
- }
-
- private void onPreExecute() {
- dialog = ProgressDialog.show(context, null, "Please wait...");
- }
-
- private String doInBackground(String... params) {
- PaymentsCache pc = new PaymentsCache(context);
- String url = godotPayments.getPurchaseValidationUrlPrefix();
- RequestParams param = new RequestParams();
- param.setUrl(url);
- param.put("ticket", pc.getConsumableValue("ticket", mSku));
- param.put("purchaseToken", pc.getConsumableValue("token", mSku));
- param.put("sku", mSku);
- //Log.d("XXX", "Haciendo request a " + url);
- //Log.d("XXX", "ticket: " + pc.getConsumableValue("ticket", sku));
- //Log.d("XXX", "purchaseToken: " + pc.getConsumableValue("token", sku));
- //Log.d("XXX", "sku: " + sku);
- param.put("package", context.getApplicationContext().getPackageName());
- HttpRequester requester = new HttpRequester();
- String jsonResponse = requester.post(param);
- //Log.d("XXX", "Validation response:\n"+jsonResponse);
- return jsonResponse;
- }
-
- private void onPostExecute(String response) {
- if (dialog != null) {
- dialog.dismiss();
- dialog = null;
- }
- JSONObject j;
- try {
- j = new JSONObject(response);
- if (j.getString("status").equals("OK")) {
- success();
- return;
- } else if (j.getString("status") != null) {
- error(j.getString("message"));
- } else {
- error("Connection error");
- }
- } catch (JSONException e) {
- error(e.getMessage());
- } catch (Exception e) {
- error(e.getMessage());
- }
- }
-
- abstract protected void success();
- abstract protected void error(String message);
- abstract protected void canceled();
-}
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java b/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java
deleted file mode 100644
index 0afcf60f38..0000000000
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/HttpRequester.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*************************************************************************/
-/* HttpRequester.java */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-package org.godotengine.godot.plugin.payment.utils;
-
-import org.godotengine.godot.utils.Crypt;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
-import java.security.KeyStore;
-import java.util.Date;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpVersion;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.conn.ssl.SSLSocketFactory;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.params.HttpProtocolParams;
-import org.apache.http.protocol.HTTP;
-import org.apache.http.util.EntityUtils;
-
-/**
- *
- * @author Luis Linietsky <luis.linietsky@gmail.com>
- */
-public class HttpRequester {
- private Context context;
- private static final int TTL = 600000; // 10 minutos
- private long cttl = 0;
-
- public HttpRequester() {
- //Log.d("XXX", "Creando http request sin contexto");
- }
-
- public HttpRequester(Context context) {
- this.context = context;
- //Log.d("XXX", "Creando http request con contexto");
- }
-
- public String post(RequestParams params) {
- HttpPost httppost = new HttpPost(params.getUrl());
- try {
- httppost.setEntity(new UrlEncodedFormEntity(params.toPairsList()));
- return request(httppost);
- } catch (UnsupportedEncodingException e) {
- return null;
- }
- }
-
- public String get(RequestParams params) {
- String response = getResponseFromCache(params.getUrl());
- if (response == null) {
- //Log.d("XXX", "Cache miss!");
- HttpGet httpget = new HttpGet(params.getUrl());
- long timeInit = new Date().getTime();
- response = request(httpget);
- long delay = new Date().getTime() - timeInit;
- Log.d("HttpRequest::get(url)", "Url: " + params.getUrl() + " downloaded in " + String.format("%.03f", delay / 1000.0f) + " seconds");
- if (response == null || response.length() == 0) {
- response = "";
- } else {
- saveResponseIntoCache(params.getUrl(), response);
- }
- }
- Log.d("XXX", "Req: " + params.getUrl());
- Log.d("XXX", "Resp: " + response);
- return response;
- }
-
- private String request(HttpUriRequest request) {
- //Log.d("XXX", "Haciendo request a: " + request.getURI() );
- Log.d("PPP", "Haciendo request a: " + request.getURI());
- long init = new Date().getTime();
- HttpClient httpclient = getNewHttpClient();
- HttpParams httpParameters = httpclient.getParams();
- HttpConnectionParams.setConnectionTimeout(httpParameters, 0);
- HttpConnectionParams.setSoTimeout(httpParameters, 0);
- HttpConnectionParams.setTcpNoDelay(httpParameters, true);
- try {
- HttpResponse response = httpclient.execute(request);
- Log.d("PPP", "Fin de request (" + (new Date().getTime() - init) + ") a: " + request.getURI());
- //Log.d("XXX1", "Status:" + response.getStatusLine().toString());
- if (response.getStatusLine().getStatusCode() == 200) {
- String strResponse = EntityUtils.toString(response.getEntity());
- //Log.d("XXX2", strResponse);
- return strResponse;
- } else {
- Log.d("XXX3", "Response status code:" + response.getStatusLine().getStatusCode() + "\n" + EntityUtils.toString(response.getEntity()));
- return null;
- }
-
- } catch (ClientProtocolException e) {
- Log.d("XXX3", e.getMessage());
- } catch (IOException e) {
- Log.d("XXX4", e.getMessage());
- }
- return null;
- }
-
- private HttpClient getNewHttpClient() {
- try {
- KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
- trustStore.load(null, null);
-
- SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
- sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
-
- HttpParams params = new BasicHttpParams();
- HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
- HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
-
- SchemeRegistry registry = new SchemeRegistry();
- registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- registry.register(new Scheme("https", sf, 443));
-
- ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
-
- return new DefaultHttpClient(ccm, params);
- } catch (Exception e) {
- return new DefaultHttpClient();
- }
- }
-
- private static String convertStreamToString(InputStream is) {
- BufferedReader reader = new BufferedReader(new InputStreamReader(is));
- StringBuilder sb = new StringBuilder();
- String line = null;
- try {
- while ((line = reader.readLine()) != null) {
- sb.append((line + "\n"));
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return sb.toString();
- }
-
- public void saveResponseIntoCache(String request, String response) {
- if (context == null) {
- //Log.d("XXX", "No context, cache failed!");
- return;
- }
- SharedPreferences sharedPref = context.getSharedPreferences("http_get_cache", Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPref.edit();
- editor.putString("request_" + Crypt.md5(request), response);
- editor.putLong("request_" + Crypt.md5(request) + "_ttl", new Date().getTime() + getTtl());
- editor.apply();
- }
-
- public String getResponseFromCache(String request) {
- if (context == null) {
- Log.d("XXX", "No context, cache miss");
- return null;
- }
- SharedPreferences sharedPref = context.getSharedPreferences("http_get_cache", Context.MODE_PRIVATE);
- long ttl = getResponseTtl(request);
- if (ttl == 0l || (new Date().getTime() - ttl) > 0l) {
- Log.d("XXX", "Cache invalid ttl:" + ttl + " vs now:" + new Date().getTime());
- return null;
- }
- return sharedPref.getString("request_" + Crypt.md5(request), null);
- }
-
- public long getResponseTtl(String request) {
- SharedPreferences sharedPref = context.getSharedPreferences(
- "http_get_cache", Context.MODE_PRIVATE);
- return sharedPref.getLong("request_" + Crypt.md5(request) + "_ttl", 0l);
- }
-
- public long getTtl() {
- return cttl > 0 ? cttl : TTL;
- }
-
- public void setTtl(long ttl) {
- this.cttl = (ttl * 1000) + new Date().getTime();
- }
-}
diff --git a/platform/android/java/settings.gradle b/platform/android/java/settings.gradle
index 9536d3de6d..524031d93f 100644
--- a/platform/android/java/settings.gradle
+++ b/platform/android/java/settings.gradle
@@ -3,4 +3,4 @@ rootProject.name = "Godot"
include ':app'
include ':lib'
-include ':plugins:godotpayment'
+include ':nativeSrcsConfigs'
diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp
index 39de3cb642..ab03599dc3 100644
--- a/platform/android/java_class_wrapper.cpp
+++ b/platform/android/java_class_wrapper.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,7 +37,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
if (!M)
return false;
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
MethodInfo *method = nullptr;
for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) {
@@ -964,7 +964,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
if (class_cache.has(p_class))
return class_cache[p_class];
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jclass bclass = env->FindClass(p_class.utf8().get_data());
ERR_FAIL_COND_V(!bclass, Ref<JavaClass>());
@@ -1148,9 +1148,9 @@ JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
singleton = this;
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
- jclass activityClass = env->FindClass("org/godotengine/godot/Godot");
+ jclass activityClass = env->FindClass("android/app/Activity");
jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
classLoader = env->CallObjectMethod(p_activity, getClassLoader);
classLoader = (jclass)env->NewGlobalRef(classLoader);
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index 0a42adeaf2..41201db32b 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,12 +29,12 @@
/*************************************************************************/
#include "java_godot_io_wrapper.h"
-#include "core/error_list.h"
+#include "core/error/error_list.h"
// JNIEnv is only valid within the thread it belongs to, in a multi threading environment
// we can't cache it.
// For GodotIO we call all access methods from our thread and we thus get a valid JNIEnv
-// from ThreadAndroid.
+// from get_jni_env().
GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instance) {
godot_io_instance = p_env->NewGlobalRef(p_godot_io_instance);
@@ -52,8 +52,9 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
_get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;");
_get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;");
_get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I");
+ _screen_get_usable_rect = p_env->GetMethodID(cls, "screenGetUsableRect", "()[I"),
_get_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;");
- _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;III)V");
+ _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;ZIII)V");
_hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V");
_set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V");
_get_screen_orientation = p_env->GetMethodID(cls, "getScreenOrientation", "()I");
@@ -71,7 +72,7 @@ jobject GodotIOJavaWrapper::get_instance() {
Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
if (_open_URI) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring jStr = env->NewStringUTF(p_uri.utf8().get_data());
return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK;
} else {
@@ -81,7 +82,7 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
String GodotIOJavaWrapper::get_user_data_dir() {
if (_get_data_dir) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
return jstring_to_string(s, env);
} else {
@@ -91,7 +92,7 @@ String GodotIOJavaWrapper::get_user_data_dir() {
String GodotIOJavaWrapper::get_locale() {
if (_get_locale) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale);
return jstring_to_string(s, env);
} else {
@@ -101,7 +102,7 @@ String GodotIOJavaWrapper::get_locale() {
String GodotIOJavaWrapper::get_model() {
if (_get_model) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model);
return jstring_to_string(s, env);
} else {
@@ -111,16 +112,29 @@ String GodotIOJavaWrapper::get_model() {
int GodotIOJavaWrapper::get_screen_dpi() {
if (_get_screen_DPI) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
return env->CallIntMethod(godot_io_instance, _get_screen_DPI);
} else {
return 160;
}
}
+void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) {
+ if (_screen_get_usable_rect) {
+ JNIEnv *env = get_jni_env();
+ jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _screen_get_usable_rect);
+ ERR_FAIL_COND(env->GetArrayLength(returnArray) != 4);
+ jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
+ for (int i = 0; i < 4; i++) {
+ p_rect_xywh[i] = arrayBody[i];
+ }
+ env->ReleaseIntArrayElements(returnArray, arrayBody, 0);
+ }
+}
+
String GodotIOJavaWrapper::get_unique_id() {
if (_get_unique_id) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id);
return jstring_to_string(s, env);
} else {
@@ -132,31 +146,31 @@ bool GodotIOJavaWrapper::has_vk() {
return (_show_keyboard != 0) && (_hide_keyboard != 0);
}
-void GodotIOJavaWrapper::show_vk(const String &p_existing, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
+void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
if (_show_keyboard) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring jStr = env->NewStringUTF(p_existing.utf8().get_data());
- env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_max_input_length, p_cursor_start, p_cursor_end);
+ env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end);
}
}
void GodotIOJavaWrapper::hide_vk() {
if (_hide_keyboard) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(godot_io_instance, _hide_keyboard);
}
}
void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
if (_set_screen_orientation) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient);
}
}
int GodotIOJavaWrapper::get_screen_orientation() {
if (_get_screen_orientation) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
return env->CallIntMethod(godot_io_instance, _get_screen_orientation);
} else {
return 0;
@@ -165,7 +179,7 @@ int GodotIOJavaWrapper::get_screen_orientation() {
String GodotIOJavaWrapper::get_system_dir(int p_dir) {
if (_get_system_dir) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir);
return jstring_to_string(s, env);
} else {
@@ -173,14 +187,14 @@ String GodotIOJavaWrapper::get_system_dir(int p_dir) {
}
}
-// volatile because it can be changed from non-main thread and we need to
+// SafeNumeric because it can be changed from non-main thread and we need to
// ensure the change is immediately visible to other threads.
-static volatile int virtual_keyboard_height;
+static SafeNumeric<int> virtual_keyboard_height;
int GodotIOJavaWrapper::get_vk_height() {
- return virtual_keyboard_height;
+ return virtual_keyboard_height.get();
}
void GodotIOJavaWrapper::set_vk_height(int p_height) {
- virtual_keyboard_height = p_height;
+ virtual_keyboard_height.set(p_height);
}
diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h
index 1742021379..394e97effa 100644
--- a/platform/android/java_godot_io_wrapper.h
+++ b/platform/android/java_godot_io_wrapper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -50,6 +50,7 @@ private:
jmethodID _get_locale = 0;
jmethodID _get_model = 0;
jmethodID _get_screen_DPI = 0;
+ jmethodID _screen_get_usable_rect = 0;
jmethodID _get_unique_id = 0;
jmethodID _show_keyboard = 0;
jmethodID _hide_keyboard = 0;
@@ -68,9 +69,10 @@ public:
String get_locale();
String get_model();
int get_screen_dpi();
+ void screen_get_usable_rect(int (&p_rect_xywh)[4]);
String get_unique_id();
bool has_vk();
- void show_vk(const String &p_existing, int p_max_input_length, int p_cursor_start, int p_cursor_end);
+ void show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end);
void hide_vk();
int get_vk_height();
void set_vk_height(int p_height);
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 23e0d24b57..bb22162879 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,13 +37,12 @@
#include "api/java_class_wrapper.h"
#include "api/jni_singleton.h"
#include "audio_driver_jandroid.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "core/input/input.h"
-#include "core/project_settings.h"
#include "dir_access_jandroid.h"
#include "display_server_android.h"
#include "file_access_android.h"
-#include "file_access_jandroid.h"
#include "jni_utils.h"
#include "main/main.h"
#include "net_socket_android.h"
@@ -51,6 +50,7 @@
#include "string_android.h"
#include "thread_jandroid.h"
+#include <android/input.h>
#include <unistd.h>
#include <android/native_window_jni.h>
@@ -77,25 +77,21 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject godot_instance, jobject p_asset_manager, jboolean p_use_apk_expansion) {
initialized = true;
JavaVM *jvm;
env->GetJavaVM(&jvm);
// create our wrapper classes
- godot_java = new GodotJavaWrapper(env, activity); // our activity is our godot instance is our activity..
+ godot_java = new GodotJavaWrapper(env, activity, godot_instance);
godot_io_java = new GodotIOJavaWrapper(env, godot_java->get_member_object("io", "Lorg/godotengine/godot/GodotIO;", env));
- ThreadAndroid::make_default(jvm);
-#ifdef USE_JAVA_FILE_ACCESS
- FileAccessJAndroid::setup(godot_io_java->get_instance());
-#else
+ init_thread_jandroid(jvm, env);
jobject amgr = env->NewGlobalRef(p_asset_manager);
FileAccessAndroid::asset_manager = AAssetManager_fromJava(env, amgr);
-#endif
DirAccessJAndroid::setup(godot_io_java->get_instance());
AudioDriverAndroid::setup(godot_io_java->get_instance());
@@ -109,7 +105,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
godot_java->on_video_init(env);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz) {
// lets cleanup
if (godot_io_java) {
delete godot_io_java;
@@ -123,7 +119,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
- ThreadAndroid::setup_thread();
+ setup_android_thread();
const char **cmdline = nullptr;
jstring *j_cmdline = nullptr;
@@ -164,9 +160,20 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
ClassDB::register_class<JNISingleton>();
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height) {
- if (os_android)
- os_android->set_display_size(Size2i(width, height));
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height) {
+ if (os_android) {
+ os_android->set_display_size(Size2i(p_width, p_height));
+
+ // No need to reset the surface during startup
+ if (step > 0) {
+ if (p_surface) {
+ ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface);
+ os_android->set_native_window(native_window);
+
+ DisplayServerAndroid::get_singleton()->reset_window();
+ }
+ }
+ }
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits) {
@@ -199,7 +206,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
return;
if (step == 0) {
- // Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id,
+ // Since Godot is initialized on the UI thread, main_thread_id was set to that thread's id,
// but for Godot purposes, the main thread is the one running the game loop
Main::setup2(Thread::get_caller_id());
++step;
@@ -226,40 +233,50 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions) {
+void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
if (step == 0)
return;
Vector<DisplayServerAndroid::TouchPos> points;
- for (int i = 0; i < count; i++) {
- jint p[3];
- env->GetIntArrayRegion(positions, i * 3, 3, p);
+ for (int i = 0; i < pointer_count; i++) {
+ jfloat p[3];
+ env->GetFloatArrayRegion(positions, i * 3, 3, p);
DisplayServerAndroid::TouchPos tp;
tp.pos = Point2(p[1], p[2]);
- tp.id = p[0];
+ tp.id = (int)p[0];
points.push_back(tp);
}
+ if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE || (input_device & AINPUT_SOURCE_MOUSE_RELATIVE) == AINPUT_SOURCE_MOUSE_RELATIVE) {
+ DisplayServerAndroid::get_singleton()->process_mouse_event(input_device, ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor);
+ } else {
+ DisplayServerAndroid::get_singleton()->process_touch(ev, pointer, points);
+ }
+}
- DisplayServerAndroid::get_singleton()->process_touch(ev, pointer, points);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position) {
+ touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position);
+}
+
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask) {
+ touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask);
+}
- /*
- if (os_android)
- os_android->process_touch(ev,pointer,points);
- */
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
+ touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask, vertical_factor, horizontal_factor);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jint p_x, jint p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y) {
if (step == 0)
return;
DisplayServerAndroid::get_singleton()->process_hover(p_type, Point2(p_x, p_y));
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y) {
if (step == 0)
return;
- DisplayServerAndroid::get_singleton()->process_double_tap(Point2(p_x, p_y));
+ DisplayServerAndroid::get_singleton()->process_double_tap(p_button_mask, Point2(p_x, p_y));
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y) {
@@ -365,7 +382,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env,
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz) {
- ThreadAndroid::setup_thread();
+ setup_android_thread();
AudioDriverAndroid::thread_func(env);
}
@@ -375,8 +392,8 @@ JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *
return env->NewStringUTF(ProjectSettings::get_singleton()->get(js).operator String().utf8().get_data());
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) {
- Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID));
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) {
+ Object *obj = ObjectDB::get_instance(ObjectID(ID));
ERR_FAIL_COND(!obj);
int res = env->PushLocalFrame(16);
@@ -405,8 +422,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en
env->PopLocalFrame(nullptr);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params) {
- Object *obj = ObjectDB::get_instance(ObjectID((uint64_t)ID));
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params) {
+ Object *obj = ObjectDB::get_instance(ObjectID(ID));
ERR_FAIL_COND(!obj);
int res = env->PushLocalFrame(16);
@@ -424,6 +441,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
env->DeleteLocalRef(obj);
};
+ static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5");
obj->call_deferred(str_method, args[0], args[1], args[2], args[3], args[4]);
// something
env->PopLocalFrame(nullptr);
@@ -445,7 +463,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNI
return;
if (os_android->get_main_loop()) {
- os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APP_RESUMED);
+ os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_RESUMED);
}
}
@@ -454,7 +472,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIE
return;
if (os_android->get_main_loop()) {
- os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APP_PAUSED);
+ os_android->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
}
}
}
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 221d701e2b..a3e2933185 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,16 +37,19 @@
// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz, jobject activity);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject activity, jobject godot_instance, jobject p_asset_manager, jboolean p_use_apk_expansion);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jint width, jint height);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint count, jintArray positions);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jint p_x, jint p_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubletap(JNIEnv *env, jclass clazz, jint p_x, jint p_y);
+void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_scroll(JNIEnv *env, jclass clazz, jint p_x, jint p_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
@@ -61,8 +64,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jclass clazz);
JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jclass clazz, jstring path);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jint ID, jstring method, jobjectArray params);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
diff --git a/platform/javascript/native/id_handler.js b/platform/android/java_godot_view_wrapper.cpp
index 67d29075b8..5b638300ef 100644
--- a/platform/javascript/native/id_handler.js
+++ b/platform/android/java_godot_view_wrapper.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* id_handler.js */
+/* java_godot_view_wrapper.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,36 +28,39 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-var IDHandler = /** @constructor */ function() {
+#include "java_godot_view_wrapper.h"
- var ids = {};
- var size = 0;
+#include "thread_jandroid.h"
- this.has = function(id) {
- return ids.hasOwnProperty(id);
- }
+GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
+ JNIEnv *env = get_jni_env();
- this.add = function(obj) {
- size += 1;
- var id = crypto.getRandomValues(new Int32Array(32))[0];
- ids[id] = obj;
- return id;
- }
+ _godot_view = env->NewGlobalRef(godot_view);
- this.get = function(id) {
- return ids[id];
- }
+ _cls = (jclass)env->NewGlobalRef(env->GetObjectClass(godot_view));
- this.remove = function(id) {
- size -= 1;
- delete ids[id];
+ if (android_get_device_api_level() >= __ANDROID_API_O__) {
+ _request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V");
+ _release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V");
}
+}
- this.size = function() {
- return size;
+void GodotJavaViewWrapper::request_pointer_capture() {
+ if (_request_pointer_capture != 0) {
+ JNIEnv *env = get_jni_env();
+ env->CallVoidMethod(_godot_view, _request_pointer_capture);
}
+}
- this.ids = ids;
-};
+void GodotJavaViewWrapper::release_pointer_capture() {
+ if (_request_pointer_capture != 0) {
+ JNIEnv *env = get_jni_env();
+ env->CallVoidMethod(_godot_view, _release_pointer_capture);
+ }
+}
-Module.IDHandler = new IDHandler;
+GodotJavaViewWrapper::~GodotJavaViewWrapper() {
+ JNIEnv *env = get_jni_env();
+ env->DeleteGlobalRef(_godot_view);
+ env->DeleteGlobalRef(_cls);
+}
diff --git a/platform/haiku/context_gl_haiku.h b/platform/android/java_godot_view_wrapper.h
index c5d258915d..548c278292 100644
--- a/platform/haiku/context_gl_haiku.h
+++ b/platform/android/java_godot_view_wrapper.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* context_gl_haiku.h */
+/* java_godot_view_wrapper.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,35 +28,29 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CONTEXT_GL_HAIKU_H
-#define CONTEXT_GL_HAIKU_H
+#ifndef GODOT_JAVA_GODOT_VIEW_WRAPPER_H
+#define GODOT_JAVA_GODOT_VIEW_WRAPPER_H
-#if defined(OPENGL_ENABLED)
+#include <android/log.h>
+#include <jni.h>
-#include "haiku_direct_window.h"
-#include "haiku_gl_view.h"
-
-class ContextGL_Haiku {
+// Class that makes functions in java/src/org/godotengine/godot/GodotView.java callable from C++
+class GodotJavaViewWrapper {
private:
- HaikuGLView *view;
- HaikuDirectWindow *window;
+ jclass _cls;
+
+ jobject _godot_view;
- bool use_vsync;
+ jmethodID _request_pointer_capture = 0;
+ jmethodID _release_pointer_capture = 0;
public:
- Error initialize();
- void release_current();
- void make_current();
- void swap_buffers();
- int get_window_width();
- int get_window_height();
-
- void set_use_vsync(bool p_use);
- bool is_using_vsync() const;
-
- ContextGL_Haiku(HaikuDirectWindow *p_window);
- ~ContextGL_Haiku();
+ GodotJavaViewWrapper(jobject godot_view);
+
+ void request_pointer_capture();
+ void release_pointer_capture();
+
+ ~GodotJavaViewWrapper();
};
-#endif
-#endif
+#endif //GODOT_JAVA_GODOT_VIEW_WRAPPER_H
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 8ef99dfab0..759980373a 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,40 +33,51 @@
// JNIEnv is only valid within the thread it belongs to, in a multi threading environment
// we can't cache it.
// For Godot we call most access methods from our thread and we thus get a valid JNIEnv
-// from ThreadAndroid. For one or two we expect to pass the environment
+// from get_jni_env(). For one or two we expect to pass the environment
// TODO we could probably create a base class for this...
-GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) {
+GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance) {
godot_instance = p_env->NewGlobalRef(p_godot_instance);
+ activity = p_env->NewGlobalRef(p_activity);
// get info about our Godot class so we can get pointers and stuff...
- cls = p_env->FindClass("org/godotengine/godot/Godot");
- if (cls) {
- cls = (jclass)p_env->NewGlobalRef(cls);
+ godot_class = p_env->FindClass("org/godotengine/godot/Godot");
+ if (godot_class) {
+ godot_class = (jclass)p_env->NewGlobalRef(godot_class);
+ } else {
+ // this is a pretty serious fail.. bail... pointers will stay 0
+ return;
+ }
+ activity_class = p_env->FindClass("android/app/Activity");
+ if (activity_class) {
+ activity_class = (jclass)p_env->NewGlobalRef(activity_class);
} else {
// this is a pretty serious fail.. bail... pointers will stay 0
return;
}
- // get some method pointers...
- _on_video_init = p_env->GetMethodID(cls, "onVideoInit", "()V");
- _restart = p_env->GetMethodID(cls, "restart", "()V");
- _finish = p_env->GetMethodID(cls, "forceQuit", "()V");
- _set_keep_screen_on = p_env->GetMethodID(cls, "setKeepScreenOn", "(Z)V");
- _alert = p_env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
- _get_GLES_version_code = p_env->GetMethodID(cls, "getGLESVersionCode", "()I");
- _get_clipboard = p_env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;");
- _set_clipboard = p_env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V");
- _request_permission = p_env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z");
- _request_permissions = p_env->GetMethodID(cls, "requestPermissions", "()Z");
- _get_granted_permissions = p_env->GetMethodID(cls, "getGrantedPermissions", "()[Ljava/lang/String;");
- _init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V");
- _get_surface = p_env->GetMethodID(cls, "getSurface", "()Landroid/view/Surface;");
- _is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
- _vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V");
- _get_input_fallback_mapping = p_env->GetMethodID(cls, "getInputFallbackMapping", "()Ljava/lang/String;");
- _on_godot_main_loop_started = p_env->GetMethodID(cls, "onGodotMainLoopStarted", "()V");
+ // get some Godot method pointers...
+ _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()V");
+ _restart = p_env->GetMethodID(godot_class, "restart", "()V");
+ _finish = p_env->GetMethodID(godot_class, "forceQuit", "()V");
+ _set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");
+ _alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
+ _get_GLES_version_code = p_env->GetMethodID(godot_class, "getGLESVersionCode", "()I");
+ _get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
+ _set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
+ _request_permission = p_env->GetMethodID(godot_class, "requestPermission", "(Ljava/lang/String;)Z");
+ _request_permissions = p_env->GetMethodID(godot_class, "requestPermissions", "()Z");
+ _get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;");
+ _init_input_devices = p_env->GetMethodID(godot_class, "initInputDevices", "()V");
+ _get_surface = p_env->GetMethodID(godot_class, "getSurface", "()Landroid/view/Surface;");
+ _is_activity_resumed = p_env->GetMethodID(godot_class, "isActivityResumed", "()Z");
+ _vibrate = p_env->GetMethodID(godot_class, "vibrate", "(I)V");
+ _get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;");
+ _on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
+
+ // get some Activity method pointers...
+ _get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
}
GodotJavaWrapper::~GodotJavaWrapper() {
@@ -74,36 +85,44 @@ GodotJavaWrapper::~GodotJavaWrapper() {
}
jobject GodotJavaWrapper::get_activity() {
- // our godot instance is our activity
- return godot_instance;
+ return activity;
}
jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) {
- if (cls) {
+ if (godot_class) {
if (p_env == nullptr)
- p_env = ThreadAndroid::get_env();
+ p_env = get_jni_env();
- jfieldID fid = p_env->GetStaticFieldID(cls, p_name, p_class);
- return p_env->GetStaticObjectField(cls, fid);
+ jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
+ return p_env->GetStaticObjectField(godot_class, fid);
} else {
return nullptr;
}
}
jobject GodotJavaWrapper::get_class_loader() {
- if (cls) {
- JNIEnv *env = ThreadAndroid::get_env();
- jmethodID getClassLoader = env->GetMethodID(cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
- return env->CallObjectMethod(godot_instance, getClassLoader);
+ if (_get_class_loader) {
+ JNIEnv *env = get_jni_env();
+ return env->CallObjectMethod(activity, _get_class_loader);
} else {
return nullptr;
}
}
+GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
+ if (_godot_view != nullptr) {
+ return _godot_view;
+ }
+ JNIEnv *env = get_jni_env();
+ jmethodID godot_view_getter = env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
+ _godot_view = new GodotJavaViewWrapper(env->CallObjectMethod(godot_instance, godot_view_getter));
+ return _godot_view;
+}
+
void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
if (_on_video_init)
if (p_env == nullptr)
- p_env = ThreadAndroid::get_env();
+ p_env = get_jni_env();
p_env->CallVoidMethod(godot_instance, _on_video_init);
}
@@ -111,7 +130,7 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
if (_on_godot_main_loop_started) {
if (p_env == nullptr) {
- p_env = ThreadAndroid::get_env();
+ p_env = get_jni_env();
}
}
p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
@@ -120,7 +139,7 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
void GodotJavaWrapper::restart(JNIEnv *p_env) {
if (_restart)
if (p_env == nullptr)
- p_env = ThreadAndroid::get_env();
+ p_env = get_jni_env();
p_env->CallVoidMethod(godot_instance, _restart);
}
@@ -128,21 +147,21 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) {
void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
if (_finish)
if (p_env == nullptr)
- p_env = ThreadAndroid::get_env();
+ p_env = get_jni_env();
p_env->CallVoidMethod(godot_instance, _finish);
}
void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
if (_set_keep_screen_on) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled);
}
}
void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
if (_alert) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());
jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());
env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle);
@@ -150,7 +169,7 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
}
int GodotJavaWrapper::get_gles_version_code() {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
if (_get_GLES_version_code) {
return env->CallIntMethod(godot_instance, _get_GLES_version_code);
}
@@ -164,7 +183,7 @@ bool GodotJavaWrapper::has_get_clipboard() {
String GodotJavaWrapper::get_clipboard() {
if (_get_clipboard) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
return jstring_to_string(s, env);
} else {
@@ -174,7 +193,7 @@ String GodotJavaWrapper::get_clipboard() {
String GodotJavaWrapper::get_input_fallback_mapping() {
if (_get_input_fallback_mapping) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
return jstring_to_string(fallback_mapping, env);
} else {
@@ -188,7 +207,7 @@ bool GodotJavaWrapper::has_set_clipboard() {
void GodotJavaWrapper::set_clipboard(const String &p_text) {
if (_set_clipboard) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring jStr = env->NewStringUTF(p_text.utf8().get_data());
env->CallVoidMethod(godot_instance, _set_clipboard, jStr);
}
@@ -196,7 +215,7 @@ void GodotJavaWrapper::set_clipboard(const String &p_text) {
bool GodotJavaWrapper::request_permission(const String &p_name) {
if (_request_permission) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jstring jStrName = env->NewStringUTF(p_name.utf8().get_data());
return env->CallBooleanMethod(godot_instance, _request_permission, jStrName);
} else {
@@ -206,7 +225,7 @@ bool GodotJavaWrapper::request_permission(const String &p_name) {
bool GodotJavaWrapper::request_permissions() {
if (_request_permissions) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
return env->CallBooleanMethod(godot_instance, _request_permissions);
} else {
return false;
@@ -216,7 +235,7 @@ bool GodotJavaWrapper::request_permissions() {
Vector<String> GodotJavaWrapper::get_granted_permissions() const {
Vector<String> permissions_list;
if (_get_granted_permissions) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);
jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);
@@ -234,14 +253,14 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
void GodotJavaWrapper::init_input_devices() {
if (_init_input_devices) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(godot_instance, _init_input_devices);
}
}
jobject GodotJavaWrapper::get_surface() {
if (_get_surface) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
return env->CallObjectMethod(godot_instance, _get_surface);
} else {
return nullptr;
@@ -250,7 +269,7 @@ jobject GodotJavaWrapper::get_surface() {
bool GodotJavaWrapper::is_activity_resumed() {
if (_is_activity_resumed) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
} else {
return false;
@@ -259,7 +278,7 @@ bool GodotJavaWrapper::is_activity_resumed() {
void GodotJavaWrapper::vibrate(int p_duration_ms) {
if (_vibrate) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
}
}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 89d6b6db46..99a60dffa1 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,13 +37,18 @@
#include <android/log.h>
#include <jni.h>
+#include "java_godot_view_wrapper.h"
#include "string_android.h"
// Class that makes functions in java/src/org/godotengine/godot/Godot.java callable from C++
class GodotJavaWrapper {
private:
jobject godot_instance;
- jclass cls;
+ jobject activity;
+ jclass godot_class;
+ jclass activity_class;
+
+ GodotJavaViewWrapper *_godot_view = nullptr;
jmethodID _on_video_init = 0;
jmethodID _restart = 0;
@@ -62,15 +67,17 @@ private:
jmethodID _vibrate = 0;
jmethodID _get_input_fallback_mapping = 0;
jmethodID _on_godot_main_loop_started = 0;
+ jmethodID _get_class_loader = 0;
public:
- GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance);
+ GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance);
~GodotJavaWrapper();
jobject get_activity();
jobject get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env = nullptr);
jobject get_class_loader();
+ GodotJavaViewWrapper *get_godot_view();
void on_video_init(JNIEnv *p_env = nullptr);
void on_godot_main_loop_started(JNIEnv *p_env = nullptr);
diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp
index 8e1ae53b78..94652ab7ac 100644
--- a/platform/android/jni_utils.cpp
+++ b/platform/android/jni_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/jni_utils.h b/platform/android/jni_utils.h
index 5320715853..f2b89392b5 100644
--- a/platform/android/jni_utils.h
+++ b/platform/android/jni_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,8 +32,8 @@
#define JNI_UTILS_H
#include "string_android.h"
-#include <core/engine.h>
-#include <core/variant.h>
+#include <core/config/engine.h>
+#include <core/variant/variant.h>
#include <jni.h>
struct jvalret {
diff --git a/platform/android/logo.png b/platform/android/logo.png
index df445f6a9c..f44d360a25 100644
--- a/platform/android/logo.png
+++ b/platform/android/logo.png
Binary files differ
diff --git a/platform/android/net_socket_android.cpp b/platform/android/net_socket_android.cpp
index 0341ef3ec6..ddc2368793 100644
--- a/platform/android/net_socket_android.cpp
+++ b/platform/android/net_socket_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,7 +38,7 @@ jmethodID NetSocketAndroid::_multicast_lock_acquire = 0;
jmethodID NetSocketAndroid::_multicast_lock_release = 0;
void NetSocketAndroid::setup(jobject p_net_utils) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
net_utils = env->NewGlobalRef(p_net_utils);
@@ -51,14 +51,14 @@ void NetSocketAndroid::setup(jobject p_net_utils) {
void NetSocketAndroid::multicast_lock_acquire() {
if (_multicast_lock_acquire) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(net_utils, _multicast_lock_acquire);
}
}
void NetSocketAndroid::multicast_lock_release() {
if (_multicast_lock_release) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
env->CallVoidMethod(net_utils, _multicast_lock_release);
}
}
diff --git a/platform/android/net_socket_android.h b/platform/android/net_socket_android.h
index 955d906535..cc2a68ac49 100644
--- a/platform/android/net_socket_android.h
+++ b/platform/android/net_socket_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index baf6ee952a..422814dd50 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,16 +30,14 @@
#include "os_android.h"
-#include "core/io/file_access_buffered_fa.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
-#include "file_access_android.h"
#include "main/main.h"
#include "platform/android/display_server_android.h"
#include "dir_access_jandroid.h"
-#include "file_access_jandroid.h"
+#include "file_access_android.h"
#include "net_socket_android.h"
#include <dlfcn.h>
@@ -62,16 +60,10 @@ void OS_Android::initialize_core() {
if (use_apk_expansion)
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
else {
-#ifdef USE_JAVA_FILE_ACCESS
- FileAccess::make_default<FileAccessBufferedFA<FileAccessJAndroid>>(FileAccess::ACCESS_RESOURCES);
-#else
- //FileAccess::make_default<FileAccessBufferedFA<FileAccessAndroid> >(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessAndroid>(FileAccess::ACCESS_RESOURCES);
-#endif
}
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
- //FileAccessBufferedFA<FileAccessUnix>::make_default();
if (use_apk_expansion)
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
else
@@ -147,18 +139,19 @@ MainLoop *OS_Android::get_main_loop() const {
void OS_Android::main_loop_begin() {
if (main_loop)
- main_loop->init();
+ main_loop->initialize();
}
bool OS_Android::main_loop_iterate() {
if (!main_loop)
return false;
+ DisplayServerAndroid::get_singleton()->process_events();
return Main::iteration();
}
void OS_Android::main_loop_end() {
if (main_loop)
- main_loop->finish();
+ main_loop->finalize();
}
void OS_Android::main_loop_focusout() {
@@ -313,6 +306,7 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
#if defined(OPENGL_ENABLED)
gl_extensions = nullptr;
use_gl2 = false;
+ use_16bits_fbo = false;
#endif
#if defined(VULKAN_ENABLED)
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index cac7efaa88..dd14b69cf9 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -68,15 +68,15 @@ private:
GodotIOJavaWrapper *godot_io_java;
public:
- virtual void initialize_core();
- virtual void initialize();
+ virtual void initialize_core() override;
+ virtual void initialize() override;
- virtual void initialize_joypads();
+ virtual void initialize_joypads() override;
- virtual void set_main_loop(MainLoop *p_main_loop);
- virtual void delete_main_loop();
+ virtual void set_main_loop(MainLoop *p_main_loop) override;
+ virtual void delete_main_loop() override;
- virtual void finalize();
+ virtual void finalize() override;
typedef int64_t ProcessID;
@@ -84,14 +84,14 @@ public:
GodotJavaWrapper *get_godot_java();
GodotIOJavaWrapper *get_godot_io_java();
- virtual bool request_permission(const String &p_name);
- virtual bool request_permissions();
- virtual Vector<String> get_granted_permissions() const;
+ virtual bool request_permission(const String &p_name) override;
+ virtual bool request_permissions() override;
+ virtual Vector<String> get_granted_permissions() const override;
- virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
+ virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override;
- virtual String get_name() const;
- virtual MainLoop *get_main_loop() const;
+ virtual String get_name() const override;
+ virtual MainLoop *get_main_loop() const override;
void main_loop_begin();
bool main_loop_iterate();
@@ -109,19 +109,19 @@ public:
void set_native_window(ANativeWindow *p_native_window);
ANativeWindow *get_native_window() const;
- virtual Error shell_open(String p_uri);
- virtual String get_user_data_dir() const;
- virtual String get_resource_dir() const;
- virtual String get_locale() const;
- virtual String get_model_name() const;
+ virtual Error shell_open(String p_uri) override;
+ virtual String get_user_data_dir() const override;
+ virtual String get_resource_dir() const override;
+ virtual String get_locale() const override;
+ virtual String get_model_name() const override;
- virtual String get_unique_id() const;
+ virtual String get_unique_id() const override;
- virtual String get_system_dir(SystemDir p_dir) const;
+ virtual String get_system_dir(SystemDir p_dir) const override;
- void vibrate_handheld(int p_duration_ms);
+ void vibrate_handheld(int p_duration_ms) override;
- virtual bool _check_internal_feature_support(const String &p_feature);
+ virtual bool _check_internal_feature_support(const String &p_feature) override;
OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion);
~OS_Android();
};
diff --git a/platform/android/platform_config.h b/platform/android/platform_config.h
index c5e896c4e1..601db9951f 100644
--- a/platform/android/platform_config.h
+++ b/platform/android/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/android/plugin/godot_plugin_config.h b/platform/android/plugin/godot_plugin_config.h
index 9ad7de1202..173ac115a2 100644
--- a/platform/android/plugin/godot_plugin_config.h
+++ b/platform/android/plugin/godot_plugin_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,26 +31,9 @@
#ifndef GODOT_PLUGIN_CONFIG_H
#define GODOT_PLUGIN_CONFIG_H
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/config_file.h"
-#include "core/ustring.h"
-
-static const char *PLUGIN_CONFIG_EXT = ".gdap";
-
-static const char *CONFIG_SECTION = "config";
-static const char *CONFIG_NAME_KEY = "name";
-static const char *CONFIG_BINARY_TYPE_KEY = "binary_type";
-static const char *CONFIG_BINARY_KEY = "binary";
-
-static const char *DEPENDENCIES_SECTION = "dependencies";
-static const char *DEPENDENCIES_LOCAL_KEY = "local";
-static const char *DEPENDENCIES_REMOTE_KEY = "remote";
-static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos";
-
-static const char *BINARY_TYPE_LOCAL = "local";
-static const char *BINARY_TYPE_REMOTE = "remote";
-
-static const char *PLUGIN_VALUE_SEPARATOR = "|";
+#include "core/string/ustring.h"
/*
The `config` section and fields are required and defined as follow:
@@ -67,9 +50,28 @@ The `dependencies` section and fields are optional and defined as follow:
See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871
*/
-struct PluginConfig {
+struct PluginConfigAndroid {
+ inline static const char *PLUGIN_CONFIG_EXT = ".gdap";
+
+ inline static const char *CONFIG_SECTION = "config";
+ inline static const char *CONFIG_NAME_KEY = "name";
+ inline static const char *CONFIG_BINARY_TYPE_KEY = "binary_type";
+ inline static const char *CONFIG_BINARY_KEY = "binary";
+
+ inline static const char *DEPENDENCIES_SECTION = "dependencies";
+ inline static const char *DEPENDENCIES_LOCAL_KEY = "local";
+ inline static const char *DEPENDENCIES_REMOTE_KEY = "remote";
+ inline static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos";
+
+ inline static const char *BINARY_TYPE_LOCAL = "local";
+ inline static const char *BINARY_TYPE_REMOTE = "remote";
+
+ inline static const char *PLUGIN_VALUE_SEPARATOR = "|";
+
// Set to true when the config file is properly loaded.
bool valid_config = false;
+ // Unix timestamp of last change to this plugin.
+ uint64_t last_updated = 0;
// Required config section
String name;
@@ -84,20 +86,22 @@ struct PluginConfig {
/*
* Set of prebuilt plugins.
+ * Currently unused, this is just for future reference:
*/
-static const PluginConfig GODOT_PAYMENT = {
- /*.valid_config =*/true,
- /*.name =*/"GodotPayment",
- /*.binary_type =*/"local",
- /*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar",
- /*.local_dependencies =*/{},
- /*.remote_dependencies =*/{},
- /*.custom_maven_repos =*/{}
-};
+// static const PluginConfigAndroid MY_PREBUILT_PLUGIN = {
+// /*.valid_config =*/true,
+// /*.last_updated =*/0,
+// /*.name =*/"GodotPayment",
+// /*.binary_type =*/"local",
+// /*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar",
+// /*.local_dependencies =*/{},
+// /*.remote_dependencies =*/String("com.android.billingclient:billing:2.2.1").split("|"),
+// /*.custom_maven_repos =*/{}
+// };
static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
String absolute_path;
- if (!dependency_path.empty()) {
+ if (!dependency_path.is_empty()) {
if (dependency_path.is_abs_path()) {
absolute_path = ProjectSettings::get_singleton()->globalize_path(dependency_path);
} else {
@@ -108,10 +112,10 @@ static inline String resolve_local_dependency_path(String plugin_config_dir, Str
return absolute_path;
}
-static inline PluginConfig resolve_prebuilt_plugin(PluginConfig prebuilt_plugin, String plugin_config_dir) {
- PluginConfig resolved = prebuilt_plugin;
- resolved.binary = resolved.binary_type == BINARY_TYPE_LOCAL ? resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.binary) : prebuilt_plugin.binary;
- if (!prebuilt_plugin.local_dependencies.empty()) {
+static inline PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir) {
+ PluginConfigAndroid resolved = prebuilt_plugin;
+ resolved.binary = resolved.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ? resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.binary) : prebuilt_plugin.binary;
+ if (!prebuilt_plugin.local_dependencies.is_empty()) {
resolved.local_dependencies.clear();
for (int i = 0; i < prebuilt_plugin.local_dependencies.size(); i++) {
resolved.local_dependencies.push_back(resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.local_dependencies[i]));
@@ -120,26 +124,27 @@ static inline PluginConfig resolve_prebuilt_plugin(PluginConfig prebuilt_plugin,
return resolved;
}
-static inline Vector<PluginConfig> get_prebuilt_plugins(String plugins_base_dir) {
- Vector<PluginConfig> prebuilt_plugins;
- prebuilt_plugins.push_back(resolve_prebuilt_plugin(GODOT_PAYMENT, plugins_base_dir));
+static inline Vector<PluginConfigAndroid> get_prebuilt_plugins(String plugins_base_dir) {
+ Vector<PluginConfigAndroid> prebuilt_plugins;
+ // prebuilt_plugins.push_back(resolve_prebuilt_plugin(MY_PREBUILT_PLUGIN, plugins_base_dir));
return prebuilt_plugins;
}
-static inline bool is_plugin_config_valid(PluginConfig plugin_config) {
- bool valid_name = !plugin_config.name.empty();
- bool valid_binary_type = plugin_config.binary_type == BINARY_TYPE_LOCAL ||
- plugin_config.binary_type == BINARY_TYPE_REMOTE;
+static inline bool is_plugin_config_valid(PluginConfigAndroid plugin_config) {
+ bool valid_name = !plugin_config.name.is_empty();
+ bool valid_binary_type = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ||
+ plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE;
bool valid_binary = false;
if (valid_binary_type) {
- valid_binary = !plugin_config.binary.empty() &&
- (plugin_config.binary_type == BINARY_TYPE_REMOTE ||
+ valid_binary = !plugin_config.binary.is_empty() &&
+ (plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE ||
+
FileAccess::exists(plugin_config.binary));
}
bool valid_local_dependencies = true;
- if (!plugin_config.local_dependencies.empty()) {
+ if (!plugin_config.local_dependencies.is_empty()) {
for (int i = 0; i < plugin_config.local_dependencies.size(); i++) {
if (!FileAccess::exists(plugin_config.local_dependencies[i])) {
valid_local_dependencies = false;
@@ -150,45 +155,58 @@ static inline bool is_plugin_config_valid(PluginConfig plugin_config) {
return valid_name && valid_binary && valid_binary_type && valid_local_dependencies;
}
-static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
- PluginConfig plugin_config = {};
+static inline uint64_t get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path) {
+ uint64_t last_updated = FileAccess::get_modified_time(config_path);
+ last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary));
+
+ for (int i = 0; i < plugin_config.local_dependencies.size(); i++) {
+ String binary = plugin_config.local_dependencies.get(i);
+ last_updated = MAX(last_updated, FileAccess::get_modified_time(binary));
+ }
+
+ return last_updated;
+}
+
+static inline PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
+ PluginConfigAndroid plugin_config = {};
if (config_file.is_valid()) {
Error err = config_file->load(path);
if (err == OK) {
String config_base_dir = path.get_base_dir();
- plugin_config.name = config_file->get_value(CONFIG_SECTION, CONFIG_NAME_KEY, String());
- plugin_config.binary_type = config_file->get_value(CONFIG_SECTION, CONFIG_BINARY_TYPE_KEY, String());
+ plugin_config.name = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_NAME_KEY, String());
+ plugin_config.binary_type = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_BINARY_TYPE_KEY, String());
- String binary_path = config_file->get_value(CONFIG_SECTION, CONFIG_BINARY_KEY, String());
- plugin_config.binary = plugin_config.binary_type == BINARY_TYPE_LOCAL ? resolve_local_dependency_path(config_base_dir, binary_path) : binary_path;
+ String binary_path = config_file->get_value(PluginConfigAndroid::CONFIG_SECTION, PluginConfigAndroid::CONFIG_BINARY_KEY, String());
+ plugin_config.binary = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ? resolve_local_dependency_path(config_base_dir, binary_path) : binary_path;
- if (config_file->has_section(DEPENDENCIES_SECTION)) {
- Vector<String> local_dependencies_paths = config_file->get_value(DEPENDENCIES_SECTION, DEPENDENCIES_LOCAL_KEY, Vector<String>());
- if (!local_dependencies_paths.empty()) {
+ if (config_file->has_section(PluginConfigAndroid::DEPENDENCIES_SECTION)) {
+ Vector<String> local_dependencies_paths = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_LOCAL_KEY, Vector<String>());
+ if (!local_dependencies_paths.is_empty()) {
for (int i = 0; i < local_dependencies_paths.size(); i++) {
plugin_config.local_dependencies.push_back(resolve_local_dependency_path(config_base_dir, local_dependencies_paths[i]));
}
}
- plugin_config.remote_dependencies = config_file->get_value(DEPENDENCIES_SECTION, DEPENDENCIES_REMOTE_KEY, Vector<String>());
- plugin_config.custom_maven_repos = config_file->get_value(DEPENDENCIES_SECTION, DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY, Vector<String>());
+ plugin_config.remote_dependencies = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_REMOTE_KEY, Vector<String>());
+ plugin_config.custom_maven_repos = config_file->get_value(PluginConfigAndroid::DEPENDENCIES_SECTION, PluginConfigAndroid::DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY, Vector<String>());
}
plugin_config.valid_config = is_plugin_config_valid(plugin_config);
+ plugin_config.last_updated = get_plugin_modification_time(plugin_config, path);
}
}
return plugin_config;
}
-static inline String get_plugins_binaries(String binary_type, Vector<PluginConfig> plugins_configs) {
+static inline String get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs) {
String plugins_binaries;
- if (!plugins_configs.empty()) {
+ if (!plugins_configs.is_empty()) {
Vector<String> binaries;
for (int i = 0; i < plugins_configs.size(); i++) {
- PluginConfig config = plugins_configs[i];
+ PluginConfigAndroid config = plugins_configs[i];
if (!config.valid_config) {
continue;
}
@@ -197,27 +215,27 @@ static inline String get_plugins_binaries(String binary_type, Vector<PluginConfi
binaries.push_back(config.binary);
}
- if (binary_type == BINARY_TYPE_LOCAL) {
+ if (binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL) {
binaries.append_array(config.local_dependencies);
}
- if (binary_type == BINARY_TYPE_REMOTE) {
+ if (binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE) {
binaries.append_array(config.remote_dependencies);
}
}
- plugins_binaries = String(PLUGIN_VALUE_SEPARATOR).join(binaries);
+ plugins_binaries = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(binaries);
}
return plugins_binaries;
}
-static inline String get_plugins_custom_maven_repos(Vector<PluginConfig> plugins_configs) {
+static inline String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs) {
String custom_maven_repos;
- if (!plugins_configs.empty()) {
+ if (!plugins_configs.is_empty()) {
Vector<String> repos_urls;
for (int i = 0; i < plugins_configs.size(); i++) {
- PluginConfig config = plugins_configs[i];
+ PluginConfigAndroid config = plugins_configs[i];
if (!config.valid_config) {
continue;
}
@@ -225,24 +243,24 @@ static inline String get_plugins_custom_maven_repos(Vector<PluginConfig> plugins
repos_urls.append_array(config.custom_maven_repos);
}
- custom_maven_repos = String(PLUGIN_VALUE_SEPARATOR).join(repos_urls);
+ custom_maven_repos = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(repos_urls);
}
return custom_maven_repos;
}
-static inline String get_plugins_names(Vector<PluginConfig> plugins_configs) {
+static inline String get_plugins_names(Vector<PluginConfigAndroid> plugins_configs) {
String plugins_names;
- if (!plugins_configs.empty()) {
+ if (!plugins_configs.is_empty()) {
Vector<String> names;
for (int i = 0; i < plugins_configs.size(); i++) {
- PluginConfig config = plugins_configs[i];
+ PluginConfigAndroid config = plugins_configs[i];
if (!config.valid_config) {
continue;
}
names.push_back(config.name);
}
- plugins_names = String(PLUGIN_VALUE_SEPARATOR).join(names);
+ plugins_names = String(PluginConfigAndroid::PLUGIN_VALUE_SEPARATOR).join(names);
}
return plugins_names;
diff --git a/platform/android/plugin/godot_plugin_jni.cpp b/platform/android/plugin/godot_plugin_jni.cpp
index 557743fa73..ba3e9fa20f 100644
--- a/platform/android/plugin/godot_plugin_jni.cpp
+++ b/platform/android/plugin/godot_plugin_jni.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,9 +30,9 @@
#include "godot_plugin_jni.h"
-#include <core/engine.h>
-#include <core/error_macros.h>
-#include <core/project_settings.h>
+#include <core/config/engine.h>
+#include <core/config/project_settings.h>
+#include <core/error/error_macros.h>
#include <platform/android/api/jni_singleton.h>
#include <platform/android/jni_utils.h>
#include <platform/android/string_android.h>
@@ -41,7 +41,7 @@ static HashMap<String, JNISingleton *> jni_singletons;
extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj) {
String singname = jstring_to_string(name, env);
JNISingleton *s = (JNISingleton *)ClassDB::instance("JNISingleton");
s->set_instance(env->NewGlobalRef(obj));
@@ -51,7 +51,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
ProjectSettings::get_singleton()->set(singname, s);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args) {
String singname = jstring_to_string(sname, env);
ERR_FAIL_COND(!jni_singletons.has(singname));
@@ -83,7 +83,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
s->add_method(mname, mid, types, get_jni_type(retval));
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types) {
String singleton_name = jstring_to_string(j_plugin_name, env);
ERR_FAIL_COND(!jni_singletons.has(singleton_name));
@@ -104,7 +104,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegis
singleton->add_signal(signal_name, types);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params) {
String singleton_name = jstring_to_string(j_plugin_name, env);
ERR_FAIL_COND(!jni_singletons.has(singleton_name));
@@ -114,19 +114,22 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitS
String signal_name = jstring_to_string(j_signal_name, env);
int count = env->GetArrayLength(j_signal_params);
- const Variant *args[count];
+ ERR_FAIL_COND_MSG(count > VARIANT_ARG_MAX, "Maximum argument count exceeded!");
+
+ Variant variant_params[VARIANT_ARG_MAX];
+ const Variant *args[VARIANT_ARG_MAX];
for (int i = 0; i < count; i++) {
jobject j_param = env->GetObjectArrayElement(j_signal_params, i);
- Variant variant = _jobject_to_variant(env, j_param);
- args[i] = &variant;
+ variant_params[i] = _jobject_to_variant(env, j_param);
+ args[i] = &variant_params[i];
env->DeleteLocalRef(j_param);
};
singleton->emit_signal(signal_name, args, count);
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jclass clazz, jobjectArray gdnlib_paths) {
int gdnlib_count = env->GetArrayLength(gdnlib_paths);
if (gdnlib_count == 0) {
return;
diff --git a/platform/android/plugin/godot_plugin_jni.h b/platform/android/plugin/godot_plugin_jni.h
index 80ce332e7c..b87f922e03 100644
--- a/platform/android/plugin/godot_plugin_jni.h
+++ b/platform/android/plugin/godot_plugin_jni.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,11 +35,11 @@
#include <jni.h>
extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jobject obj, jstring name);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jobject obj, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jobject obj, jobjectArray gdnlib_paths);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSingleton(JNIEnv *env, jclass clazz, jstring name, jobject obj);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterMethod(JNIEnv *env, jclass clazz, jstring sname, jstring name, jstring ret, jobjectArray args);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_param_types);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeEmitSignal(JNIEnv *env, jclass clazz, jstring j_plugin_name, jstring j_signal_name, jobjectArray j_signal_params);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_plugin_GodotPlugin_nativeRegisterGDNativeLibraries(JNIEnv *env, jclass clazz, jobjectArray gdnlib_paths);
}
#endif // GODOT_PLUGIN_JNI_H
diff --git a/platform/android/string_android.h b/platform/android/string_android.h
index 88ccd3b652..3721315d3f 100644
--- a/platform/android/string_android.h
+++ b/platform/android/string_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,21 +30,21 @@
#ifndef STRING_ANDROID_H
#define STRING_ANDROID_H
-#include "core/ustring.h"
+#include "core/string/ustring.h"
#include "thread_jandroid.h"
#include <jni.h>
/**
* Converts JNI jstring to Godot String.
* @param source Source JNI string. If null an empty string is returned.
- * @param env JNI environment instance. If null obtained by ThreadAndroid::get_env().
+ * @param env JNI environment instance. If null obtained by get_jni_env().
* @return Godot string instance.
*/
static inline String jstring_to_string(jstring source, JNIEnv *env = nullptr) {
String result;
if (source) {
if (!env) {
- env = ThreadAndroid::get_env();
+ env = get_jni_env();
}
const char *const source_utf8 = env->GetStringUTFChars(source, nullptr);
if (source_utf8) {
diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp
index 13aa313ebf..afcc7294f2 100644
--- a/platform/android/thread_jandroid.cpp
+++ b/platform/android/thread_jandroid.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,116 +30,29 @@
#include "thread_jandroid.h"
-#include "core/os/memory.h"
-#include "core/safe_refcount.h"
-#include "core/script_language.h"
+#include "core/os/thread.h"
-static void _thread_id_key_destr_callback(void *p_value) {
- memdelete(static_cast<Thread::ID *>(p_value));
-}
-
-static pthread_key_t _create_thread_id_key() {
- pthread_key_t key;
- pthread_key_create(&key, &_thread_id_key_destr_callback);
- return key;
-}
-
-pthread_key_t ThreadAndroid::thread_id_key = _create_thread_id_key();
-Thread::ID ThreadAndroid::next_thread_id = 0;
-
-Thread::ID ThreadAndroid::get_id() const {
- return id;
-}
-
-Thread *ThreadAndroid::create_thread_jandroid() {
- return memnew(ThreadAndroid);
-}
-
-void *ThreadAndroid::thread_callback(void *userdata) {
- ThreadAndroid *t = reinterpret_cast<ThreadAndroid *>(userdata);
- setup_thread();
- ScriptServer::thread_enter(); //scripts may need to attach a stack
- t->id = atomic_increment(&next_thread_id);
- pthread_setspecific(thread_id_key, (void *)memnew(ID(t->id)));
- t->callback(t->user);
- ScriptServer::thread_exit();
- return nullptr;
-}
-
-Thread *ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback, void *p_user, const Settings &) {
- ThreadAndroid *tr = memnew(ThreadAndroid);
- tr->callback = p_callback;
- tr->user = p_user;
- pthread_attr_init(&tr->pthread_attr);
- pthread_attr_setdetachstate(&tr->pthread_attr, PTHREAD_CREATE_JOINABLE);
-
- pthread_create(&tr->pthread, &tr->pthread_attr, thread_callback, tr);
-
- return tr;
-}
+static JavaVM *java_vm = nullptr;
+static thread_local JNIEnv *env = nullptr;
-Thread::ID ThreadAndroid::get_thread_id_func_jandroid() {
- void *value = pthread_getspecific(thread_id_key);
-
- if (value)
- return *static_cast<ID *>(value);
-
- ID new_id = atomic_increment(&next_thread_id);
- pthread_setspecific(thread_id_key, (void *)memnew(ID(new_id)));
- return new_id;
-}
-
-void ThreadAndroid::wait_to_finish_func_jandroid(Thread *p_thread) {
- ThreadAndroid *tp = static_cast<ThreadAndroid *>(p_thread);
- ERR_FAIL_COND(!tp);
- ERR_FAIL_COND(tp->pthread == 0);
-
- pthread_join(tp->pthread, nullptr);
- tp->pthread = 0;
-}
-
-void ThreadAndroid::_thread_destroyed(void *value) {
- /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
- JNIEnv *env = (JNIEnv *)value;
- if (env != nullptr) {
- java_vm->DetachCurrentThread();
- pthread_setspecific(jvm_key, nullptr);
- }
-}
-
-pthread_key_t ThreadAndroid::jvm_key;
-JavaVM *ThreadAndroid::java_vm = nullptr;
-
-void ThreadAndroid::setup_thread() {
- if (pthread_getspecific(jvm_key))
- return; //already setup
- JNIEnv *env;
+static void init_thread() {
java_vm->AttachCurrentThread(&env, nullptr);
- pthread_setspecific(jvm_key, (void *)env);
}
-void ThreadAndroid::make_default(JavaVM *p_java_vm) {
- java_vm = p_java_vm;
- create_func = create_func_jandroid;
- get_thread_id_func = get_thread_id_func_jandroid;
- wait_to_finish_func = wait_to_finish_func_jandroid;
- pthread_key_create(&jvm_key, _thread_destroyed);
- setup_thread();
+static void term_thread() {
+ java_vm->DetachCurrentThread();
}
-JNIEnv *ThreadAndroid::get_env() {
- if (!pthread_getspecific(jvm_key)) {
- setup_thread();
- }
-
- JNIEnv *env = nullptr;
- java_vm->AttachCurrentThread(&env, nullptr);
- return env;
+void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) {
+ java_vm = p_jvm;
+ env = p_env;
+ Thread::_set_platform_funcs(nullptr, nullptr, &init_thread, &term_thread);
}
-ThreadAndroid::ThreadAndroid() {
- pthread = 0;
+void setup_android_thread() {
+ init_thread();
}
-ThreadAndroid::~ThreadAndroid() {
+JNIEnv *get_jni_env() {
+ return env;
}
diff --git a/platform/android/thread_jandroid.h b/platform/android/thread_jandroid.h
index 9cfcc64813..ff13ae911f 100644
--- a/platform/android/thread_jandroid.h
+++ b/platform/android/thread_jandroid.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,46 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef THREAD_POSIX_H
-#define THREAD_POSIX_H
+#ifndef THREAD_JANDROID_H
+#define THREAD_JANDROID_H
-#include "core/os/thread.h"
#include <jni.h>
-#include <pthread.h>
-#include <sys/types.h>
-class ThreadAndroid : public Thread {
- static pthread_key_t thread_id_key;
- static ID next_thread_id;
+void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env);
- pthread_t pthread;
- pthread_attr_t pthread_attr;
- ThreadCreateCallback callback;
- void *user;
- ID id;
-
- static Thread *create_thread_jandroid();
-
- static void *thread_callback(void *userdata);
-
- static Thread *create_func_jandroid(ThreadCreateCallback p_callback, void *, const Settings &);
- static ID get_thread_id_func_jandroid();
- static void wait_to_finish_func_jandroid(Thread *p_thread);
-
- static void _thread_destroyed(void *value);
- ThreadAndroid();
-
- static pthread_key_t jvm_key;
- static JavaVM *java_vm;
-
-public:
- virtual ID get_id() const;
-
- static void make_default(JavaVM *p_java_vm);
- static void setup_thread();
- static JNIEnv *get_env();
-
- ~ThreadAndroid();
-};
+void setup_android_thread();
+JNIEnv *get_jni_env();
#endif
diff --git a/platform/android/vulkan/vulkan_context_android.cpp b/platform/android/vulkan/vulkan_context_android.cpp
index 5fb7a83da4..1bf85f07f1 100644
--- a/platform/android/vulkan/vulkan_context_android.cpp
+++ b/platform/android/vulkan/vulkan_context_android.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "vulkan_context_android.h"
+
#include <vulkan/vulkan_android.h>
const char *VulkanContextAndroid::_get_platform_surface_extension() const {
diff --git a/platform/android/vulkan/vulkan_context_android.h b/platform/android/vulkan/vulkan_context_android.h
index 6bd3cbee36..c608f2d665 100644
--- a/platform/android/vulkan/vulkan_context_android.h
+++ b/platform/android/vulkan/vulkan_context_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/haiku/SCsub b/platform/haiku/SCsub
deleted file mode 100644
index dbff6c5ae9..0000000000
--- a/platform/haiku/SCsub
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-
-common_haiku = [
- "os_haiku.cpp",
- "context_gl_haiku.cpp",
- "haiku_application.cpp",
- "haiku_direct_window.cpp",
- "haiku_gl_view.cpp",
- "key_mapping_haiku.cpp",
- "audio_driver_media_kit.cpp",
-]
-
-target = env.add_program("#bin/godot", ["godot_haiku.cpp"] + common_haiku)
-
-command = env.Command("#bin/godot.rsrc", "#platform/haiku/godot.rdef", ["rc -o $TARGET $SOURCE"])
-
-
-def addResourcesAction(target=None, source=None, env=None):
- return env.Execute("xres -o " + File(target)[0].path + " bin/godot.rsrc")
-
-
-env.AddPostAction(target, addResourcesAction)
-env.Depends(target, command)
diff --git a/platform/haiku/audio_driver_media_kit.cpp b/platform/haiku/audio_driver_media_kit.cpp
deleted file mode 100644
index 2fbbeeb176..0000000000
--- a/platform/haiku/audio_driver_media_kit.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/*************************************************************************/
-/* audio_driver_media_kit.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "audio_driver_media_kit.h"
-
-#ifdef MEDIA_KIT_ENABLED
-
-#include "core/project_settings.h"
-
-int32_t *AudioDriverMediaKit::samples_in = nullptr;
-
-Error AudioDriverMediaKit::init() {
- active = false;
-
- mix_rate = GLOBAL_GET("audio/mix_rate");
- speaker_mode = SPEAKER_MODE_STEREO;
- channels = 2;
-
- int latency = GLOBAL_GET("audio/output_latency");
- buffer_size = next_power_of_2(latency * mix_rate / 1000);
- samples_in = memnew_arr(int32_t, buffer_size * channels);
-
- media_raw_audio_format format;
- format = media_raw_audio_format::wildcard;
- format.frame_rate = mix_rate;
- format.channel_count = channels;
- format.format = media_raw_audio_format::B_AUDIO_INT;
- format.byte_order = B_MEDIA_LITTLE_ENDIAN;
- format.buffer_size = buffer_size * sizeof(int32_t) * channels;
-
- player = new BSoundPlayer(
- &format,
- "godot_sound_server",
- AudioDriverMediaKit::PlayBuffer,
- nullptr,
- this);
-
- if (player->InitCheck() != B_OK) {
- fprintf(stderr, "MediaKit ERR: can not create a BSoundPlayer instance\n");
- ERR_FAIL_COND_V(player == nullptr, ERR_CANT_OPEN);
- }
-
- player->Start();
-
- return OK;
-}
-
-void AudioDriverMediaKit::PlayBuffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format) {
- AudioDriverMediaKit *ad = (AudioDriverMediaKit *)cookie;
- int32_t *buf = (int32_t *)buffer;
-
- if (!ad->active) {
- for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) {
- AudioDriverMediaKit::samples_in[i] = 0;
- }
- } else {
- ad->lock();
- ad->audio_server_process(ad->buffer_size, AudioDriverMediaKit::samples_in);
- ad->unlock();
- }
-
- for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) {
- buf[i] = AudioDriverMediaKit::samples_in[i];
- }
-}
-
-void AudioDriverMediaKit::start() {
- active = true;
-}
-
-int AudioDriverMediaKit::get_mix_rate() const {
- return mix_rate;
-}
-
-AudioDriverMediaKit::SpeakerMode AudioDriverMediaKit::get_speaker_mode() const {
- return speaker_mode;
-}
-
-void AudioDriverMediaKit::lock() {
- if (!mutex)
- return;
-
- mutex.lock();
-}
-
-void AudioDriverMediaKit::unlock() {
- if (!mutex)
- return;
-
- mutex.unlock();
-}
-
-void AudioDriverMediaKit::finish() {
- delete player;
-
- if (samples_in) {
- memdelete_arr(samples_in);
- };
-}
-
-AudioDriverMediaKit::AudioDriverMediaKit() {
- player = nullptr;
-}
-
-AudioDriverMediaKit::~AudioDriverMediaKit() {
-}
-
-#endif
diff --git a/platform/haiku/context_gl_haiku.cpp b/platform/haiku/context_gl_haiku.cpp
deleted file mode 100644
index 3c4d43ff71..0000000000
--- a/platform/haiku/context_gl_haiku.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*************************************************************************/
-/* context_gl_haiku.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "context_gl_haiku.h"
-
-#if defined(OPENGL_ENABLED)
-
-ContextGL_Haiku::ContextGL_Haiku(HaikuDirectWindow *p_window) {
- window = p_window;
-
- uint32 type = BGL_RGB | BGL_DOUBLE | BGL_DEPTH;
- view = new HaikuGLView(window->Bounds(), type);
-
- use_vsync = false;
-}
-
-ContextGL_Haiku::~ContextGL_Haiku() {
- delete view;
-}
-
-Error ContextGL_Haiku::initialize() {
- window->AddChild(view);
- window->SetHaikuGLView(view);
-
- return OK;
-}
-
-void ContextGL_Haiku::release_current() {
- view->UnlockGL();
-}
-
-void ContextGL_Haiku::make_current() {
- view->LockGL();
-}
-
-void ContextGL_Haiku::swap_buffers() {
- view->SwapBuffers(use_vsync);
-}
-
-int ContextGL_Haiku::get_window_width() {
- return window->Bounds().IntegerWidth();
-}
-
-int ContextGL_Haiku::get_window_height() {
- return window->Bounds().IntegerHeight();
-}
-
-void ContextGL_Haiku::set_use_vsync(bool p_use) {
- use_vsync = p_use;
-}
-
-bool ContextGL_Haiku::is_using_vsync() const {
- return use_vsync;
-}
-
-#endif
diff --git a/platform/haiku/detect.py b/platform/haiku/detect.py
deleted file mode 100644
index 0b84df8f9b..0000000000
--- a/platform/haiku/detect.py
+++ /dev/null
@@ -1,158 +0,0 @@
-import os
-import sys
-
-
-def is_active():
- return True
-
-
-def get_name():
- return "Haiku"
-
-
-def can_build():
-
- if os.name != "posix" or sys.platform == "darwin":
- return False
-
- return True
-
-
-def get_opts():
- from SCons.Variables import EnumVariable
-
- return [
- EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
- ]
-
-
-def get_flags():
-
- return []
-
-
-def configure(env):
-
- ## Build type
-
- if env["target"] == "release":
- env.Prepend(CCFLAGS=["-O3"])
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
- env.Prepend(CCFLAGS=["-g2"])
-
- elif env["target"] == "release_debug":
- env.Prepend(CCFLAGS=["-O2", "-DDEBUG_ENABLED"])
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
- env.Prepend(CCFLAGS=["-g2"])
-
- elif env["target"] == "debug":
- env.Prepend(CCFLAGS=["-g3", "-DDEBUG_ENABLED", "-DDEBUG_MEMORY_ENABLED"])
-
- ## Architecture
-
- is64 = sys.maxsize > 2 ** 32
- if env["bits"] == "default":
- env["bits"] = "64" if is64 else "32"
-
- ## Compiler configuration
-
- env["CC"] = "gcc-x86"
- env["CXX"] = "g++-x86"
-
- ## Dependencies
-
- if not env["builtin_libwebp"]:
- env.ParseConfig("pkg-config libwebp --cflags --libs")
-
- # freetype depends on libpng and zlib, so bundling one of them while keeping others
- # as shared libraries leads to weird issues
- if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]:
- env["builtin_freetype"] = True
- env["builtin_libpng"] = True
- env["builtin_zlib"] = True
-
- if not env["builtin_freetype"]:
- env.ParseConfig("pkg-config freetype2 --cflags --libs")
-
- if not env["builtin_libpng"]:
- env.ParseConfig("pkg-config libpng16 --cflags --libs")
-
- if not env["builtin_bullet"]:
- # We need at least version 2.88
- import subprocess
-
- bullet_version = subprocess.check_output(["pkg-config", "bullet", "--modversion"]).strip()
- if bullet_version < "2.88":
- # Abort as system bullet was requested but too old
- print(
- "Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(
- bullet_version, "2.88"
- )
- )
- sys.exit(255)
- env.ParseConfig("pkg-config bullet --cflags --libs")
-
- if not env["builtin_enet"]:
- env.ParseConfig("pkg-config libenet --cflags --libs")
-
- if not env["builtin_squish"]:
- env.ParseConfig("pkg-config libsquish --cflags --libs")
-
- if not env["builtin_zstd"]:
- env.ParseConfig("pkg-config libzstd --cflags --libs")
-
- # Sound and video libraries
- # Keep the order as it triggers chained dependencies (ogg needed by others, etc.)
-
- if not env["builtin_libtheora"]:
- env["builtin_libogg"] = False # Needed to link against system libtheora
- env["builtin_libvorbis"] = False # Needed to link against system libtheora
- env.ParseConfig("pkg-config theora theoradec --cflags --libs")
-
- if not env["builtin_libvpx"]:
- env.ParseConfig("pkg-config vpx --cflags --libs")
-
- if not env["builtin_libvorbis"]:
- env["builtin_libogg"] = False # Needed to link against system libvorbis
- env.ParseConfig("pkg-config vorbis vorbisfile --cflags --libs")
-
- if not env["builtin_opus"]:
- env["builtin_libogg"] = False # Needed to link against system opus
- env.ParseConfig("pkg-config opus opusfile --cflags --libs")
-
- if not env["builtin_libogg"]:
- env.ParseConfig("pkg-config ogg --cflags --libs")
-
- if env["builtin_libtheora"]:
- list_of_x86 = ["x86_64", "x86", "i386", "i586"]
- if any(platform.machine() in s for s in list_of_x86):
- env["x86_libtheora_opt_gcc"] = True
-
- if not env["builtin_wslay"]:
- env.ParseConfig("pkg-config libwslay --cflags --libs")
-
- if not env["builtin_mbedtls"]:
- # mbedTLS does not provide a pkgconfig config yet. See https://github.com/ARMmbed/mbedtls/issues/228
- env.Append(LIBS=["mbedtls", "mbedcrypto", "mbedx509"])
-
- if not env["builtin_miniupnpc"]:
- # No pkgconfig file so far, hardcode default paths.
- env.Prepend(CPPPATH=["/system/develop/headers/x86/miniupnpc"])
- env.Append(LIBS=["miniupnpc"])
-
- # On Linux wchar_t should be 32-bits
- # 16-bit library shouldn't be required due to compiler optimisations
- if not env["builtin_pcre2"]:
- env.ParseConfig("pkg-config libpcre2-32 --cflags --libs")
-
- ## Flags
-
- env.Prepend(CPPPATH=["#platform/haiku"])
- env.Append(CPPDEFINES=["UNIX_ENABLED", "OPENGL_ENABLED", "GLES_ENABLED"])
- env.Append(CPPDEFINES=["MEDIA_KIT_ENABLED"])
- env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"]) # TODO: enable when we have pthread_setname_np
- env.Append(LIBS=["be", "game", "media", "network", "bnetapi", "z", "GL"])
diff --git a/platform/haiku/godot.rdef b/platform/haiku/godot.rdef
deleted file mode 100644
index a55cddbf0d..0000000000
--- a/platform/haiku/godot.rdef
+++ /dev/null
@@ -1,60 +0,0 @@
-resource app_version {
- major = 2,
- middle = 0,
- minor = 0,
-
- variety = B_APPV_FINAL,
- internal = 0,
-
- short_info = "Godot Game Engine",
- long_info = "An advanced, feature packed, multi-platform 2D and 3D game engine."
-};
-
-resource app_signature "application/x-vnd.godot";
-
-resource vector_icon {
- $"6E6369660403A39F9F05FF03478CBF03414042090A04B37FB379CC26B379CC26"
- $"CC20B37FCC200A09B5E9C41B2AC240B8E1BDFBBFA1BDA4C6A7BDFFCA1AC45CC9"
- $"7AC607C01CC75BB6F4C65A062AFE9FFF9F69FE7FFEDFCF0FC95FC3D7C95FC51E"
- $"C95FC51EC95FC53EC92BC565C94AC55BC92BC565C728C60BC728C60BC712C612"
- $"C6E6C600C6F9C60EC6D3C5F2C6C7C5C4C6C7C5DCC6C7C5C4C460C4E5C4BCC4E5"
- $"C626C4E5C626C4E5C64BC4A5C670C4CAC66BC4A5C670C1EDC6CFC1EDC6CFC1E9"
- $"C6CFC1E2C6D0C1E6C6D0C1D1C6D0C1B2C6BEC1BFC6C9C1A2C6AFC19851C198C6"
- $"9BC19851C505C031C507C507C016C507BFFCC507C507BE94C505BE9451BE9451"
- $"BE94C69BBE7BC6BEBE8BC6AFBE6DC6C9BE4AC6D0BE5CC6D0BE47C6D0BE40C6CF"
- $"BE44C6CFBE40C6CFBB87C670BB87C670BB63C66BBB47C626BB47C64BBB47C626"
- $"C4BCB965C460B965C5C4B965C5C4B965C5DCB947C600B95AC5F2B934C60EB904"
- $"C60BB91BC612B904C60BB701C565B701C565B6E3C55BB6CEC51EB6CEC53EB6CE"
- $"C51EC3D7B590C36CB590C36CB581C3B0B578C43AB578C3F5B578C78FBFF8CA27"
- $"BA2ACA22BFF8CA27BFFABFFCCA27BFFCCA27C5CACA22CA7CC43ACA7CC78FCA7C"
- $"C3FBCA67C37ECA754ACA67C37E0639F6F97FFEF8E7FFF9F6FFFFFFFFFF03B67D"
- $"BDEEC31FB730C35CB730C35CB74EC3662BC3A22BC3822BC3A2C4E8B8D1C55EB8"
- $"D1C406B8D1C406B8D1C3F0B8ECC3CDB8DBC3DBB8FDC3BFB929C3BDB913C3B9B9"
- $"29C3BDBB9FC436BB9FC436BBC2C43CBBDCC47EBBDCC45BBBDCC47EC5E6BE00C6"
- $"31BE00C4BBBE00C4BBBE00C4A7BE16C486BE08C494BE24C479BE4AC471BE37C4"
- $"71BE4AC471BE4BC016C473C1E2C471C1E2C471C1F6C471C217C486C209C479C2"
- $"25C494C22DC4BBC22DC4A7C22DC4BBC631C451C5E6C451C47EC451C47EC451C4"
- $"5BC48DC436C46AC43CC48DC436C704C3BDC704C3BDC719C3B9C741C3CDC730C3"
- $"BF53C3DBC75CC406C75CC3F0C75CC406C55EC8CAC4E8C8CAC3A2C8CAC3A2C8CA"
- $"C382C8FDC35CC8DFC366C8FDC35CC977C333BDEEC97ABDEEC97ABDEEC9F1BD56"
- $"CAC9BC0BCA60BCB6CA3DBB1CC8D9B981C991BA47C82FB9D7C6EDBAA0C789BA38"
- $"C69FBA52C5F0B9D0C647BA12C59BB98BC4E0B91FC53BB959C4FBB855C50EB6C0"
- $"C509B78FC424B64AC22DB5C4C32AB5FCC1C8B66DC11BB7D9C16BB725C0BCB7C9"
- $"BFFCB7C2C05CB7C3BFFCB7C2BFFCB7C2BFFCB7C2BFFBB7C2BFFAB7C2BFFAB7C2"
- $"BFF9B7C2BFF8B7C2BFF9B7C2BFF8B7C2BFF8B7C2BFF8B7C2BF98B7C3BED9B7D9"
- $"BF38B7C9BE88B725BDC7B5C4BE2CB66DBCCAB5FCBAE6B6C0BBD0B64ABAEBB78F"
- $"BB13B91F34B855BAB8B959BA04B9D0BA59B98BB9ADBA12B907BAA0B955BA52B8"
- $"6ABA38B71AB981B7C5B9D7B663BA47B52BBC0BB5B7BB1CB594BCB6B679BDEEB6"
- $"02BD56B679BDEE0005BD3EC06CBD3EC06CBD3EC197BB2147BC4C47B9F647B904"
- $"C06CB904C197B904BF41BB21BE4FB9F6BE4FBC4CBE4FBD3EC06CBD3EBF41BD3E"
- $"C06C0005BCBC42BCBC42BCBCC153BB55C1F3BC1BC1F3BA8EC1F3B9ED42B9EDC1"
- $"53B9EDBFC6BB55BF25BA8EBF25BC1BBF25BCBC42BCBCBFC6BCBC420007C01BC2"
- $"BBC01BC2BBBFBAC2BBBF6CC21CBF6CC274BF6CC21CBF6CC02ABF6CC02ABF6CBF"
- $"D3C01BBF8CBFBABF8CC07BBF8CC0C9C02AC0C9BFD3C0C9C02AC0C9C21CC0C9C2"
- $"1CC0C9C274C01BC2BBC07BC2BBC01BC2BB0005C2F7C06CC2F7C06CC2F7C197C5"
- $"1547C3E947C64047C732C06CC732C197C732BF41C515BE4FC640BE4FC3E9BE4F"
- $"C2F7C06CC2F7BF41C2F7C06C0005C37942C37942C379C153C4E1C1F3C41AC1F3"
- $"C5A7C1F3C64842C648C153C648BFC6C4E1BF25C5A7BF25C41ABF25C37942C379"
- $"BFC6C37942090A0000000A010101000A020102000A020103000A010104000A03"
- $"0105000A010106000A010107000A03010800"
-};
diff --git a/platform/haiku/haiku_direct_window.cpp b/platform/haiku/haiku_direct_window.cpp
deleted file mode 100644
index 0a40f847f4..0000000000
--- a/platform/haiku/haiku_direct_window.cpp
+++ /dev/null
@@ -1,363 +0,0 @@
-/*************************************************************************/
-/* haiku_direct_window.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 <UnicodeChar.h>
-
-#include "core/os/keyboard.h"
-#include "haiku_direct_window.h"
-#include "key_mapping_haiku.h"
-#include "main/main.h"
-
-HaikuDirectWindow::HaikuDirectWindow(BRect p_frame) :
- BDirectWindow(p_frame, "Godot", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE) {
- last_mouse_pos_valid = false;
- last_buttons_state = 0;
- last_button_mask = 0;
- last_key_modifier_state = 0;
-
- view = nullptr;
- update_runner = nullptr;
- input = nullptr;
- main_loop = nullptr;
-}
-
-HaikuDirectWindow::~HaikuDirectWindow() {
-}
-
-void HaikuDirectWindow::SetHaikuGLView(HaikuGLView *p_view) {
- view = p_view;
-}
-
-void HaikuDirectWindow::StartMessageRunner() {
- update_runner = new BMessageRunner(BMessenger(this),
- new BMessage(REDRAW_MSG), 1000000 / 60 /* 60 fps */);
-}
-
-void HaikuDirectWindow::StopMessageRunner() {
- delete update_runner;
-}
-
-void HaikuDirectWindow::SetInput(InputDefault *p_input) {
- input = p_input;
-}
-
-void HaikuDirectWindow::SetMainLoop(MainLoop *p_main_loop) {
- main_loop = p_main_loop;
-}
-
-bool HaikuDirectWindow::QuitRequested() {
- StopMessageRunner();
- main_loop->notification(NOTIFICATION_WM_CLOSE_REQUEST);
- return false;
-}
-
-void HaikuDirectWindow::DirectConnected(direct_buffer_info *info) {
- view->DirectConnected(info);
- view->EnableDirectMode(true);
-}
-
-void HaikuDirectWindow::MessageReceived(BMessage *message) {
- switch (message->what) {
- case REDRAW_MSG:
- if (Main::iteration()) {
- view->EnableDirectMode(false);
- Quit();
- }
- break;
-
- default:
- BDirectWindow::MessageReceived(message);
- }
-}
-
-void HaikuDirectWindow::DispatchMessage(BMessage *message, BHandler *handler) {
- switch (message->what) {
- case B_MOUSE_DOWN:
- case B_MOUSE_UP:
- HandleMouseButton(message);
- break;
-
- case B_MOUSE_MOVED:
- HandleMouseMoved(message);
- break;
-
- case B_MOUSE_WHEEL_CHANGED:
- HandleMouseWheelChanged(message);
- break;
-
- case B_KEY_DOWN:
- case B_KEY_UP:
- HandleKeyboardEvent(message);
- break;
-
- case B_MODIFIERS_CHANGED:
- HandleKeyboardModifierEvent(message);
- break;
-
- case B_WINDOW_RESIZED:
- HandleWindowResized(message);
- break;
-
- case LOCKGL_MSG:
- view->LockGL();
- break;
-
- case UNLOCKGL_MSG:
- view->UnlockGL();
- break;
-
- default:
- BDirectWindow::DispatchMessage(message, handler);
- }
-}
-
-void HaikuDirectWindow::HandleMouseButton(BMessage *message) {
- BPoint where;
- if (message->FindPoint("where", &where) != B_OK) {
- return;
- }
-
- uint32 modifiers = message->FindInt32("modifiers");
- uint32 buttons = message->FindInt32("buttons");
- uint32 button = buttons ^ last_buttons_state;
- last_buttons_state = buttons;
-
- // TODO: implement the mouse_mode checks
- /*
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
- event.xbutton.x=last_mouse_pos.x;
- event.xbutton.y=last_mouse_pos.y;
- }
- */
-
- Ref<InputEventMouseButton> mouse_event;
- mouse_event.instance();
-
- mouse_event->set_button_mask(GetMouseButtonState(buttons));
- mouse_event->set_position({ where.x, where.y });
- mouse_event->set_global_position({ where.x, where.y });
- GetKeyModifierState(mouse_event, modifiers);
-
- switch (button) {
- default:
- case B_PRIMARY_MOUSE_BUTTON:
- mouse_event->set_button_index(1);
- break;
-
- case B_SECONDARY_MOUSE_BUTTON:
- mouse_event->set_button_index(2);
- break;
-
- case B_TERTIARY_MOUSE_BUTTON:
- mouse_event->set_button_index(3);
- break;
- }
-
- mouse_event->set_pressed(message->what == B_MOUSE_DOWN);
-
- if (message->what == B_MOUSE_DOWN && mouse_event->get_button_index() == 1) {
- int32 clicks = message->FindInt32("clicks");
-
- if (clicks > 1) {
- mouse_event->set_doubleclick(true);
- }
- }
-
- input->parse_input_event(mouse_event);
-}
-
-void HaikuDirectWindow::HandleMouseMoved(BMessage *message) {
- BPoint where;
- if (message->FindPoint("where", &where) != B_OK) {
- return;
- }
-
- Point2i pos(where.x, where.y);
- uint32 modifiers = message->FindInt32("modifiers");
- uint32 buttons = message->FindInt32("buttons");
-
- if (!last_mouse_pos_valid) {
- last_mouse_position = pos;
- last_mouse_pos_valid = true;
- }
-
- Point2i rel = pos - last_mouse_position;
-
- Ref<InputEventMouseMotion> motion_event;
- motion_event.instance();
- GetKeyModifierState(motion_event, modifiers);
-
- motion_event->set_button_mask(GetMouseButtonState(buttons));
- motion_event->set_position({ pos.x, pos.y });
- input->set_mouse_position(pos);
- motion_event->set_global_position({ pos.x, pos.y });
- motion_event->set_speed({ input->get_last_mouse_speed().x,
- input->get_last_mouse_speed().y });
-
- motion_event->set_relative({ rel.x, rel.y });
-
- last_mouse_position = pos;
-
- input->parse_input_event(motion_event);
-}
-
-void HaikuDirectWindow::HandleMouseWheelChanged(BMessage *message) {
- float wheel_delta_y = 0;
- if (message->FindFloat("be:wheel_delta_y", &wheel_delta_y) != B_OK) {
- return;
- }
-
- Ref<InputEventMouseButton> mouse_event;
- mouse_event.instance();
- //GetKeyModifierState(mouse_event, modifiers);
-
- mouse_event->set_button_index(wheel_delta_y < 0 ? 4 : 5);
- mouse_event->set_button_mask(last_button_mask);
- mouse_event->set_position({ last_mouse_position.x,
- last_mouse_position.y });
- mouse_event->set_global_position({ last_mouse_position.x,
- last_mouse_position.y });
-
- mouse_event->set_pressed(true);
- input->parse_input_event(mouse_event);
-
- mouse_event->set_pressed(false);
- input->parse_input_event(mouse_event);
-}
-
-void HaikuDirectWindow::HandleKeyboardEvent(BMessage *message) {
- int32 raw_char = 0;
- int32 key = 0;
- int32 modifiers = 0;
-
- if (message->FindInt32("raw_char", &raw_char) != B_OK) {
- return;
- }
-
- if (message->FindInt32("key", &key) != B_OK) {
- return;
- }
-
- if (message->FindInt32("modifiers", &modifiers) != B_OK) {
- return;
- }
-
- Ref<InputEventKey> event;
- event.instance();
- GetKeyModifierState(event, modifiers);
- event->set_pressed(message->what == B_KEY_DOWN);
- event->set_keycode(KeyMappingHaiku::get_keysym(raw_char, key));
- event->set_physical_keycode(KeyMappingHaiku::get_keysym(raw_char, key));
- event->set_echo(message->HasInt32("be:key_repeat"));
- event->set_unicode(0);
-
- const char *bytes = nullptr;
- if (message->FindString("bytes", &bytes) == B_OK) {
- event->set_unicode(BUnicodeChar::FromUTF8(&bytes));
- }
-
- //make it consistent across platforms.
- if (event->get_keycode() == KEY_BACKTAB) {
- event->set_keycode(KEY_TAB);
- event->set_physical_keycode(KEY_TAB);
- event->set_shift(true);
- }
-
- input->parse_input_event(event);
-}
-
-void HaikuDirectWindow::HandleKeyboardModifierEvent(BMessage *message) {
- int32 old_modifiers = 0;
- int32 modifiers = 0;
-
- if (message->FindInt32("be:old_modifiers", &old_modifiers) != B_OK) {
- return;
- }
-
- if (message->FindInt32("modifiers", &modifiers) != B_OK) {
- return;
- }
-
- int32 key = old_modifiers ^ modifiers;
-
- Ref<InputEventWithModifiers> event;
- event.instance();
- GetKeyModifierState(event, modifiers);
-
- event->set_shift(key & B_SHIFT_KEY);
- event->set_alt(key & B_OPTION_KEY);
- event->set_control(key & B_CONTROL_KEY);
- event->set_command(key & B_COMMAND_KEY);
-
- input->parse_input_event(event);
-}
-
-void HaikuDirectWindow::HandleWindowResized(BMessage *message) {
- int32 width = 0;
- int32 height = 0;
-
- if ((message->FindInt32("width", &width) != B_OK) || (message->FindInt32("height", &height) != B_OK)) {
- return;
- }
-
- current_video_mode->width = width;
- current_video_mode->height = height;
-}
-
-inline void HaikuDirectWindow::GetKeyModifierState(Ref<InputEventWithModifiers> event, uint32 p_state) {
- last_key_modifier_state = p_state;
-
- event->set_shift(p_state & B_SHIFT_KEY);
- event->set_control(p_state & B_CONTROL_KEY);
- event->set_alt(p_state & B_OPTION_KEY);
- event->set_metakey(p_state & B_COMMAND_KEY);
-
- return state;
-}
-
-inline int HaikuDirectWindow::GetMouseButtonState(uint32 p_state) {
- int state = 0;
-
- if (p_state & B_PRIMARY_MOUSE_BUTTON) {
- state |= 1 << 0;
- }
-
- if (p_state & B_SECONDARY_MOUSE_BUTTON) {
- state |= 1 << 1;
- }
-
- if (p_state & B_TERTIARY_MOUSE_BUTTON) {
- state |= 1 << 2;
- }
-
- last_button_mask = state;
-
- return state;
-}
diff --git a/platform/haiku/haiku_direct_window.h b/platform/haiku/haiku_direct_window.h
deleted file mode 100644
index 4817abbb7a..0000000000
--- a/platform/haiku/haiku_direct_window.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*************************************************************************/
-/* haiku_direct_window.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#ifndef HAIKU_DIRECT_WINDOW_H
-#define HAIKU_DIRECT_WINDOW_H
-
-#include <kernel/image.h> // needed for image_id
-
-#include <DirectWindow.h>
-
-#include "core/input/input.h"
-#include "core/os/os.h"
-
-#include "haiku_gl_view.h"
-
-#define REDRAW_MSG 'rdrw'
-#define LOCKGL_MSG 'glck'
-#define UNLOCKGL_MSG 'ulck'
-
-class HaikuDirectWindow : public BDirectWindow {
-private:
- Point2i last_mouse_position;
- bool last_mouse_pos_valid;
- uint32 last_buttons_state;
- uint32 last_key_modifier_state;
- int last_button_mask;
- OS::VideoMode *current_video_mode;
-
- MainLoop *main_loop;
- InputDefault *input;
- HaikuGLView *view;
- BMessageRunner *update_runner;
-
- void HandleMouseButton(BMessage *message);
- void HandleMouseMoved(BMessage *message);
- void HandleMouseWheelChanged(BMessage *message);
- void HandleWindowResized(BMessage *message);
- void HandleKeyboardEvent(BMessage *message);
- void HandleKeyboardModifierEvent(BMessage *message);
- inline void GetKeyModifierState(Ref<InputEventWithModifiers> event, uint32 p_state);
- inline int GetMouseButtonState(uint32 p_state);
-
-public:
- HaikuDirectWindow(BRect p_frame);
- ~HaikuDirectWindow();
-
- void SetHaikuGLView(HaikuGLView *p_view);
- void StartMessageRunner();
- void StopMessageRunner();
- void SetInput(InputDefault *p_input);
- void SetMainLoop(MainLoop *p_main_loop);
- inline void SetVideoMode(OS::VideoMode *video_mode) { current_video_mode = video_mode; };
- virtual bool QuitRequested();
- virtual void DirectConnected(direct_buffer_info *info);
- virtual void MessageReceived(BMessage *message);
- virtual void DispatchMessage(BMessage *message, BHandler *handler);
-
- inline Point2i GetLastMousePosition() { return last_mouse_position; };
- inline int GetLastButtonMask() { return last_button_mask; };
-};
-
-#endif
diff --git a/platform/haiku/haiku_gl_view.cpp b/platform/haiku/haiku_gl_view.cpp
deleted file mode 100644
index 970a1276fd..0000000000
--- a/platform/haiku/haiku_gl_view.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*************************************************************************/
-/* haiku_gl_view.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "haiku_gl_view.h"
-#include "main/main.h"
-
-HaikuGLView::HaikuGLView(BRect frame, uint32 type) :
- BGLView(frame, "GodotGLView", B_FOLLOW_ALL_SIDES, 0, type) {
-}
-
-void HaikuGLView::AttachedToWindow(void) {
- LockGL();
- BGLView::AttachedToWindow();
- UnlockGL();
- MakeFocus();
-}
-
-void HaikuGLView::Draw(BRect updateRect) {
- Main::force_redraw();
-}
diff --git a/platform/haiku/key_mapping_haiku.cpp b/platform/haiku/key_mapping_haiku.cpp
deleted file mode 100644
index 692a1e5a78..0000000000
--- a/platform/haiku/key_mapping_haiku.cpp
+++ /dev/null
@@ -1,249 +0,0 @@
-/*************************************************************************/
-/* key_mapping_haiku.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 <InterfaceDefs.h>
-
-#include "core/os/keyboard.h"
-#include "key_mapping_haiku.h"
-
-struct _HaikuTranslatePair {
- unsigned int keysym;
- int32 keycode;
-};
-
-static _HaikuTranslatePair _mod_to_keycode[] = {
- { KEY_SHIFT, B_SHIFT_KEY },
- { KEY_ALT, B_COMMAND_KEY },
- { KEY_CONTROL, B_CONTROL_KEY },
- { KEY_CAPSLOCK, B_CAPS_LOCK },
- { KEY_SCROLLLOCK, B_SCROLL_LOCK },
- { KEY_NUMLOCK, B_NUM_LOCK },
- { KEY_SUPER_L, B_OPTION_KEY },
- { KEY_MENU, B_MENU_KEY },
- { KEY_SHIFT, B_LEFT_SHIFT_KEY },
- { KEY_SHIFT, B_RIGHT_SHIFT_KEY },
- { KEY_ALT, B_LEFT_COMMAND_KEY },
- { KEY_ALT, B_RIGHT_COMMAND_KEY },
- { KEY_CONTROL, B_LEFT_CONTROL_KEY },
- { KEY_CONTROL, B_RIGHT_CONTROL_KEY },
- { KEY_SUPER_L, B_LEFT_OPTION_KEY },
- { KEY_SUPER_R, B_RIGHT_OPTION_KEY },
- { KEY_UNKNOWN, 0 }
-};
-
-static _HaikuTranslatePair _fn_to_keycode[] = {
- { KEY_F1, B_F1_KEY },
- { KEY_F2, B_F2_KEY },
- { KEY_F3, B_F3_KEY },
- { KEY_F4, B_F4_KEY },
- { KEY_F5, B_F5_KEY },
- { KEY_F6, B_F6_KEY },
- { KEY_F7, B_F7_KEY },
- { KEY_F8, B_F8_KEY },
- { KEY_F9, B_F9_KEY },
- { KEY_F10, B_F10_KEY },
- { KEY_F11, B_F11_KEY },
- { KEY_F12, B_F12_KEY },
- //{ KEY_F13, ? },
- //{ KEY_F14, ? },
- //{ KEY_F15, ? },
- //{ KEY_F16, ? },
- { KEY_PRINT, B_PRINT_KEY },
- { KEY_SCROLLLOCK, B_SCROLL_KEY },
- { KEY_PAUSE, B_PAUSE_KEY },
- { KEY_UNKNOWN, 0 }
-};
-
-static _HaikuTranslatePair _hb_to_keycode[] = {
- { KEY_BACKSPACE, B_BACKSPACE },
- { KEY_TAB, B_TAB },
- { KEY_ENTER, B_RETURN },
- { KEY_CAPSLOCK, B_CAPS_LOCK },
- { KEY_ESCAPE, B_ESCAPE },
- { KEY_SPACE, B_SPACE },
- { KEY_PAGEUP, B_PAGE_UP },
- { KEY_PAGEDOWN, B_PAGE_DOWN },
- { KEY_END, B_END },
- { KEY_HOME, B_HOME },
- { KEY_LEFT, B_LEFT_ARROW },
- { KEY_UP, B_UP_ARROW },
- { KEY_RIGHT, B_RIGHT_ARROW },
- { KEY_DOWN, B_DOWN_ARROW },
- { KEY_PRINT, B_PRINT_KEY },
- { KEY_INSERT, B_INSERT },
- { KEY_DELETE, B_DELETE },
- // { KEY_HELP, ??? },
-
- { KEY_0, (0x30) },
- { KEY_1, (0x31) },
- { KEY_2, (0x32) },
- { KEY_3, (0x33) },
- { KEY_4, (0x34) },
- { KEY_5, (0x35) },
- { KEY_6, (0x36) },
- { KEY_7, (0x37) },
- { KEY_8, (0x38) },
- { KEY_9, (0x39) },
- { KEY_A, (0x61) },
- { KEY_B, (0x62) },
- { KEY_C, (0x63) },
- { KEY_D, (0x64) },
- { KEY_E, (0x65) },
- { KEY_F, (0x66) },
- { KEY_G, (0x67) },
- { KEY_H, (0x68) },
- { KEY_I, (0x69) },
- { KEY_J, (0x6A) },
- { KEY_K, (0x6B) },
- { KEY_L, (0x6C) },
- { KEY_M, (0x6D) },
- { KEY_N, (0x6E) },
- { KEY_O, (0x6F) },
- { KEY_P, (0x70) },
- { KEY_Q, (0x71) },
- { KEY_R, (0x72) },
- { KEY_S, (0x73) },
- { KEY_T, (0x74) },
- { KEY_U, (0x75) },
- { KEY_V, (0x76) },
- { KEY_W, (0x77) },
- { KEY_X, (0x78) },
- { KEY_Y, (0x79) },
- { KEY_Z, (0x7A) },
-
- /*
-{ KEY_PLAY, VK_PLAY},// (0xFA)
-{ KEY_STANDBY,VK_SLEEP },//(0x5F)
-{ KEY_BACK,VK_BROWSER_BACK},// (0xA6)
-{ KEY_FORWARD,VK_BROWSER_FORWARD},// (0xA7)
-{ KEY_REFRESH,VK_BROWSER_REFRESH},// (0xA8)
-{ KEY_STOP,VK_BROWSER_STOP},// (0xA9)
-{ KEY_SEARCH,VK_BROWSER_SEARCH},// (0xAA)
-{ KEY_FAVORITES, VK_BROWSER_FAVORITES},// (0xAB)
-{ KEY_HOMEPAGE,VK_BROWSER_HOME},// (0xAC)
-{ KEY_VOLUMEMUTE,VK_VOLUME_MUTE},// (0xAD)
-{ KEY_VOLUMEDOWN,VK_VOLUME_DOWN},// (0xAE)
-{ KEY_VOLUMEUP,VK_VOLUME_UP},// (0xAF)
-{ KEY_MEDIANEXT,VK_MEDIA_NEXT_TRACK},// (0xB0)
-{ KEY_MEDIAPREVIOUS,VK_MEDIA_PREV_TRACK},// (0xB1)
-{ KEY_MEDIASTOP,VK_MEDIA_STOP},// (0xB2)
-{ KEY_LAUNCHMAIL, VK_LAUNCH_MAIL},// (0xB4)
-{ KEY_LAUNCHMEDIA,VK_LAUNCH_MEDIA_SELECT},// (0xB5)
-{ KEY_LAUNCH0,VK_LAUNCH_APP1},// (0xB6)
-{ KEY_LAUNCH1,VK_LAUNCH_APP2},// (0xB7)
-*/
-
- { KEY_SEMICOLON, 0x3B },
- { KEY_EQUAL, 0x3D },
- { KEY_COLON, 0x2C },
- { KEY_MINUS, 0x2D },
- { KEY_PERIOD, 0x2E },
- { KEY_SLASH, 0x2F },
- { KEY_KP_MULTIPLY, 0x2A },
- { KEY_KP_ADD, 0x2B },
-
- { KEY_QUOTELEFT, 0x60 },
- { KEY_BRACKETLEFT, 0x5B },
- { KEY_BACKSLASH, 0x5C },
- { KEY_BRACKETRIGHT, 0x5D },
- { KEY_APOSTROPHE, 0x27 },
-
- { KEY_UNKNOWN, 0 }
-};
-
-unsigned int KeyMappingHaiku::get_keysym(int32 raw_char, int32 key) {
- if (raw_char == B_INSERT && key == 0x64) {
- return KEY_KP_0;
- }
- if (raw_char == B_END && key == 0x58) {
- return KEY_KP_1;
- }
- if (raw_char == B_DOWN_ARROW && key == 0x59) {
- return KEY_KP_2;
- }
- if (raw_char == B_PAGE_DOWN && key == 0x5A) {
- return KEY_KP_3;
- }
- if (raw_char == B_LEFT_ARROW && key == 0x48) {
- return KEY_KP_4;
- }
- if (raw_char == 0x35 && key == 0x49) {
- return KEY_KP_5;
- }
- if (raw_char == B_RIGHT_ARROW && key == 0x4A) {
- return KEY_KP_6;
- }
- if (raw_char == B_HOME && key == 0x37) {
- return KEY_KP_7;
- }
- if (raw_char == B_UP_ARROW && key == 0x38) {
- return KEY_KP_8;
- }
- if (raw_char == B_PAGE_UP && key == 0x39) {
- return KEY_KP_9;
- }
- if (raw_char == 0x2F && key == 0x23) {
- return KEY_KP_DIVIDE;
- }
- if (raw_char == 0x2D && key == 0x25) {
- return KEY_KP_SUBTRACT;
- }
- if (raw_char == B_DELETE && key == 0x65) {
- return KEY_KP_PERIOD;
- }
-
- if (raw_char == 0x10) {
- for (int i = 0; _fn_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
- if (_fn_to_keycode[i].keycode == key) {
- return _fn_to_keycode[i].keysym;
- }
- }
-
- return KEY_UNKNOWN;
- }
-
- for (int i = 0; _hb_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
- if (_hb_to_keycode[i].keycode == raw_char) {
- return _hb_to_keycode[i].keysym;
- }
- }
-
- return KEY_UNKNOWN;
-}
-
-unsigned int KeyMappingHaiku::get_modifier_keysym(int32 key) {
- for (int i = 0; _mod_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
- if ((_mod_to_keycode[i].keycode & key) != 0) {
- return _mod_to_keycode[i].keysym;
- }
- }
-
- return KEY_UNKNOWN;
-}
diff --git a/platform/haiku/logo.png b/platform/haiku/logo.png
deleted file mode 100644
index a2d8e242a6..0000000000
--- a/platform/haiku/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp
deleted file mode 100644
index 7a2591784f..0000000000
--- a/platform/haiku/os_haiku.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*************************************************************************/
-/* os_haiku.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "os_haiku.h"
-
-#include "drivers/gles2/rasterizer_gles2.h"
-#include "main/main.h"
-#include "servers/physics_3d/physics_server_3d_sw.h"
-#include "servers/rendering/rendering_server_raster.h"
-#include "servers/rendering/rendering_server_wrap_mt.h"
-
-#include <Screen.h>
-
-OS_Haiku::OS_Haiku() {
-#ifdef MEDIA_KIT_ENABLED
- AudioDriverManager::add_driver(&driver_media_kit);
-#endif
-};
-
-void OS_Haiku::run() {
- if (!main_loop) {
- return;
- }
-
- main_loop->init();
- context_gl->release_current();
-
- // TODO: clean up
- BMessenger *bms = new BMessenger(window);
- BMessage *msg = new BMessage();
- bms->SendMessage(LOCKGL_MSG, msg);
-
- window->StartMessageRunner();
- app->Run();
- window->StopMessageRunner();
-
- delete app;
-
- delete bms;
- delete msg;
- main_loop->finish();
-}
-
-String OS_Haiku::get_name() const {
- return "Haiku";
-}
-
-int OS_Haiku::get_video_driver_count() const {
- return 1;
-}
-
-const char *OS_Haiku::get_video_driver_name(int p_driver) const {
- return "GLES2";
-}
-
-int OS_Haiku::get_current_video_driver() const {
- return video_driver_index;
-}
-
-Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
- main_loop = nullptr;
- current_video_mode = p_desired;
-
- app = new HaikuApplication();
-
- BRect frame;
- frame.Set(50, 50, 50 + current_video_mode.width - 1, 50 + current_video_mode.height - 1);
-
- window = new HaikuDirectWindow(frame);
- window->SetVideoMode(&current_video_mode);
-
- if (current_video_mode.fullscreen) {
- window->SetFullScreen(true);
- }
-
- if (!current_video_mode.resizable) {
- uint32 flags = window->Flags();
- flags |= B_NOT_RESIZABLE;
- window->SetFlags(flags);
- }
-
-#if defined(OPENGL_ENABLED)
- context_gl = memnew(ContextGL_Haiku(window));
- context_gl->initialize();
- context_gl->make_current();
- context_gl->set_use_vsync(current_video_mode.use_vsync);
- // FIXME: That's not how the rasterizer setup should happen.
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
-#endif
-
- rendering_server = memnew(RenderingServerRaster);
- // FIXME: Reimplement threaded rendering
- if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- rendering_server = memnew(RenderingServerWrapMT(rendering_server, false));
- }
-
- ERR_FAIL_COND_V(!rendering_server, ERR_UNAVAILABLE);
-
- video_driver_index = p_video_driver;
-
- input = memnew(InputDefault);
- window->SetInput(input);
-
- window->Show();
- rendering_server->init();
-
- AudioDriverManager::initialize(p_audio_driver);
-
- return OK;
-}
-
-void OS_Haiku::finalize() {
- if (main_loop) {
- memdelete(main_loop);
- }
-
- main_loop = nullptr;
-
- rendering_server->finish();
- memdelete(rendering_server);
-
- memdelete(input);
-
-#if defined(OPENGL_ENABLED)
- memdelete(context_gl);
-#endif
-}
-
-void OS_Haiku::set_main_loop(MainLoop *p_main_loop) {
- main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
- window->SetMainLoop(p_main_loop);
-}
-
-MainLoop *OS_Haiku::get_main_loop() const {
- return main_loop;
-}
-
-void OS_Haiku::delete_main_loop() {
- if (main_loop) {
- memdelete(main_loop);
- }
-
- main_loop = nullptr;
- window->SetMainLoop(nullptr);
-}
-
-void OS_Haiku::release_rendering_thread() {
- context_gl->release_current();
-}
-
-void OS_Haiku::make_rendering_thread() {
- context_gl->make_current();
-}
-
-bool OS_Haiku::can_draw() const {
- // TODO: implement
- return true;
-}
-
-void OS_Haiku::swap_buffers() {
- context_gl->swap_buffers();
-}
-
-Point2 OS_Haiku::get_mouse_position() const {
- return window->GetLastMousePosition();
-}
-
-int OS_Haiku::get_mouse_button_state() const {
- return window->GetLastButtonMask();
-}
-
-void OS_Haiku::set_cursor_shape(CursorShape p_shape) {
- //ERR_PRINT("set_cursor_shape() NOT IMPLEMENTED");
-}
-
-OS::CursorShape OS_Haiku::get_cursor_shape() const {
- // TODO: implement get_cursor_shape
-}
-
-void OS_Haiku::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
- // TODO
-}
-
-int OS_Haiku::get_screen_count() const {
- // TODO: implement get_screen_count()
- return 1;
-}
-
-int OS_Haiku::get_current_screen() const {
- // TODO: implement get_current_screen()
- return 0;
-}
-
-void OS_Haiku::set_current_screen(int p_screen) {
- // TODO: implement set_current_screen()
-}
-
-Point2 OS_Haiku::get_screen_position(int p_screen) const {
- // TODO: make this work with the p_screen parameter
- BScreen *screen = new BScreen(window);
- BRect frame = screen->Frame();
- delete screen;
- return Point2i(frame.left, frame.top);
-}
-
-Size2 OS_Haiku::get_screen_size(int p_screen) const {
- // TODO: make this work with the p_screen parameter
- BScreen *screen = new BScreen(window);
- BRect frame = screen->Frame();
- delete screen;
- return Size2i(frame.IntegerWidth() + 1, frame.IntegerHeight() + 1);
-}
-
-void OS_Haiku::set_window_title(const String &p_title) {
- window->SetTitle(p_title.utf8().get_data());
-}
-
-Size2 OS_Haiku::get_window_size() const {
- BSize size = window->Size();
- return Size2i(size.IntegerWidth() + 1, size.IntegerHeight() + 1);
-}
-
-void OS_Haiku::set_window_size(const Size2 p_size) {
- // TODO: why does it stop redrawing after this is called?
- window->ResizeTo(p_size.x, p_size.y);
-}
-
-Point2 OS_Haiku::get_window_position() const {
- BPoint point(0, 0);
- window->ConvertToScreen(&point);
- return Point2i(point.x, point.y);
-}
-
-void OS_Haiku::set_window_position(const Point2 &p_position) {
- window->MoveTo(p_position.x, p_position.y);
-}
-
-void OS_Haiku::set_window_fullscreen(bool p_enabled) {
- window->SetFullScreen(p_enabled);
- current_video_mode.fullscreen = p_enabled;
- rendering_server->init();
-}
-
-bool OS_Haiku::is_window_fullscreen() const {
- return current_video_mode.fullscreen;
-}
-
-void OS_Haiku::set_window_resizable(bool p_enabled) {
- uint32 flags = window->Flags();
-
- if (p_enabled) {
- flags &= ~(B_NOT_RESIZABLE);
- } else {
- flags |= B_NOT_RESIZABLE;
- }
-
- window->SetFlags(flags);
- current_video_mode.resizable = p_enabled;
-}
-
-bool OS_Haiku::is_window_resizable() const {
- return current_video_mode.resizable;
-}
-
-void OS_Haiku::set_window_minimized(bool p_enabled) {
- window->Minimize(p_enabled);
-}
-
-bool OS_Haiku::is_window_minimized() const {
- return window->IsMinimized();
-}
-
-void OS_Haiku::set_window_maximized(bool p_enabled) {
- window->Minimize(!p_enabled);
-}
-
-bool OS_Haiku::is_window_maximized() const {
- return !window->IsMinimized();
-}
-
-void OS_Haiku::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
- ERR_PRINT("set_video_mode() NOT IMPLEMENTED");
-}
-
-OS::VideoMode OS_Haiku::get_video_mode(int p_screen) const {
- return current_video_mode;
-}
-
-void OS_Haiku::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
- ERR_PRINT("get_fullscreen_mode_list() NOT IMPLEMENTED");
-}
-
-String OS_Haiku::get_executable_path() const {
- return OS::get_executable_path();
-}
-
-bool OS_Haiku::_check_internal_feature_support(const String &p_feature) {
- return p_feature == "pc";
-}
-
-String OS_Haiku::get_config_path() const {
- if (has_environment("XDG_CONFIG_HOME")) {
- return get_environment("XDG_CONFIG_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file("config/settings");
- } else {
- return ".";
- }
-}
-
-String OS_Haiku::get_data_path() const {
- if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file("config/data");
- } else {
- return get_config_path();
- }
-}
-
-String OS_Haiku::get_cache_path() const {
- if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file("config/cache");
- } else {
- return get_config_path();
- }
-}
diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h
deleted file mode 100644
index d3ef9400d4..0000000000
--- a/platform/haiku/os_haiku.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*************************************************************************/
-/* os_haiku.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#ifndef OS_HAIKU_H
-#define OS_HAIKU_H
-
-#include "audio_driver_media_kit.h"
-#include "context_gl_haiku.h"
-#include "core/input/input.h"
-#include "drivers/unix/os_unix.h"
-#include "haiku_application.h"
-#include "haiku_direct_window.h"
-#include "servers/audio_server.h"
-#include "servers/rendering_server.h"
-
-class OS_Haiku : public OS_Unix {
-private:
- HaikuApplication *app;
- HaikuDirectWindow *window;
- MainLoop *main_loop;
- InputDefault *input;
- RenderingServer *rendering_server;
- VideoMode current_video_mode;
- int video_driver_index;
-
-#ifdef MEDIA_KIT_ENABLED
- AudioDriverMediaKit driver_media_kit;
-#endif
-
-#if defined(OPENGL_ENABLED)
- ContextGL_Haiku *context_gl;
-#endif
-
- virtual void delete_main_loop();
-
-protected:
- virtual int get_video_driver_count() const;
- virtual const char *get_video_driver_name(int p_driver) const;
- virtual int get_current_video_driver() const;
-
- virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
- virtual void finalize();
-
- virtual void set_main_loop(MainLoop *p_main_loop);
-
-public:
- OS_Haiku();
- void run();
-
- virtual String get_name() const;
-
- virtual MainLoop *get_main_loop() const;
-
- virtual bool can_draw() const;
- virtual void release_rendering_thread();
- virtual void make_rendering_thread();
- virtual void swap_buffers();
-
- virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
- virtual void set_cursor_shape(CursorShape p_shape);
- virtual CursorShape get_cursor_shape() const;
- virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
-
- virtual int get_screen_count() const;
- virtual int get_current_screen() const;
- virtual void set_current_screen(int p_screen);
- virtual Point2 get_screen_position(int p_screen = -1) const;
- virtual Size2 get_screen_size(int p_screen = -1) const;
- virtual void set_window_title(const String &p_title);
- virtual Size2 get_window_size() const;
- virtual void set_window_size(const Size2 p_size);
- virtual Point2 get_window_position() const;
- virtual void set_window_position(const Point2 &p_position);
- virtual void set_window_fullscreen(bool p_enabled);
- virtual bool is_window_fullscreen() const;
- virtual void set_window_resizable(bool p_enabled);
- virtual bool is_window_resizable() const;
- virtual void set_window_minimized(bool p_enabled);
- virtual bool is_window_minimized() const;
- virtual void set_window_maximized(bool p_enabled);
- virtual bool is_window_maximized() const;
-
- virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0);
- virtual VideoMode get_video_mode(int p_screen = 0) const;
- virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const;
- virtual String get_executable_path() const;
-
- virtual bool _check_internal_feature_support(const String &p_feature);
-
- virtual String get_config_path() const;
- virtual String get_data_path() const;
- virtual String get_cache_path() const;
-};
-
-#endif
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index a48629f720..ee7b2f4ab5 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -3,23 +3,31 @@
Import("env")
iphone_lib = [
- "godot_iphone.cpp",
- "os_iphone.cpp",
- "semaphore_iphone.cpp",
- "gl_view.mm",
+ "godot_iphone.mm",
+ "os_iphone.mm",
"main.m",
"app_delegate.mm",
"view_controller.mm",
- "game_center.mm",
- "in_app_store.mm",
- "icloud.mm",
"ios.mm",
"vulkan_context_iphone.mm",
+ "display_server_iphone.mm",
+ "joypad_iphone.mm",
+ "godot_view.mm",
+ "display_layer.mm",
+ "godot_app_delegate.m",
+ "godot_view_renderer.mm",
+ "godot_view_gesture_recognizer.mm",
+ "device_metrics.m",
+ "keyboard_input_view.mm",
+ "native_video_view.m",
]
env_ios = env.Clone()
ios_lib = env_ios.add_library("iphone", iphone_lib)
+# (iOS) Enable module support
+env_ios.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
def combine_libs(target=None, source=None, env=None):
lib_path = target[0].srcnode().abspath
diff --git a/platform/iphone/app_delegate.h b/platform/iphone/app_delegate.h
index 27552d781a..d6a2292dd2 100644
--- a/platform/iphone/app_delegate.h
+++ b/platform/iphone/app_delegate.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,29 +28,20 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#if defined(OPENGL_ENABLED)
-#import "gl_view.h"
-#endif
-#import "view_controller.h"
#import <UIKit/UIKit.h>
-#import <CoreMotion/CoreMotion.h>
+@class ViewController;
// FIXME: Add support for both GLES2 and Vulkan when GLES2 is implemented again,
// so it can't be done with compilation time branching.
//#if defined(OPENGL_ENABLED)
//@interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> {
//#endif
-#if defined(VULKAN_ENABLED)
-@interface AppDelegate : NSObject <UIApplicationDelegate> {
-#endif
- //@property (strong, nonatomic) UIWindow *window;
- ViewController *view_controller;
- bool is_focus_out;
-};
+//#if defined(VULKAN_ENABLED)
+@interface AppDelegate : NSObject <UIApplicationDelegate>
+//#endif
@property(strong, nonatomic) UIWindow *window;
-
-+ (ViewController *)getViewController;
+@property(strong, class, readonly, nonatomic) ViewController *viewController;
@end
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index c4ef185bf1..d10ea5c68c 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,644 +29,61 @@
/*************************************************************************/
#import "app_delegate.h"
-
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "drivers/coreaudio/audio_driver_coreaudio.h"
-#if defined(OPENGL_ENABLED)
-#import "gl_view.h"
-#endif
+#import "godot_view.h"
#include "main/main.h"
#include "os_iphone.h"
+#import "view_controller.h"
-#import "GameController/GameController.h"
+#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioServices.h>
-#define kFilteringFactor 0.1
#define kRenderingFrequency 60
-#define kAccelerometerFrequency 100.0 // Hz
-
-Error _shell_open(String);
-void _set_keep_screen_on(bool p_enabled);
-
-Error _shell_open(String p_uri) {
- NSString *url = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()];
-
- if (![[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:url]])
- return ERR_CANT_OPEN;
-
- printf("opening url %ls\n", p_uri.c_str());
- [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
- [url release];
- return OK;
-};
-
-void _set_keep_screen_on(bool p_enabled) {
- [[UIApplication sharedApplication] setIdleTimerDisabled:(BOOL)p_enabled];
-};
-
-void _vibrate() {
- AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
-};
-
-@implementation AppDelegate
-
-@synthesize window;
extern int gargc;
extern char **gargv;
-extern int iphone_main(int, int, int, char **, String);
+
+extern int iphone_main(int, char **, String);
extern void iphone_finish();
-CMMotionManager *motionManager;
-bool motionInitialised;
+@implementation AppDelegate
static ViewController *mainViewController = nil;
-+ (ViewController *)getViewController {
- return mainViewController;
-}
-
-NSMutableDictionary *ios_joysticks = nil;
-NSMutableArray *pending_ios_joysticks = nil;
-
-- (GCControllerPlayerIndex)getFreePlayerIndex {
- bool have_player_1 = false;
- bool have_player_2 = false;
- bool have_player_3 = false;
- bool have_player_4 = false;
-
- if (ios_joysticks == nil) {
- NSArray *keys = [ios_joysticks allKeys];
- for (NSNumber *key in keys) {
- GCController *controller = [ios_joysticks objectForKey:key];
- if (controller.playerIndex == GCControllerPlayerIndex1) {
- have_player_1 = true;
- } else if (controller.playerIndex == GCControllerPlayerIndex2) {
- have_player_2 = true;
- } else if (controller.playerIndex == GCControllerPlayerIndex3) {
- have_player_3 = true;
- } else if (controller.playerIndex == GCControllerPlayerIndex4) {
- have_player_4 = true;
- };
- };
- };
-
- if (!have_player_1) {
- return GCControllerPlayerIndex1;
- } else if (!have_player_2) {
- return GCControllerPlayerIndex2;
- } else if (!have_player_3) {
- return GCControllerPlayerIndex3;
- } else if (!have_player_4) {
- return GCControllerPlayerIndex4;
- } else {
- return GCControllerPlayerIndexUnset;
- };
-};
-
-void _ios_add_joystick(GCController *controller, AppDelegate *delegate) {
- // get a new id for our controller
- int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
- if (joy_id != -1) {
- // assign our player index
- if (controller.playerIndex == GCControllerPlayerIndexUnset) {
- controller.playerIndex = [delegate getFreePlayerIndex];
- };
-
- // tell Godot about our new controller
- OSIPhone::get_singleton()->joy_connection_changed(
- joy_id, true, [controller.vendorName UTF8String]);
-
- // add it to our dictionary, this will retain our controllers
- [ios_joysticks setObject:controller
- forKey:[NSNumber numberWithInt:joy_id]];
-
- // set our input handler
- [delegate setControllerInputHandler:controller];
- } else {
- printf("Couldn't retrieve new joy id\n");
- };
-}
-
-static void on_focus_out(ViewController *view_controller, bool *is_focus_out) {
- if (!*is_focus_out) {
- *is_focus_out = true;
- if (OS::get_singleton()->get_main_loop())
- OS::get_singleton()->get_main_loop()->notification(
- MainLoop::NOTIFICATION_WM_FOCUS_OUT);
-
- [view_controller.view stopAnimation];
- if (OS::get_singleton()->native_video_is_playing()) {
- OSIPhone::get_singleton()->native_video_focus_out();
- }
- AudioDriverCoreAudio *audio = dynamic_cast<AudioDriverCoreAudio *>(AudioDriverCoreAudio::get_singleton());
- if (audio)
- audio->stop();
- }
-}
-
-static void on_focus_in(ViewController *view_controller, bool *is_focus_out) {
- if (*is_focus_out) {
- *is_focus_out = false;
- if (OS::get_singleton()->get_main_loop())
- OS::get_singleton()->get_main_loop()->notification(
- MainLoop::NOTIFICATION_WM_FOCUS_IN);
-
- [view_controller.view startAnimation];
- if (OSIPhone::get_singleton()->native_video_is_playing()) {
- OSIPhone::get_singleton()->native_video_unpause();
- }
-
- AudioDriverCoreAudio *audio = dynamic_cast<AudioDriverCoreAudio *>(AudioDriverCoreAudio::get_singleton());
- if (audio)
- audio->start();
- }
++ (ViewController *)viewController {
+ return mainViewController;
}
-- (void)controllerWasConnected:(NSNotification *)notification {
- // create our dictionary if we don't have one yet
- if (ios_joysticks == nil) {
- ios_joysticks = [[NSMutableDictionary alloc] init];
- };
-
- // get our controller
- GCController *controller = (GCController *)notification.object;
- if (controller == nil) {
- printf("Couldn't retrieve new controller\n");
- } else if ([[ios_joysticks allKeysForObject:controller] count] != 0) {
- printf("Controller is already registered\n");
- } else if (frame_count > 1) {
- _ios_add_joystick(controller, self);
- } else {
- if (pending_ios_joysticks == nil)
- pending_ios_joysticks = [[NSMutableArray alloc] init];
- [pending_ios_joysticks addObject:controller];
- };
-};
-
-- (void)controllerWasDisconnected:(NSNotification *)notification {
- if (ios_joysticks != nil) {
- // find our joystick, there should be only one in our dictionary
- GCController *controller = (GCController *)notification.object;
- NSArray *keys = [ios_joysticks allKeysForObject:controller];
- for (NSNumber *key in keys) {
- // tell Godot this joystick is no longer there
- int joy_id = [key intValue];
- OSIPhone::get_singleton()->joy_connection_changed(joy_id, false, "");
-
- // and remove it from our dictionary
- [ios_joysticks removeObjectForKey:key];
- };
- };
-};
-
-- (int)getJoyIdForController:(GCController *)controller {
- if (ios_joysticks != nil) {
- // find our joystick, there should be only one in our dictionary
- NSArray *keys = [ios_joysticks allKeysForObject:controller];
- for (NSNumber *key in keys) {
- int joy_id = [key intValue];
- return joy_id;
- };
- };
-
- return -1;
-};
-
-- (void)setControllerInputHandler:(GCController *)controller {
- // Hook in the callback handler for the correct gamepad profile.
- // This is a bit of a weird design choice on Apples part.
- // You need to select the most capable gamepad profile for the
- // gamepad attached.
- if (controller.extendedGamepad != nil) {
- // The extended gamepad profile has all the input you could possibly find on
- // a gamepad but will only be active if your gamepad actually has all of
- // these...
- controller.extendedGamepad.valueChangedHandler = ^(
- GCExtendedGamepad *gamepad, GCControllerElement *element) {
- int joy_id = [self getJoyIdForController:controller];
-
- if (element == gamepad.buttonA) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
- gamepad.buttonA.isPressed);
- } else if (element == gamepad.buttonB) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_B,
- gamepad.buttonB.isPressed);
- } else if (element == gamepad.buttonX) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
- gamepad.buttonX.isPressed);
- } else if (element == gamepad.buttonY) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_Y,
- gamepad.buttonY.isPressed);
- } else if (element == gamepad.leftShoulder) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_LEFT_SHOULDER,
- gamepad.leftShoulder.isPressed);
- } else if (element == gamepad.rightShoulder) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
- gamepad.rightShoulder.isPressed);
- } else if (element == gamepad.dpad) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
- gamepad.dpad.up.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
- gamepad.dpad.down.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
- gamepad.dpad.left.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
- gamepad.dpad.right.isPressed);
- };
-
- InputDefault::JoyAxis jx;
- jx.min = -1;
- if (element == gamepad.leftThumbstick) {
- jx.value = gamepad.leftThumbstick.xAxis.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_LEFT_X, jx);
- jx.value = -gamepad.leftThumbstick.yAxis.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_LEFT_Y, jx);
- } else if (element == gamepad.rightThumbstick) {
- jx.value = gamepad.rightThumbstick.xAxis.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_RIGHT_X, jx);
- jx.value = -gamepad.rightThumbstick.yAxis.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_RIGHT_Y, jx);
- } else if (element == gamepad.leftTrigger) {
- jx.value = gamepad.leftTrigger.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_LEFT, jx);
- } else if (element == gamepad.rightTrigger) {
- jx.value = gamepad.rightTrigger.value;
- OSIPhone::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_RIGHT, jx);
- };
- };
- } else if (controller.gamepad != nil) {
- // gamepad is the standard profile with 4 buttons, shoulder buttons and a
- // D-pad
- controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad,
- GCControllerElement *element) {
- int joy_id = [self getJoyIdForController:controller];
-
- if (element == gamepad.buttonA) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
- gamepad.buttonA.isPressed);
- } else if (element == gamepad.buttonB) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_B,
- gamepad.buttonB.isPressed);
- } else if (element == gamepad.buttonX) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
- gamepad.buttonX.isPressed);
- } else if (element == gamepad.buttonY) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_Y,
- gamepad.buttonY.isPressed);
- } else if (element == gamepad.leftShoulder) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_LEFT_SHOULDER,
- gamepad.leftShoulder.isPressed);
- } else if (element == gamepad.rightShoulder) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
- gamepad.rightShoulder.isPressed);
- } else if (element == gamepad.dpad) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
- gamepad.dpad.up.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
- gamepad.dpad.down.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
- gamepad.dpad.left.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
- gamepad.dpad.right.isPressed);
- };
- };
-#ifdef ADD_MICRO_GAMEPAD // disabling this for now, only available on iOS 9+,
- // while we are setting that as the minimum, seems our
- // build environment doesn't like it
- } else if (controller.microGamepad != nil) {
- // micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad
- controller.microGamepad.valueChangedHandler =
- ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
- int joy_id = [self getJoyIdForController:controller];
-
- if (element == gamepad.buttonA) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
- gamepad.buttonA.isPressed);
- } else if (element == gamepad.buttonX) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
- gamepad.buttonX.isPressed);
- } else if (element == gamepad.dpad) {
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
- gamepad.dpad.up.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
- gamepad.dpad.down.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
- gamepad.dpad.left.isPressed);
- OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
- gamepad.dpad.right.isPressed);
- };
- };
-#endif
- };
-
- ///@TODO need to add support for controller.motion which gives us access to
- /// the orientation of the device (if supported)
-
- ///@TODO need to add support for controllerPausedHandler which should be a
- /// toggle
-};
-
-- (void)initGameControllers {
- // get told when controllers connect, this will be called right away for
- // already connected controllers
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(controllerWasConnected:)
- name:GCControllerDidConnectNotification
- object:nil];
-
- // get told when controllers disconnect
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(controllerWasDisconnected:)
- name:GCControllerDidDisconnectNotification
- object:nil];
-};
-
-- (void)deinitGameControllers {
- [[NSNotificationCenter defaultCenter]
- removeObserver:self
- name:GCControllerDidConnectNotification
- object:nil];
- [[NSNotificationCenter defaultCenter]
- removeObserver:self
- name:GCControllerDidDisconnectNotification
- object:nil];
-
- if (ios_joysticks != nil) {
- [ios_joysticks dealloc];
- ios_joysticks = nil;
- };
-
- if (pending_ios_joysticks != nil) {
- [pending_ios_joysticks dealloc];
- pending_ios_joysticks = nil;
- };
-};
-
-OS::VideoMode _get_video_mode() {
- int backingWidth;
- int backingHeight;
-#if defined(OPENGL_ENABLED)
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-#endif
-
- OS::VideoMode vm;
- vm.fullscreen = true;
- vm.width = backingWidth;
- vm.height = backingHeight;
- vm.resizable = false;
- return vm;
-};
-
-static int frame_count = 0;
-- (void)drawView:(UIView *)view;
-{
- switch (frame_count) {
- case 0: {
- OS::get_singleton()->set_video_mode(_get_video_mode());
-
- if (!OS::get_singleton()) {
- exit(0);
- };
- ++frame_count;
-
- NSString *locale_code = [[NSLocale currentLocale] localeIdentifier];
- OSIPhone::get_singleton()->set_locale(
- String::utf8([locale_code UTF8String]));
-
- NSString *uuid;
- if ([[UIDevice currentDevice]
- respondsToSelector:@selector(identifierForVendor)]) {
- uuid = [UIDevice currentDevice].identifierForVendor.UUIDString;
- } else {
- // before iOS 6, so just generate an identifier and store it
- uuid = [[NSUserDefaults standardUserDefaults]
- objectForKey:@"identiferForVendor"];
- if (!uuid) {
- CFUUIDRef cfuuid = CFUUIDCreate(NULL);
- uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, cfuuid);
- CFRelease(cfuuid);
- [[NSUserDefaults standardUserDefaults]
- setObject:uuid
- forKey:@"identifierForVendor"];
- }
- }
-
- OSIPhone::get_singleton()->set_unique_id(String::utf8([uuid UTF8String]));
-
- }; break;
-
- case 1: {
- Main::setup2();
- ++frame_count;
-
- if (pending_ios_joysticks != nil) {
- for (GCController *controller in pending_ios_joysticks) {
- _ios_add_joystick(controller, self);
- }
- [pending_ios_joysticks dealloc];
- pending_ios_joysticks = nil;
- }
-
- // this might be necessary before here
- NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
- for (NSString *key in dict) {
- NSObject *value = [dict objectForKey:key];
- String ukey = String::utf8([key UTF8String]);
-
- // we need a NSObject to Variant conversor
-
- if ([value isKindOfClass:[NSString class]]) {
- NSString *str = (NSString *)value;
- String uval = String::utf8([str UTF8String]);
-
- ProjectSettings::get_singleton()->set("Info.plist/" + ukey, uval);
-
- } else if ([value isKindOfClass:[NSNumber class]]) {
- NSNumber *n = (NSNumber *)value;
- double dval = [n doubleValue];
-
- ProjectSettings::get_singleton()->set("Info.plist/" + ukey, dval);
- };
- // do stuff
- }
-
- }; break;
-
- case 2: {
- Main::start();
- ++frame_count;
-
- }; break; // no fallthrough
-
- default: {
- if (OSIPhone::get_singleton()) {
- // OSIPhone::get_singleton()->update_accelerometer(accel[0], accel[1],
- // accel[2]);
- if (motionInitialised) {
- // Just using polling approach for now, we can set this up so it sends
- // data to us in intervals, might be better. See Apple reference pages
- // for more details:
- // https://developer.apple.com/reference/coremotion/cmmotionmanager?language=objc
-
- // Apple splits our accelerometer date into a gravity and user movement
- // component. We add them back together
- CMAcceleration gravity = motionManager.deviceMotion.gravity;
- CMAcceleration acceleration =
- motionManager.deviceMotion.userAcceleration;
-
- ///@TODO We don't seem to be getting data here, is my device broken or
- /// is this code incorrect?
- CMMagneticField magnetic =
- motionManager.deviceMotion.magneticField.field;
-
- ///@TODO we can access rotationRate as a CMRotationRate variable
- ///(processed date) or CMGyroData (raw data), have to see what works
- /// best
- CMRotationRate rotation = motionManager.deviceMotion.rotationRate;
-
- // Adjust for screen orientation.
- // [[UIDevice currentDevice] orientation] changes even if we've fixed
- // our orientation which is not a good thing when you're trying to get
- // your user to move the screen in all directions and want consistent
- // output
-
- ///@TODO Using [[UIApplication sharedApplication] statusBarOrientation]
- /// is a bit of a hack. Godot obviously knows the orientation so maybe
- /// we
- // can use that instead? (note that left and right seem swapped)
-
- switch ([[UIApplication sharedApplication] statusBarOrientation]) {
- case UIDeviceOrientationLandscapeLeft: {
- OSIPhone::get_singleton()->update_gravity(-gravity.y, gravity.x,
- gravity.z);
- OSIPhone::get_singleton()->update_accelerometer(
- -(acceleration.y + gravity.y), (acceleration.x + gravity.x),
- acceleration.z + gravity.z);
- OSIPhone::get_singleton()->update_magnetometer(
- -magnetic.y, magnetic.x, magnetic.z);
- OSIPhone::get_singleton()->update_gyroscope(-rotation.y, rotation.x,
- rotation.z);
- }; break;
- case UIDeviceOrientationLandscapeRight: {
- OSIPhone::get_singleton()->update_gravity(gravity.y, -gravity.x,
- gravity.z);
- OSIPhone::get_singleton()->update_accelerometer(
- (acceleration.y + gravity.y), -(acceleration.x + gravity.x),
- acceleration.z + gravity.z);
- OSIPhone::get_singleton()->update_magnetometer(
- magnetic.y, -magnetic.x, magnetic.z);
- OSIPhone::get_singleton()->update_gyroscope(rotation.y, -rotation.x,
- rotation.z);
- }; break;
- case UIDeviceOrientationPortraitUpsideDown: {
- OSIPhone::get_singleton()->update_gravity(-gravity.x, gravity.y,
- gravity.z);
- OSIPhone::get_singleton()->update_accelerometer(
- -(acceleration.x + gravity.x), (acceleration.y + gravity.y),
- acceleration.z + gravity.z);
- OSIPhone::get_singleton()->update_magnetometer(
- -magnetic.x, magnetic.y, magnetic.z);
- OSIPhone::get_singleton()->update_gyroscope(-rotation.x, rotation.y,
- rotation.z);
- }; break;
- default: { // assume portrait
- OSIPhone::get_singleton()->update_gravity(gravity.x, gravity.y,
- gravity.z);
- OSIPhone::get_singleton()->update_accelerometer(
- acceleration.x + gravity.x, acceleration.y + gravity.y,
- acceleration.z + gravity.z);
- OSIPhone::get_singleton()->update_magnetometer(magnetic.x, magnetic.y,
- magnetic.z);
- OSIPhone::get_singleton()->update_gyroscope(rotation.x, rotation.y,
- rotation.z);
- }; break;
- };
- }
-
- bool quit_request = OSIPhone::get_singleton()->iterate();
- };
-
- }; break;
- };
-};
-
-- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(
- MainLoop::NOTIFICATION_OS_MEMORY_WARNING);
- }
-};
-
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- CGRect rect = [[UIScreen mainScreen] bounds];
+ // TODO: might be required to make an early return, so app wouldn't crash because of timeout.
+ // TODO: logo screen is not displayed while shaders are compiling
+ // DummyViewController(Splash/LoadingViewController) -> setup -> GodotViewController
- is_focus_out = false;
-
- [application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
- // disable idle timer
- // application.idleTimerDisabled = YES;
+ CGRect windowBounds = [[UIScreen mainScreen] bounds];
// Create a full-screen window
- window = [[UIWindow alloc] initWithFrame:rect];
-
- OS::VideoMode vm = _get_video_mode();
+ self.window = [[UIWindow alloc] initWithFrame:windowBounds];
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
- NSUserDomainMask, YES);
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
- int err = iphone_main(vm.width, vm.height, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
+ int err = iphone_main(gargc, gargv, String::utf8([documentsDirectory UTF8String]));
+
if (err != 0) {
// bail, things did not go very well for us, should probably output a message on screen with our error code...
exit(0);
- return FALSE;
+ return NO;
};
-#if defined(OPENGL_ENABLED)
- // WARNING: We must *always* create the GLView after we have constructed the
- // OS with iphone_main. This allows the GLView to access project settings so
- // it can properly initialize the OpenGL context
- GLView *glView = [[GLView alloc] initWithFrame:rect];
- glView.delegate = self;
-
- view_controller = [[ViewController alloc] init];
- view_controller.view = glView;
-
- _set_keep_screen_on(bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)) ? YES : NO);
- glView.useCADisplayLink =
- bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
- printf("cadisaplylink: %d", glView.useCADisplayLink);
- glView.animationInterval = 1.0 / kRenderingFrequency;
- [glView startAnimation];
-#endif
-
-#if defined(VULKAN_ENABLED)
- view_controller = [[ViewController alloc] init];
-#endif
+ ViewController *viewController = [[ViewController alloc] init];
+ viewController.godotView.useCADisplayLink = bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
+ viewController.godotView.renderingInterval = 1.0 / kRenderingFrequency;
- window.rootViewController = view_controller;
+ self.window.rootViewController = viewController;
// Show the window
- [window makeKeyAndVisible];
-
- // Configure and start accelerometer
- if (!motionInitialised) {
- motionManager = [[CMMotionManager alloc] init];
- if (motionManager.deviceMotionAvailable) {
- motionManager.deviceMotionUpdateInterval = 1.0 / 70.0;
- [motionManager startDeviceMotionUpdatesUsingReferenceFrame:
- CMAttitudeReferenceFrameXMagneticNorthZVertical];
- motionInitialised = YES;
- };
- };
-
- [self initGameControllers];
+ [self.window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter]
addObserver:self
@@ -674,40 +91,33 @@ static int frame_count = 0;
name:AVAudioSessionInterruptionNotification
object:[AVAudioSession sharedInstance]];
- // OSIPhone::screen_width = rect.size.width - rect.origin.x;
- // OSIPhone::screen_height = rect.size.height - rect.origin.y;
-
- mainViewController = view_controller;
+ mainViewController = viewController;
// prevent to stop music in another background app
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
- return TRUE;
+ return YES;
};
- (void)onAudioInterruption:(NSNotification *)notification {
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
NSLog(@"Audio interruption began");
- on_focus_out(view_controller, &is_focus_out);
+ OSIPhone::get_singleton()->on_focus_out();
} else if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]) {
NSLog(@"Audio interruption ended");
- on_focus_in(view_controller, &is_focus_out);
+ OSIPhone::get_singleton()->on_focus_in();
}
}
};
-- (void)applicationWillTerminate:(UIApplication *)application {
- [self deinitGameControllers];
-
- if (motionInitialised) {
- ///@TODO is this the right place to clean this up?
- [motionManager stopDeviceMotionUpdates];
- [motionManager release];
- motionManager = nil;
- motionInitialised = NO;
- };
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_MEMORY_WARNING);
+ }
+};
+- (void)applicationWillTerminate:(UIApplication *)application {
iphone_finish();
};
@@ -722,16 +132,15 @@ static int frame_count = 0;
// notification panel by swiping from the upper part of the screen.
- (void)applicationWillResignActive:(UIApplication *)application {
- on_focus_out(view_controller, &is_focus_out);
+ OSIPhone::get_singleton()->on_focus_out();
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
- on_focus_in(view_controller, &is_focus_out);
+ OSIPhone::get_singleton()->on_focus_in();
}
- (void)dealloc {
- [window release];
- [super dealloc];
+ self.window = nil;
}
@end
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index 3e6c2f0ecf..17796beb6f 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -12,7 +12,6 @@ def get_name():
def can_build():
-
if sys.platform == "darwin" or ("OSXCROSS_IOS" in os.environ):
return True
@@ -31,26 +30,23 @@ def get_opts():
("IPHONESDK", "Path to the iPhone SDK", ""),
BoolVariable(
"use_static_mvk",
- "Link MoltenVK statically as Level-0 driver (better portability) or use Vulkan ICD loader (enables validation layers)",
+ "Link MoltenVK statically as Level-0 driver (better portability) or use Vulkan ICD loader (enables"
+ " validation layers)",
False,
),
- BoolVariable("game_center", "Support for game center", True),
- BoolVariable("store_kit", "Support for in-app store", True),
- BoolVariable("icloud", "Support for iCloud", True),
+ BoolVariable("ios_simulator", "Build for iOS Simulator", False),
BoolVariable("ios_exceptions", "Enable exceptions", False),
("ios_triple", "Triple for ios toolchain", ""),
]
def get_flags():
-
return [
("tools", False),
]
def configure(env):
-
## Build type
if env["target"].startswith("release"):
@@ -67,7 +63,7 @@ def configure(env):
elif env["target"] == "debug":
env.Append(CCFLAGS=["-gdwarf-2", "-O0"])
- env.Append(CPPDEFINES=["_DEBUG", ("DEBUG", 1), "DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Append(CPPDEFINES=["_DEBUG", ("DEBUG", 1), "DEBUG_ENABLED"])
if env["use_lto"]:
env.Append(CCFLAGS=["-flto"])
@@ -112,26 +108,46 @@ def configure(env):
## Compile flags
- if env["arch"] == "x86" or env["arch"] == "x86_64":
+ if env["ios_simulator"]:
detect_darwin_sdk_path("iphonesimulator", env)
+ env.Append(CCFLAGS=["-mios-simulator-version-min=13.0"])
+ env.Append(LINKFLAGS=["-mios-simulator-version-min=13.0"])
+ env.extra_suffix = ".simulator" + env.extra_suffix
+ else:
+ detect_darwin_sdk_path("iphone", env)
+ env.Append(CCFLAGS=["-miphoneos-version-min=11.0"])
+ env.Append(LINKFLAGS=["-miphoneos-version-min=11.0"])
+
+ if env["arch"] == "x86" or env["arch"] == "x86_64":
env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = "10.9"
arch_flag = "i386" if env["arch"] == "x86" else env["arch"]
env.Append(
CCFLAGS=(
- "-arch "
+ "-fobjc-arc -arch "
+ arch_flag
- + " -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks -fasm-blocks -isysroot $IPHONESDK -mios-simulator-version-min=10.0"
+ + " -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks"
+ " -fasm-blocks -isysroot $IPHONESDK"
).split()
)
elif env["arch"] == "arm":
- detect_darwin_sdk_path("iphone", env)
env.Append(
- CCFLAGS='-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=10.0 -MMD -MT dependencies'.split()
+ CCFLAGS=(
+ "-fobjc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing"
+ " -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits"
+ " -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb"
+ ' "-DIBOutlet=__attribute__((iboutlet))"'
+ ' "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))"'
+ ' "-DIBAction=void)__attribute__((ibaction)" -MMD -MT dependencies'.split()
+ )
)
elif env["arch"] == "arm64":
- detect_darwin_sdk_path("iphone", env)
env.Append(
- CCFLAGS="-fno-objc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies -miphoneos-version-min=10.0 -isysroot $IPHONESDK".split()
+ CCFLAGS=(
+ "-fobjc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing"
+ " -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits"
+ " -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies"
+ " -isysroot $IPHONESDK".split()
+ )
)
env.Append(CPPDEFINES=["NEED_LONG_INT"])
env.Append(CPPDEFINES=["LIBYUV_DISABLE_NEON"])
@@ -143,6 +159,9 @@ def configure(env):
else:
env.Append(CCFLAGS=["-fno-exceptions"])
+ # Temp fix for ABS/MAX/MIN macros in iPhone SDK blocking compilation
+ env.Append(CCFLAGS=["-Wno-ambiguous-macro"])
+
## Link flags
if env["arch"] == "x86" or env["arch"] == "x86_64":
@@ -151,7 +170,6 @@ def configure(env):
LINKFLAGS=[
"-arch",
arch_flag,
- "-mios-simulator-version-min=10.0",
"-isysroot",
"$IPHONESDK",
"-Xlinker",
@@ -162,63 +180,22 @@ def configure(env):
]
)
elif env["arch"] == "arm":
- env.Append(LINKFLAGS=["-arch", "armv7", "-Wl,-dead_strip", "-miphoneos-version-min=10.0"])
+ env.Append(LINKFLAGS=["-arch", "armv7", "-Wl,-dead_strip"])
if env["arch"] == "arm64":
- env.Append(LINKFLAGS=["-arch", "arm64", "-Wl,-dead_strip", "-miphoneos-version-min=10.0"])
+ env.Append(LINKFLAGS=["-arch", "arm64", "-Wl,-dead_strip"])
env.Append(
LINKFLAGS=[
"-isysroot",
"$IPHONESDK",
- "-framework",
- "AudioToolbox",
- "-framework",
- "AVFoundation",
- "-framework",
- "CoreAudio",
- "-framework",
- "CoreGraphics",
- "-framework",
- "CoreMedia",
- "-framework",
- "CoreVideo",
- "-framework",
- "CoreMotion",
- "-framework",
- "Foundation",
- "-framework",
- "GameController",
- "-framework",
- "MediaPlayer",
- "-framework",
- "Metal",
- "-framework",
- "QuartzCore",
- "-framework",
- "Security",
- "-framework",
- "SystemConfiguration",
- "-framework",
- "UIKit",
- "-framework",
- "ARKit",
]
)
- # Feature options
- if env["game_center"]:
- env.Append(CPPDEFINES=["GAME_CENTER_ENABLED"])
- env.Append(LINKFLAGS=["-framework", "GameKit"])
-
- if env["store_kit"]:
- env.Append(CPPDEFINES=["STOREKIT_ENABLED"])
- env.Append(LINKFLAGS=["-framework", "StoreKit"])
-
- if env["icloud"]:
- env.Append(CPPDEFINES=["ICLOUD_ENABLED"])
-
env.Prepend(
- CPPPATH=["$IPHONESDK/usr/include", "$IPHONESDK/System/Library/Frameworks/AudioUnit.framework/Headers",]
+ CPPPATH=[
+ "$IPHONESDK/usr/include",
+ "$IPHONESDK/System/Library/Frameworks/AudioUnit.framework/Headers",
+ ]
)
env["ENV"]["CODESIGN_ALLOCATE"] = "/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate"
@@ -228,8 +205,7 @@ def configure(env):
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
env.Append(LINKFLAGS=["-framework", "IOSurface"])
- if env["use_static_mvk"]:
- env.Append(LINKFLAGS=["-framework", "MoltenVK"])
- env["builtin_vulkan"] = False
- elif not env["builtin_vulkan"]:
- env.Append(LIBS=["vulkan"])
+
+ # Use Static Vulkan for iOS. Dynamic Framework works fine too.
+ env.Append(LINKFLAGS=["-framework", "MoltenVK"])
+ env["builtin_vulkan"] = False
diff --git a/platform/haiku/platform_config.h b/platform/iphone/device_metrics.h
index f2d5418adf..bc8f761dde 100644
--- a/platform/haiku/platform_config.h
+++ b/platform/iphone/device_metrics.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* platform_config.h */
+/* device_metrics.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,9 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include <alloca.h>
+#import <Foundation/Foundation.h>
-// for ifaddrs.h needed in drivers/unix/ip_unix.cpp
-#define _BSD_SOURCE 1
+@interface GodotDeviceMetrics : NSObject
-#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h"
+@property(nonatomic, class, readonly, strong) NSDictionary<NSArray *, NSNumber *> *dpiList;
+
+@end
diff --git a/platform/iphone/device_metrics.m b/platform/iphone/device_metrics.m
new file mode 100644
index 0000000000..306ca95d03
--- /dev/null
+++ b/platform/iphone/device_metrics.m
@@ -0,0 +1,152 @@
+/*************************************************************************/
+/* device_metrics.m */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import "device_metrics.h"
+
+@implementation GodotDeviceMetrics
+
++ (NSDictionary *)dpiList {
+ return @{
+ @[
+ @"iPad1,1",
+ @"iPad2,1",
+ @"iPad2,2",
+ @"iPad2,3",
+ @"iPad2,4",
+ ] : @132,
+ @[
+ @"iPhone1,1",
+ @"iPhone1,2",
+ @"iPhone2,1",
+ @"iPad2,5",
+ @"iPad2,6",
+ @"iPad2,7",
+ @"iPod1,1",
+ @"iPod2,1",
+ @"iPod3,1",
+ ] : @163,
+ @[
+ @"iPad3,1",
+ @"iPad3,2",
+ @"iPad3,3",
+ @"iPad3,4",
+ @"iPad3,5",
+ @"iPad3,6",
+ @"iPad4,1",
+ @"iPad4,2",
+ @"iPad4,3",
+ @"iPad5,3",
+ @"iPad5,4",
+ @"iPad6,3",
+ @"iPad6,4",
+ @"iPad6,7",
+ @"iPad6,8",
+ @"iPad6,11",
+ @"iPad6,12",
+ @"iPad7,1",
+ @"iPad7,2",
+ @"iPad7,3",
+ @"iPad7,4",
+ @"iPad7,5",
+ @"iPad7,6",
+ @"iPad7,11",
+ @"iPad7,12",
+ @"iPad8,1",
+ @"iPad8,2",
+ @"iPad8,3",
+ @"iPad8,4",
+ @"iPad8,5",
+ @"iPad8,6",
+ @"iPad8,7",
+ @"iPad8,8",
+ @"iPad8,9",
+ @"iPad8,10",
+ @"iPad8,11",
+ @"iPad8,12",
+ @"iPad11,3",
+ @"iPad11,4",
+ ] : @264,
+ @[
+ @"iPhone3,1",
+ @"iPhone3,2",
+ @"iPhone3,3",
+ @"iPhone4,1",
+ @"iPhone5,1",
+ @"iPhone5,2",
+ @"iPhone5,3",
+ @"iPhone5,4",
+ @"iPhone6,1",
+ @"iPhone6,2",
+ @"iPhone7,2",
+ @"iPhone8,1",
+ @"iPhone8,4",
+ @"iPhone9,1",
+ @"iPhone9,3",
+ @"iPhone10,1",
+ @"iPhone10,4",
+ @"iPhone11,8",
+ @"iPhone12,1",
+ @"iPhone12,8",
+ @"iPad4,4",
+ @"iPad4,5",
+ @"iPad4,6",
+ @"iPad4,7",
+ @"iPad4,8",
+ @"iPad4,9",
+ @"iPad5,1",
+ @"iPad5,2",
+ @"iPad11,1",
+ @"iPad11,2",
+ @"iPod4,1",
+ @"iPod5,1",
+ @"iPod7,1",
+ @"iPod9,1",
+ ] : @326,
+ @[
+ @"iPhone7,1",
+ @"iPhone8,2",
+ @"iPhone9,2",
+ @"iPhone9,4",
+ @"iPhone10,2",
+ @"iPhone10,5",
+ ] : @401,
+ @[
+ @"iPhone10,3",
+ @"iPhone10,6",
+ @"iPhone11,2",
+ @"iPhone11,4",
+ @"iPhone11,6",
+ @"iPhone12,3",
+ @"iPhone12,5",
+ ] : @458,
+ };
+}
+
+@end
diff --git a/platform/iphone/display_layer.h b/platform/iphone/display_layer.h
new file mode 100644
index 0000000000..1b8435d959
--- /dev/null
+++ b/platform/iphone/display_layer.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* display_layer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import <OpenGLES/EAGLDrawable.h>
+#import <QuartzCore/QuartzCore.h>
+
+@protocol DisplayLayer <NSObject>
+
+- (void)renderDisplayLayer;
+- (void)initializeDisplayLayer;
+- (void)layoutDisplayLayer;
+
+@end
+
+// An ugly workaround for iOS simulator
+#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
+#if defined(__IPHONE_13_0)
+API_AVAILABLE(ios(13.0))
+@interface GodotMetalLayer : CAMetalLayer <DisplayLayer>
+#else
+@interface GodotMetalLayer : CALayer <DisplayLayer>
+#endif
+#else
+@interface GodotMetalLayer : CAMetalLayer <DisplayLayer>
+#endif
+@end
+
+API_DEPRECATED("OpenGLES is deprecated", ios(2.0, 12.0))
+@interface GodotOpenGLLayer : CAEAGLLayer <DisplayLayer>
+
+@end
diff --git a/platform/iphone/display_layer.mm b/platform/iphone/display_layer.mm
new file mode 100644
index 0000000000..b8df81b89a
--- /dev/null
+++ b/platform/iphone/display_layer.mm
@@ -0,0 +1,183 @@
+/*************************************************************************/
+/* display_layer.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import "display_layer.h"
+#include "core/config/project_settings.h"
+#include "core/os/keyboard.h"
+#include "display_server_iphone.h"
+#include "main/main.h"
+#include "os_iphone.h"
+#include "servers/audio_server.h"
+
+#import <AudioToolbox/AudioServices.h>
+#import <GameController/GameController.h>
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+#import <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+
+@implementation GodotMetalLayer
+
+- (void)initializeDisplayLayer {
+#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
+ if (@available(iOS 13, *)) {
+ // Simulator supports Metal since iOS 13
+ } else {
+ NSLog(@"iOS Simulator prior to iOS 13 does not support Metal rendering.");
+ }
+#endif
+}
+
+- (void)layoutDisplayLayer {
+}
+
+- (void)renderDisplayLayer {
+}
+
+@end
+
+@implementation GodotOpenGLLayer {
+ // The pixel dimensions of the backbuffer
+ GLint backingWidth;
+ GLint backingHeight;
+
+ EAGLContext *context;
+ GLuint viewRenderbuffer, viewFramebuffer;
+ GLuint depthRenderbuffer;
+}
+
+- (void)initializeDisplayLayer {
+ // Get our backing layer
+
+ // Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color.
+ self.opaque = YES;
+ self.drawableProperties = [NSDictionary
+ dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:FALSE],
+ kEAGLDrawablePropertyRetainedBacking,
+ kEAGLColorFormatRGBA8,
+ kEAGLDrawablePropertyColorFormat,
+ nil];
+
+ // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
+
+ // Create GL ES 2 context
+ if (GLOBAL_GET("rendering/driver/driver_name") == "GLES2") {
+ context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+ NSLog(@"Setting up an OpenGL ES 2.0 context.");
+ if (!context) {
+ NSLog(@"Failed to create OpenGL ES 2.0 context!");
+ return;
+ }
+ }
+
+ if (![EAGLContext setCurrentContext:context]) {
+ NSLog(@"Failed to set EAGLContext!");
+ return;
+ }
+ if (![self createFramebuffer]) {
+ NSLog(@"Failed to create frame buffer!");
+ return;
+ }
+}
+
+- (void)layoutDisplayLayer {
+ [EAGLContext setCurrentContext:context];
+ [self destroyFramebuffer];
+ [self createFramebuffer];
+}
+
+- (void)renderDisplayLayer {
+ [EAGLContext setCurrentContext:context];
+}
+
+- (void)dealloc {
+ if ([EAGLContext currentContext] == context) {
+ [EAGLContext setCurrentContext:nil];
+ }
+
+ if (context) {
+ context = nil;
+ }
+}
+
+- (BOOL)createFramebuffer {
+ glGenFramebuffersOES(1, &viewFramebuffer);
+ glGenRenderbuffersOES(1, &viewRenderbuffer);
+
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
+ // This call associates the storage for the current render buffer with the EAGLDrawable (our CAself)
+ // allowing us to draw into a buffer that will later be rendered to screen wherever the layer is (which corresponds with our view).
+ [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self];
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
+
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+
+ // For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer.
+ glGenRenderbuffersOES(1, &depthRenderbuffer);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
+ glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
+
+ if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
+ NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
+ return NO;
+ }
+
+ // if (OS::get_singleton()) {
+ // OS::VideoMode vm;
+ // vm.fullscreen = true;
+ // vm.width = backingWidth;
+ // vm.height = backingHeight;
+ // vm.resizable = false;
+ // OS::get_singleton()->set_video_mode(vm);
+ // OSIPhone::get_singleton()->set_base_framebuffer(viewFramebuffer);
+ // };
+ // gl_view_base_fb = viewFramebuffer;
+
+ return YES;
+}
+
+// Clean up any buffers we have allocated.
+- (void)destroyFramebuffer {
+ glDeleteFramebuffersOES(1, &viewFramebuffer);
+ viewFramebuffer = 0;
+ glDeleteRenderbuffersOES(1, &viewRenderbuffer);
+ viewRenderbuffer = 0;
+
+ if (depthRenderbuffer) {
+ glDeleteRenderbuffersOES(1, &depthRenderbuffer);
+ depthRenderbuffer = 0;
+ }
+}
+
+@end
diff --git a/platform/iphone/display_server_iphone.h b/platform/iphone/display_server_iphone.h
new file mode 100644
index 0000000000..31a44723a5
--- /dev/null
+++ b/platform/iphone/display_server_iphone.h
@@ -0,0 +1,202 @@
+/*************************************************************************/
+/* display_server_iphone.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifndef display_server_iphone_h
+#define display_server_iphone_h
+
+#include "core/input/input.h"
+#include "servers/display_server.h"
+
+#if defined(VULKAN_ENABLED)
+#include "drivers/vulkan/rendering_device_vulkan.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+
+#include "vulkan_context_iphone.h"
+
+#import <QuartzCore/CAMetalLayer.h>
+#include <vulkan/vulkan_metal.h>
+#endif
+
+class DisplayServerIPhone : public DisplayServer {
+ GDCLASS(DisplayServerIPhone, DisplayServer)
+
+ _THREAD_SAFE_CLASS_
+
+#if defined(VULKAN_ENABLED)
+ VulkanContextIPhone *context_vulkan;
+ RenderingDeviceVulkan *rendering_device_vulkan;
+#endif
+
+ DisplayServer::ScreenOrientation screen_orientation;
+
+ ObjectID window_attached_instance_id;
+
+ Callable window_event_callback;
+ Callable window_resize_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+
+ int virtual_keyboard_height = 0;
+
+ void perform_event(const Ref<InputEvent> &p_event);
+
+ DisplayServerIPhone(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ ~DisplayServerIPhone();
+
+public:
+ String rendering_driver;
+
+ static DisplayServerIPhone *get_singleton();
+
+ static void register_iphone_driver();
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static Vector<String> get_rendering_drivers_func();
+
+ // MARK: - Events
+
+ virtual void process_events() override;
+
+ virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void send_input_event(const Ref<InputEvent> &p_event) const;
+ void send_input_text(const String &p_text) const;
+ void send_window_event(DisplayServer::WindowEvent p_event) const;
+ void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
+
+ // MARK: - Input
+
+ // MARK: Touches
+
+ void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick);
+ void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y);
+ void touches_cancelled(int p_idx);
+
+ // MARK: Keyboard
+
+ void key(uint32_t p_key, bool p_pressed);
+
+ // MARK: Motion
+
+ void update_gravity(float p_x, float p_y, float p_z);
+ void update_accelerometer(float p_x, float p_y, float p_z);
+ void update_magnetometer(float p_x, float p_y, float p_z);
+ void update_gyroscope(float p_x, float p_y, float p_z);
+
+ // MARK: -
+
+ virtual bool has_feature(Feature p_feature) const override;
+ virtual String get_name() const override;
+
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
+
+ virtual int get_screen_count() const override;
+ virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+
+ virtual Vector<DisplayServer::WindowID> get_window_list() const override;
+
+ virtual WindowID
+ get_window_at_screen_position(const Point2i &p_position) const override;
+
+ virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
+
+ virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
+
+ virtual float screen_get_max_scale() const override;
+
+ virtual void screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) override;
+ virtual DisplayServer::ScreenOrientation screen_get_orientation(int p_screen) const override;
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
+
+ virtual bool can_any_window_draw() const override;
+
+ virtual bool screen_is_touchscreen(int p_screen) const override;
+
+ virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_length, int p_cursor_start, int p_cursor_end) override;
+ virtual void virtual_keyboard_hide() override;
+
+ void virtual_keyboard_set_height(int height);
+ virtual int virtual_keyboard_get_height() const override;
+
+ virtual void clipboard_set(const String &p_text) override;
+ virtual String clipboard_get() const override;
+
+ virtual void screen_set_keep_on(bool p_enable) override;
+ virtual bool screen_is_kept_on() const override;
+
+ virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen = SCREEN_OF_MAIN_WINDOW) override;
+ virtual bool native_video_is_playing() const override;
+ virtual void native_video_pause() override;
+ virtual void native_video_unpause() override;
+ virtual void native_video_stop() override;
+
+ void resize_window(CGSize size);
+};
+
+#endif /* display_server_iphone_h */
diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm
new file mode 100644
index 0000000000..05dc78bb4d
--- /dev/null
+++ b/platform/iphone/display_server_iphone.mm
@@ -0,0 +1,648 @@
+/*************************************************************************/
+/* display_server_iphone.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "display_server_iphone.h"
+#import "app_delegate.h"
+#include "core/config/project_settings.h"
+#include "core/io/file_access_pack.h"
+#import "device_metrics.h"
+#import "godot_view.h"
+#include "ios.h"
+#import "keyboard_input_view.h"
+#import "native_video_view.h"
+#include "os_iphone.h"
+#import "view_controller.h"
+
+#import <Foundation/Foundation.h>
+#import <sys/utsname.h>
+
+static const float kDisplayServerIPhoneAcceleration = 1;
+
+DisplayServerIPhone *DisplayServerIPhone::get_singleton() {
+ return (DisplayServerIPhone *)DisplayServer::get_singleton();
+}
+
+DisplayServerIPhone::DisplayServerIPhone(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ rendering_driver = p_rendering_driver;
+
+#if defined(OPENGL_ENABLED)
+ // FIXME: Add support for both GLES2 and Vulkan when GLES2 is implemented
+ // again,
+
+ if (rendering_driver == "opengl_es") {
+ bool gl_initialization_error = false;
+
+ // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
+
+ if (RasterizerGLES2::is_viable() == OK) {
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ } else {
+ gl_initialization_error = true;
+ }
+
+ if (gl_initialization_error) {
+ OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.", "Unable to initialize video driver");
+ // return ERR_UNAVAILABLE;
+ }
+
+ // rendering_server = memnew(RenderingServerDefault);
+ // // FIXME: Reimplement threaded rendering
+ // if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
+ // rendering_server = memnew(RenderingServerWrapMT(rendering_server,
+ // false));
+ // }
+ // rendering_server->init();
+ // rendering_server->cursor_set_visible(false, 0);
+
+ // reset this to what it should be, it will have been set to 0 after
+ // rendering_server->init() is called
+ // RasterizerStorageGLES2::system_fbo = gl_view_base_fb;
+ }
+#endif
+
+#if defined(VULKAN_ENABLED)
+ rendering_driver = "vulkan";
+
+ context_vulkan = nullptr;
+ rendering_device_vulkan = nullptr;
+
+ if (rendering_driver == "vulkan") {
+ context_vulkan = memnew(VulkanContextIPhone);
+ if (context_vulkan->initialize() != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = nullptr;
+ ERR_FAIL_MSG("Failed to initialize Vulkan context");
+ }
+
+ CALayer *layer = [AppDelegate.viewController.godotView initializeRenderingForDriver:@"vulkan"];
+
+ if (!layer) {
+ ERR_FAIL_MSG("Failed to create iOS rendering layer.");
+ }
+
+ Size2i size = Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_max_scale();
+ if (context_vulkan->window_create(MAIN_WINDOW_ID, layer, size.width, size.height) != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = nullptr;
+ ERR_FAIL_MSG("Failed to create Vulkan window.");
+ }
+
+ rendering_device_vulkan = memnew(RenderingDeviceVulkan);
+ rendering_device_vulkan->initialize(context_vulkan);
+
+ RendererCompositorRD::make_current();
+ }
+#endif
+
+ bool keep_screen_on = bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
+ screen_set_keep_on(keep_screen_on);
+
+ Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
+
+ r_error = OK;
+}
+
+DisplayServerIPhone::~DisplayServerIPhone() {
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ if (rendering_device_vulkan) {
+ rendering_device_vulkan->finalize();
+ memdelete(rendering_device_vulkan);
+ rendering_device_vulkan = NULL;
+ }
+
+ if (context_vulkan) {
+ context_vulkan->window_destroy(MAIN_WINDOW_ID);
+ memdelete(context_vulkan);
+ context_vulkan = NULL;
+ }
+ }
+#endif
+}
+
+DisplayServer *DisplayServerIPhone::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ return memnew(DisplayServerIPhone(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+}
+
+Vector<String> DisplayServerIPhone::get_rendering_drivers_func() {
+ Vector<String> drivers;
+
+#if defined(VULKAN_ENABLED)
+ drivers.push_back("vulkan");
+#endif
+#if defined(OPENGL_ENABLED)
+ drivers.push_back("opengl_es");
+#endif
+
+ return drivers;
+}
+
+void DisplayServerIPhone::register_iphone_driver() {
+ register_create_function("iphone", create_func, get_rendering_drivers_func);
+}
+
+// MARK: Events
+
+void DisplayServerIPhone::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+ window_resize_callback = p_callable;
+}
+
+void DisplayServerIPhone::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+ window_event_callback = p_callable;
+}
+void DisplayServerIPhone::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+ input_event_callback = p_callable;
+}
+
+void DisplayServerIPhone::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+ input_text_callback = p_callable;
+}
+
+void DisplayServerIPhone::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+void DisplayServerIPhone::process_events() {
+}
+
+void DisplayServerIPhone::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ DisplayServerIPhone::get_singleton()->send_input_event(p_event);
+}
+
+void DisplayServerIPhone::send_input_event(const Ref<InputEvent> &p_event) const {
+ _window_callback(input_event_callback, p_event);
+}
+
+void DisplayServerIPhone::send_input_text(const String &p_text) const {
+ _window_callback(input_text_callback, p_text);
+}
+
+void DisplayServerIPhone::send_window_event(DisplayServer::WindowEvent p_event) const {
+ _window_callback(window_event_callback, int(p_event));
+}
+
+void DisplayServerIPhone::_window_callback(const Callable &p_callable, const Variant &p_arg) const {
+ if (!p_callable.is_null()) {
+ const Variant *argp = &p_arg;
+ Variant ret;
+ Callable::CallError ce;
+ p_callable.call((const Variant **)&argp, 1, ret, ce);
+ }
+}
+
+// MARK: - Input
+
+// MARK: Touches
+
+void DisplayServerIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick) {
+ if (!GLOBAL_DEF("debug/disable_touch", false)) {
+ Ref<InputEventScreenTouch> ev;
+ ev.instance();
+
+ ev->set_index(p_idx);
+ ev->set_pressed(p_pressed);
+ ev->set_position(Vector2(p_x, p_y));
+ perform_event(ev);
+ }
+};
+
+void DisplayServerIPhone::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) {
+ if (!GLOBAL_DEF("debug/disable_touch", false)) {
+ Ref<InputEventScreenDrag> ev;
+ ev.instance();
+ ev->set_index(p_idx);
+ ev->set_position(Vector2(p_x, p_y));
+ ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
+ perform_event(ev);
+ };
+};
+
+void DisplayServerIPhone::perform_event(const Ref<InputEvent> &p_event) {
+ Input::get_singleton()->parse_input_event(p_event);
+};
+
+void DisplayServerIPhone::touches_cancelled(int p_idx) {
+ touch_press(p_idx, -1, -1, false, false);
+};
+
+// MARK: Keyboard
+
+void DisplayServerIPhone::key(uint32_t p_key, bool p_pressed) {
+ Ref<InputEventKey> ev;
+ ev.instance();
+ ev->set_echo(false);
+ ev->set_pressed(p_pressed);
+ ev->set_keycode(p_key);
+ ev->set_physical_keycode(p_key);
+ ev->set_unicode(p_key);
+ perform_event(ev);
+};
+
+// MARK: Motion
+
+void DisplayServerIPhone::update_gravity(float p_x, float p_y, float p_z) {
+ Input::get_singleton()->set_gravity(Vector3(p_x, p_y, p_z));
+};
+
+void DisplayServerIPhone::update_accelerometer(float p_x, float p_y, float p_z) {
+ // Found out the Z should not be negated! Pass as is!
+ Vector3 v_accelerometer = Vector3(
+ p_x / kDisplayServerIPhoneAcceleration,
+ p_y / kDisplayServerIPhoneAcceleration,
+ p_z / kDisplayServerIPhoneAcceleration);
+
+ Input::get_singleton()->set_accelerometer(v_accelerometer);
+};
+
+void DisplayServerIPhone::update_magnetometer(float p_x, float p_y, float p_z) {
+ Input::get_singleton()->set_magnetometer(Vector3(p_x, p_y, p_z));
+};
+
+void DisplayServerIPhone::update_gyroscope(float p_x, float p_y, float p_z) {
+ Input::get_singleton()->set_gyroscope(Vector3(p_x, p_y, p_z));
+};
+
+// MARK: -
+
+bool DisplayServerIPhone::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+ // case FEATURE_CONSOLE_WINDOW:
+ // case FEATURE_CURSOR_SHAPE:
+ // case FEATURE_CUSTOM_CURSOR_SHAPE:
+ // case FEATURE_GLOBAL_MENU:
+ // case FEATURE_HIDPI:
+ // case FEATURE_ICON:
+ // case FEATURE_IME:
+ // case FEATURE_MOUSE:
+ // case FEATURE_MOUSE_WARP:
+ // case FEATURE_NATIVE_DIALOG:
+ // case FEATURE_NATIVE_ICON:
+ // case FEATURE_NATIVE_VIDEO:
+ // case FEATURE_WINDOW_TRANSPARENCY:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_KEEP_SCREEN_ON:
+ case FEATURE_ORIENTATION:
+ case FEATURE_TOUCHSCREEN:
+ case FEATURE_VIRTUAL_KEYBOARD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+String DisplayServerIPhone::get_name() const {
+ return "iPhone";
+}
+
+void DisplayServerIPhone::alert(const String &p_alert, const String &p_title) {
+ const CharString utf8_alert = p_alert.utf8();
+ const CharString utf8_title = p_title.utf8();
+ iOS::alert(utf8_alert.get_data(), utf8_title.get_data());
+}
+
+int DisplayServerIPhone::get_screen_count() const {
+ return 1;
+}
+
+Point2i DisplayServerIPhone::screen_get_position(int p_screen) const {
+ return Size2i();
+}
+
+Size2i DisplayServerIPhone::screen_get_size(int p_screen) const {
+ CALayer *layer = AppDelegate.viewController.godotView.renderingLayer;
+
+ if (!layer) {
+ return Size2i();
+ }
+
+ return Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_scale(p_screen);
+}
+
+Rect2i DisplayServerIPhone::screen_get_usable_rect(int p_screen) const {
+ if (@available(iOS 11, *)) {
+ UIEdgeInsets insets = UIEdgeInsetsZero;
+ UIView *view = AppDelegate.viewController.godotView;
+
+ if ([view respondsToSelector:@selector(safeAreaInsets)]) {
+ insets = [view safeAreaInsets];
+ }
+
+ float scale = screen_get_scale(p_screen);
+ Size2i insets_position = Size2i(insets.left, insets.top) * scale;
+ Size2i insets_size = Size2i(insets.left + insets.right, insets.top + insets.bottom) * scale;
+
+ return Rect2i(screen_get_position(p_screen) + insets_position, screen_get_size(p_screen) - insets_size);
+ } else {
+ return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));
+ }
+}
+
+int DisplayServerIPhone::screen_get_dpi(int p_screen) const {
+ struct utsname systemInfo;
+ uname(&systemInfo);
+
+ NSString *string = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
+
+ NSDictionary *iOSModelToDPI = [GodotDeviceMetrics dpiList];
+
+ for (NSArray *keyArray in iOSModelToDPI) {
+ if ([keyArray containsObject:string]) {
+ NSNumber *value = iOSModelToDPI[keyArray];
+ return [value intValue];
+ }
+ }
+
+ // If device wasn't found in dictionary
+ // make a best guess from device metrics.
+ CGFloat scale = [UIScreen mainScreen].scale;
+
+ UIUserInterfaceIdiom idiom = [UIDevice currentDevice].userInterfaceIdiom;
+
+ switch (idiom) {
+ case UIUserInterfaceIdiomPad:
+ return scale == 2 ? 264 : 132;
+ case UIUserInterfaceIdiomPhone: {
+ if (scale == 3) {
+ CGFloat nativeScale = [UIScreen mainScreen].nativeScale;
+ return nativeScale == 3 ? 458 : 401;
+ }
+
+ return 326;
+ }
+ default:
+ return 72;
+ }
+}
+
+float DisplayServerIPhone::screen_get_scale(int p_screen) const {
+ return [UIScreen mainScreen].nativeScale;
+}
+
+Vector<DisplayServer::WindowID> DisplayServerIPhone::get_window_list() const {
+ Vector<DisplayServer::WindowID> list;
+ list.push_back(MAIN_WINDOW_ID);
+ return list;
+}
+
+DisplayServer::WindowID DisplayServerIPhone::get_window_at_screen_position(const Point2i &p_position) const {
+ return MAIN_WINDOW_ID;
+}
+
+void DisplayServerIPhone::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+ window_attached_instance_id = p_instance;
+}
+
+ObjectID DisplayServerIPhone::window_get_attached_instance_id(WindowID p_window) const {
+ return window_attached_instance_id;
+}
+
+void DisplayServerIPhone::window_set_title(const String &p_title, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+int DisplayServerIPhone::window_get_current_screen(WindowID p_window) const {
+ return SCREEN_OF_MAIN_WINDOW;
+}
+
+void DisplayServerIPhone::window_set_current_screen(int p_screen, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Point2i DisplayServerIPhone::window_get_position(WindowID p_window) const {
+ return Point2i();
+}
+
+void DisplayServerIPhone::window_set_position(const Point2i &p_position, WindowID p_window) {
+ // Probably not supported for single window iOS app
+}
+
+void DisplayServerIPhone::window_set_transient(WindowID p_window, WindowID p_parent) {
+ // Probably not supported for iOS
+}
+
+void DisplayServerIPhone::window_set_max_size(const Size2i p_size, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Size2i DisplayServerIPhone::window_get_max_size(WindowID p_window) const {
+ return Size2i();
+}
+
+void DisplayServerIPhone::window_set_min_size(const Size2i p_size, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Size2i DisplayServerIPhone::window_get_min_size(WindowID p_window) const {
+ return Size2i();
+}
+
+void DisplayServerIPhone::window_set_size(const Size2i p_size, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+Size2i DisplayServerIPhone::window_get_size(WindowID p_window) const {
+ CGRect screenBounds = [UIScreen mainScreen].bounds;
+ return Size2i(screenBounds.size.width, screenBounds.size.height) * screen_get_max_scale();
+}
+
+Size2i DisplayServerIPhone::window_get_real_size(WindowID p_window) const {
+ return window_get_size(p_window);
+}
+
+void DisplayServerIPhone::window_set_mode(WindowMode p_mode, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+DisplayServer::WindowMode DisplayServerIPhone::window_get_mode(WindowID p_window) const {
+ return WindowMode::WINDOW_MODE_FULLSCREEN;
+}
+
+bool DisplayServerIPhone::window_is_maximize_allowed(WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerIPhone::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+bool DisplayServerIPhone::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+ return false;
+}
+
+void DisplayServerIPhone::window_request_attention(WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+void DisplayServerIPhone::window_move_to_foreground(WindowID p_window) {
+ // Probably not supported for iOS
+}
+
+float DisplayServerIPhone::screen_get_max_scale() const {
+ return screen_get_scale(SCREEN_OF_MAIN_WINDOW);
+};
+
+void DisplayServerIPhone::screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) {
+ screen_orientation = p_orientation;
+}
+
+DisplayServer::ScreenOrientation DisplayServerIPhone::screen_get_orientation(int p_screen) const {
+ return screen_orientation;
+}
+
+bool DisplayServerIPhone::window_can_draw(WindowID p_window) const {
+ return true;
+}
+
+bool DisplayServerIPhone::can_any_window_draw() const {
+ return true;
+}
+
+bool DisplayServerIPhone::screen_is_touchscreen(int p_screen) const {
+ return true;
+}
+
+void DisplayServerIPhone::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_length, int p_cursor_start, int p_cursor_end) {
+ NSString *existingString = [[NSString alloc] initWithUTF8String:p_existing_text.utf8().get_data()];
+
+ [AppDelegate.viewController.keyboardView
+ becomeFirstResponderWithString:existingString
+ multiline:p_multiline
+ cursorStart:p_cursor_start
+ cursorEnd:p_cursor_end];
+}
+
+void DisplayServerIPhone::virtual_keyboard_hide() {
+ [AppDelegate.viewController.keyboardView resignFirstResponder];
+}
+
+void DisplayServerIPhone::virtual_keyboard_set_height(int height) {
+ virtual_keyboard_height = height * screen_get_max_scale();
+}
+
+int DisplayServerIPhone::virtual_keyboard_get_height() const {
+ return virtual_keyboard_height;
+}
+
+void DisplayServerIPhone::clipboard_set(const String &p_text) {
+ [UIPasteboard generalPasteboard].string = [NSString stringWithUTF8String:p_text.utf8()];
+}
+
+String DisplayServerIPhone::clipboard_get() const {
+ NSString *text = [UIPasteboard generalPasteboard].string;
+
+ return String::utf8([text UTF8String]);
+}
+
+void DisplayServerIPhone::screen_set_keep_on(bool p_enable) {
+ [UIApplication sharedApplication].idleTimerDisabled = p_enable;
+}
+
+bool DisplayServerIPhone::screen_is_kept_on() const {
+ return [UIApplication sharedApplication].idleTimerDisabled;
+}
+
+Error DisplayServerIPhone::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen) {
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ bool exists = f && f->is_open();
+
+ String user_data_dir = OSIPhone::get_singleton()->get_user_data_dir();
+
+ if (!exists) {
+ return FAILED;
+ }
+
+ String tempFile = OSIPhone::get_singleton()->get_user_data_dir();
+
+ if (p_path.begins_with("res://")) {
+ if (PackedData::get_singleton()->has_path(p_path)) {
+ printf("Unable to play %s using the native player as it resides in a .pck file\n", p_path.utf8().get_data());
+ return ERR_INVALID_PARAMETER;
+ } else {
+ p_path = p_path.replace("res:/", ProjectSettings::get_singleton()->get_resource_path());
+ }
+ } else if (p_path.begins_with("user://")) {
+ p_path = p_path.replace("user:/", user_data_dir);
+ }
+
+ memdelete(f);
+
+ printf("Playing video: %s\n", p_path.utf8().get_data());
+
+ String file_path = ProjectSettings::get_singleton()->globalize_path(p_path);
+
+ NSString *filePath = [[NSString alloc] initWithUTF8String:file_path.utf8().get_data()];
+ NSString *audioTrack = [NSString stringWithUTF8String:p_audio_track.utf8()];
+ NSString *subtitleTrack = [NSString stringWithUTF8String:p_subtitle_track.utf8()];
+
+ if (![AppDelegate.viewController playVideoAtPath:filePath
+ volume:p_volume
+ audio:audioTrack
+ subtitle:subtitleTrack]) {
+ return OK;
+ }
+
+ return FAILED;
+}
+
+bool DisplayServerIPhone::native_video_is_playing() const {
+ return [AppDelegate.viewController.videoView isVideoPlaying];
+}
+
+void DisplayServerIPhone::native_video_pause() {
+ if (native_video_is_playing()) {
+ [AppDelegate.viewController.videoView pauseVideo];
+ }
+}
+
+void DisplayServerIPhone::native_video_unpause() {
+ [AppDelegate.viewController.videoView unpauseVideo];
+};
+
+void DisplayServerIPhone::native_video_stop() {
+ if (native_video_is_playing()) {
+ [AppDelegate.viewController.videoView stopVideo];
+ }
+}
+
+void DisplayServerIPhone::resize_window(CGSize viewSize) {
+ Size2i size = Size2i(viewSize.width, viewSize.height) * screen_get_max_scale();
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ if (context_vulkan) {
+ context_vulkan->window_resize(MAIN_WINDOW_ID, size.x, size.y);
+ }
+ }
+#endif
+
+ Variant resize_rect = Rect2i(Point2i(), size);
+ _window_callback(window_resize_callback, resize_rect);
+}
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index 63c3cb8c23..c585c2afbe 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,19 +30,21 @@
#include "export.h"
+#include "core/config/project_settings.h"
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
#include "core/io/zip_io.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
+#include "core/templates/safe_refcount.h"
#include "core/version.h"
#include "editor/editor_export.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "main/splash.gen.h"
#include "platform/iphone/logo.gen.h"
+#include "platform/iphone/plugin/godot_plugin_config.h"
#include "string.h"
#include <sys/stat.h>
@@ -54,6 +56,13 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
Ref<ImageTexture> logo;
+ // Plugins
+ SafeFlag plugins_changed;
+ Thread check_for_changes_thread;
+ SafeFlag quit_request;
+ Mutex plugins_lock;
+ Vector<PluginConfigIOS> plugins;
+
typedef Error (*FileHandler)(String p_file, void *p_userdata);
static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata);
static Error _codesign(String p_file, void *p_userdata);
@@ -70,6 +79,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
String modules_fileref;
String modules_buildphase;
String modules_buildgrp;
+ Vector<String> capabilities;
};
struct ExportArchitecture {
String name;
@@ -85,22 +95,26 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
struct IOSExportAsset {
String exported_path;
- bool is_framework; // framework is anything linked to the binary, otherwise it's a resource
+ bool is_framework = false; // framework is anything linked to the binary, otherwise it's a resource
+ bool should_embed = false;
};
String _get_additional_plist_content();
String _get_linker_flags();
String _get_cpp_code();
void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug);
- Error _export_loading_screens(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir);
+ Error _export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir);
+ Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir);
Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir);
Vector<ExportArchitecture> _get_supported_architectures();
Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset);
void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets);
- Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, Vector<IOSExportAsset> &r_exported_assets);
+ Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
+ Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
+ Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug);
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
String pname = p_package;
@@ -113,7 +127,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
}
for (int i = 0; i < pname.length(); i++) {
- CharType c = pname[i];
+ char32_t c = pname[i];
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) {
if (r_error) {
*r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
@@ -125,42 +139,162 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
return true;
}
+ static void _check_for_changes_poll_thread(void *ud) {
+ EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud;
+
+ while (!ea->quit_request.is_set()) {
+ // Nothing to do if we already know the plugins have changed.
+ if (!ea->plugins_changed.is_set()) {
+ MutexLock lock(ea->plugins_lock);
+
+ Vector<PluginConfigIOS> loaded_plugins = get_plugins();
+
+ if (ea->plugins.size() != loaded_plugins.size()) {
+ ea->plugins_changed.set();
+ } else {
+ for (int i = 0; i < ea->plugins.size(); i++) {
+ if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) {
+ ea->plugins_changed.set();
+ break;
+ }
+ }
+ }
+ }
+
+ uint64_t wait = 3000000;
+ uint64_t time = OS::get_singleton()->get_ticks_usec();
+ while (OS::get_singleton()->get_ticks_usec() - time < wait) {
+ OS::get_singleton()->delay_usec(300000);
+
+ if (ea->quit_request.is_set()) {
+ break;
+ }
+ }
+ }
+ }
+
protected:
- virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
- virtual void get_export_options(List<ExportOption> *r_options);
+ virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override;
+ virtual void get_export_options(List<ExportOption> *r_options) override;
public:
- virtual String get_name() const { return "iOS"; }
- virtual String get_os_name() const { return "iOS"; }
- virtual Ref<Texture2D> get_logo() const { return logo; }
+ virtual String get_name() const override { return "iOS"; }
+ virtual String get_os_name() const override { return "iOS"; }
+ virtual Ref<Texture2D> get_logo() const override { return logo; }
+
+ virtual bool should_update_export_options() override {
+ bool export_options_changed = plugins_changed.is_set();
+ if (export_options_changed) {
+ // don't clear unless we're reporting true, to avoid race
+ plugins_changed.clear();
+ }
+ return export_options_changed;
+ }
- virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
List<String> list;
list.push_back("ipa");
return list;
}
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
- virtual void add_module_code(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &p_name, const String &p_fid, const String &p_gid);
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
- virtual void get_platform_features(List<String> *r_features) {
+ virtual void get_platform_features(List<String> *r_features) override {
r_features->push_back("mobile");
r_features->push_back("iOS");
}
- virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
}
EditorExportPlatformIOS();
~EditorExportPlatformIOS();
+
+ /// List the gdip files in the directory specified by the p_path parameter.
+ static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) {
+ Vector<String> dir_files;
+ DirAccessRef da = DirAccess::open(p_path);
+ if (da) {
+ da->list_dir_begin();
+ while (true) {
+ String file = da->get_next();
+ if (file.is_empty()) {
+ break;
+ }
+
+ if (file == "." || file == "..") {
+ continue;
+ }
+
+ if (da->current_is_hidden()) {
+ continue;
+ }
+
+ if (da->current_is_dir()) {
+ if (p_check_directories) {
+ Vector<String> directory_files = list_plugin_config_files(p_path.plus_file(file), false);
+ for (int i = 0; i < directory_files.size(); ++i) {
+ dir_files.push_back(file.plus_file(directory_files[i]));
+ }
+ }
+
+ continue;
+ }
+
+ if (file.ends_with(PluginConfigIOS::PLUGIN_CONFIG_EXT)) {
+ dir_files.push_back(file);
+ }
+ }
+ da->list_dir_end();
+ }
+
+ return dir_files;
+ }
+
+ static Vector<PluginConfigIOS> get_plugins() {
+ Vector<PluginConfigIOS> loaded_plugins;
+
+ String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("ios/plugins");
+
+ if (DirAccess::exists(plugins_dir)) {
+ Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true);
+
+ if (!plugins_filenames.is_empty()) {
+ Ref<ConfigFile> config_file = memnew(ConfigFile);
+ for (int i = 0; i < plugins_filenames.size(); i++) {
+ PluginConfigIOS config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i]));
+ if (config.valid_config) {
+ loaded_plugins.push_back(config);
+ } else {
+ print_error("Invalid plugin config file " + plugins_filenames[i]);
+ }
+ }
+ }
+ }
+
+ return loaded_plugins;
+ }
+
+ static Vector<PluginConfigIOS> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
+ Vector<PluginConfigIOS> enabled_plugins;
+ Vector<PluginConfigIOS> all_plugins = get_plugins();
+ for (int i = 0; i < all_plugins.size(); i++) {
+ PluginConfigIOS plugin = all_plugins[i];
+ bool enabled = p_presets->get("plugins/" + plugin.name);
+ if (enabled) {
+ enabled_plugins.push_back(plugin);
+ }
+ }
+
+ return enabled_plugins;
+ }
};
void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
- String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name");
- if (driver == "GLES2") {
- r_features->push_back("etc");
- } else if (driver == "Vulkan") {
+ String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
+ r_features->push_back("pvrtc");
+ if (driver == "Vulkan") {
// FIXME: Review if this is correct.
r_features->push_back("etc2");
}
@@ -181,9 +315,9 @@ Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_ge
struct LoadingScreenInfo {
const char *preset_key;
const char *export_name;
- int width;
- int height;
- bool rotate;
+ int width = 0;
+ int height = 0;
+ bool rotate = false;
};
static const LoadingScreenInfo loading_screen_infos[] = {
@@ -205,30 +339,36 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ Vector<ExportArchitecture> architectures = _get_supported_architectures();
+ for (int i = 0; i < architectures.size(); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default));
+ }
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "iPhone Developer"));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1));
-
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/arkit"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/camera"), false));
+ Vector<PluginConfigIOS> found_plugins = get_plugins();
+ for (int i = 0; i < found_plugins.size(); i++) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false));
+ }
+ plugins_changed.clear();
+ plugins = found_plugins;
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/game_center"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/in_app_purchases"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_files_app"), false));
@@ -255,16 +395,18 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale To Fit,Scale To Fill,Scale"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color()));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "launch_screens/generate_missing"), false));
for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), ""));
}
-
- Vector<ExportArchitecture> architectures = _get_supported_architectures();
- for (int i = 0; i < architectures.size(); ++i) {
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default));
- }
}
void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) {
@@ -274,6 +416,12 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
"ad-hoc",
"enterprise"
};
+ static const String storyboard_image_scale_mode[] = {
+ "center",
+ "scaleAspectFit",
+ "scaleAspectFill",
+ "scaleToFill"
+ };
String str;
String strnew;
str.parse_utf8((const char *)pfile.ptr(), pfile.size());
@@ -293,8 +441,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$name", p_config.pkg_name) + "\n";
} else if (lines[i].find("$info") != -1) {
strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n";
- } else if (lines[i].find("$identifier") != -1) {
- strnew += lines[i].replace("$identifier", p_preset->get("application/identifier")) + "\n";
+ } else if (lines[i].find("$bundle_identifier") != -1) {
+ strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n";
} else if (lines[i].find("$short_version") != -1) {
strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
} else if (lines[i].find("$version") != -1) {
@@ -331,33 +479,23 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$docs_in_place", ((bool)p_preset->get("user_data/accessible_from_files_app")) ? "<true/>" : "<false/>") + "\n";
} else if (lines[i].find("$docs_sharing") != -1) {
strnew += lines[i].replace("$docs_sharing", ((bool)p_preset->get("user_data/accessible_from_itunes_sharing")) ? "<true/>" : "<false/>") + "\n";
- } else if (lines[i].find("$access_wifi") != -1) {
- bool is_on = p_preset->get("capabilities/access_wifi");
- strnew += lines[i].replace("$access_wifi", is_on ? "1" : "0") + "\n";
- } else if (lines[i].find("$game_center") != -1) {
- bool is_on = p_preset->get("capabilities/game_center");
- strnew += lines[i].replace("$game_center", is_on ? "1" : "0") + "\n";
- } else if (lines[i].find("$in_app_purchases") != -1) {
- bool is_on = p_preset->get("capabilities/in_app_purchases");
- strnew += lines[i].replace("$in_app_purchases", is_on ? "1" : "0") + "\n";
- } else if (lines[i].find("$push_notifications") != -1) {
+ } else if (lines[i].find("$entitlements_push_notifications") != -1) {
bool is_on = p_preset->get("capabilities/push_notifications");
- strnew += lines[i].replace("$push_notifications", is_on ? "1" : "0") + "\n";
+ strnew += lines[i].replace("$entitlements_push_notifications", is_on ? "<key>aps-environment</key><string>development</string>" : "") + "\n";
} else if (lines[i].find("$required_device_capabilities") != -1) {
String capabilities;
// I've removed armv7 as we can run on 64bit only devices
// Note that capabilities listed here are requirements for the app to be installed.
// They don't enable anything.
+ Vector<String> capabilities_list = p_config.capabilities;
- if ((bool)p_preset->get("capabilities/arkit")) {
- capabilities += "<string>arkit</string>\n";
- }
- if ((bool)p_preset->get("capabilities/game_center")) {
- capabilities += "<string>gamekit</string>\n";
+ if ((bool)p_preset->get("capabilities/access_wifi") && !capabilities_list.has("wifi")) {
+ capabilities_list.push_back("wifi");
}
- if ((bool)p_preset->get("capabilities/access_wifi")) {
- capabilities += "<string>wifi</string>\n";
+
+ for (int idx = 0; idx < capabilities_list.size(); idx++) {
+ capabilities += "<string>" + capabilities_list[idx] + "</string>\n";
}
strnew += lines[i].replace("$required_device_capabilities", capabilities);
@@ -387,6 +525,60 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
} else if (lines[i].find("$photolibrary_usage_description") != -1) {
String description = p_preset->get("privacy/photolibrary_usage_description");
strnew += lines[i].replace("$photolibrary_usage_description", description) + "\n";
+ } else if (lines[i].find("$plist_launch_screen_name") != -1) {
+ bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+ String value = is_on ? "<key>UILaunchStoryboardName</key>\n<string>Launch Screen</string>" : "";
+ strnew += lines[i].replace("$plist_launch_screen_name", value) + "\n";
+ } else if (lines[i].find("$pbx_launch_screen_file_reference") != -1) {
+ bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+ String value = is_on ? "90DD2D9D24B36E8000717FE1 = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = \"Launch Screen.storyboard\"; sourceTree = \"<group>\"; };" : "";
+ strnew += lines[i].replace("$pbx_launch_screen_file_reference", value) + "\n";
+ } else if (lines[i].find("$pbx_launch_screen_copy_files") != -1) {
+ bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+ String value = is_on ? "90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */," : "";
+ strnew += lines[i].replace("$pbx_launch_screen_copy_files", value) + "\n";
+ } else if (lines[i].find("$pbx_launch_screen_build_phase") != -1) {
+ bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+ String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */," : "";
+ strnew += lines[i].replace("$pbx_launch_screen_build_phase", value) + "\n";
+ } else if (lines[i].find("$pbx_launch_screen_build_reference") != -1) {
+ bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+ String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; };" : "";
+ strnew += lines[i].replace("$pbx_launch_screen_build_reference", value) + "\n";
+ } else if (lines[i].find("$pbx_launch_image_usage_setting") != -1) {
+ bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard");
+ String value = is_on ? "" : "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;";
+ strnew += lines[i].replace("$pbx_launch_image_usage_setting", value) + "\n";
+ } else if (lines[i].find("$launch_screen_image_mode") != -1) {
+ int image_scale_mode = p_preset->get("storyboard/image_scale_mode");
+ String value;
+
+ switch (image_scale_mode) {
+ case 0: {
+ String logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
+ bool is_on = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize");
+ // If custom logo is not specified, Godot does not scale default one, so we should do the same.
+ value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center";
+ } break;
+ default: {
+ value = storyboard_image_scale_mode[image_scale_mode - 1];
+ }
+ }
+
+ strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n";
+ } else if (lines[i].find("$launch_screen_background_color") != -1) {
+ bool use_custom = p_preset->get("storyboard/use_custom_bg_color");
+ Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : ProjectSettings::get_singleton()->get("application/boot_splash/bg_color");
+ const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"";
+
+ Dictionary value_dictionary;
+ value_dictionary["red"] = color.r;
+ value_dictionary["green"] = color.g;
+ value_dictionary["blue"] = color.b;
+ value_dictionary["alpha"] = color.a;
+ String value = value_format.format(value_dictionary, "$_");
+
+ strnew += lines[i].replace("$launch_screen_background_color", value) + "\n";
} else {
strnew += lines[i] + "\n";
}
@@ -476,7 +668,7 @@ struct IconInfo {
const char *actual_size_side;
const char *scale;
const char *unscaled_size;
- bool is_required;
+ bool is_required = false;
};
static const IconInfo icon_infos[] = {
@@ -496,7 +688,6 @@ static const IconInfo icon_infos[] = {
{ "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false },
{ "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false }
-
};
Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) {
@@ -588,7 +779,75 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
return OK;
}
-Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
+Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
+ const String custom_launch_image_2x = p_preset->get("storyboard/custom_image@2x");
+ const String custom_launch_image_3x = p_preset->get("storyboard/custom_image@3x");
+
+ if (custom_launch_image_2x.length() > 0 && custom_launch_image_3x.length() > 0) {
+ Ref<Image> image;
+ String image_path = p_dest_dir.plus_file("splash@2x.png");
+ image.instance();
+ Error err = image->load(custom_launch_image_2x);
+
+ if (err) {
+ image.unref();
+ return err;
+ }
+
+ if (image->save_png(image_path) != OK) {
+ return ERR_FILE_CANT_WRITE;
+ }
+
+ image.unref();
+ image_path = p_dest_dir.plus_file("splash@3x.png");
+ image.instance();
+ err = image->load(custom_launch_image_3x);
+
+ if (err) {
+ image.unref();
+ return err;
+ }
+
+ if (image->save_png(image_path) != OK) {
+ return ERR_FILE_CANT_WRITE;
+ }
+ } else {
+ Ref<Image> splash;
+
+ const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image");
+
+ if (!splash_path.is_empty()) {
+ splash.instance();
+ const Error err = splash->load(splash_path);
+ if (err) {
+ splash.unref();
+ }
+ }
+
+ if (splash.is_null()) {
+ splash = Ref<Image>(memnew(Image(boot_splash_png)));
+ }
+
+ // Using same image for both @2x and @3x
+ // because Godot's own boot logo uses single image for all resolutions.
+ // Also not using @1x image, because devices using this image variant
+ // are not supported by iOS 9, which is minimal target.
+ const String splash_png_path_2x = p_dest_dir.plus_file("splash@2x.png");
+ const String splash_png_path_3x = p_dest_dir.plus_file("splash@3x.png");
+
+ if (splash->save_png(splash_png_path_2x) != OK) {
+ return ERR_FILE_CANT_WRITE;
+ }
+
+ if (splash->save_png(splash_png_path_3x) != OK) {
+ return ERR_FILE_CANT_WRITE;
+ }
+ }
+
+ return OK;
+}
+
+Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
DirAccess *da = DirAccess::open(p_dest_dir);
ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_dest_dir + "'.");
@@ -704,7 +963,7 @@ Error EditorExportPlatformIOS::_walk_dir_recursive(DirAccess *p_da, FileHandler
struct CodesignData {
const Ref<EditorExportPreset> &preset;
- bool debug;
+ bool debug = false;
CodesignData(const Ref<EditorExportPreset> &p_preset, bool p_debug) :
preset(p_preset),
@@ -721,7 +980,7 @@ Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) {
codesign_args.push_back("-s");
codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release"));
codesign_args.push_back(p_file);
- return OS::get_singleton()->execute("codesign", codesign_args, true);
+ return OS::get_singleton()->execute("codesign", codesign_args);
}
return OK;
}
@@ -773,15 +1032,6 @@ struct ExportLibsData {
};
void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) {
- Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
- Vector<String> frameworks;
- for (int i = 0; i < export_plugins.size(); ++i) {
- Vector<String> plugin_frameworks = export_plugins[i]->get_ios_frameworks();
- for (int j = 0; j < plugin_frameworks.size(); ++j) {
- frameworks.push_back(plugin_frameworks[j]);
- }
- }
-
// that is just a random number, we just need Godot IDs not to clash with
// existing IDs in the project.
PbxId current_id = { 0x58938401, 0, 0 };
@@ -790,17 +1040,37 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
String pbx_frameworks_refs;
String pbx_resources_build;
String pbx_resources_refs;
+ String pbx_embeded_frameworks;
const String file_info_format = String("$build_id = {isa = PBXBuildFile; fileRef = $ref_id; };\n") +
"$ref_id = {isa = PBXFileReference; lastKnownFileType = $file_type; name = \"$name\"; path = \"$file_path\"; sourceTree = \"<group>\"; };\n";
+
for (int i = 0; i < p_additional_assets.size(); ++i) {
+ String additional_asset_info_format = file_info_format;
+
String build_id = (++current_id).str();
String ref_id = (++current_id).str();
+ String framework_id = "";
+
const IOSExportAsset &asset = p_additional_assets[i];
String type;
if (asset.exported_path.ends_with(".framework")) {
+ if (asset.should_embed) {
+ additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n";
+ framework_id = (++current_id).str();
+ pbx_embeded_frameworks += framework_id + ",\n";
+ }
+
type = "wrapper.framework";
+ } else if (asset.exported_path.ends_with(".xcframework")) {
+ if (asset.should_embed) {
+ additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n";
+ framework_id = (++current_id).str();
+ pbx_embeded_frameworks += framework_id + ",\n";
+ }
+
+ type = "wrapper.xcframework";
} else if (asset.exported_path.ends_with(".dylib")) {
type = "compiled.mach-o.dylib";
} else if (asset.exported_path.ends_with(".a")) {
@@ -825,40 +1095,22 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
format_dict["name"] = asset.exported_path.get_file();
format_dict["file_path"] = asset.exported_path;
format_dict["file_type"] = type;
- pbx_files += file_info_format.format(format_dict, "$_");
+ if (framework_id.length() > 0) {
+ format_dict["framework_id"] = framework_id;
+ }
+ pbx_files += additional_asset_info_format.format(format_dict, "$_");
}
// Note, frameworks like gamekit are always included in our project.pbxprof file
// even if turned off in capabilities.
- // We do need our ARKit framework
- if ((bool)p_preset->get("capabilities/arkit")) {
- String build_id = (++current_id).str();
- String ref_id = (++current_id).str();
-
- if (pbx_frameworks_build.length() > 0) {
- pbx_frameworks_build += ",\n";
- pbx_frameworks_refs += ",\n";
- }
-
- pbx_frameworks_build += build_id;
- pbx_frameworks_refs += ref_id;
-
- Dictionary format_dict;
- format_dict["build_id"] = build_id;
- format_dict["ref_id"] = ref_id;
- format_dict["name"] = "ARKit.framework";
- format_dict["file_path"] = "System/Library/Frameworks/ARKit.framework";
- format_dict["file_type"] = "wrapper.framework";
- pbx_files += file_info_format.format(format_dict, "$_");
- }
-
String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size());
str = str.replace("$additional_pbx_files", pbx_files);
str = str.replace("$additional_pbx_frameworks_build", pbx_frameworks_build);
str = str.replace("$additional_pbx_frameworks_refs", pbx_frameworks_refs);
str = str.replace("$additional_pbx_resources_build", pbx_resources_build);
str = str.replace("$additional_pbx_resources_refs", pbx_resources_refs);
+ str = str.replace("$pbx_embeded_frameworks", pbx_embeded_frameworks);
CharString cs = str.utf8();
p_project_data.resize(cs.size() - 1);
@@ -867,51 +1119,175 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
}
}
-Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, Vector<IOSExportAsset> &r_exported_assets) {
+Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'.");
+
+ String binary_name = p_out_dir.get_file().get_basename();
+
+ DirAccess *da = DirAccess::create_for_path(p_asset);
+ if (!da) {
+ memdelete(filesystem_da);
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + ".");
+ }
+ bool file_exists = da->file_exists(p_asset);
+ bool dir_exists = da->dir_exists(p_asset);
+ if (!file_exists && !dir_exists) {
+ memdelete(da);
+ memdelete(filesystem_da);
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ String base_dir = p_asset.get_base_dir().replace("res://", "");
+ String destination_dir;
+ String destination;
+ String asset_path;
+
+ bool create_framework = false;
+
+ if (p_is_framework && p_asset.ends_with(".dylib")) {
+ // For iOS we need to turn .dylib into .framework
+ // to be able to send application to AppStore
+ asset_path = String("dylibs").plus_file(base_dir);
+
+ String file_name;
+
+ if (!p_custom_file_name) {
+ file_name = p_asset.get_basename().get_file();
+ } else {
+ file_name = *p_custom_file_name;
+ }
+
+ String framework_name = file_name + ".framework";
+
+ asset_path = asset_path.plus_file(framework_name);
+ destination_dir = p_out_dir.plus_file(asset_path);
+ destination = destination_dir.plus_file(file_name);
+ create_framework = true;
+ } else if (p_is_framework && (p_asset.ends_with(".framework") || p_asset.ends_with(".xcframework"))) {
+ asset_path = String("dylibs").plus_file(base_dir);
+
+ String file_name;
+
+ if (!p_custom_file_name) {
+ file_name = p_asset.get_file();
+ } else {
+ file_name = *p_custom_file_name;
+ }
+
+ asset_path = asset_path.plus_file(file_name);
+ destination_dir = p_out_dir.plus_file(asset_path);
+ destination = destination_dir;
+ } else {
+ asset_path = base_dir;
+
+ String file_name;
+
+ if (!p_custom_file_name) {
+ file_name = p_asset.get_file();
+ } else {
+ file_name = *p_custom_file_name;
+ }
+
+ destination_dir = p_out_dir.plus_file(asset_path);
+ asset_path = asset_path.plus_file(file_name);
+ destination = p_out_dir.plus_file(asset_path);
+ }
+
+ if (!filesystem_da->dir_exists(destination_dir)) {
+ Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
+ if (make_dir_err) {
+ memdelete(da);
+ memdelete(filesystem_da);
+ return make_dir_err;
+ }
+ }
+
+ Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
+ memdelete(da);
+ if (err) {
+ memdelete(filesystem_da);
+ return err;
+ }
+ IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed };
+ r_exported_assets.push_back(exported_asset);
+
+ if (create_framework) {
+ String file_name;
+
+ if (!p_custom_file_name) {
+ file_name = p_asset.get_basename().get_file();
+ } else {
+ file_name = *p_custom_file_name;
+ }
+
+ String framework_name = file_name + ".framework";
+
+ // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib
+ {
+ List<String> install_name_args;
+ install_name_args.push_back("-id");
+ install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name));
+ install_name_args.push_back(destination);
+
+ OS::get_singleton()->execute("install_name_tool", install_name_args);
+ }
+
+ // Creating Info.plist
+ {
+ String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ "<plist version=\"1.0\">\n"
+ "<dict>\n"
+ "<key>CFBundleShortVersionString</key>\n"
+ "<string>1.0</string>\n"
+ "<key>CFBundleIdentifier</key>\n"
+ "<string>com.gdnative.framework.$name</string>\n"
+ "<key>CFBundleName</key>\n"
+ "<string>$name</string>\n"
+ "<key>CFBundleExecutable</key>\n"
+ "<string>$name</string>\n"
+ "<key>DTPlatformName</key>\n"
+ "<string>iphoneos</string>\n"
+ "<key>CFBundleInfoDictionaryVersion</key>\n"
+ "<string>6.0</string>\n"
+ "<key>CFBundleVersion</key>\n"
+ "<string>1</string>\n"
+ "<key>CFBundlePackageType</key>\n"
+ "<string>FMWK</string>\n"
+ "<key>MinimumOSVersion</key>\n"
+ "<string>10.0</string>\n"
+ "</dict>\n"
+ "</plist>";
+
+ String info_plist = info_plist_format.replace("$name", file_name);
+
+ FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE);
+ if (f) {
+ f->store_string(info_plist);
+ f->close();
+ memdelete(f);
+ }
+ }
+ }
+
+ memdelete(filesystem_da);
+
+ return OK;
+}
+
+Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) {
String asset = p_assets[f_idx];
if (!asset.begins_with("res://")) {
// either SDK-builtin or already a part of the export template
- IOSExportAsset exported_asset = { asset, p_is_framework };
+ IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed };
r_exported_assets.push_back(exported_asset);
} else {
- DirAccess *da = DirAccess::create_for_path(asset);
- if (!da) {
- memdelete(filesystem_da);
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + asset + ".");
- }
- bool file_exists = da->file_exists(asset);
- bool dir_exists = da->dir_exists(asset);
- if (!file_exists && !dir_exists) {
- memdelete(da);
- memdelete(filesystem_da);
- return ERR_FILE_NOT_FOUND;
- }
- String additional_dir = p_is_framework && asset.ends_with(".dylib") ? "/dylibs/" : "/";
- String destination_dir = p_out_dir + additional_dir + asset.get_base_dir().replace("res://", "");
- if (!filesystem_da->dir_exists(destination_dir)) {
- Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
- if (make_dir_err) {
- memdelete(da);
- memdelete(filesystem_da);
- return make_dir_err;
- }
- }
-
- String destination = destination_dir.plus_file(asset.get_file());
- Error err = dir_exists ? da->copy_dir(asset, destination) : da->copy(asset, destination);
- memdelete(da);
- if (err) {
- memdelete(filesystem_da);
- return err;
- }
- IOSExportAsset exported_asset = { destination, p_is_framework };
- r_exported_assets.push_back(exported_asset);
+ Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
}
}
- memdelete(filesystem_da);
return OK;
}
@@ -919,19 +1295,23 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) {
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
for (int i = 0; i < export_plugins.size(); i++) {
- Vector<String> frameworks = export_plugins[i]->get_ios_frameworks();
- Error err = _export_additional_assets(p_out_dir, frameworks, true, r_exported_assets);
+ Vector<String> linked_frameworks = export_plugins[i]->get_ios_frameworks();
+ Error err = _export_additional_assets(p_out_dir, linked_frameworks, true, false, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
+
+ Vector<String> embedded_frameworks = export_plugins[i]->get_ios_embedded_frameworks();
+ err = _export_additional_assets(p_out_dir, embedded_frameworks, true, true, r_exported_assets);
ERR_FAIL_COND_V(err, err);
Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs();
for (int j = 0; j < project_static_libs.size(); j++) {
project_static_libs.write[j] = project_static_libs[j].get_file(); // Only the file name as it's copied to the project
}
- err = _export_additional_assets(p_out_dir, project_static_libs, true, r_exported_assets);
+ err = _export_additional_assets(p_out_dir, project_static_libs, true, true, r_exported_assets);
ERR_FAIL_COND_V(err, err);
Vector<String> ios_bundle_files = export_plugins[i]->get_ios_bundle_files();
- err = _export_additional_assets(p_out_dir, ios_bundle_files, false, r_exported_assets);
+ err = _export_additional_assets(p_out_dir, ios_bundle_files, false, false, r_exported_assets);
ERR_FAIL_COND_V(err, err);
}
@@ -939,7 +1319,7 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
for (int i = 0; i < p_libraries.size(); ++i) {
library_paths.push_back(p_libraries[i].path);
}
- Error err = _export_additional_assets(p_out_dir, library_paths, true, r_exported_assets);
+ Error err = _export_additional_assets(p_out_dir, library_paths, true, true, r_exported_assets);
ERR_FAIL_COND_V(err, err);
return OK;
@@ -957,20 +1337,198 @@ Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<Edit
return enabled_archs;
}
-void EditorExportPlatformIOS::add_module_code(const Ref<EditorExportPreset> &p_preset, EditorExportPlatformIOS::IOSConfigData &p_config_data, const String &p_name, const String &p_fid, const String &p_gid) {
- if ((bool)p_preset->get("capabilities/" + p_name)) {
- //add module static library
- print_line("ADDING MODULE: " + p_name);
+Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug) {
+ String plugin_definition_cpp_code;
+ String plugin_initialization_cpp_code;
+ String plugin_deinitialization_cpp_code;
- p_config_data.modules_buildfile += p_gid + " /* libgodot_" + p_name + "_module.a in Frameworks */ = {isa = PBXBuildFile; fileRef = " + p_fid + " /* libgodot_" + p_name + "_module.a */; };\n\t\t";
- p_config_data.modules_fileref += p_fid + " /* libgodot_" + p_name + "_module.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = godot_" + p_name + "_module ; path = \"libgodot_" + p_name + "_module.a\"; sourceTree = \"<group>\"; };\n\t\t";
- p_config_data.modules_buildphase += p_gid + " /* libgodot_" + p_name + "_module.a */,\n\t\t\t\t";
- p_config_data.modules_buildgrp += p_fid + " /* libgodot_" + p_name + "_module.a */,\n\t\t\t\t";
- } else {
- //add stub function for disabled module
- p_config_data.cpp_code += "void register_" + p_name + "_types() { /*stub*/ };\n";
- p_config_data.cpp_code += "void unregister_" + p_name + "_types() { /*stub*/ };\n";
+ Vector<String> plugin_linked_dependencies;
+ Vector<String> plugin_embedded_dependencies;
+ Vector<String> plugin_files;
+
+ Vector<PluginConfigIOS> enabled_plugins = get_enabled_plugins(p_preset);
+
+ Vector<String> added_linked_dependenciy_names;
+ Vector<String> added_embedded_dependenciy_names;
+ HashMap<String, String> plist_values;
+
+ Set<String> plugin_linker_flags;
+
+ Error err;
+
+ for (int i = 0; i < enabled_plugins.size(); i++) {
+ PluginConfigIOS plugin = enabled_plugins[i];
+
+ // Export plugin binary.
+ String plugin_main_binary = get_plugin_main_binary(plugin, p_debug);
+ String plugin_binary_result_file = plugin.binary.get_file();
+ // We shouldn't embed .xcframework that contains static libraries.
+ // Static libraries are not embedded anyway.
+ err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
+
+ ERR_FAIL_COND_V(err, err);
+
+ // Adding dependencies.
+ // Use separate container for names to check for duplicates.
+ for (int j = 0; j < plugin.linked_dependencies.size(); j++) {
+ String dependency = plugin.linked_dependencies[j];
+ String name = dependency.get_file();
+
+ if (added_linked_dependenciy_names.has(name)) {
+ continue;
+ }
+
+ added_linked_dependenciy_names.push_back(name);
+ plugin_linked_dependencies.push_back(dependency);
+ }
+
+ for (int j = 0; j < plugin.system_dependencies.size(); j++) {
+ String dependency = plugin.system_dependencies[j];
+ String name = dependency.get_file();
+
+ if (added_linked_dependenciy_names.has(name)) {
+ continue;
+ }
+
+ added_linked_dependenciy_names.push_back(name);
+ plugin_linked_dependencies.push_back(dependency);
+ }
+
+ for (int j = 0; j < plugin.embedded_dependencies.size(); j++) {
+ String dependency = plugin.embedded_dependencies[j];
+ String name = dependency.get_file();
+
+ if (added_embedded_dependenciy_names.has(name)) {
+ continue;
+ }
+
+ added_embedded_dependenciy_names.push_back(name);
+ plugin_embedded_dependencies.push_back(dependency);
+ }
+
+ plugin_files.append_array(plugin.files_to_copy);
+
+ // Capabilities
+ // Also checking for duplicates.
+ for (int j = 0; j < plugin.capabilities.size(); j++) {
+ String capability = plugin.capabilities[j];
+
+ if (p_config_data.capabilities.has(capability)) {
+ continue;
+ }
+
+ p_config_data.capabilities.push_back(capability);
+ }
+
+ // Linker flags
+ // Checking duplicates
+ for (int j = 0; j < plugin.linker_flags.size(); j++) {
+ String linker_flag = plugin.linker_flags[j];
+ plugin_linker_flags.insert(linker_flag);
+ }
+
+ // Plist
+ // Using hash map container to remove duplicates
+ const String *K = nullptr;
+
+ while ((K = plugin.plist.next(K))) {
+ String key = *K;
+ String value = plugin.plist[key];
+
+ if (key.is_empty() || value.is_empty()) {
+ continue;
+ }
+
+ plist_values[key] = value;
+ }
+
+ // CPP Code
+ String definition_comment = "// Plugin: " + plugin.name + "\n";
+ String initialization_method = plugin.initialization_method + "();\n";
+ String deinitialization_method = plugin.deinitialization_method + "();\n";
+
+ plugin_definition_cpp_code += definition_comment +
+ "extern void " + initialization_method +
+ "extern void " + deinitialization_method + "\n";
+
+ plugin_initialization_cpp_code += "\t" + initialization_method;
+ plugin_deinitialization_cpp_code += "\t" + deinitialization_method;
+ }
+
+ // Updating `Info.plist`
+ {
+ const String *K = nullptr;
+ while ((K = plist_values.next(K))) {
+ String key = *K;
+ String value = plist_values[key];
+
+ if (key.is_empty() || value.is_empty()) {
+ continue;
+ }
+
+ p_config_data.plist_content += "<key>" + key + "</key><string>" + value + "</string>\n";
+ }
+ }
+
+ // Export files
+ {
+ // Export linked plugin dependency
+ err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
+
+ // Export embedded plugin dependency
+ err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
+
+ // Export plugin files
+ err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets);
+ ERR_FAIL_COND_V(err, err);
}
+
+ // Update CPP
+ {
+ Dictionary plugin_format;
+ plugin_format["definition"] = plugin_definition_cpp_code;
+ plugin_format["initialization"] = plugin_initialization_cpp_code;
+ plugin_format["deinitialization"] = plugin_deinitialization_cpp_code;
+
+ String plugin_cpp_code = "\n// Godot Plugins\n"
+ "void godot_ios_plugins_initialize();\n"
+ "void godot_ios_plugins_deinitialize();\n"
+ "// Exported Plugins\n\n"
+ "$definition"
+ "// Use Plugins\n"
+ "void godot_ios_plugins_initialize() {\n"
+ "$initialization"
+ "}\n\n"
+ "void godot_ios_plugins_deinitialize() {\n"
+ "$deinitialization"
+ "}\n";
+
+ p_config_data.cpp_code += plugin_cpp_code.format(plugin_format, "$_");
+ }
+
+ // Update Linker Flag Values
+ {
+ String result_linker_flags = " ";
+ for (Set<String>::Element *E = plugin_linker_flags.front(); E; E = E->next()) {
+ const String &flag = E->get();
+
+ if (flag.length() == 0) {
+ continue;
+ }
+
+ if (result_linker_flags.length() > 0) {
+ result_linker_flags += ' ';
+ }
+
+ result_linker_flags += flag;
+ }
+ result_linker_flags = result_linker_flags.replace("\"", "\\\"");
+ p_config_data.linker_flags += result_linker_flags;
+ }
+
+ return OK;
}
Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
@@ -1043,9 +1601,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
return ERR_SKIP;
}
- String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".fat.a";
+ String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".xcframework";
- print_line("Static library: " + library_to_use);
+ print_line("Static framework: " + library_to_use);
String pkg_name;
if (p_preset->get("application/name") != "") {
pkg_name = p_preset->get("application/name"); // app_name
@@ -1066,6 +1624,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
files_to_parse.insert("godot_ios/dummy.cpp");
files_to_parse.insert("godot_ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata");
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");
IOSConfigData config_data = {
pkg_name,
@@ -1077,9 +1637,12 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
"",
"",
"",
- ""
+ "",
+ Vector<String>()
};
+ Vector<IOSExportAsset> assets;
+
DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir);
ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE);
@@ -1092,8 +1655,8 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
return ERR_CANT_OPEN;
}
- add_module_code(p_preset, config_data, "arkit", "F9B95E6E2391205500AF0000", "F9C95E812391205C00BF0000");
- add_module_code(p_preset, config_data, "camera", "F9B95E6E2391205500AF0001", "F9C95E812391205C00BF0001");
+ err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug);
+ ERR_FAIL_COND_V(err, err);
//export rest of the files
int ret = unzGoToFirstFile(src_pkg_zip);
@@ -1126,7 +1689,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
if (files_to_parse.has(file)) {
_fix_config_file(p_preset, data, config_data, p_debug);
} else if (file.begins_with("libgodot.iphone")) {
- if (file != library_to_use) {
+ if (!file.begins_with(library_to_use) || file.ends_with(String("/empty"))) {
ret = unzGoToNextFile(src_pkg_zip);
continue; //ignore!
}
@@ -1134,22 +1697,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
#if defined(OSX_ENABLED) || defined(X11_ENABLED)
is_execute = true;
#endif
- file = "godot_ios.a";
- } else if (file.begins_with("libgodot_arkit")) {
- if ((bool)p_preset->get("capabilities/arkit") && file.ends_with(String(p_debug ? "debug" : "release") + ".fat.a")) {
- file = "libgodot_arkit_module.a";
- } else {
- ret = unzGoToNextFile(src_pkg_zip);
- continue; //ignore!
- }
- } else if (file.begins_with("libgodot_camera")) {
- if ((bool)p_preset->get("capabilities/camera") && file.ends_with(String(p_debug ? "debug" : "release") + ".fat.a")) {
- file = "libgodot_camera_module.a";
- } else {
- ret = unzGoToNextFile(src_pkg_zip);
- continue; //ignore!
- }
+ file = file.replace(library_to_use, binary_name + ".xcframework");
}
+
if (file == project_file) {
project_file_data = data;
}
@@ -1241,13 +1791,48 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
return err;
}
- err = _export_loading_screens(p_preset, dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/");
+ bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard");
+
+ String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/";
+ String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/";
+
+ DirAccess *launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+ if (!launch_screen_da) {
+ return ERR_CANT_CREATE;
+ }
+
+ if (use_storyboard) {
+ print_line("Using Launch Storyboard");
+
+ if (launch_screen_da->change_dir(launch_image_path) == OK) {
+ launch_screen_da->erase_contents_recursive();
+ launch_screen_da->remove(launch_image_path);
+ }
+
+ err = _export_loading_screen_file(p_preset, splash_image_path);
+ } else {
+ print_line("Using Launch Images");
+
+ const String launch_screen_path = dest_dir + binary_name + "/Launch Screen.storyboard";
+
+ launch_screen_da->remove(launch_screen_path);
+
+ if (launch_screen_da->change_dir(splash_image_path) == OK) {
+ launch_screen_da->erase_contents_recursive();
+ launch_screen_da->remove(splash_image_path);
+ }
+
+ err = _export_loading_screen_images(p_preset, launch_image_path);
+ }
+
+ memdelete(launch_screen_da);
+
if (err) {
return err;
}
print_line("Exporting additional assets");
- Vector<IOSExportAsset> assets;
_export_additional_assets(dest_dir + binary_name, libraries, assets);
_add_assets_to_project(p_preset, project_file_data, assets);
String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj";
@@ -1289,7 +1874,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
archive_args.push_back("archive");
archive_args.push_back("-archivePath");
archive_args.push_back(archive_path);
- err = OS::get_singleton()->execute("xcodebuild", archive_args, true);
+ err = OS::get_singleton()->execute("xcodebuild", archive_args);
ERR_FAIL_COND_V(err, err);
if (ep.step("Making .ipa", 4)) {
@@ -1304,7 +1889,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
export_args.push_back("-allowProvisioningUpdates");
export_args.push_back("-exportPath");
export_args.push_back(dest_dir);
- err = OS::get_singleton()->execute("xcodebuild", export_args, true);
+ err = OS::get_singleton()->execute("xcodebuild", export_args);
ERR_FAIL_COND_V(err, err);
#else
print_line(".ipa can only be built on macOS. Leaving Xcode project without building the package.");
@@ -1346,7 +1931,7 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset
valid = false;
}
- String identifier = p_preset->get("application/identifier");
+ String identifier = p_preset->get("application/bundle_identifier");
String pn_err;
if (!is_package_name_valid(identifier, &pn_err)) {
err += TTR("Invalid Identifier:") + " " + pn_err + "\n";
@@ -1365,13 +1950,13 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset
}
}
- String etc_error = test_etc2();
+ String etc_error = test_etc2_or_pvrtc();
if (etc_error != String()) {
valid = false;
err += etc_error;
}
- if (!err.empty()) {
+ if (!err.is_empty()) {
r_error = err;
}
@@ -1382,9 +1967,15 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() {
Ref<Image> img = memnew(Image(_iphone_logo));
logo.instance();
logo->create_from_image(img);
+
+ plugins_changed.set();
+
+ check_for_changes_thread.start(_check_for_changes_poll_thread, this);
}
EditorExportPlatformIOS::~EditorExportPlatformIOS() {
+ quit_request.set();
+ check_for_changes_thread.wait_to_finish();
}
void register_iphone_exporter() {
diff --git a/platform/iphone/export/export.h b/platform/iphone/export/export.h
index 043d21f533..4a79ca279f 100644
--- a/platform/iphone/export/export.h
+++ b/platform/iphone/export/export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm
deleted file mode 100644
index 8d470da1a8..0000000000
--- a/platform/iphone/game_center.mm
+++ /dev/null
@@ -1,387 +0,0 @@
-/*************************************************************************/
-/* game_center.mm */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#ifdef GAME_CENTER_ENABLED
-
-#include "game_center.h"
-
-#ifdef __IPHONE_9_0
-
-#import <GameKit/GameKit.h>
-extern "C" {
-
-#else
-
-extern "C" {
-#import <GameKit/GameKit.h>
-
-#endif
-
-#import "app_delegate.h"
-};
-
-GameCenter *GameCenter::instance = NULL;
-
-void GameCenter::_bind_methods() {
- ClassDB::bind_method(D_METHOD("is_authenticated"), &GameCenter::is_authenticated);
-
- ClassDB::bind_method(D_METHOD("post_score"), &GameCenter::post_score);
- ClassDB::bind_method(D_METHOD("award_achievement"), &GameCenter::award_achievement);
- ClassDB::bind_method(D_METHOD("reset_achievements"), &GameCenter::reset_achievements);
- ClassDB::bind_method(D_METHOD("request_achievements"), &GameCenter::request_achievements);
- ClassDB::bind_method(D_METHOD("request_achievement_descriptions"), &GameCenter::request_achievement_descriptions);
- ClassDB::bind_method(D_METHOD("show_game_center"), &GameCenter::show_game_center);
- ClassDB::bind_method(D_METHOD("request_identity_verification_signature"), &GameCenter::request_identity_verification_signature);
-
- ClassDB::bind_method(D_METHOD("get_pending_event_count"), &GameCenter::get_pending_event_count);
- ClassDB::bind_method(D_METHOD("pop_pending_event"), &GameCenter::pop_pending_event);
-};
-
-void GameCenter::return_connect_error(const char *p_error_description) {
- authenticated = false;
- Dictionary ret;
- ret["type"] = "authentication";
- ret["result"] = "error";
- ret["error_code"] = 0;
- ret["error_description"] = p_error_description;
- pending_events.push_back(ret);
-}
-
-void GameCenter::connect() {
- //if this class isn't available, game center isn't implemented
- if ((NSClassFromString(@"GKLocalPlayer")) == nil) {
- return_connect_error("GameCenter not available");
- return;
- }
-
- GKLocalPlayer *player = [GKLocalPlayer localPlayer];
- if (![player respondsToSelector:@selector(authenticateHandler)]) {
- return_connect_error("GameCenter doesn't respond to 'authenticateHandler'");
- return;
- }
-
- ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
- if (!root_controller) {
- return_connect_error("Window doesn't have root ViewController");
- return;
- }
-
- // This handler is called several times. First when the view needs to be shown, then again
- // after the view is cancelled or the user logs in. Or if the user's already logged in, it's
- // called just once to confirm they're authenticated. This is why no result needs to be specified
- // in the presentViewController phase. In this case, more calls to this function will follow.
- player.authenticateHandler = (^(UIViewController *controller, NSError *error) {
- if (controller) {
- [root_controller presentViewController:controller animated:YES completion:nil];
- } else {
- Dictionary ret;
- ret["type"] = "authentication";
- if (player.isAuthenticated) {
- ret["result"] = "ok";
- ret["player_id"] = [player.playerID UTF8String];
- GameCenter::get_singleton()->authenticated = true;
- } else {
- ret["result"] = "error";
- ret["error_code"] = (int64_t)error.code;
- ret["error_description"] = [error.localizedDescription UTF8String];
- GameCenter::get_singleton()->authenticated = false;
- };
-
- pending_events.push_back(ret);
- };
- });
-};
-
-bool GameCenter::is_authenticated() {
- return authenticated;
-};
-
-Error GameCenter::post_score(Variant p_score) {
- Dictionary params = p_score;
- ERR_FAIL_COND_V(!params.has("score") || !params.has("category"), ERR_INVALID_PARAMETER);
- float score = params["score"];
- String category = params["category"];
-
- NSString *cat_str = [[[NSString alloc] initWithUTF8String:category.utf8().get_data()] autorelease];
- GKScore *reporter = [[[GKScore alloc] initWithLeaderboardIdentifier:cat_str] autorelease];
- reporter.value = score;
-
- ERR_FAIL_COND_V([GKScore respondsToSelector:@selector(reportScores)], ERR_UNAVAILABLE);
-
- [GKScore reportScores:@[ reporter ]
- withCompletionHandler:^(NSError *error) {
- Dictionary ret;
- ret["type"] = "post_score";
- if (error == nil) {
- ret["result"] = "ok";
- } else {
- ret["result"] = "error";
- ret["error_code"] = (int64_t)error.code;
- ret["error_description"] = [error.localizedDescription UTF8String];
- };
-
- pending_events.push_back(ret);
- }];
-
- return OK;
-};
-
-Error GameCenter::award_achievement(Variant p_params) {
- Dictionary params = p_params;
- ERR_FAIL_COND_V(!params.has("name") || !params.has("progress"), ERR_INVALID_PARAMETER);
- String name = params["name"];
- float progress = params["progress"];
-
- NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
- GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier:name_str] autorelease];
- ERR_FAIL_COND_V(!achievement, FAILED);
-
- ERR_FAIL_COND_V([GKAchievement respondsToSelector:@selector(reportAchievements)], ERR_UNAVAILABLE);
-
- achievement.percentComplete = progress;
- achievement.showsCompletionBanner = NO;
- if (params.has("show_completion_banner")) {
- achievement.showsCompletionBanner = params["show_completion_banner"] ? YES : NO;
- }
-
- [GKAchievement reportAchievements:@[ achievement ]
- withCompletionHandler:^(NSError *error) {
- Dictionary ret;
- ret["type"] = "award_achievement";
- if (error == nil) {
- ret["result"] = "ok";
- } else {
- ret["result"] = "error";
- ret["error_code"] = (int64_t)error.code;
- };
-
- pending_events.push_back(ret);
- }];
-
- return OK;
-};
-
-void GameCenter::request_achievement_descriptions() {
- [GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:^(NSArray *descriptions, NSError *error) {
- Dictionary ret;
- ret["type"] = "achievement_descriptions";
- if (error == nil) {
- ret["result"] = "ok";
- PackedStringArray names;
- PackedStringArray titles;
- PackedStringArray unachieved_descriptions;
- PackedStringArray achieved_descriptions;
- PackedInt32Array maximum_points;
- Array hidden;
- Array replayable;
-
- for (int i = 0; i < [descriptions count]; i++) {
- GKAchievementDescription *description = [descriptions objectAtIndex:i];
-
- const char *str = [description.identifier UTF8String];
- names.push_back(String::utf8(str != NULL ? str : ""));
-
- str = [description.title UTF8String];
- titles.push_back(String::utf8(str != NULL ? str : ""));
-
- str = [description.unachievedDescription UTF8String];
- unachieved_descriptions.push_back(String::utf8(str != NULL ? str : ""));
-
- str = [description.achievedDescription UTF8String];
- achieved_descriptions.push_back(String::utf8(str != NULL ? str : ""));
-
- maximum_points.push_back(description.maximumPoints);
-
- hidden.push_back(description.hidden == YES);
-
- replayable.push_back(description.replayable == YES);
- }
-
- ret["names"] = names;
- ret["titles"] = titles;
- ret["unachieved_descriptions"] = unachieved_descriptions;
- ret["achieved_descriptions"] = achieved_descriptions;
- ret["maximum_points"] = maximum_points;
- ret["hidden"] = hidden;
- ret["replayable"] = replayable;
-
- } else {
- ret["result"] = "error";
- ret["error_code"] = (int64_t)error.code;
- };
-
- pending_events.push_back(ret);
- }];
-};
-
-void GameCenter::request_achievements() {
- [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
- Dictionary ret;
- ret["type"] = "achievements";
- if (error == nil) {
- ret["result"] = "ok";
- PackedStringArray names;
- PackedFloat32Array percentages;
-
- for (int i = 0; i < [achievements count]; i++) {
- GKAchievement *achievement = [achievements objectAtIndex:i];
- const char *str = [achievement.identifier UTF8String];
- names.push_back(String::utf8(str != NULL ? str : ""));
-
- percentages.push_back(achievement.percentComplete);
- }
-
- ret["names"] = names;
- ret["progress"] = percentages;
-
- } else {
- ret["result"] = "error";
- ret["error_code"] = (int64_t)error.code;
- };
-
- pending_events.push_back(ret);
- }];
-};
-
-void GameCenter::reset_achievements() {
- [GKAchievement resetAchievementsWithCompletionHandler:^(NSError *error) {
- Dictionary ret;
- ret["type"] = "reset_achievements";
- if (error == nil) {
- ret["result"] = "ok";
- } else {
- ret["result"] = "error";
- ret["error_code"] = (int64_t)error.code;
- };
-
- pending_events.push_back(ret);
- }];
-};
-
-Error GameCenter::show_game_center(Variant p_params) {
- ERR_FAIL_COND_V(!NSProtocolFromString(@"GKGameCenterControllerDelegate"), FAILED);
-
- Dictionary params = p_params;
-
- GKGameCenterViewControllerState view_state = GKGameCenterViewControllerStateDefault;
- if (params.has("view")) {
- String view_name = params["view"];
- if (view_name == "default") {
- view_state = GKGameCenterViewControllerStateDefault;
- } else if (view_name == "leaderboards") {
- view_state = GKGameCenterViewControllerStateLeaderboards;
- } else if (view_name == "achievements") {
- view_state = GKGameCenterViewControllerStateAchievements;
- } else if (view_name == "challenges") {
- view_state = GKGameCenterViewControllerStateChallenges;
- } else {
- return ERR_INVALID_PARAMETER;
- }
- }
-
- GKGameCenterViewController *controller = [[GKGameCenterViewController alloc] init];
- ERR_FAIL_COND_V(!controller, FAILED);
-
- ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
- ERR_FAIL_COND_V(!root_controller, FAILED);
-
- controller.gameCenterDelegate = root_controller;
- controller.viewState = view_state;
- if (view_state == GKGameCenterViewControllerStateLeaderboards) {
- controller.leaderboardIdentifier = nil;
- if (params.has("leaderboard_name")) {
- String name = params["leaderboard_name"];
- NSString *name_str = [[[NSString alloc] initWithUTF8String:name.utf8().get_data()] autorelease];
- controller.leaderboardIdentifier = name_str;
- }
- }
-
- [root_controller presentViewController:controller animated:YES completion:nil];
-
- return OK;
-};
-
-Error GameCenter::request_identity_verification_signature() {
- ERR_FAIL_COND_V(!is_authenticated(), ERR_UNAUTHORIZED);
-
- GKLocalPlayer *player = [GKLocalPlayer localPlayer];
- [player generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) {
- Dictionary ret;
- ret["type"] = "identity_verification_signature";
- if (error == nil) {
- ret["result"] = "ok";
- ret["public_key_url"] = [publicKeyUrl.absoluteString UTF8String];
- ret["signature"] = [[signature base64EncodedStringWithOptions:0] UTF8String];
- ret["salt"] = [[salt base64EncodedStringWithOptions:0] UTF8String];
- ret["timestamp"] = timestamp;
- ret["player_id"] = [player.playerID UTF8String];
- } else {
- ret["result"] = "error";
- ret["error_code"] = (int64_t)error.code;
- ret["error_description"] = [error.localizedDescription UTF8String];
- };
-
- pending_events.push_back(ret);
- }];
-
- return OK;
-};
-
-void GameCenter::game_center_closed() {
- Dictionary ret;
- ret["type"] = "show_game_center";
- ret["result"] = "ok";
- pending_events.push_back(ret);
-}
-
-int GameCenter::get_pending_event_count() {
- return pending_events.size();
-};
-
-Variant GameCenter::pop_pending_event() {
- Variant front = pending_events.front()->get();
- pending_events.pop_front();
-
- return front;
-};
-
-GameCenter *GameCenter::get_singleton() {
- return instance;
-};
-
-GameCenter::GameCenter() {
- ERR_FAIL_COND(instance != NULL);
- instance = this;
- authenticated = false;
-};
-
-GameCenter::~GameCenter() {}
-
-#endif
diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h
deleted file mode 100644
index 975aa4b70a..0000000000
--- a/platform/iphone/gl_view.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*************************************************************************/
-/* gl_view.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#import <AVFoundation/AVFoundation.h>
-#import <MediaPlayer/MediaPlayer.h>
-#import <OpenGLES/EAGL.h>
-#import <OpenGLES/ES1/gl.h>
-#import <OpenGLES/ES1/glext.h>
-#import <UIKit/UIKit.h>
-
-@protocol GLViewDelegate;
-
-@interface GLView : UIView <UIKeyInput> {
-@private
- // The pixel dimensions of the backbuffer
- GLint backingWidth;
- GLint backingHeight;
-
- EAGLContext *context;
-
- // OpenGL names for the renderbuffer and framebuffers used to render to this view
- GLuint viewRenderbuffer, viewFramebuffer;
-
- // OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist)
- GLuint depthRenderbuffer;
-
- BOOL useCADisplayLink;
- // CADisplayLink available on 3.1+ synchronizes the animation timer & drawing with the refresh rate of the display, only supports animation intervals of 1/60 1/30 & 1/15
- CADisplayLink *displayLink;
-
- // An animation timer that, when animation is started, will periodically call -drawView at the given rate.
- // Only used if CADisplayLink is not
- NSTimer *animationTimer;
-
- NSTimeInterval animationInterval;
-
- // Delegate to do our drawing, called by -drawView, which can be called manually or via the animation timer.
- id<GLViewDelegate> delegate;
-
- // Flag to denote that the -setupView method of a delegate has been called.
- // Resets to NO whenever the delegate changes.
- BOOL delegateSetup;
- BOOL active;
- float screen_scale;
-}
-
-@property(nonatomic, assign) id<GLViewDelegate> delegate;
-
-// AVPlayer-related properties
-@property(strong, nonatomic) AVAsset *avAsset;
-@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
-@property(strong, nonatomic) AVPlayer *avPlayer;
-@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
-
-@property(strong, nonatomic) UIWindow *backgroundWindow;
-
-@property(nonatomic) UITextAutocorrectionType autocorrectionType;
-
-- (void)startAnimation;
-- (void)stopAnimation;
-- (void)drawView;
-
-- (BOOL)canBecomeFirstResponder;
-
-- (void)open_keyboard;
-- (void)hide_keyboard;
-- (void)deleteBackward;
-- (BOOL)hasText;
-- (void)insertText:(NSString *)p_text;
-
-- (id)initGLES;
-- (BOOL)createFramebuffer;
-- (void)destroyFramebuffer;
-
-- (void)audioRouteChangeListenerCallback:(NSNotification *)notification;
-- (void)keyboardOnScreen:(NSNotification *)notification;
-- (void)keyboardHidden:(NSNotification *)notification;
-
-@property NSTimeInterval animationInterval;
-@property(nonatomic, assign) BOOL useCADisplayLink;
-
-@end
-
-@protocol GLViewDelegate <NSObject>
-
-@required
-
-// Draw with OpenGL ES
-- (void)drawView:(GLView *)view;
-
-@optional
-
-// Called whenever you need to do some initialization before rendering.
-- (void)setupView:(GLView *)view;
-
-@end
diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm
deleted file mode 100644
index 1169ebc6b4..0000000000
--- a/platform/iphone/gl_view.mm
+++ /dev/null
@@ -1,702 +0,0 @@
-/*************************************************************************/
-/* gl_view.mm */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#import "gl_view.h"
-
-#include "core/os/keyboard.h"
-#include "core/project_settings.h"
-#include "os_iphone.h"
-#include "servers/audio_server.h"
-
-#import <OpenGLES/EAGLDrawable.h>
-#import <QuartzCore/QuartzCore.h>
-
-/*
-@interface GLView (private)
-
-- (id)initGLES;
-- (BOOL)createFramebuffer;
-- (void)destroyFramebuffer;
-@end
-*/
-
-int gl_view_base_fb;
-static String keyboard_text;
-static GLView *_instance = NULL;
-
-static bool video_found_error = false;
-static bool video_playing = false;
-static CMTime video_current_time;
-
-void _show_keyboard(String);
-void _hide_keyboard();
-bool _play_video(String, float, String, String);
-bool _is_video_playing();
-void _pause_video();
-void _focus_out_video();
-void _unpause_video();
-void _stop_video();
-CGFloat _points_to_pixels(CGFloat);
-
-void _show_keyboard(String p_existing) {
- keyboard_text = p_existing;
- printf("instance on show is %p\n", _instance);
- [_instance open_keyboard];
-};
-
-void _hide_keyboard() {
- printf("instance on hide is %p\n", _instance);
- [_instance hide_keyboard];
- keyboard_text = "";
-};
-
-Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height) {
- UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, 0, 0);
- if (_instance != nil && [_instance respondsToSelector:@selector(safeAreaInsets)]) {
- insets = [_instance safeAreaInsets];
- }
- ERR_FAIL_COND_V(insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0,
- Rect2(0, 0, p_window_width, p_window_height));
- UIEdgeInsets window_insets = UIEdgeInsetsMake(_points_to_pixels(insets.top), _points_to_pixels(insets.left), _points_to_pixels(insets.bottom), _points_to_pixels(insets.right));
- return Rect2(window_insets.left, window_insets.top, p_window_width - window_insets.right - window_insets.left, p_window_height - window_insets.bottom - window_insets.top);
-}
-
-bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) {
- p_path = ProjectSettings::get_singleton()->globalize_path(p_path);
-
- NSString *file_path = [[[NSString alloc] initWithUTF8String:p_path.utf8().get_data()] autorelease];
-
- _instance.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:file_path]];
-
- _instance.avPlayerItem = [[AVPlayerItem alloc] initWithAsset:_instance.avAsset];
- [_instance.avPlayerItem addObserver:_instance forKeyPath:@"status" options:0 context:nil];
-
- _instance.avPlayer = [[AVPlayer alloc] initWithPlayerItem:_instance.avPlayerItem];
- _instance.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:_instance.avPlayer];
-
- [_instance.avPlayer addObserver:_instance forKeyPath:@"status" options:0 context:nil];
- [[NSNotificationCenter defaultCenter]
- addObserver:_instance
- selector:@selector(playerItemDidReachEnd:)
- name:AVPlayerItemDidPlayToEndTimeNotification
- object:[_instance.avPlayer currentItem]];
-
- [_instance.avPlayer addObserver:_instance forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
-
- [_instance.avPlayerLayer setFrame:_instance.bounds];
- [_instance.layer addSublayer:_instance.avPlayerLayer];
- [_instance.avPlayer play];
-
- AVMediaSelectionGroup *audioGroup = [_instance.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
-
- NSMutableArray *allAudioParams = [NSMutableArray array];
- for (id track in audioGroup.options) {
- NSString *language = [[track locale] localeIdentifier];
- NSLog(@"subtitle lang: %@", language);
-
- if ([language isEqualToString:[NSString stringWithUTF8String:p_audio_track.utf8()]]) {
- AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
- [audioInputParams setVolume:p_volume atTime:kCMTimeZero];
- [audioInputParams setTrackID:[track trackID]];
- [allAudioParams addObject:audioInputParams];
-
- AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
- [audioMix setInputParameters:allAudioParams];
-
- [_instance.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
- [_instance.avPlayer.currentItem setAudioMix:audioMix];
-
- break;
- }
- }
-
- AVMediaSelectionGroup *subtitlesGroup = [_instance.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
- NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
-
- for (id track in useableTracks) {
- NSString *language = [[track locale] localeIdentifier];
- NSLog(@"subtitle lang: %@", language);
-
- if ([language isEqualToString:[NSString stringWithUTF8String:p_subtitle_track.utf8()]]) {
- [_instance.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
- break;
- }
- }
-
- video_playing = true;
-
- return true;
-}
-
-bool _is_video_playing() {
- if (_instance.avPlayer.error) {
- printf("Error during playback\n");
- }
- return (_instance.avPlayer.rate > 0 && !_instance.avPlayer.error);
-}
-
-void _pause_video() {
- video_current_time = _instance.avPlayer.currentTime;
- [_instance.avPlayer pause];
- video_playing = false;
-}
-
-void _focus_out_video() {
- printf("focus out pausing video\n");
- [_instance.avPlayer pause];
-};
-
-void _unpause_video() {
- [_instance.avPlayer play];
- video_playing = true;
-};
-
-void _stop_video() {
- [_instance.avPlayer pause];
- [_instance.avPlayerLayer removeFromSuperlayer];
- _instance.avPlayer = nil;
- video_playing = false;
-}
-
-CGFloat _points_to_pixels(CGFloat points) {
- float pixelPerInch;
- if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
- pixelPerInch = 132;
- } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
- pixelPerInch = 163;
- } else {
- pixelPerInch = 160;
- }
- CGFloat pointsPerInch = 72.0;
- return (points / pointsPerInch * pixelPerInch);
-}
-
-@implementation GLView
-
-@synthesize animationInterval;
-
-static const int max_touches = 8;
-static UITouch *touches[max_touches];
-
-static void init_touches() {
- for (int i = 0; i < max_touches; i++) {
- touches[i] = NULL;
- };
-};
-
-static int get_touch_id(UITouch *p_touch) {
- int first = -1;
- for (int i = 0; i < max_touches; i++) {
- if (first == -1 && touches[i] == NULL) {
- first = i;
- continue;
- };
- if (touches[i] == p_touch)
- return i;
- };
-
- if (first != -1) {
- touches[first] = p_touch;
- return first;
- };
-
- return -1;
-};
-
-static int remove_touch(UITouch *p_touch) {
- int remaining = 0;
- for (int i = 0; i < max_touches; i++) {
- if (touches[i] == NULL)
- continue;
- if (touches[i] == p_touch)
- touches[i] = NULL;
- else
- ++remaining;
- };
- return remaining;
-};
-
-static void clear_touches() {
- for (int i = 0; i < max_touches; i++) {
- touches[i] = NULL;
- };
-};
-
-// Implement this to override the default layer class (which is [CALayer class]).
-// We do this so that our view will be backed by a layer that is capable of OpenGL ES rendering.
-+ (Class)layerClass {
- return [CAEAGLLayer class];
-}
-
-//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
-- (id)initWithCoder:(NSCoder *)coder {
- active = FALSE;
- if ((self = [super initWithCoder:coder])) {
- self = [self initGLES];
- }
- return self;
-}
-
-- (id)initGLES {
- // Get our backing layer
- CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
-
- // Configure it so that it is opaque, does not retain the contents of the backbuffer when displayed, and uses RGBA8888 color.
- eaglLayer.opaque = YES;
- eaglLayer.drawableProperties = [NSDictionary
- dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:FALSE],
- kEAGLDrawablePropertyRetainedBacking,
- kEAGLColorFormatRGBA8,
- kEAGLDrawablePropertyColorFormat,
- nil];
-
- // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
-
- // Create GL ES 2 context
- if (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES2") {
- context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
- NSLog(@"Setting up an OpenGL ES 2.0 context.");
- if (!context) {
- NSLog(@"Failed to create OpenGL ES 2.0 context!");
- return nil;
- }
- }
-
- if (![EAGLContext setCurrentContext:context]) {
- NSLog(@"Failed to set EAGLContext!");
- return nil;
- }
- if (![self createFramebuffer]) {
- NSLog(@"Failed to create frame buffer!");
- return nil;
- }
-
- // Default the animation interval to 1/60th of a second.
- animationInterval = 1.0 / 60.0;
- return self;
-}
-
-- (id<GLViewDelegate>)delegate {
- return delegate;
-}
-
-// Update the delegate, and if it needs a -setupView: call, set our internal flag so that it will be called.
-- (void)setDelegate:(id<GLViewDelegate>)d {
- delegate = d;
- delegateSetup = ![delegate respondsToSelector:@selector(setupView:)];
-}
-
-@synthesize useCADisplayLink;
-
-// If our view is resized, we'll be asked to layout subviews.
-// This is the perfect opportunity to also update the framebuffer so that it is
-// the same size as our display area.
-
-- (void)layoutSubviews {
- [EAGLContext setCurrentContext:context];
- [self destroyFramebuffer];
- [self createFramebuffer];
- [self drawView];
-}
-
-- (BOOL)createFramebuffer {
- // Generate IDs for a framebuffer object and a color renderbuffer
- UIScreen *mainscr = [UIScreen mainScreen];
- printf("******** screen size %i, %i\n", (int)mainscr.currentMode.size.width, (int)mainscr.currentMode.size.height);
- self.contentScaleFactor = mainscr.nativeScale;
-
- glGenFramebuffersOES(1, &viewFramebuffer);
- glGenRenderbuffersOES(1, &viewRenderbuffer);
-
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
- // This call associates the storage for the current render buffer with the EAGLDrawable (our CAEAGLLayer)
- // allowing us to draw into a buffer that will later be rendered to screen wherever the layer is (which corresponds with our view).
- [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer];
- glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
-
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-
- // For this sample, we also need a depth buffer, so we'll create and attach one via another renderbuffer.
- glGenRenderbuffersOES(1, &depthRenderbuffer);
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
- glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
- glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
-
- if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
- NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
- return NO;
- }
-
- if (OS::get_singleton()) {
- OS::VideoMode vm;
- vm.fullscreen = true;
- vm.width = backingWidth;
- vm.height = backingHeight;
- vm.resizable = false;
- OS::get_singleton()->set_video_mode(vm);
- OSIPhone::get_singleton()->set_base_framebuffer(viewFramebuffer);
- };
- gl_view_base_fb = viewFramebuffer;
-
- return YES;
-}
-
-// Clean up any buffers we have allocated.
-- (void)destroyFramebuffer {
- glDeleteFramebuffersOES(1, &viewFramebuffer);
- viewFramebuffer = 0;
- glDeleteRenderbuffersOES(1, &viewRenderbuffer);
- viewRenderbuffer = 0;
-
- if (depthRenderbuffer) {
- glDeleteRenderbuffersOES(1, &depthRenderbuffer);
- depthRenderbuffer = 0;
- }
-}
-
-- (void)startAnimation {
- if (active)
- return;
- active = TRUE;
- printf("start animation!\n");
- if (useCADisplayLink) {
- // Approximate frame rate
- // assumes device refreshes at 60 fps
- int frameInterval = (int)floor(animationInterval * 60.0f);
-
- displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
- [displayLink setFrameInterval:frameInterval];
-
- // Setup DisplayLink in main thread
- [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- } else {
- animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
- }
-
- if (video_playing) {
- _unpause_video();
- }
-}
-
-- (void)stopAnimation {
- if (!active)
- return;
- active = FALSE;
- printf("******** stop animation!\n");
-
- if (useCADisplayLink) {
- [displayLink invalidate];
- displayLink = nil;
- } else {
- [animationTimer invalidate];
- animationTimer = nil;
- }
-
- clear_touches();
-
- if (video_playing) {
- // save position
- }
-}
-
-- (void)setAnimationInterval:(NSTimeInterval)interval {
- animationInterval = interval;
- if ((useCADisplayLink && displayLink) || (!useCADisplayLink && animationTimer)) {
- [self stopAnimation];
- [self startAnimation];
- }
-}
-
-// Updates the OpenGL view when the timer fires
-- (void)drawView {
- if (!active) {
- printf("draw view not active!\n");
- return;
- };
- if (useCADisplayLink) {
- // Pause the CADisplayLink to avoid recursion
- [displayLink setPaused:YES];
-
- // Process all input events
- while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource)
- ;
-
- // We are good to go, resume the CADisplayLink
- [displayLink setPaused:NO];
- }
-
- // Make sure that you are drawing to the current context
- [EAGLContext setCurrentContext:context];
-
- // If our drawing delegate needs to have the view setup, then call -setupView: and flag that it won't need to be called again.
- if (!delegateSetup) {
- [delegate setupView:self];
- delegateSetup = YES;
- }
-
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
-
- [delegate drawView:self];
-
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
- [context presentRenderbuffer:GL_RENDERBUFFER_OES];
-
-#ifdef DEBUG_ENABLED
- GLenum err = glGetError();
- if (err)
- NSLog(@"DrawView: %x error", err);
-#endif
-}
-
-- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
- NSArray *tlist = [[event allTouches] allObjects];
- for (unsigned int i = 0; i < [tlist count]; i++) {
- if ([touches containsObject:[tlist objectAtIndex:i]]) {
- UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseBegan)
- continue;
- int tid = get_touch_id(touch);
- ERR_FAIL_COND(tid == -1);
- CGPoint touchPoint = [touch locationInView:self];
- OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
- };
- };
-}
-
-- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
- NSArray *tlist = [[event allTouches] allObjects];
- for (unsigned int i = 0; i < [tlist count]; i++) {
- if ([touches containsObject:[tlist objectAtIndex:i]]) {
- UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseMoved)
- continue;
- int tid = get_touch_id(touch);
- ERR_FAIL_COND(tid == -1);
- CGPoint touchPoint = [touch locationInView:self];
- CGPoint prev_point = [touch previousLocationInView:self];
- OSIPhone::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor);
- };
- };
-}
-
-- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
- NSArray *tlist = [[event allTouches] allObjects];
- for (unsigned int i = 0; i < [tlist count]; i++) {
- if ([touches containsObject:[tlist objectAtIndex:i]]) {
- UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseEnded)
- continue;
- int tid = get_touch_id(touch);
- ERR_FAIL_COND(tid == -1);
- remove_touch(touch);
- CGPoint touchPoint = [touch locationInView:self];
- OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
- };
- };
-}
-
-- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
- OSIPhone::get_singleton()->touches_cancelled();
- clear_touches();
-};
-
-- (BOOL)canBecomeFirstResponder {
- return YES;
-};
-
-- (void)open_keyboard {
- //keyboard_text = p_existing;
- [self becomeFirstResponder];
-};
-
-- (void)hide_keyboard {
- //keyboard_text = p_existing;
- [self resignFirstResponder];
-};
-
-- (void)keyboardOnScreen:(NSNotification *)notification {
- NSDictionary *info = notification.userInfo;
- NSValue *value = info[UIKeyboardFrameEndUserInfoKey];
-
- CGRect rawFrame = [value CGRectValue];
- CGRect keyboardFrame = [self convertRect:rawFrame fromView:nil];
-
- OSIPhone::get_singleton()->set_virtual_keyboard_height(_points_to_pixels(keyboardFrame.size.height));
-}
-
-- (void)keyboardHidden:(NSNotification *)notification {
- OSIPhone::get_singleton()->set_virtual_keyboard_height(0);
-}
-
-- (void)deleteBackward {
- if (keyboard_text.length())
- keyboard_text.erase(keyboard_text.length() - 1, 1);
- OSIPhone::get_singleton()->key(KEY_BACKSPACE, true);
-};
-
-- (BOOL)hasText {
- return keyboard_text.length() ? YES : NO;
-};
-
-- (void)insertText:(NSString *)p_text {
- String character;
- character.parse_utf8([p_text UTF8String]);
- keyboard_text = keyboard_text + character;
- OSIPhone::get_singleton()->key(character[0] == 10 ? KEY_ENTER : character[0], true);
- printf("inserting text with character %lc\n", (CharType)character[0]);
-};
-
-- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
- printf("*********** route changed!\n");
- NSDictionary *interuptionDict = notification.userInfo;
-
- NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
-
- switch (routeChangeReason) {
- case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
- NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
- NSLog(@"Headphone/Line plugged in");
- }; break;
-
- case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
- NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
- NSLog(@"Headphone/Line was pulled. Resuming video play....");
- if (_is_video_playing()) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [_instance.avPlayer play]; // NOTE: change this line according your current player implementation
- NSLog(@"resumed play");
- });
- };
- }; break;
-
- case AVAudioSessionRouteChangeReasonCategoryChange: {
- // called at start - also when other audio wants to play
- NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
- }; break;
- }
-}
-
-// When created via code however, we get initWithFrame
-- (id)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
- _instance = self;
- printf("after init super %p\n", self);
- if (self != nil) {
- self = [self initGLES];
- printf("after init gles %p\n", self);
- }
- init_touches();
- self.multipleTouchEnabled = YES;
- self.autocorrectionType = UITextAutocorrectionTypeNo;
-
- printf("******** adding observer for sound routing changes\n");
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(audioRouteChangeListenerCallback:)
- name:AVAudioSessionRouteChangeNotification
- object:nil];
-
- printf("******** adding observer for keyboard show/hide\n");
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(keyboardOnScreen:)
- name:UIKeyboardDidShowNotification
- object:nil];
- [[NSNotificationCenter defaultCenter]
- addObserver:self
- selector:@selector(keyboardHidden:)
- name:UIKeyboardDidHideNotification
- object:nil];
-
- //self.autoresizesSubviews = YES;
- //[self setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth];
-
- return self;
-}
-
-//- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers {
-// return YES;
-//}
-
-//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
-// return YES;
-//}
-
-// Stop animating and release resources when they are no longer needed.
-- (void)dealloc {
- [self stopAnimation];
-
- if ([EAGLContext currentContext] == context) {
- [EAGLContext setCurrentContext:nil];
- }
-
- [context release];
- context = nil;
-
- [super dealloc];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- if (object == _instance.avPlayerItem && [keyPath isEqualToString:@"status"]) {
- if (_instance.avPlayerItem.status == AVPlayerStatusFailed || _instance.avPlayer.status == AVPlayerStatusFailed) {
- _stop_video();
- video_found_error = true;
- }
-
- if (_instance.avPlayer.status == AVPlayerStatusReadyToPlay &&
- _instance.avPlayerItem.status == AVPlayerItemStatusReadyToPlay &&
- CMTIME_COMPARE_INLINE(video_current_time, ==, kCMTimeZero)) {
- //NSLog(@"time: %@", video_current_time);
-
- [_instance.avPlayer seekToTime:video_current_time];
- video_current_time = kCMTimeZero;
- }
- }
-
- if (object == _instance.avPlayer && [keyPath isEqualToString:@"rate"]) {
- NSLog(@"Player playback rate changed: %.5f", _instance.avPlayer.rate);
- if (_is_video_playing() && _instance.avPlayer.rate == 0.0 && !_instance.avPlayer.error) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [_instance.avPlayer play]; // NOTE: change this line according your current player implementation
- NSLog(@"resumed play");
- });
-
- NSLog(@" . . . PAUSED (or just started)");
- }
- }
-}
-
-- (void)playerItemDidReachEnd:(NSNotification *)notification {
- _stop_video();
-}
-
-@end
diff --git a/platform/iphone/godot_app_delegate.h b/platform/iphone/godot_app_delegate.h
new file mode 100644
index 0000000000..6335ada50e
--- /dev/null
+++ b/platform/iphone/godot_app_delegate.h
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* godot_app_delegate.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+typedef NSObject<UIApplicationDelegate> ApplicationDelegateService;
+
+@interface GodotApplicalitionDelegate : NSObject <UIApplicationDelegate>
+
+@property(class, readonly, strong) NSArray<ApplicationDelegateService *> *services;
+
++ (void)addService:(ApplicationDelegateService *)service;
+
+@end
diff --git a/platform/iphone/godot_app_delegate.m b/platform/iphone/godot_app_delegate.m
new file mode 100644
index 0000000000..3ce9bffc79
--- /dev/null
+++ b/platform/iphone/godot_app_delegate.m
@@ -0,0 +1,467 @@
+/*************************************************************************/
+/* godot_app_delegate.m */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import "godot_app_delegate.h"
+
+#import "app_delegate.h"
+
+@interface GodotApplicalitionDelegate ()
+
+@end
+
+@implementation GodotApplicalitionDelegate
+
+static NSMutableArray<ApplicationDelegateService *> *services = nil;
+
++ (NSArray<ApplicationDelegateService *> *)services {
+ return services;
+}
+
++ (void)load {
+ services = [NSMutableArray new];
+ [services addObject:[AppDelegate new]];
+}
+
++ (void)addService:(ApplicationDelegateService *)service {
+ if (!services || !service) {
+ return;
+ }
+ [services addObject:service];
+}
+
+// UIApplicationDelegate documantation can be found here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate
+
+// MARK: Window
+
+- (UIWindow *)window {
+ UIWindow *result = nil;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ UIWindow *value = [service window];
+
+ if (value) {
+ result = value;
+ }
+ }
+
+ return result;
+}
+
+// MARK: Initializing
+
+- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application willFinishLaunchingWithOptions:launchOptions]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application didFinishLaunchingWithOptions:launchOptions]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+/* Can be handled by Info.plist. Not yet supported by Godot.
+
+// MARK: Scene
+
+- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {}
+
+- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {}
+
+*/
+
+// MARK: Life-Cycle
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationDidBecomeActive:application];
+ }
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationWillResignActive:application];
+ }
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationDidEnterBackground:application];
+ }
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationWillEnterForeground:application];
+ }
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationWillTerminate:application];
+ }
+}
+
+// MARK: Environment Changes
+
+- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationProtectedDataDidBecomeAvailable:application];
+ }
+}
+
+- (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationProtectedDataWillBecomeUnavailable:application];
+ }
+}
+
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationDidReceiveMemoryWarning:application];
+ }
+}
+
+- (void)applicationSignificantTimeChange:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationSignificantTimeChange:application];
+ }
+}
+
+// MARK: App State Restoration
+
+- (BOOL)application:(UIApplication *)application shouldSaveSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application shouldSaveSecureApplicationState:coder]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (BOOL)application:(UIApplication *)application shouldRestoreSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application shouldRestoreSecureApplicationState:coder]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ UIViewController *controller = [service application:application viewControllerWithRestorationIdentifierPath:identifierComponents coder:coder];
+
+ if (controller) {
+ return controller;
+ }
+ }
+
+ return nil;
+}
+
+- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application willEncodeRestorableStateWithCoder:coder];
+ }
+}
+
+- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application didDecodeRestorableStateWithCoder:coder];
+ }
+}
+
+// MARK: Download Data in Background
+
+- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application handleEventsForBackgroundURLSession:identifier completionHandler:completionHandler];
+ }
+
+ completionHandler();
+}
+
+// MARK: Remote Notification
+
+// Moved to the iOS Plugin
+
+// MARK: User Activity and Handling Quick Actions
+
+- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application willContinueUserActivityWithType:userActivityType]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application continueUserActivity:userActivity restorationHandler:restorationHandler]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+- (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application didUpdateUserActivity:userActivity];
+ }
+}
+
+- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application didFailToContinueUserActivityWithType:userActivityType error:error];
+ }
+}
+
+- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application performActionForShortcutItem:shortcutItem completionHandler:completionHandler];
+ }
+}
+
+// MARK: WatchKit
+
+- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application handleWatchKitExtensionRequest:userInfo reply:reply];
+ }
+}
+
+// MARK: HealthKit
+
+- (void)applicationShouldRequestHealthAuthorization:(UIApplication *)application {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service applicationShouldRequestHealthAuthorization:application];
+ }
+}
+
+// MARK: Opening an URL
+
+- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:app openURL:url options:options]) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+// MARK: Disallowing Specified App Extension Types
+
+- (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(UIApplicationExtensionPointIdentifier)extensionPointIdentifier {
+ BOOL result = NO;
+
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ if ([service application:application shouldAllowExtensionPointIdentifier:extensionPointIdentifier]) {
+ result = YES;
+ }
+ }
+
+ return result;
+}
+
+// MARK: SiriKit
+
+- (id)application:(UIApplication *)application handlerForIntent:(INIntent *)intent API_AVAILABLE(ios(14.0)) {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ id result = [service application:application handlerForIntent:intent];
+
+ if (result) {
+ return result;
+ }
+ }
+
+ return nil;
+}
+
+// MARK: CloudKit
+
+- (void)application:(UIApplication *)application userDidAcceptCloudKitShareWithMetadata:(CKShareMetadata *)cloudKitShareMetadata {
+ for (ApplicationDelegateService *service in services) {
+ if (![service respondsToSelector:_cmd]) {
+ continue;
+ }
+
+ [service application:application userDidAcceptCloudKitShareWithMetadata:cloudKitShareMetadata];
+ }
+}
+
+/* Handled By Info.plist file for now
+
+// MARK: Interface Geometry
+
+- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {}
+
+*/
+
+@end
diff --git a/platform/iphone/godot_iphone.cpp b/platform/iphone/godot_iphone.mm
index b9d217c9d2..62bc2e6e52 100644
--- a/platform/iphone/godot_iphone.cpp
+++ b/platform/iphone/godot_iphone.mm
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* godot_iphone.cpp */
+/* godot_iphone.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/ustring.h"
+#include "core/string/ustring.h"
#include "main/main.h"
#include "os_iphone.h"
@@ -38,19 +38,49 @@
static OSIPhone *os = nullptr;
-extern "C" {
-int add_path(int p_argc, char **p_args);
-int add_cmdline(int p_argc, char **p_args);
+int add_path(int, char **);
+int add_cmdline(int, char **);
+int iphone_main(int, char **, String);
+
+int add_path(int p_argc, char **p_args) {
+ NSString *str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"];
+ if (!str) {
+ return p_argc;
+ }
+
+ p_args[p_argc++] = (char *)"--path";
+ p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
+ p_args[p_argc] = NULL;
+
+ return p_argc;
};
-int iphone_main(int, int, int, char **, String);
+int add_cmdline(int p_argc, char **p_args) {
+ NSArray *arr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_cmdline"];
+ if (!arr) {
+ return p_argc;
+ }
+
+ for (NSUInteger i = 0; i < [arr count]; i++) {
+ NSString *str = [arr objectAtIndex:i];
+ if (!str) {
+ continue;
+ }
+ p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
+ };
+
+ p_args[p_argc] = NULL;
+
+ return p_argc;
+};
-int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
+int iphone_main(int argc, char **argv, String data_dir) {
size_t len = strlen(argv[0]);
while (len--) {
- if (argv[0][len] == '/')
+ if (argv[0][len] == '/') {
break;
+ }
}
if (len >= 0) {
@@ -65,7 +95,10 @@ int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
char cwd[512];
getcwd(cwd, sizeof(cwd));
printf("cwd %s\n", cwd);
- os = new OSIPhone(width, height, data_dir);
+ os = new OSIPhone(data_dir);
+
+ // We must override main when testing is enabled
+ TEST_MAIN_OVERRIDE
char *fargv[64];
for (int i = 0; i < argc; i++) {
@@ -76,10 +109,14 @@ int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
argc = add_cmdline(argc, fargv);
printf("os created\n");
+
Error err = Main::setup(fargv[0], argc - 1, &fargv[1], false);
printf("setup %i\n", err);
- if (err != OK)
+ if (err != OK) {
return 255;
+ }
+
+ os->initialize_modules();
return 0;
};
diff --git a/platform/uwp/thread_uwp.h b/platform/iphone/godot_view.h
index 9b2a2590a8..265f826173 100644
--- a/platform/uwp/thread_uwp.h
+++ b/platform/iphone/godot_view.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* thread_uwp.h */
+/* godot_view.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,32 +28,35 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef THREAD_UWP_H
-#define THREAD_UWP_H
+#import <UIKit/UIKit.h>
-#ifdef UWP_ENABLED
+class String;
-#include "core/os/thread.h"
+@class GodotView;
+@protocol DisplayLayer;
+@protocol GodotViewRendererProtocol;
-#include <thread>
+@protocol GodotViewDelegate
-class ThreadUWP : public Thread {
- std::thread thread;
+- (BOOL)godotViewFinishedSetup:(GodotView *)view;
- static Thread *create_func_uwp(ThreadCreateCallback p_callback, void *, const Settings &);
- static ID get_thread_id_func_uwp();
- static void wait_to_finish_func_uwp(Thread *p_thread);
+@end
- ThreadUWP() {}
+@interface GodotView : UIView
-public:
- virtual ID get_id() const;
+@property(assign, nonatomic) id<GodotViewRendererProtocol> renderer;
+@property(assign, nonatomic) id<GodotViewDelegate> delegate;
- static void make_default();
+@property(assign, readonly, nonatomic) BOOL isActive;
- ~ThreadUWP() {}
-};
+@property(assign, nonatomic) BOOL useCADisplayLink;
+@property(strong, readonly, nonatomic) CALayer<DisplayLayer> *renderingLayer;
+@property(assign, readonly, nonatomic) BOOL canRender;
-#endif // UWP_ENABLED
+@property(assign, nonatomic) NSTimeInterval renderingInterval;
-#endif // THREAD_UWP_H
+- (CALayer<DisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName;
+- (void)stopRendering;
+- (void)startRendering;
+
+@end
diff --git a/platform/iphone/godot_view.mm b/platform/iphone/godot_view.mm
new file mode 100644
index 0000000000..887297848e
--- /dev/null
+++ b/platform/iphone/godot_view.mm
@@ -0,0 +1,467 @@
+/*************************************************************************/
+/* godot_view.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import "godot_view.h"
+#include "core/os/keyboard.h"
+#include "core/string/ustring.h"
+#import "display_layer.h"
+#include "display_server_iphone.h"
+#import "godot_view_gesture_recognizer.h"
+#import "godot_view_renderer.h"
+
+#import <CoreMotion/CoreMotion.h>
+
+static const int max_touches = 8;
+
+@interface GodotView () {
+ UITouch *godot_touches[max_touches];
+}
+
+@property(assign, nonatomic) BOOL isActive;
+
+// CADisplayLink available on 3.1+ synchronizes the animation timer & drawing with the refresh rate of the display, only supports animation intervals of 1/60 1/30 & 1/15
+@property(strong, nonatomic) CADisplayLink *displayLink;
+
+// An animation timer that, when animation is started, will periodically call -drawView at the given rate.
+// Only used if CADisplayLink is not
+@property(strong, nonatomic) NSTimer *animationTimer;
+
+@property(strong, nonatomic) CALayer<DisplayLayer> *renderingLayer;
+
+@property(strong, nonatomic) CMMotionManager *motionManager;
+
+@property(strong, nonatomic) GodotViewGestureRecognizer *delayGestureRecognizer;
+
+@end
+
+@implementation GodotView
+
+- (CALayer<DisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName {
+ if (self.renderingLayer) {
+ return self.renderingLayer;
+ }
+
+ CALayer<DisplayLayer> *layer;
+
+ if ([driverName isEqualToString:@"vulkan"]) {
+ layer = [GodotMetalLayer layer];
+ } else if ([driverName isEqualToString:@"opengl_es"]) {
+ if (@available(iOS 13, *)) {
+ NSLog(@"OpenGL ES is deprecated on iOS 13");
+ }
+#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
+ return nil;
+#else
+ layer = [GodotOpenGLLayer layer];
+#endif
+ } else {
+ return nil;
+ }
+
+ layer.frame = self.bounds;
+ layer.contentsScale = self.contentScaleFactor;
+
+ [self.layer addSublayer:layer];
+ self.renderingLayer = layer;
+
+ [layer initializeDisplayLayer];
+
+ return self.renderingLayer;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [self stopRendering];
+
+ self.renderer = nil;
+ self.delegate = nil;
+
+ if (self.renderingLayer) {
+ [self.renderingLayer removeFromSuperlayer];
+ self.renderingLayer = nil;
+ }
+
+ if (self.motionManager) {
+ [self.motionManager stopDeviceMotionUpdates];
+ self.motionManager = nil;
+ }
+
+ if (self.displayLink) {
+ [self.displayLink invalidate];
+ self.displayLink = nil;
+ }
+
+ if (self.animationTimer) {
+ [self.animationTimer invalidate];
+ self.animationTimer = nil;
+ }
+
+ if (self.delayGestureRecognizer) {
+ self.delayGestureRecognizer = nil;
+ }
+}
+
+- (void)godot_commonInit {
+ self.contentScaleFactor = [UIScreen mainScreen].nativeScale;
+
+ [self initTouches];
+
+ // Configure and start accelerometer
+ if (!self.motionManager) {
+ self.motionManager = [[CMMotionManager alloc] init];
+ if (self.motionManager.deviceMotionAvailable) {
+ self.motionManager.deviceMotionUpdateInterval = 1.0 / 70.0;
+ [self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical];
+ } else {
+ self.motionManager = nil;
+ }
+ }
+
+ // Initialize delay gesture recognizer
+ GodotViewGestureRecognizer *gestureRecognizer = [[GodotViewGestureRecognizer alloc] init];
+ self.delayGestureRecognizer = gestureRecognizer;
+ [self addGestureRecognizer:self.delayGestureRecognizer];
+}
+
+- (void)stopRendering {
+ if (!self.isActive) {
+ return;
+ }
+
+ self.isActive = NO;
+
+ printf("******** stop animation!\n");
+
+ if (self.useCADisplayLink) {
+ [self.displayLink invalidate];
+ self.displayLink = nil;
+ } else {
+ [self.animationTimer invalidate];
+ self.animationTimer = nil;
+ }
+
+ [self clearTouches];
+}
+
+- (void)startRendering {
+ if (self.isActive) {
+ return;
+ }
+
+ self.isActive = YES;
+
+ printf("start animation!\n");
+
+ if (self.useCADisplayLink) {
+ self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
+
+ // Approximate frame rate
+ // assumes device refreshes at 60 fps
+ int displayFPS = (NSInteger)(1.0 / self.renderingInterval);
+
+ self.displayLink.preferredFramesPerSecond = displayFPS;
+
+ // Setup DisplayLink in main thread
+ [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+ } else {
+ self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:self.renderingInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
+ }
+}
+
+- (void)drawView {
+ if (!self.isActive) {
+ printf("draw view not active!\n");
+ return;
+ }
+
+ if (self.useCADisplayLink) {
+ // Pause the CADisplayLink to avoid recursion
+ [self.displayLink setPaused:YES];
+
+ // Process all input events
+ while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource)
+ ;
+
+ // We are good to go, resume the CADisplayLink
+ [self.displayLink setPaused:NO];
+ }
+
+ [self.renderingLayer renderDisplayLayer];
+
+ if (!self.renderer) {
+ return;
+ }
+
+ if ([self.renderer setupView:self]) {
+ return;
+ }
+
+ if (self.delegate) {
+ BOOL delegateFinishedSetup = [self.delegate godotViewFinishedSetup:self];
+
+ if (!delegateFinishedSetup) {
+ return;
+ }
+ }
+
+ [self handleMotion];
+ [self.renderer renderOnView:self];
+}
+
+- (BOOL)canRender {
+ if (self.useCADisplayLink) {
+ return self.displayLink != nil;
+ } else {
+ return self.animationTimer != nil;
+ }
+}
+
+- (void)setRenderingInterval:(NSTimeInterval)renderingInterval {
+ _renderingInterval = renderingInterval;
+
+ if (self.canRender) {
+ [self stopRendering];
+ [self startRendering];
+ }
+}
+
+- (void)layoutSubviews {
+ if (self.renderingLayer) {
+ self.renderingLayer.frame = self.bounds;
+ [self.renderingLayer layoutDisplayLayer];
+
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->resize_window(self.bounds.size);
+ }
+ }
+
+ [super layoutSubviews];
+}
+
+// MARK: - Input
+
+// MARK: Touches
+
+- (void)initTouches {
+ for (int i = 0; i < max_touches; i++) {
+ godot_touches[i] = NULL;
+ }
+}
+
+- (int)getTouchIDForTouch:(UITouch *)p_touch {
+ int first = -1;
+ for (int i = 0; i < max_touches; i++) {
+ if (first == -1 && godot_touches[i] == NULL) {
+ first = i;
+ continue;
+ }
+ if (godot_touches[i] == p_touch) {
+ return i;
+ }
+ }
+
+ if (first != -1) {
+ godot_touches[first] = p_touch;
+ return first;
+ }
+
+ return -1;
+}
+
+- (int)removeTouch:(UITouch *)p_touch {
+ int remaining = 0;
+ for (int i = 0; i < max_touches; i++) {
+ if (godot_touches[i] == NULL) {
+ continue;
+ }
+ if (godot_touches[i] == p_touch) {
+ godot_touches[i] = NULL;
+ } else {
+ ++remaining;
+ }
+ }
+ return remaining;
+}
+
+- (void)clearTouches {
+ for (int i = 0; i < max_touches; i++) {
+ godot_touches[i] = NULL;
+ }
+}
+
+- (void)touchesBegan:(NSSet *)touchesSet withEvent:(UIEvent *)event {
+ NSArray *tlist = [event.allTouches allObjects];
+ for (unsigned int i = 0; i < [tlist count]; i++) {
+ if ([touchesSet containsObject:[tlist objectAtIndex:i]]) {
+ UITouch *touch = [tlist objectAtIndex:i];
+ int tid = [self getTouchIDForTouch:touch];
+ ERR_FAIL_COND(tid == -1);
+ CGPoint touchPoint = [touch locationInView:self];
+ DisplayServerIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
+ }
+ }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSArray *tlist = [event.allTouches allObjects];
+ for (unsigned int i = 0; i < [tlist count]; i++) {
+ if ([touches containsObject:[tlist objectAtIndex:i]]) {
+ UITouch *touch = [tlist objectAtIndex:i];
+ int tid = [self getTouchIDForTouch:touch];
+ ERR_FAIL_COND(tid == -1);
+ CGPoint touchPoint = [touch locationInView:self];
+ CGPoint prev_point = [touch previousLocationInView:self];
+ DisplayServerIPhone::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor);
+ }
+ }
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSArray *tlist = [event.allTouches allObjects];
+ for (unsigned int i = 0; i < [tlist count]; i++) {
+ if ([touches containsObject:[tlist objectAtIndex:i]]) {
+ UITouch *touch = [tlist objectAtIndex:i];
+ int tid = [self getTouchIDForTouch:touch];
+ ERR_FAIL_COND(tid == -1);
+ [self removeTouch:touch];
+ CGPoint touchPoint = [touch locationInView:self];
+ DisplayServerIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
+ }
+ }
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSArray *tlist = [event.allTouches allObjects];
+ for (unsigned int i = 0; i < [tlist count]; i++) {
+ if ([touches containsObject:[tlist objectAtIndex:i]]) {
+ UITouch *touch = [tlist objectAtIndex:i];
+ int tid = [self getTouchIDForTouch:touch];
+ ERR_FAIL_COND(tid == -1);
+ DisplayServerIPhone::get_singleton()->touches_cancelled(tid);
+ }
+ }
+ [self clearTouches];
+}
+
+// MARK: Motion
+
+- (void)handleMotion {
+ if (!self.motionManager) {
+ return;
+ }
+
+ // Just using polling approach for now, we can set this up so it sends
+ // data to us in intervals, might be better. See Apple reference pages
+ // for more details:
+ // https://developer.apple.com/reference/coremotion/cmmotionmanager?language=objc
+
+ // Apple splits our accelerometer date into a gravity and user movement
+ // component. We add them back together
+ CMAcceleration gravity = self.motionManager.deviceMotion.gravity;
+ CMAcceleration acceleration = self.motionManager.deviceMotion.userAcceleration;
+
+ ///@TODO We don't seem to be getting data here, is my device broken or
+ /// is this code incorrect?
+ CMMagneticField magnetic = self.motionManager.deviceMotion.magneticField.field;
+
+ ///@TODO we can access rotationRate as a CMRotationRate variable
+ ///(processed date) or CMGyroData (raw data), have to see what works
+ /// best
+ CMRotationRate rotation = self.motionManager.deviceMotion.rotationRate;
+
+ // Adjust for screen orientation.
+ // [[UIDevice currentDevice] orientation] changes even if we've fixed
+ // our orientation which is not a good thing when you're trying to get
+ // your user to move the screen in all directions and want consistent
+ // output
+
+ ///@TODO Using [[UIApplication sharedApplication] statusBarOrientation]
+ /// is a bit of a hack. Godot obviously knows the orientation so maybe
+ /// we
+ // can use that instead? (note that left and right seem swapped)
+
+ UIInterfaceOrientation interfaceOrientation = UIInterfaceOrientationUnknown;
+
+ if (@available(iOS 13, *)) {
+ interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
+#endif
+ }
+
+ switch (interfaceOrientation) {
+ case UIInterfaceOrientationLandscapeLeft: {
+ DisplayServerIPhone::get_singleton()->update_gravity(-gravity.y, gravity.x, gravity.z);
+ DisplayServerIPhone::get_singleton()->update_accelerometer(-(acceleration.y + gravity.y), (acceleration.x + gravity.x), acceleration.z + gravity.z);
+ DisplayServerIPhone::get_singleton()->update_magnetometer(-magnetic.y, magnetic.x, magnetic.z);
+ DisplayServerIPhone::get_singleton()->update_gyroscope(-rotation.y, rotation.x, rotation.z);
+ } break;
+ case UIInterfaceOrientationLandscapeRight: {
+ DisplayServerIPhone::get_singleton()->update_gravity(gravity.y, -gravity.x, gravity.z);
+ DisplayServerIPhone::get_singleton()->update_accelerometer((acceleration.y + gravity.y), -(acceleration.x + gravity.x), acceleration.z + gravity.z);
+ DisplayServerIPhone::get_singleton()->update_magnetometer(magnetic.y, -magnetic.x, magnetic.z);
+ DisplayServerIPhone::get_singleton()->update_gyroscope(rotation.y, -rotation.x, rotation.z);
+ } break;
+ case UIInterfaceOrientationPortraitUpsideDown: {
+ DisplayServerIPhone::get_singleton()->update_gravity(-gravity.x, gravity.y, gravity.z);
+ DisplayServerIPhone::get_singleton()->update_accelerometer(-(acceleration.x + gravity.x), (acceleration.y + gravity.y), acceleration.z + gravity.z);
+ DisplayServerIPhone::get_singleton()->update_magnetometer(-magnetic.x, magnetic.y, magnetic.z);
+ DisplayServerIPhone::get_singleton()->update_gyroscope(-rotation.x, rotation.y, rotation.z);
+ } break;
+ default: { // assume portrait
+ DisplayServerIPhone::get_singleton()->update_gravity(gravity.x, gravity.y, gravity.z);
+ DisplayServerIPhone::get_singleton()->update_accelerometer(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z);
+ DisplayServerIPhone::get_singleton()->update_magnetometer(magnetic.x, magnetic.y, magnetic.z);
+ DisplayServerIPhone::get_singleton()->update_gyroscope(rotation.x, rotation.y, rotation.z);
+ } break;
+ }
+}
+
+@end
diff --git a/platform/iphone/icloud.h b/platform/iphone/godot_view_gesture_recognizer.h
index b11e22fec6..48b2d5ffad 100644
--- a/platform/iphone/icloud.h
+++ b/platform/iphone/godot_view_gesture_recognizer.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* icloud.h */
+/* godot_view_gesture_recognizer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,37 +28,19 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef ICLOUD_ENABLED
+// GLViewGestureRecognizer allows iOS gestures to work currectly by
+// emulating UIScrollView's UIScrollViewDelayedTouchesBeganGestureRecognizer.
+// It catches all gestures incoming to UIView and delays them for 150ms
+// (the same value used by UIScrollViewDelayedTouchesBeganGestureRecognizer)
+// If touch cancellation or end message is fired it fires delayed
+// begin touch immediately as well as last touch signal
-#ifndef ICLOUD_H
-#define ICLOUD_H
+#import <UIKit/UIKit.h>
-#include "core/object.h"
+@interface GodotViewGestureRecognizer : UIGestureRecognizer
-class ICloud : public Object {
- GDCLASS(ICloud, Object);
+@property(nonatomic, readonly, assign) NSTimeInterval delayTimeInterval;
- static ICloud *instance;
- static void _bind_methods();
+- (instancetype)init;
- List<Variant> pending_events;
-
-public:
- Error remove_key(Variant p_param);
- Variant set_key_values(Variant p_param);
- Variant get_key_value(Variant p_param);
- Error synchronize_key_values();
- Variant get_all_key_values();
-
- int get_pending_event_count();
- Variant pop_pending_event();
-
- static ICloud *get_singleton();
-
- ICloud();
- ~ICloud();
-};
-
-#endif
-
-#endif
+@end
diff --git a/platform/iphone/godot_view_gesture_recognizer.mm b/platform/iphone/godot_view_gesture_recognizer.mm
new file mode 100644
index 0000000000..cf6e5c5883
--- /dev/null
+++ b/platform/iphone/godot_view_gesture_recognizer.mm
@@ -0,0 +1,171 @@
+/*************************************************************************/
+/* godot_view_gesture_recognizer.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import "godot_view_gesture_recognizer.h"
+
+#include "core/config/project_settings.h"
+
+// Minimum distance for touches to move to fire
+// a delay timer before scheduled time.
+// Should be the low enough to not cause issues with dragging
+// but big enough to allow click to work.
+const CGFloat kGLGestureMovementDistance = 0.5;
+
+@interface GodotViewGestureRecognizer ()
+
+@property(nonatomic, readwrite, assign) NSTimeInterval delayTimeInterval;
+
+@end
+
+@interface GodotViewGestureRecognizer ()
+
+// Timer used to delay begin touch message.
+// Should work as simple emulation of UIDelayedAction
+@property(strong, nonatomic) NSTimer *delayTimer;
+
+// Delayed touch parameters
+@property(strong, nonatomic) NSSet *delayedTouches;
+@property(strong, nonatomic) UIEvent *delayedEvent;
+
+@end
+
+@implementation GodotViewGestureRecognizer
+
+- (instancetype)init {
+ self = [super init];
+
+ self.cancelsTouchesInView = YES;
+ self.delaysTouchesBegan = YES;
+ self.delaysTouchesEnded = YES;
+
+ self.delayTimeInterval = GLOBAL_GET("input_devices/pointing/ios/touch_delay");
+
+ return self;
+}
+
+- (void)dealloc {
+ if (self.delayTimer) {
+ [self.delayTimer invalidate];
+ self.delayTimer = nil;
+ }
+
+ if (self.delayedTouches) {
+ self.delayedTouches = nil;
+ }
+
+ if (self.delayedEvent) {
+ self.delayedEvent = nil;
+ }
+}
+
+- (void)delayTouches:(NSSet *)touches andEvent:(UIEvent *)event {
+ [self.delayTimer fire];
+
+ self.delayedTouches = touches;
+ self.delayedEvent = event;
+
+ self.delayTimer = [NSTimer
+ scheduledTimerWithTimeInterval:self.delayTimeInterval
+ target:self
+ selector:@selector(fireDelayedTouches:)
+ userInfo:nil
+ repeats:NO];
+}
+
+- (void)fireDelayedTouches:(id)timer {
+ [self.delayTimer invalidate];
+ self.delayTimer = nil;
+
+ if (self.delayedTouches) {
+ [self.view touchesBegan:self.delayedTouches withEvent:self.delayedEvent];
+ }
+
+ self.delayedTouches = nil;
+ self.delayedEvent = nil;
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseBegan];
+ [self delayTouches:cleared andEvent:event];
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseMoved];
+
+ if (self.delayTimer) {
+ // We should check if movement was significant enough to fire an event
+ // for dragging to work correctly.
+ for (UITouch *touch in cleared) {
+ CGPoint from = [touch locationInView:self.view];
+ CGPoint to = [touch previousLocationInView:self.view];
+ CGFloat xDistance = from.x - to.x;
+ CGFloat yDistance = from.y - to.y;
+
+ CGFloat distance = sqrt(xDistance * xDistance + yDistance * yDistance);
+
+ // Early exit, since one of touches has moved enough to fire a drag event.
+ if (distance > kGLGestureMovementDistance) {
+ [self.delayTimer fire];
+ [self.view touchesMoved:cleared withEvent:event];
+ return;
+ }
+ }
+
+ return;
+ }
+
+ [self.view touchesMoved:cleared withEvent:event];
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+ [self.delayTimer fire];
+
+ NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseEnded];
+ [self.view touchesEnded:cleared withEvent:event];
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+ [self.delayTimer fire];
+ [self.view touchesCancelled:touches withEvent:event];
+};
+
+- (NSSet *)copyClearedTouches:(NSSet *)touches phase:(UITouchPhase)phaseToSave {
+ NSMutableSet *cleared = [touches mutableCopy];
+
+ for (UITouch *touch in touches) {
+ if (touch.phase != phaseToSave) {
+ [cleared removeObject:touch];
+ }
+ }
+
+ return cleared;
+}
+
+@end
diff --git a/platform/haiku/godot_haiku.cpp b/platform/iphone/godot_view_renderer.h
index 0657f4c052..c6b0a05a4e 100644
--- a/platform/haiku/godot_haiku.cpp
+++ b/platform/iphone/godot_view_renderer.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* godot_haiku.cpp */
+/* godot_view_renderer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,22 +28,17 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "main/main.h"
-#include "os_haiku.h"
+#import <UIKit/UIKit.h>
-int main(int argc, char *argv[]) {
- OS_Haiku os;
+@protocol GodotViewRendererProtocol <NSObject>
- Error error = Main::setup(argv[0], argc - 1, &argv[1]);
- if (error != OK) {
- return 255;
- }
+@property(assign, readonly, nonatomic) BOOL hasFinishedSetup;
- if (Main::start()) {
- os.run();
- }
+- (BOOL)setupView:(UIView *)view;
+- (void)renderOnView:(UIView *)view;
- Main::cleanup();
+@end
- return os.get_exit_code();
-}
+@interface GodotViewRenderer : NSObject <GodotViewRendererProtocol>
+
+@end
diff --git a/platform/iphone/godot_view_renderer.mm b/platform/iphone/godot_view_renderer.mm
new file mode 100644
index 0000000000..07d664715a
--- /dev/null
+++ b/platform/iphone/godot_view_renderer.mm
@@ -0,0 +1,117 @@
+/*************************************************************************/
+/* godot_view_renderer.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import "godot_view_renderer.h"
+#include "core/config/project_settings.h"
+#include "core/os/keyboard.h"
+#import "display_server_iphone.h"
+#include "main/main.h"
+#include "os_iphone.h"
+#include "servers/audio_server.h"
+
+#import <AudioToolbox/AudioServices.h>
+#import <CoreMotion/CoreMotion.h>
+#import <GameController/GameController.h>
+#import <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+
+@interface GodotViewRenderer ()
+
+@property(assign, nonatomic) BOOL hasFinishedProjectDataSetup;
+@property(assign, nonatomic) BOOL hasStartedMain;
+@property(assign, nonatomic) BOOL hasFinishedSetup;
+
+@end
+
+@implementation GodotViewRenderer
+
+- (BOOL)setupView:(UIView *)view {
+ if (self.hasFinishedSetup) {
+ return NO;
+ }
+
+ if (!OS::get_singleton()) {
+ exit(0);
+ }
+
+ if (!self.hasFinishedProjectDataSetup) {
+ [self setupProjectData];
+ return YES;
+ }
+
+ if (!self.hasStartedMain) {
+ self.hasStartedMain = YES;
+ OSIPhone::get_singleton()->start();
+ return YES;
+ }
+
+ self.hasFinishedSetup = YES;
+
+ return NO;
+}
+
+- (void)setupProjectData {
+ self.hasFinishedProjectDataSetup = YES;
+
+ Main::setup2();
+
+ // this might be necessary before here
+ NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
+ for (NSString *key in dict) {
+ NSObject *value = [dict objectForKey:key];
+ String ukey = String::utf8([key UTF8String]);
+
+ // we need a NSObject to Variant conversor
+
+ if ([value isKindOfClass:[NSString class]]) {
+ NSString *str = (NSString *)value;
+ String uval = String::utf8([str UTF8String]);
+
+ ProjectSettings::get_singleton()->set("Info.plist/" + ukey, uval);
+
+ } else if ([value isKindOfClass:[NSNumber class]]) {
+ NSNumber *n = (NSNumber *)value;
+ double dval = [n doubleValue];
+
+ ProjectSettings::get_singleton()->set("Info.plist/" + ukey, dval);
+ };
+ // do stuff
+ }
+}
+
+- (void)renderOnView:(UIView *)view {
+ if (!OSIPhone::get_singleton()) {
+ return;
+ }
+
+ OSIPhone::get_singleton()->iterate();
+}
+
+@end
diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm
deleted file mode 100644
index c768274b1f..0000000000
--- a/platform/iphone/icloud.mm
+++ /dev/null
@@ -1,359 +0,0 @@
-/*************************************************************************/
-/* icloud.mm */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#ifdef ICLOUD_ENABLED
-
-#include "icloud.h"
-
-#ifndef __IPHONE_9_0
-extern "C" {
-#endif
-
-#import "app_delegate.h"
-
-#import <Foundation/Foundation.h>
-
-#ifndef __IPHONE_9_0
-};
-#endif
-
-ICloud *ICloud::instance = NULL;
-
-void ICloud::_bind_methods() {
- ClassDB::bind_method(D_METHOD("remove_key"), &ICloud::remove_key);
- ClassDB::bind_method(D_METHOD("set_key_values"), &ICloud::set_key_values);
- ClassDB::bind_method(D_METHOD("get_key_value"), &ICloud::get_key_value);
- ClassDB::bind_method(D_METHOD("synchronize_key_values"), &ICloud::synchronize_key_values);
- ClassDB::bind_method(D_METHOD("get_all_key_values"), &ICloud::get_all_key_values);
-
- ClassDB::bind_method(D_METHOD("get_pending_event_count"), &ICloud::get_pending_event_count);
- ClassDB::bind_method(D_METHOD("pop_pending_event"), &ICloud::pop_pending_event);
-};
-
-int ICloud::get_pending_event_count() {
- return pending_events.size();
-};
-
-Variant ICloud::pop_pending_event() {
- Variant front = pending_events.front()->get();
- pending_events.pop_front();
-
- return front;
-};
-
-ICloud *ICloud::get_singleton() {
- return instance;
-};
-
-//convert from apple's abstract type to godot's abstract type....
-Variant nsobject_to_variant(NSObject *object) {
- if ([object isKindOfClass:[NSString class]]) {
- const char *str = [(NSString *)object UTF8String];
- return String::utf8(str != NULL ? str : "");
- } else if ([object isKindOfClass:[NSData class]]) {
- PackedByteArray ret;
- NSData *data = (NSData *)object;
- if ([data length] > 0) {
- ret.resize([data length]);
- {
- // PackedByteArray::Write w = ret.write();
- copymem((void *)ret.ptr(), [data bytes], [data length]);
- }
- }
- return ret;
- } else if ([object isKindOfClass:[NSArray class]]) {
- Array result;
- NSArray *array = (NSArray *)object;
- for (unsigned int i = 0; i < [array count]; ++i) {
- NSObject *value = [array objectAtIndex:i];
- result.push_back(nsobject_to_variant(value));
- }
- return result;
- } else if ([object isKindOfClass:[NSDictionary class]]) {
- Dictionary result;
- NSDictionary *dic = (NSDictionary *)object;
-
- NSArray *keys = [dic allKeys];
- int count = [keys count];
- for (int i = 0; i < count; ++i) {
- NSObject *k = [keys objectAtIndex:i];
- NSObject *v = [dic objectForKey:k];
-
- result[nsobject_to_variant(k)] = nsobject_to_variant(v);
- }
- return result;
- } else if ([object isKindOfClass:[NSNumber class]]) {
- //Every type except numbers can reliably identify its type. The following is comparing to the *internal* representation, which isn't guaranteed to match the type that was used to create it, and is not advised, particularly when dealing with potential platform differences (ie, 32/64 bit)
- //To avoid errors, we'll cast as broadly as possible, and only return int or float.
- //bool, char, int, uint, longlong -> int
- //float, double -> float
- NSNumber *num = (NSNumber *)object;
- if (strcmp([num objCType], @encode(BOOL)) == 0) {
- return Variant((int)[num boolValue]);
- } else if (strcmp([num objCType], @encode(char)) == 0) {
- return Variant((int)[num charValue]);
- } else if (strcmp([num objCType], @encode(int)) == 0) {
- return Variant([num intValue]);
- } else if (strcmp([num objCType], @encode(unsigned int)) == 0) {
- return Variant((int)[num unsignedIntValue]);
- } else if (strcmp([num objCType], @encode(long long)) == 0) {
- return Variant((int)[num longValue]);
- } else if (strcmp([num objCType], @encode(float)) == 0) {
- return Variant([num floatValue]);
- } else if (strcmp([num objCType], @encode(double)) == 0) {
- return Variant((float)[num doubleValue]);
- } else {
- return Variant();
- }
- } else if ([object isKindOfClass:[NSDate class]]) {
- //this is a type that icloud supports...but how did you submit it in the first place?
- //I guess this is a type that *might* show up, if you were, say, trying to make your game
- //compatible with existing cloud data written by another engine's version of your game
- WARN_PRINT("NSDate unsupported, returning null Variant");
- return Variant();
- } else if ([object isKindOfClass:[NSNull class]] or object == nil) {
- return Variant();
- } else {
- WARN_PRINT("Trying to convert unknown NSObject type to Variant");
- return Variant();
- }
-}
-
-NSObject *variant_to_nsobject(Variant v) {
- if (v.get_type() == Variant::STRING) {
- return [[[NSString alloc] initWithUTF8String:((String)v).utf8().get_data()] autorelease];
- } else if (v.get_type() == Variant::REAL) {
- return [NSNumber numberWithDouble:(double)v];
- } else if (v.get_type() == Variant::INT) {
- return [NSNumber numberWithLongLong:(long)(int)v];
- } else if (v.get_type() == Variant::BOOL) {
- return [NSNumber numberWithBool:BOOL((bool)v)];
- } else if (v.get_type() == Variant::DICTIONARY) {
- NSMutableDictionary *result = [[[NSMutableDictionary alloc] init] autorelease];
- Dictionary dic = v;
- Array keys = dic.keys();
- for (unsigned int i = 0; i < keys.size(); ++i) {
- NSString *key = [[[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()] autorelease];
- NSObject *value = variant_to_nsobject(dic[keys[i]]);
-
- if (key == NULL || value == NULL) {
- return NULL;
- }
-
- [result setObject:value forKey:key];
- }
- return result;
- } else if (v.get_type() == Variant::ARRAY) {
- NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
- Array arr = v;
- for (unsigned int i = 0; i < arr.size(); ++i) {
- NSObject *value = variant_to_nsobject(arr[i]);
- if (value == NULL) {
- //trying to add something unsupported to the array. cancel the whole array
- return NULL;
- }
- [result addObject:value];
- }
- return result;
- } else if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
- PackedByteArray arr = v;
- // PackedByteArray::Read r = arr.read();
- NSData *result = [NSData dataWithBytes:arr.ptr() length:arr.size()];
- return result;
- }
- WARN_PRINT(String("Could not add unsupported type to iCloud: '" + Variant::get_type_name(v.get_type()) + "'").utf8().get_data());
- return NULL;
-}
-
-Error ICloud::remove_key(Variant p_param) {
- String param = p_param;
- NSString *key = [[[NSString alloc] initWithUTF8String:param.utf8().get_data()] autorelease];
-
- NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
-
- if (![[store dictionaryRepresentation] objectForKey:key]) {
- return ERR_INVALID_PARAMETER;
- }
-
- [store removeObjectForKey:key];
- return OK;
-}
-
-//return an array of the keys that could not be set
-Variant ICloud::set_key_values(Variant p_params) {
- Dictionary params = p_params;
- Array keys = params.keys();
-
- Array error_keys;
-
- for (unsigned int i = 0; i < keys.size(); ++i) {
- String variant_key = keys[i];
- Variant variant_value = params[variant_key];
-
- NSString *key = [[[NSString alloc] initWithUTF8String:variant_key.utf8().get_data()] autorelease];
- if (key == NULL) {
- error_keys.push_back(variant_key);
- continue;
- }
-
- NSObject *value = variant_to_nsobject(variant_value);
-
- if (value == NULL) {
- error_keys.push_back(variant_key);
- continue;
- }
-
- NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
- [store setObject:value forKey:key];
- }
-
- return error_keys;
-}
-
-Variant ICloud::get_key_value(Variant p_param) {
- String param = p_param;
-
- NSString *key = [[[NSString alloc] initWithUTF8String:param.utf8().get_data()] autorelease];
- NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
-
- if (![[store dictionaryRepresentation] objectForKey:key]) {
- return Variant();
- }
-
- Variant result = nsobject_to_variant([[store dictionaryRepresentation] objectForKey:key]);
-
- return result;
-}
-
-Variant ICloud::get_all_key_values() {
- Dictionary result;
-
- NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
- NSDictionary *store_dictionary = [store dictionaryRepresentation];
-
- NSArray *keys = [store_dictionary allKeys];
- int count = [keys count];
- for (int i = 0; i < count; ++i) {
- NSString *k = [keys objectAtIndex:i];
- NSObject *v = [store_dictionary objectForKey:k];
-
- const char *str = [k UTF8String];
- if (str != NULL) {
- result[String::utf8(str)] = nsobject_to_variant(v);
- }
- }
-
- return result;
-}
-
-Error ICloud::synchronize_key_values() {
- NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
- BOOL result = [store synchronize];
- if (result == YES) {
- return OK;
- } else {
- return FAILED;
- }
-}
-
-/*
-Error ICloud::initial_sync() {
- //you sometimes have to write something to the store to get it to download new data. go apple!
- NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
- if ([store boolForKey:@"isb"])
- {
- [store setBool:NO forKey:@"isb"];
- }
- else
- {
- [store setBool:YES forKey:@"isb"];
- }
- return synchronize();
-}
-
-*/
-ICloud::ICloud() {
- ERR_FAIL_COND(instance != NULL);
- instance = this;
- //connected = false;
-
- [[NSNotificationCenter defaultCenter]
- addObserverForName:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
- object:[NSUbiquitousKeyValueStore defaultStore]
- queue:nil
- usingBlock:^(NSNotification *notification) {
- NSDictionary *userInfo = [notification userInfo];
- NSInteger change = [[userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey] integerValue];
-
- Dictionary ret;
- ret["type"] = "key_value_changed";
-
- //PackedStringArray result_keys;
- //Array result_values;
- Dictionary keyValues;
- String reason = "";
-
- if (change == NSUbiquitousKeyValueStoreServerChange) {
- reason = "server";
- } else if (change == NSUbiquitousKeyValueStoreInitialSyncChange) {
- reason = "initial_sync";
- } else if (change == NSUbiquitousKeyValueStoreQuotaViolationChange) {
- reason = "quota_violation";
- } else if (change == NSUbiquitousKeyValueStoreAccountChange) {
- reason = "account";
- }
-
- ret["reason"] = reason;
-
- NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
-
- NSArray *keys = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
- for (NSString *key in keys) {
- const char *str = [key UTF8String];
- if (str == NULL) {
- continue;
- }
-
- NSObject *object = [store objectForKey:key];
-
- //figure out what kind of object it is
- Variant value = nsobject_to_variant(object);
-
- keyValues[String::utf8(str)] = value;
- }
-
- ret["changed_values"] = keyValues;
- pending_events.push_back(ret);
- }];
-}
-
-ICloud::~ICloud() {}
-
-#endif
diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm
deleted file mode 100644
index a2efd6691b..0000000000
--- a/platform/iphone/in_app_store.mm
+++ /dev/null
@@ -1,326 +0,0 @@
-/*************************************************************************/
-/* in_app_store.mm */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#ifdef STOREKIT_ENABLED
-
-#include "in_app_store.h"
-
-extern "C" {
-#import <Foundation/Foundation.h>
-#import <StoreKit/StoreKit.h>
-};
-
-bool auto_finish_transactions = true;
-NSMutableDictionary *pending_transactions = [NSMutableDictionary dictionary];
-
-@interface SKProduct (LocalizedPrice)
-@property(nonatomic, readonly) NSString *localizedPrice;
-@end
-
-//----------------------------------//
-// SKProduct extension
-//----------------------------------//
-@implementation SKProduct (LocalizedPrice)
-- (NSString *)localizedPrice {
- NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
- [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
- [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
- [numberFormatter setLocale:self.priceLocale];
- NSString *formattedString = [numberFormatter stringFromNumber:self.price];
- [numberFormatter release];
- return formattedString;
-}
-
-@end
-
-InAppStore *InAppStore::instance = NULL;
-
-void InAppStore::_bind_methods() {
- ClassDB::bind_method(D_METHOD("request_product_info"), &InAppStore::request_product_info);
- ClassDB::bind_method(D_METHOD("restore_purchases"), &InAppStore::restore_purchases);
- ClassDB::bind_method(D_METHOD("purchase"), &InAppStore::purchase);
-
- ClassDB::bind_method(D_METHOD("get_pending_event_count"), &InAppStore::get_pending_event_count);
- ClassDB::bind_method(D_METHOD("pop_pending_event"), &InAppStore::pop_pending_event);
- ClassDB::bind_method(D_METHOD("finish_transaction"), &InAppStore::finish_transaction);
- ClassDB::bind_method(D_METHOD("set_auto_finish_transaction"), &InAppStore::set_auto_finish_transaction);
-};
-
-@interface ProductsDelegate : NSObject <SKProductsRequestDelegate> {
-};
-
-@end
-
-@implementation ProductsDelegate
-
-- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
- NSArray *products = response.products;
- Dictionary ret;
- ret["type"] = "product_info";
- ret["result"] = "ok";
- PackedStringArray titles;
- PackedStringArray descriptions;
- PackedFloat32Array prices;
- PackedStringArray ids;
- PackedStringArray localized_prices;
- PackedStringArray currency_codes;
-
- for (NSUInteger i = 0; i < [products count]; i++) {
- SKProduct *product = [products objectAtIndex:i];
-
- const char *str = [product.localizedTitle UTF8String];
- titles.push_back(String::utf8(str != NULL ? str : ""));
-
- str = [product.localizedDescription UTF8String];
- descriptions.push_back(String::utf8(str != NULL ? str : ""));
- prices.push_back([product.price doubleValue]);
- ids.push_back(String::utf8([product.productIdentifier UTF8String]));
- localized_prices.push_back(String::utf8([product.localizedPrice UTF8String]));
- currency_codes.push_back(String::utf8([[[product priceLocale] objectForKey:NSLocaleCurrencyCode] UTF8String]));
- };
- ret["titles"] = titles;
- ret["descriptions"] = descriptions;
- ret["prices"] = prices;
- ret["ids"] = ids;
- ret["localized_prices"] = localized_prices;
- ret["currency_codes"] = currency_codes;
-
- PackedStringArray invalid_ids;
-
- for (NSString *ipid in response.invalidProductIdentifiers) {
- invalid_ids.push_back(String::utf8([ipid UTF8String]));
- };
- ret["invalid_ids"] = invalid_ids;
-
- InAppStore::get_singleton()->_post_event(ret);
-
- [request release];
-};
-
-@end
-
-Error InAppStore::request_product_info(Variant p_params) {
- Dictionary params = p_params;
- ERR_FAIL_COND_V(!params.has("product_ids"), ERR_INVALID_PARAMETER);
-
- PackedStringArray pids = params["product_ids"];
- printf("************ request product info! %i\n", pids.size());
-
- NSMutableArray *array = [[[NSMutableArray alloc] initWithCapacity:pids.size()] autorelease];
- for (int i = 0; i < pids.size(); i++) {
- printf("******** adding %ls to product list\n", pids[i].c_str());
- NSString *pid = [[[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()] autorelease];
- [array addObject:pid];
- };
-
- NSSet *products = [[[NSSet alloc] initWithArray:array] autorelease];
- SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:products];
-
- ProductsDelegate *delegate = [[ProductsDelegate alloc] init];
-
- request.delegate = delegate;
- [request start];
-
- return OK;
-};
-
-Error InAppStore::restore_purchases() {
- printf("restoring purchases!\n");
- [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
-
- return OK;
-};
-
-@interface TransObserver : NSObject <SKPaymentTransactionObserver> {
-};
-@end
-
-@implementation TransObserver
-
-- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
- printf("transactions updated!\n");
- for (SKPaymentTransaction *transaction in transactions) {
- switch (transaction.transactionState) {
- case SKPaymentTransactionStatePurchased: {
- printf("status purchased!\n");
- String pid = String::utf8([transaction.payment.productIdentifier UTF8String]);
- String transactionId = String::utf8([transaction.transactionIdentifier UTF8String]);
- InAppStore::get_singleton()->_record_purchase(pid);
- Dictionary ret;
- ret["type"] = "purchase";
- ret["result"] = "ok";
- ret["product_id"] = pid;
- ret["transaction_id"] = transactionId;
-
- NSData *receipt = nil;
- int sdk_version = 6;
-
- if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
- NSURL *receiptFileURL = nil;
- NSBundle *bundle = [NSBundle mainBundle];
- if ([bundle respondsToSelector:@selector(appStoreReceiptURL)]) {
- // Get the transaction receipt file path location in the app bundle.
- receiptFileURL = [bundle appStoreReceiptURL];
-
- // Read in the contents of the transaction file.
- receipt = [NSData dataWithContentsOfURL:receiptFileURL];
- sdk_version = 7;
-
- } else {
- // Fall back to deprecated transaction receipt,
- // which is still available in iOS 7.
-
- // Use SKPaymentTransaction's transactionReceipt.
- receipt = transaction.transactionReceipt;
- }
-
- } else {
- receipt = transaction.transactionReceipt;
- }
-
- NSString *receipt_to_send = nil;
- if (receipt != nil) {
- receipt_to_send = [receipt description];
- }
- Dictionary receipt_ret;
- receipt_ret["receipt"] = String::utf8(receipt_to_send != nil ? [receipt_to_send UTF8String] : "");
- receipt_ret["sdk"] = sdk_version;
- ret["receipt"] = receipt_ret;
-
- InAppStore::get_singleton()->_post_event(ret);
-
- if (auto_finish_transactions) {
- [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- } else {
- [pending_transactions setObject:transaction forKey:transaction.payment.productIdentifier];
- }
-
- }; break;
- case SKPaymentTransactionStateFailed: {
- printf("status transaction failed!\n");
- String pid = String::utf8([transaction.payment.productIdentifier UTF8String]);
- Dictionary ret;
- ret["type"] = "purchase";
- ret["result"] = "error";
- ret["product_id"] = pid;
- ret["error"] = String::utf8([transaction.error.localizedDescription UTF8String]);
- InAppStore::get_singleton()->_post_event(ret);
- [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- } break;
- case SKPaymentTransactionStateRestored: {
- printf("status transaction restored!\n");
- String pid = String::utf8([transaction.originalTransaction.payment.productIdentifier UTF8String]);
- InAppStore::get_singleton()->_record_purchase(pid);
- Dictionary ret;
- ret["type"] = "restore";
- ret["result"] = "ok";
- ret["product_id"] = pid;
- InAppStore::get_singleton()->_post_event(ret);
- [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- } break;
- default: {
- printf("status default %i!\n", (int)transaction.transactionState);
- }; break;
- };
- };
-};
-
-@end
-
-Error InAppStore::purchase(Variant p_params) {
- ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE);
- if (![SKPaymentQueue canMakePayments])
- return ERR_UNAVAILABLE;
-
- printf("purchasing!\n");
- Dictionary params = p_params;
- ERR_FAIL_COND_V(!params.has("product_id"), ERR_INVALID_PARAMETER);
-
- NSString *pid = [[[NSString alloc] initWithUTF8String:String(params["product_id"]).utf8().get_data()] autorelease];
- SKPayment *payment = [SKPayment paymentWithProductIdentifier:pid];
- SKPaymentQueue *defq = [SKPaymentQueue defaultQueue];
- [defq addPayment:payment];
- printf("purchase sent!\n");
-
- return OK;
-};
-
-int InAppStore::get_pending_event_count() {
- return pending_events.size();
-};
-
-Variant InAppStore::pop_pending_event() {
- Variant front = pending_events.front()->get();
- pending_events.pop_front();
-
- return front;
-};
-
-void InAppStore::_post_event(Variant p_event) {
- pending_events.push_back(p_event);
-};
-
-void InAppStore::_record_purchase(String product_id) {
- String skey = "purchased/" + product_id;
- NSString *key = [[[NSString alloc] initWithUTF8String:skey.utf8().get_data()] autorelease];
- [[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
- [[NSUserDefaults standardUserDefaults] synchronize];
-};
-
-InAppStore *InAppStore::get_singleton() {
- return instance;
-};
-
-InAppStore::InAppStore() {
- ERR_FAIL_COND(instance != NULL);
- instance = this;
- auto_finish_transactions = false;
-
- TransObserver *observer = [[TransObserver alloc] init];
- [[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
- //pending_transactions = [NSMutableDictionary dictionary];
-};
-
-void InAppStore::finish_transaction(String product_id) {
- NSString *prod_id = [NSString stringWithCString:product_id.utf8().get_data() encoding:NSUTF8StringEncoding];
-
- if ([pending_transactions objectForKey:prod_id]) {
- [[SKPaymentQueue defaultQueue] finishTransaction:[pending_transactions objectForKey:prod_id]];
- [pending_transactions removeObjectForKey:prod_id];
- }
-};
-
-void InAppStore::set_auto_finish_transaction(bool b) {
- auto_finish_transactions = b;
-}
-
-InAppStore::~InAppStore() {}
-
-#endif
diff --git a/platform/iphone/ios.h b/platform/iphone/ios.h
index 2b29e6f268..4bf64d4bc1 100644
--- a/platform/iphone/ios.h
+++ b/platform/iphone/ios.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,7 +31,7 @@
#ifndef IOS_H
#define IOS_H
-#include "core/object.h"
+#include "core/object/class_db.h"
class iOS : public Object {
GDCLASS(iOS, Object);
diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm
index 5923f558a5..cef03534c4 100644
--- a/platform/iphone/ios.mm
+++ b/platform/iphone/ios.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,17 +29,28 @@
/*************************************************************************/
#include "ios.h"
-#include <sys/sysctl.h>
-
+#import "app_delegate.h"
+#import "view_controller.h"
#import <UIKit/UIKit.h>
+#include <sys/sysctl.h>
void iOS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &iOS::get_rate_url);
};
void iOS::alert(const char *p_alert, const char *p_title) {
- UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:[NSString stringWithUTF8String:p_title] message:[NSString stringWithUTF8String:p_alert] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] autorelease];
- [alert show];
+ NSString *title = [NSString stringWithUTF8String:p_title];
+ NSString *message = [NSString stringWithUTF8String:p_alert];
+
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
+ UIAlertAction *button = [UIAlertAction actionWithTitle:@"OK"
+ style:UIAlertActionStyleCancel
+ handler:^(id){
+ }];
+
+ [alert addAction:button];
+
+ [AppDelegate.viewController presentViewController:alert animated:YES completion:nil];
}
String iOS::get_model() const {
@@ -58,25 +69,11 @@ String iOS::get_model() const {
}
String iOS::get_rate_url(int p_app_id) const {
- String templ = "itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
- String templ_iOS7 = "itms-apps://itunes.apple.com/app/idAPP_ID";
- String templ_iOS8 = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software";
-
- //ios7 before
- String ret = templ;
-
- if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 7.1) {
- // iOS 7 needs a different templateReviewURL @see https://github.com/arashpayan/appirater/issues/131
- ret = templ_iOS7;
- } else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
- // iOS 8 needs a different templateReviewURL also @see https://github.com/arashpayan/appirater/issues/182
- ret = templ_iOS8;
- }
+ String app_url_path = "itms-apps://itunes.apple.com/app/idAPP_ID";
- // ios7 for everything?
- ret = templ_iOS7.replace("APP_ID", String::num(p_app_id));
+ String ret = app_url_path.replace("APP_ID", String::num(p_app_id));
- printf("returning rate url %ls\n", ret.c_str());
+ printf("returning rate url %s\n", ret.utf8().get_data());
return ret;
};
diff --git a/platform/haiku/haiku_gl_view.h b/platform/iphone/joypad_iphone.h
index 59e02d2367..329cc8207f 100644
--- a/platform/haiku/haiku_gl_view.h
+++ b/platform/iphone/joypad_iphone.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* haiku_gl_view.h */
+/* joypad_iphone.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,18 +28,23 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef HAIKU_GL_VIEW_H
-#define HAIKU_GL_VIEW_H
+#import <GameController/GameController.h>
-#include <kernel/image.h> // needed for image_id
+@interface JoypadIPhoneObserver : NSObject
-#include <GLView.h>
+- (void)startObserving;
+- (void)startProcessing;
+- (void)finishObserving;
+
+@end
+
+class JoypadIPhone {
+private:
+ JoypadIPhoneObserver *observer;
-class HaikuGLView : public BGLView {
public:
- HaikuGLView(BRect frame, uint32 type);
- virtual void AttachedToWindow(void);
- virtual void Draw(BRect updateRect);
-};
+ JoypadIPhone();
+ ~JoypadIPhone();
-#endif
+ void start_processing();
+};
diff --git a/platform/iphone/joypad_iphone.mm b/platform/iphone/joypad_iphone.mm
new file mode 100644
index 0000000000..a0f0eee5d3
--- /dev/null
+++ b/platform/iphone/joypad_iphone.mm
@@ -0,0 +1,345 @@
+/*************************************************************************/
+/* joypad_iphone.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import "joypad_iphone.h"
+#include "core/config/project_settings.h"
+#include "drivers/coreaudio/audio_driver_coreaudio.h"
+#include "main/main.h"
+
+#import "godot_view.h"
+
+#include "os_iphone.h"
+
+JoypadIPhone::JoypadIPhone() {
+ observer = [[JoypadIPhoneObserver alloc] init];
+ [observer startObserving];
+}
+
+JoypadIPhone::~JoypadIPhone() {
+ if (observer) {
+ [observer finishObserving];
+ observer = nil;
+ }
+}
+
+void JoypadIPhone::start_processing() {
+ if (observer) {
+ [observer startProcessing];
+ }
+}
+
+@interface JoypadIPhoneObserver ()
+
+@property(assign, nonatomic) BOOL isObserving;
+@property(assign, nonatomic) BOOL isProcessing;
+@property(strong, nonatomic) NSMutableDictionary *connectedJoypads;
+@property(strong, nonatomic) NSMutableArray *joypadsQueue;
+
+@end
+
+@implementation JoypadIPhoneObserver
+
+- (instancetype)init {
+ self = [super init];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.isObserving = NO;
+ self.isProcessing = NO;
+}
+
+- (void)startProcessing {
+ self.isProcessing = YES;
+
+ for (GCController *controller in self.joypadsQueue) {
+ [self addiOSJoypad:controller];
+ }
+
+ [self.joypadsQueue removeAllObjects];
+}
+
+- (void)startObserving {
+ if (self.isObserving) {
+ return;
+ }
+
+ self.isObserving = YES;
+
+ self.connectedJoypads = [NSMutableDictionary dictionary];
+ self.joypadsQueue = [NSMutableArray array];
+
+ // get told when controllers connect, this will be called right away for
+ // already connected controllers
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(controllerWasConnected:)
+ name:GCControllerDidConnectNotification
+ object:nil];
+
+ // get told when controllers disconnect
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(controllerWasDisconnected:)
+ name:GCControllerDidDisconnectNotification
+ object:nil];
+}
+
+- (void)finishObserving {
+ if (self.isObserving) {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ }
+
+ self.isObserving = NO;
+ self.isProcessing = NO;
+
+ self.connectedJoypads = nil;
+ self.joypadsQueue = nil;
+}
+
+- (void)dealloc {
+ [self finishObserving];
+}
+
+- (int)getJoyIdForController:(GCController *)controller {
+ NSArray *keys = [self.connectedJoypads allKeysForObject:controller];
+
+ for (NSNumber *key in keys) {
+ int joy_id = [key intValue];
+ return joy_id;
+ };
+
+ return -1;
+};
+
+- (void)addiOSJoypad:(GCController *)controller {
+ // get a new id for our controller
+ int joy_id = Input::get_singleton()->get_unused_joy_id();
+
+ if (joy_id == -1) {
+ printf("Couldn't retrieve new joy id\n");
+ return;
+ }
+
+ // assign our player index
+ if (controller.playerIndex == GCControllerPlayerIndexUnset) {
+ controller.playerIndex = [self getFreePlayerIndex];
+ };
+
+ // tell Godot about our new controller
+ Input::get_singleton()->joy_connection_changed(joy_id, true, [controller.vendorName UTF8String]);
+
+ // add it to our dictionary, this will retain our controllers
+ [self.connectedJoypads setObject:controller forKey:[NSNumber numberWithInt:joy_id]];
+
+ // set our input handler
+ [self setControllerInputHandler:controller];
+}
+
+- (void)controllerWasConnected:(NSNotification *)notification {
+ // get our controller
+ GCController *controller = (GCController *)notification.object;
+
+ if (!controller) {
+ printf("Couldn't retrieve new controller\n");
+ return;
+ }
+
+ if ([[self.connectedJoypads allKeysForObject:controller] count] > 0) {
+ printf("Controller is already registered\n");
+ } else if (!self.isProcessing) {
+ [self.joypadsQueue addObject:controller];
+ } else {
+ [self addiOSJoypad:controller];
+ }
+}
+
+- (void)controllerWasDisconnected:(NSNotification *)notification {
+ // find our joystick, there should be only one in our dictionary
+ GCController *controller = (GCController *)notification.object;
+
+ if (!controller) {
+ return;
+ }
+
+ NSArray *keys = [self.connectedJoypads allKeysForObject:controller];
+ for (NSNumber *key in keys) {
+ // tell Godot this joystick is no longer there
+ int joy_id = [key intValue];
+ Input::get_singleton()->joy_connection_changed(joy_id, false, "");
+
+ // and remove it from our dictionary
+ [self.connectedJoypads removeObjectForKey:key];
+ };
+};
+
+- (GCControllerPlayerIndex)getFreePlayerIndex {
+ bool have_player_1 = false;
+ bool have_player_2 = false;
+ bool have_player_3 = false;
+ bool have_player_4 = false;
+
+ if (self.connectedJoypads == nil) {
+ NSArray *keys = [self.connectedJoypads allKeys];
+ for (NSNumber *key in keys) {
+ GCController *controller = [self.connectedJoypads objectForKey:key];
+ if (controller.playerIndex == GCControllerPlayerIndex1) {
+ have_player_1 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex2) {
+ have_player_2 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex3) {
+ have_player_3 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex4) {
+ have_player_4 = true;
+ };
+ };
+ };
+
+ if (!have_player_1) {
+ return GCControllerPlayerIndex1;
+ } else if (!have_player_2) {
+ return GCControllerPlayerIndex2;
+ } else if (!have_player_3) {
+ return GCControllerPlayerIndex3;
+ } else if (!have_player_4) {
+ return GCControllerPlayerIndex4;
+ } else {
+ return GCControllerPlayerIndexUnset;
+ };
+}
+
+- (void)setControllerInputHandler:(GCController *)controller {
+ // Hook in the callback handler for the correct gamepad profile.
+ // This is a bit of a weird design choice on Apples part.
+ // You need to select the most capable gamepad profile for the
+ // gamepad attached.
+ if (controller.extendedGamepad != nil) {
+ // The extended gamepad profile has all the input you could possibly find on
+ // a gamepad but will only be active if your gamepad actually has all of
+ // these...
+ _weakify(self);
+ _weakify(controller);
+
+ controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
+ _strongify(self);
+ _strongify(controller);
+
+ int joy_id = [self getJoyIdForController:controller];
+
+ if (element == gamepad.buttonA) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
+ gamepad.buttonA.isPressed);
+ } else if (element == gamepad.buttonB) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_B,
+ gamepad.buttonB.isPressed);
+ } else if (element == gamepad.buttonX) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
+ gamepad.buttonX.isPressed);
+ } else if (element == gamepad.buttonY) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_Y,
+ gamepad.buttonY.isPressed);
+ } else if (element == gamepad.leftShoulder) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_LEFT_SHOULDER,
+ gamepad.leftShoulder.isPressed);
+ } else if (element == gamepad.rightShoulder) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_RIGHT_SHOULDER,
+ gamepad.rightShoulder.isPressed);
+ } else if (element == gamepad.dpad) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
+ gamepad.dpad.up.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
+ gamepad.dpad.down.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT,
+ gamepad.dpad.left.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT,
+ gamepad.dpad.right.isPressed);
+ };
+
+ Input::JoyAxis jx;
+ jx.min = -1;
+ if (element == gamepad.leftThumbstick) {
+ jx.value = gamepad.leftThumbstick.xAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_LEFT_X, jx);
+ jx.value = -gamepad.leftThumbstick.yAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_LEFT_Y, jx);
+ } else if (element == gamepad.rightThumbstick) {
+ jx.value = gamepad.rightThumbstick.xAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_RIGHT_X, jx);
+ jx.value = -gamepad.rightThumbstick.yAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_RIGHT_Y, jx);
+ } else if (element == gamepad.leftTrigger) {
+ jx.value = gamepad.leftTrigger.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_LEFT, jx);
+ } else if (element == gamepad.rightTrigger) {
+ jx.value = gamepad.rightTrigger.value;
+ Input::get_singleton()->joy_axis(joy_id, JOY_AXIS_TRIGGER_RIGHT, jx);
+ };
+ };
+ } else if (controller.microGamepad != nil) {
+ // micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad
+ _weakify(self);
+ _weakify(controller);
+
+ controller.microGamepad.valueChangedHandler = ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
+ _strongify(self);
+ _strongify(controller);
+
+ int joy_id = [self getJoyIdForController:controller];
+
+ if (element == gamepad.buttonA) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_A,
+ gamepad.buttonA.isPressed);
+ } else if (element == gamepad.buttonX) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_X,
+ gamepad.buttonX.isPressed);
+ } else if (element == gamepad.dpad) {
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_UP,
+ gamepad.dpad.up.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_DOWN,
+ gamepad.dpad.down.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_LEFT, gamepad.dpad.left.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JOY_BUTTON_DPAD_RIGHT, gamepad.dpad.right.isPressed);
+ };
+ };
+ }
+
+ ///@TODO need to add support for controller.motion which gives us access to
+ /// the orientation of the device (if supported)
+
+ ///@TODO need to add support for controllerPausedHandler which should be a
+ /// toggle
+};
+
+@end
diff --git a/platform/haiku/haiku_application.h b/platform/iphone/keyboard_input_view.h
index 2e04d921bf..cfbfc7a120 100644
--- a/platform/haiku/haiku_application.h
+++ b/platform/iphone/keyboard_input_view.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* haiku_application.h */
+/* keyboard_input_view.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,16 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef HAIKU_APPLICATION_H
-#define HAIKU_APPLICATION_H
+#import <UIKit/UIKit.h>
-#include <kernel/image.h> // needed for image_id
+@interface GodotKeyboardInputView : UITextView
-#include <Application.h>
+- (BOOL)becomeFirstResponderWithString:(NSString *)existingString multiline:(BOOL)flag cursorStart:(NSInteger)start cursorEnd:(NSInteger)end;
-class HaikuApplication : public BApplication {
-public:
- HaikuApplication();
-};
-
-#endif
+@end
diff --git a/platform/iphone/keyboard_input_view.mm b/platform/iphone/keyboard_input_view.mm
new file mode 100644
index 0000000000..1408f78e90
--- /dev/null
+++ b/platform/iphone/keyboard_input_view.mm
@@ -0,0 +1,195 @@
+/*************************************************************************/
+/* keyboard_input_view.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import "keyboard_input_view.h"
+
+#include "core/os/keyboard.h"
+#include "display_server_iphone.h"
+#include "os_iphone.h"
+
+@interface GodotKeyboardInputView () <UITextViewDelegate>
+
+@property(nonatomic, copy) NSString *previousText;
+@property(nonatomic, assign) NSRange previousSelectedRange;
+
+@end
+
+@implementation GodotKeyboardInputView
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer {
+ self = [super initWithFrame:frame textContainer:textContainer];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.hidden = YES;
+ self.delegate = self;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(observeTextChange:)
+ name:UITextViewTextDidChangeNotification
+ object:self];
+}
+
+- (void)dealloc {
+ self.delegate = nil;
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+// MARK: Keyboard
+
+- (BOOL)canBecomeFirstResponder {
+ return YES;
+}
+
+- (BOOL)becomeFirstResponderWithString:(NSString *)existingString multiline:(BOOL)flag cursorStart:(NSInteger)start cursorEnd:(NSInteger)end {
+ self.text = existingString;
+ self.previousText = existingString;
+
+ NSRange textRange;
+
+ // Either a simple cursor or a selection.
+ if (end > 0) {
+ textRange = NSMakeRange(start, end - start);
+ } else {
+ textRange = NSMakeRange(start, 0);
+ }
+
+ self.selectedRange = textRange;
+ self.previousSelectedRange = textRange;
+
+ return [self becomeFirstResponder];
+}
+
+- (BOOL)resignFirstResponder {
+ self.text = nil;
+ self.previousText = nil;
+ return [super resignFirstResponder];
+}
+
+// MARK: OS Messages
+
+- (void)deleteText:(NSInteger)charactersToDelete {
+ for (int i = 0; i < charactersToDelete; i++) {
+ DisplayServerIPhone::get_singleton()->key(KEY_BACKSPACE, true);
+ DisplayServerIPhone::get_singleton()->key(KEY_BACKSPACE, false);
+ }
+}
+
+- (void)enterText:(NSString *)substring {
+ String characters;
+ characters.parse_utf8([substring UTF8String]);
+
+ for (int i = 0; i < characters.size(); i++) {
+ int character = characters[i];
+
+ switch (character) {
+ case 10:
+ character = KEY_ENTER;
+ break;
+ case 8198:
+ character = KEY_SPACE;
+ break;
+ default:
+ break;
+ }
+
+ DisplayServerIPhone::get_singleton()->key(character, true);
+ DisplayServerIPhone::get_singleton()->key(character, false);
+ }
+}
+
+// MARK: Observer
+
+- (void)observeTextChange:(NSNotification *)notification {
+ if (notification.object != self) {
+ return;
+ }
+
+ if (self.previousSelectedRange.length == 0) {
+ // We are deleting all text before cursor if no range was selected.
+ // This way any inserted or changed text will be updated.
+ NSString *substringToDelete = [self.previousText substringToIndex:self.previousSelectedRange.location];
+ [self deleteText:substringToDelete.length];
+ } else {
+ // If text was previously selected
+ // we are sending only one `backspace`.
+ // It will remove all text from text input.
+ [self deleteText:1];
+ }
+
+ NSString *substringToEnter;
+
+ if (self.selectedRange.length == 0) {
+ // If previous cursor had a selection
+ // we have to calculate an inserted text.
+ if (self.previousSelectedRange.length != 0) {
+ NSInteger rangeEnd = self.selectedRange.location + self.selectedRange.length;
+ NSInteger rangeStart = MIN(self.previousSelectedRange.location, self.selectedRange.location);
+ NSInteger rangeLength = MAX(0, rangeEnd - rangeStart);
+
+ NSRange calculatedRange;
+
+ if (rangeLength >= 0) {
+ calculatedRange = NSMakeRange(rangeStart, rangeLength);
+ } else {
+ calculatedRange = NSMakeRange(rangeStart, 0);
+ }
+
+ substringToEnter = [self.text substringWithRange:calculatedRange];
+ } else {
+ substringToEnter = [self.text substringToIndex:self.selectedRange.location];
+ }
+ } else {
+ substringToEnter = [self.text substringWithRange:self.selectedRange];
+ }
+
+ [self enterText:substringToEnter];
+
+ self.previousText = self.text;
+ self.previousSelectedRange = self.selectedRange;
+}
+
+@end
diff --git a/platform/iphone/logo.png b/platform/iphone/logo.png
index 405b6f93ca..966d8aa70a 100644
--- a/platform/iphone/logo.png
+++ b/platform/iphone/logo.png
Binary files differ
diff --git a/platform/iphone/main.m b/platform/iphone/main.m
index 164db2a74b..fddd5bd99f 100644
--- a/platform/iphone/main.m
+++ b/platform/iphone/main.m
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,24 +28,30 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#import "app_delegate.h"
+#import "godot_app_delegate.h"
#import <UIKit/UIKit.h>
#include <stdio.h>
+#include <vulkan/vulkan.h>
int gargc;
char **gargv;
int main(int argc, char *argv[]) {
+#if defined(VULKAN_ENABLED)
+ //MoltenVK - enable full component swizzling support
+ setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
+#endif
+
printf("*********** main.m\n");
gargc = argc;
gargv = argv;
- NSAutoreleasePool *pool = [NSAutoreleasePool new];
- AppDelegate *app = [AppDelegate alloc];
printf("running app main\n");
- UIApplicationMain(argc, argv, nil, @"AppDelegate");
- printf("main done, pool release\n");
- [pool release];
+ @autoreleasepool {
+ NSString *className = NSStringFromClass([GodotApplicalitionDelegate class]);
+ UIApplicationMain(argc, argv, nil, className);
+ }
+ printf("main done\n");
return 0;
}
diff --git a/platform/haiku/key_mapping_haiku.h b/platform/iphone/native_video_view.h
index e735108e44..2df5e27fa4 100644
--- a/platform/haiku/key_mapping_haiku.h
+++ b/platform/iphone/native_video_view.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* key_mapping_haiku.h */
+/* native_video_view.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,15 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef KEY_MAPPING_HAIKU_H
-#define KEY_MAPPING_HAIKU_H
+#import <UIKit/UIKit.h>
-class KeyMappingHaiku {
- KeyMappingHaiku() {}
+@interface GodotNativeVideoView : UIView
-public:
- static unsigned int get_keysym(int32 raw_char, int32 key);
- static unsigned int get_modifier_keysym(int32 key);
-};
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
+- (BOOL)isVideoPlaying;
+- (void)pauseVideo;
+- (void)unpauseVideo;
+- (void)stopVideo;
-#endif
+@end
diff --git a/platform/iphone/native_video_view.m b/platform/iphone/native_video_view.m
new file mode 100644
index 0000000000..f126749600
--- /dev/null
+++ b/platform/iphone/native_video_view.m
@@ -0,0 +1,266 @@
+/*************************************************************************/
+/* native_video_view.m */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#import "native_video_view.h"
+#import <AVFoundation/AVFoundation.h>
+
+@interface GodotNativeVideoView ()
+
+@property(strong, nonatomic) AVAsset *avAsset;
+@property(strong, nonatomic) AVPlayerItem *avPlayerItem;
+@property(strong, nonatomic) AVPlayer *avPlayer;
+@property(strong, nonatomic) AVPlayerLayer *avPlayerLayer;
+@property(assign, nonatomic) CMTime videoCurrentTime;
+@property(assign, nonatomic) BOOL isVideoCurrentlyPlaying;
+
+@end
+
+@implementation GodotNativeVideoView
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.isVideoCurrentlyPlaying = NO;
+ self.videoCurrentTime = kCMTimeZero;
+
+ [self observeVideoAudio];
+}
+
+- (void)layoutSubviews {
+ [super layoutSubviews];
+
+ self.avPlayerLayer.frame = self.bounds;
+}
+
+- (void)observeVideoAudio {
+ printf("******** adding observer for sound routing changes\n");
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(audioRouteChangeListenerCallback:)
+ name:AVAudioSessionRouteChangeNotification
+ object:nil];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ if (object == self.avPlayerItem && [keyPath isEqualToString:@"status"]) {
+ [self handleVideoOrPlayerStatus];
+ }
+
+ if (object == self.avPlayer && [keyPath isEqualToString:@"rate"]) {
+ [self handleVideoPlayRate];
+ }
+}
+
+// MARK: Video Audio
+
+- (void)audioRouteChangeListenerCallback:(NSNotification *)notification {
+ printf("*********** route changed!\n");
+ NSDictionary *interuptionDict = notification.userInfo;
+
+ NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
+
+ switch (routeChangeReason) {
+ case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
+ NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
+ NSLog(@"Headphone/Line plugged in");
+ } break;
+ case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: {
+ NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
+ NSLog(@"Headphone/Line was pulled. Resuming video play....");
+ if ([self isVideoPlaying]) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+ [self.avPlayer play]; // NOTE: change this line according your current player implementation
+ NSLog(@"resumed play");
+ });
+ }
+ } break;
+ case AVAudioSessionRouteChangeReasonCategoryChange: {
+ // called at start - also when other audio wants to play
+ NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
+ } break;
+ }
+}
+
+// MARK: Native Video Player
+
+- (void)handleVideoOrPlayerStatus {
+ if (self.avPlayerItem.status == AVPlayerItemStatusFailed || self.avPlayer.status == AVPlayerStatusFailed) {
+ [self stopVideo];
+ }
+
+ if (self.avPlayer.status == AVPlayerStatusReadyToPlay && self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay && CMTimeCompare(self.videoCurrentTime, kCMTimeZero) == 0) {
+ // NSLog(@"time: %@", self.video_current_time);
+ [self.avPlayer seekToTime:self.videoCurrentTime];
+ self.videoCurrentTime = kCMTimeZero;
+ }
+}
+
+- (void)handleVideoPlayRate {
+ NSLog(@"Player playback rate changed: %.5f", self.avPlayer.rate);
+ if ([self isVideoPlaying] && self.avPlayer.rate == 0.0 && !self.avPlayer.error) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+ [self.avPlayer play]; // NOTE: change this line according your current player implementation
+ NSLog(@"resumed play");
+ });
+
+ NSLog(@" . . . PAUSED (or just started)");
+ }
+}
+
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
+ self.avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
+
+ self.avPlayerItem = [AVPlayerItem playerItemWithAsset:self.avAsset];
+ [self.avPlayerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
+
+ self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
+ self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
+
+ [self.avPlayer addObserver:self forKeyPath:@"status" options:0 context:nil];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(playerItemDidReachEnd:)
+ name:AVPlayerItemDidPlayToEndTimeNotification
+ object:[self.avPlayer currentItem]];
+
+ [self.avPlayer addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:0];
+
+ [self.avPlayerLayer setFrame:self.bounds];
+ [self.layer addSublayer:self.avPlayerLayer];
+ [self.avPlayer play];
+
+ AVMediaSelectionGroup *audioGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
+
+ NSMutableArray *allAudioParams = [NSMutableArray array];
+ for (id track in audioGroup.options) {
+ NSString *language = [[track locale] localeIdentifier];
+ NSLog(@"subtitle lang: %@", language);
+
+ if ([language isEqualToString:audioTrack]) {
+ AVMutableAudioMixInputParameters *audioInputParams = [AVMutableAudioMixInputParameters audioMixInputParameters];
+ [audioInputParams setVolume:videoVolume atTime:kCMTimeZero];
+ [audioInputParams setTrackID:[track trackID]];
+ [allAudioParams addObject:audioInputParams];
+
+ AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
+ [audioMix setInputParameters:allAudioParams];
+
+ [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:audioGroup];
+ [self.avPlayer.currentItem setAudioMix:audioMix];
+
+ break;
+ }
+ }
+
+ AVMediaSelectionGroup *subtitlesGroup = [self.avAsset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
+ NSArray *useableTracks = [AVMediaSelectionGroup mediaSelectionOptionsFromArray:subtitlesGroup.options withoutMediaCharacteristics:[NSArray arrayWithObject:AVMediaCharacteristicContainsOnlyForcedSubtitles]];
+
+ for (id track in useableTracks) {
+ NSString *language = [[track locale] localeIdentifier];
+ NSLog(@"subtitle lang: %@", language);
+
+ if ([language isEqualToString:subtitleTrack]) {
+ [self.avPlayer.currentItem selectMediaOption:track inMediaSelectionGroup:subtitlesGroup];
+ break;
+ }
+ }
+
+ self.isVideoCurrentlyPlaying = YES;
+
+ return true;
+}
+
+- (BOOL)isVideoPlaying {
+ if (self.avPlayer.error) {
+ printf("Error during playback\n");
+ }
+ return (self.avPlayer.rate > 0 && !self.avPlayer.error);
+}
+
+- (void)pauseVideo {
+ self.videoCurrentTime = self.avPlayer.currentTime;
+ [self.avPlayer pause];
+ self.isVideoCurrentlyPlaying = NO;
+}
+
+- (void)unpauseVideo {
+ [self.avPlayer play];
+ self.isVideoCurrentlyPlaying = YES;
+}
+
+- (void)playerItemDidReachEnd:(NSNotification *)notification {
+ [self stopVideo];
+}
+
+- (void)finishPlayingVideo {
+ [self.avPlayer pause];
+ [self.avPlayerLayer removeFromSuperlayer];
+ self.avPlayerLayer = nil;
+
+ if (self.avPlayerItem) {
+ [self.avPlayerItem removeObserver:self forKeyPath:@"status"];
+ self.avPlayerItem = nil;
+ }
+
+ if (self.avPlayer) {
+ [self.avPlayer removeObserver:self forKeyPath:@"status"];
+ self.avPlayer = nil;
+ }
+
+ self.avAsset = nil;
+
+ self.isVideoCurrentlyPlaying = NO;
+}
+
+- (void)stopVideo {
+ [self finishPlayingVideo];
+
+ [self removeFromSuperview];
+}
+
+@end
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
deleted file mode 100644
index 41dd623e69..0000000000
--- a/platform/iphone/os_iphone.cpp
+++ /dev/null
@@ -1,632 +0,0 @@
-/*************************************************************************/
-/* os_iphone.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-#ifdef IPHONE_ENABLED
-
-#include "os_iphone.h"
-
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
-// #import <QuartzCore/CAMetalLayer.h>
-#include <vulkan/vulkan_metal.h>
-#endif
-
-#include "servers/rendering/rendering_server_raster.h"
-#include "servers/rendering/rendering_server_wrap_mt.h"
-
-#include "main/main.h"
-
-#include "core/io/file_access_pack.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
-#include "core/project_settings.h"
-#include "drivers/unix/syslog_logger.h"
-
-#include "semaphore_iphone.h"
-
-#include <dlfcn.h>
-
-int OSIPhone::get_video_driver_count() const {
- return 2;
-};
-
-const char *OSIPhone::get_video_driver_name(int p_driver) const {
- switch (p_driver) {
- case VIDEO_DRIVER_GLES2:
- return "GLES2";
- }
- ERR_FAIL_V_MSG(nullptr, "Invalid video driver index: " + itos(p_driver) + ".");
-};
-
-OSIPhone *OSIPhone::get_singleton() {
- return (OSIPhone *)OS::get_singleton();
-};
-
-extern int gl_view_base_fb; // from gl_view.mm
-
-void OSIPhone::set_data_dir(String p_dir) {
- DirAccess *da = DirAccess::open(p_dir);
-
- data_dir = da->get_current_dir();
- printf("setting data dir to %ls from %ls\n", data_dir.c_str(), p_dir.c_str());
- memdelete(da);
-};
-
-void OSIPhone::set_unique_id(String p_id) {
- unique_id = p_id;
-};
-
-String OSIPhone::get_unique_id() const {
- return unique_id;
-};
-
-void OSIPhone::initialize_core() {
- OS_Unix::initialize_core();
-
- set_data_dir(data_dir);
-};
-
-int OSIPhone::get_current_video_driver() const {
- return video_driver_index;
-}
-
-Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
- video_driver_index = p_video_driver;
-
-#if defined(OPENGL_ENABLED)
- bool gl_initialization_error = false;
-
- // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
-
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- } else {
- gl_initialization_error = true;
- }
-
- if (gl_initialization_error) {
- OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.",
- "Unable to initialize video driver");
- return ERR_UNAVAILABLE;
- }
-#endif
-
-#if defined(VULKAN_ENABLED)
- RasterizerRD::make_current();
-#endif
-
- rendering_server = memnew(RenderingServerRaster);
- // FIXME: Reimplement threaded rendering
- if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- rendering_server = memnew(RenderingServerWrapMT(rendering_server, false));
- }
- rendering_server->init();
- //rendering_server->cursor_set_visible(false, 0);
-
-#if defined(OPENGL_ENABLED)
- // reset this to what it should be, it will have been set to 0 after rendering_server->init() is called
- RasterizerStorageGLES2::system_fbo = gl_view_base_fb;
-#endif
-
- AudioDriverManager::initialize(p_audio_driver);
-
- input = memnew(InputDefault);
-
-#ifdef GAME_CENTER_ENABLED
- game_center = memnew(GameCenter);
- Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center));
- game_center->connect();
-#endif
-
-#ifdef STOREKIT_ENABLED
- store_kit = memnew(InAppStore);
- Engine::get_singleton()->add_singleton(Engine::Singleton("InAppStore", store_kit));
-#endif
-
-#ifdef ICLOUD_ENABLED
- icloud = memnew(ICloud);
- Engine::get_singleton()->add_singleton(Engine::Singleton("ICloud", icloud));
- //icloud->connect();
-#endif
- ios = memnew(iOS);
- Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", ios));
-
- return OK;
-};
-
-MainLoop *OSIPhone::get_main_loop() const {
- return main_loop;
-};
-
-void OSIPhone::set_main_loop(MainLoop *p_main_loop) {
- main_loop = p_main_loop;
-
- if (main_loop) {
- input->set_main_loop(p_main_loop);
- main_loop->init();
- }
-};
-
-bool OSIPhone::iterate() {
- if (!main_loop)
- return true;
-
- if (main_loop) {
- for (int i = 0; i < event_count; i++) {
- input->parse_input_event(event_queue[i]);
- };
- };
- event_count = 0;
-
- return Main::iteration();
-};
-
-void OSIPhone::key(uint32_t p_key, bool p_pressed) {
- Ref<InputEventKey> ev;
- ev.instance();
- ev->set_echo(false);
- ev->set_pressed(p_pressed);
- ev->set_keycode(p_key);
- ev->set_physical_keycode(p_key);
- ev->set_unicode(p_key);
- queue_event(ev);
-};
-
-void OSIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick) {
- if (!GLOBAL_DEF("debug/disable_touch", false)) {
- Ref<InputEventScreenTouch> ev;
- ev.instance();
-
- ev->set_index(p_idx);
- ev->set_pressed(p_pressed);
- ev->set_position(Vector2(p_x, p_y));
- queue_event(ev);
- };
-
- touch_list.pressed[p_idx] = p_pressed;
-};
-
-void OSIPhone::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) {
- if (!GLOBAL_DEF("debug/disable_touch", false)) {
- Ref<InputEventScreenDrag> ev;
- ev.instance();
- ev->set_index(p_idx);
- ev->set_position(Vector2(p_x, p_y));
- ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
- queue_event(ev);
- };
-};
-
-void OSIPhone::queue_event(const Ref<InputEvent> &p_event) {
- ERR_FAIL_INDEX(event_count, MAX_EVENTS);
-
- event_queue[event_count++] = p_event;
-};
-
-void OSIPhone::touches_cancelled() {
- for (int i = 0; i < MAX_MOUSE_COUNT; i++) {
- if (touch_list.pressed[i]) {
- // send a mouse_up outside the screen
- touch_press(i, -1, -1, false, false);
- };
- };
-};
-
-static const float ACCEL_RANGE = 1;
-
-void OSIPhone::update_gravity(float p_x, float p_y, float p_z) {
- input->set_gravity(Vector3(p_x, p_y, p_z));
-};
-
-void OSIPhone::update_accelerometer(float p_x, float p_y, float p_z) {
- // Found out the Z should not be negated! Pass as is!
- input->set_accelerometer(Vector3(p_x / (float)ACCEL_RANGE, p_y / (float)ACCEL_RANGE, p_z / (float)ACCEL_RANGE));
-
- /*
- if (p_x != last_accel.x) {
- //printf("updating accel x %f\n", p_x);
- InputEvent ev;
- ev.type = InputEvent::JOYPAD_MOTION;
- ev.device = 0;
- ev.joy_motion.axis = JOY_ANALOG_0;
- ev.joy_motion.axis_value = (p_x / (float)ACCEL_RANGE);
- last_accel.x = p_x;
- queue_event(ev);
- };
- if (p_y != last_accel.y) {
- //printf("updating accel y %f\n", p_y);
- InputEvent ev;
- ev.type = InputEvent::JOYPAD_MOTION;
- ev.device = 0;
- ev.joy_motion.axis = JOY_ANALOG_1;
- ev.joy_motion.axis_value = (p_y / (float)ACCEL_RANGE);
- last_accel.y = p_y;
- queue_event(ev);
- };
- if (p_z != last_accel.z) {
- //printf("updating accel z %f\n", p_z);
- InputEvent ev;
- ev.type = InputEvent::JOYPAD_MOTION;
- ev.device = 0;
- ev.joy_motion.axis = JOY_ANALOG_2;
- ev.joy_motion.axis_value = ( (1.0 - p_z) / (float)ACCEL_RANGE);
- last_accel.z = p_z;
- queue_event(ev);
- };
- */
-};
-
-void OSIPhone::update_magnetometer(float p_x, float p_y, float p_z) {
- input->set_magnetometer(Vector3(p_x, p_y, p_z));
-};
-
-void OSIPhone::update_gyroscope(float p_x, float p_y, float p_z) {
- input->set_gyroscope(Vector3(p_x, p_y, p_z));
-};
-
-int OSIPhone::get_unused_joy_id() {
- return input->get_unused_joy_id();
-};
-
-void OSIPhone::joy_connection_changed(int p_idx, bool p_connected, String p_name) {
- input->joy_connection_changed(p_idx, p_connected, p_name);
-};
-
-void OSIPhone::joy_button(int p_device, int p_button, bool p_pressed) {
- input->joy_button(p_device, p_button, p_pressed);
-};
-
-void OSIPhone::joy_axis(int p_device, int p_axis, const InputDefault::JoyAxis &p_value) {
- input->joy_axis(p_device, p_axis, p_value);
-};
-
-void OSIPhone::delete_main_loop() {
- if (main_loop) {
- main_loop->finish();
- memdelete(main_loop);
- };
-
- main_loop = nullptr;
-};
-
-void OSIPhone::finalize() {
- delete_main_loop();
-
- memdelete(input);
- memdelete(ios);
-
-#ifdef GAME_CENTER_ENABLED
- memdelete(game_center);
-#endif
-
-#ifdef STOREKIT_ENABLED
- memdelete(store_kit);
-#endif
-
-#ifdef ICLOUD_ENABLED
- memdelete(icloud);
-#endif
-
- rendering_server->finish();
- memdelete(rendering_server);
- // memdelete(rasterizer);
-
- // Free unhandled events before close
- for (int i = 0; i < MAX_EVENTS; i++) {
- event_queue[i].unref();
- };
- event_count = 0;
-};
-
-void OSIPhone::set_mouse_show(bool p_show) {}
-void OSIPhone::set_mouse_grab(bool p_grab) {}
-
-bool OSIPhone::is_mouse_grab_enabled() const {
- return true;
-};
-
-Point2 OSIPhone::get_mouse_position() const {
- return Point2();
-};
-
-int OSIPhone::get_mouse_button_state() const {
- return 0;
-};
-
-void OSIPhone::set_window_title(const String &p_title) {}
-
-void OSIPhone::alert(const String &p_alert, const String &p_title) {
- const CharString utf8_alert = p_alert.utf8();
- const CharString utf8_title = p_title.utf8();
- iOS::alert(utf8_alert.get_data(), utf8_title.get_data());
-}
-
-Error OSIPhone::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
- if (p_path.length() == 0) {
- p_library_handle = RTLD_SELF;
- return OK;
- }
- return OS_Unix::open_dynamic_library(p_path, p_library_handle, p_also_set_library_path);
-}
-
-Error OSIPhone::close_dynamic_library(void *p_library_handle) {
- if (p_library_handle == RTLD_SELF) {
- return OK;
- }
- return OS_Unix::close_dynamic_library(p_library_handle);
-}
-
-HashMap<String, void *> OSIPhone::dynamic_symbol_lookup_table;
-void register_dynamic_symbol(char *name, void *address) {
- OSIPhone::dynamic_symbol_lookup_table[String(name)] = address;
-}
-
-Error OSIPhone::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional) {
- if (p_library_handle == RTLD_SELF) {
- void **ptr = OSIPhone::dynamic_symbol_lookup_table.getptr(p_name);
- if (ptr) {
- p_symbol_handle = *ptr;
- return OK;
- }
- }
- return OS_Unix::get_dynamic_library_symbol_handle(p_library_handle, p_name, p_symbol_handle, p_optional);
-}
-
-void OSIPhone::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
- video_mode = p_video_mode;
-};
-
-OS::VideoMode OSIPhone::get_video_mode(int p_screen) const {
- return video_mode;
-};
-
-void OSIPhone::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
- p_list->push_back(video_mode);
-};
-
-bool OSIPhone::can_draw() const {
- if (native_video_is_playing())
- return false;
- return true;
-};
-
-int OSIPhone::set_base_framebuffer(int p_fb) {
-#if defined(OPENGL_ENABLED)
- // gl_view_base_fb has not been updated yet
- RasterizerStorageGLES2::system_fbo = p_fb;
-#endif
-
- return 0;
-};
-
-bool OSIPhone::has_virtual_keyboard() const {
- return true;
-};
-
-extern void _show_keyboard(String p_existing);
-extern void _hide_keyboard();
-extern Error _shell_open(String p_uri);
-extern void _set_keep_screen_on(bool p_enabled);
-extern void _vibrate();
-
-void OSIPhone::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
- _show_keyboard(p_existing_text);
-};
-
-void OSIPhone::hide_virtual_keyboard() {
- _hide_keyboard();
-};
-
-void OSIPhone::set_virtual_keyboard_height(int p_height) {
- virtual_keyboard_height = p_height;
-}
-
-int OSIPhone::get_virtual_keyboard_height() const {
- return virtual_keyboard_height;
-}
-
-Error OSIPhone::shell_open(String p_uri) {
- return _shell_open(p_uri);
-};
-
-void OSIPhone::set_keep_screen_on(bool p_enabled) {
- OS::set_keep_screen_on(p_enabled);
- _set_keep_screen_on(p_enabled);
-};
-
-String OSIPhone::get_user_data_dir() const {
- return data_dir;
-};
-
-String OSIPhone::get_name() const {
- return "iOS";
-};
-
-String OSIPhone::get_model_name() const {
- String model = ios->get_model();
- if (model != "")
- return model;
-
- return OS_Unix::get_model_name();
-}
-
-Size2 OSIPhone::get_window_size() const {
- return Vector2(video_mode.width, video_mode.height);
-}
-
-extern Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height);
-
-Rect2 OSIPhone::get_window_safe_area() const {
- return _get_ios_window_safe_area(video_mode.width, video_mode.height);
-}
-
-bool OSIPhone::has_touchscreen_ui_hint() const {
- return true;
-}
-
-void OSIPhone::set_locale(String p_locale) {
- locale_code = p_locale;
-}
-
-String OSIPhone::get_locale() const {
- return locale_code;
-}
-
-extern bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track);
-extern bool _is_video_playing();
-extern void _pause_video();
-extern void _unpause_video();
-extern void _stop_video();
-extern void _focus_out_video();
-
-Error OSIPhone::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) {
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- bool exists = f && f->is_open();
-
- String tempFile = get_user_data_dir();
- if (!exists)
- return FAILED;
-
- if (p_path.begins_with("res://")) {
- if (PackedData::get_singleton()->has_path(p_path)) {
- print("Unable to play %S using the native player as it resides in a .pck file\n", p_path.c_str());
- return ERR_INVALID_PARAMETER;
- } else {
- p_path = p_path.replace("res:/", ProjectSettings::get_singleton()->get_resource_path());
- }
- } else if (p_path.begins_with("user://"))
- p_path = p_path.replace("user:/", get_user_data_dir());
-
- memdelete(f);
-
- print("Playing video: %S\n", p_path.c_str());
- if (_play_video(p_path, p_volume, p_audio_track, p_subtitle_track))
- return OK;
- return FAILED;
-}
-
-bool OSIPhone::native_video_is_playing() const {
- return _is_video_playing();
-}
-
-void OSIPhone::native_video_pause() {
- if (native_video_is_playing())
- _pause_video();
-}
-
-void OSIPhone::native_video_unpause() {
- _unpause_video();
-};
-
-void OSIPhone::native_video_focus_out() {
- _focus_out_video();
-};
-
-void OSIPhone::native_video_stop() {
- if (native_video_is_playing())
- _stop_video();
-}
-
-void OSIPhone::vibrate_handheld(int p_duration_ms) {
- // iOS does not support duration for vibration
- _vibrate();
-}
-
-bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
- return p_feature == "mobile";
-}
-
-// Initialization order between compilation units is not guaranteed,
-// so we use this as a hack to ensure certain code is called before
-// everything else, but after all units are initialized.
-typedef void (*init_callback)();
-static init_callback *ios_init_callbacks = nullptr;
-static int ios_init_callbacks_count = 0;
-static int ios_init_callbacks_capacity = 0;
-
-void add_ios_init_callback(init_callback cb) {
- if (ios_init_callbacks_count == ios_init_callbacks_capacity) {
- void *new_ptr = realloc(ios_init_callbacks, sizeof(cb) * 32);
- if (new_ptr) {
- ios_init_callbacks = (init_callback *)(new_ptr);
- ios_init_callbacks_capacity += 32;
- }
- }
- if (ios_init_callbacks_capacity > ios_init_callbacks_count) {
- ios_init_callbacks[ios_init_callbacks_count] = cb;
- ++ios_init_callbacks_count;
- }
-}
-
-OSIPhone::OSIPhone(int width, int height, String p_data_dir) {
- for (int i = 0; i < ios_init_callbacks_count; ++i) {
- ios_init_callbacks[i]();
- }
- free(ios_init_callbacks);
- ios_init_callbacks = nullptr;
- ios_init_callbacks_count = 0;
- ios_init_callbacks_capacity = 0;
-
- main_loop = nullptr;
- rendering_server = nullptr;
-
- VideoMode vm;
- vm.fullscreen = true;
- vm.width = width;
- vm.height = height;
- vm.resizable = false;
- set_video_mode(vm);
- event_count = 0;
- virtual_keyboard_height = 0;
-
- // can't call set_data_dir from here, since it requires DirAccess
- // which is initialized in initialize_core
- data_dir = p_data_dir;
-
- Vector<Logger *> loggers;
- loggers.push_back(memnew(SyslogLogger));
-#ifdef DEBUG_ENABLED
- // it seems iOS app's stdout/stderr is only obtainable if you launch it from Xcode
- loggers.push_back(memnew(StdLogger));
-#endif
- _set_logger(memnew(CompositeLogger(loggers)));
-
- AudioDriverManager::add_driver(&audio_driver);
-};
-
-OSIPhone::~OSIPhone() {
-}
-
-#endif
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 955eb15d57..f4ff909adf 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,180 +33,92 @@
#ifndef OS_IPHONE_H
#define OS_IPHONE_H
-#include "core/input/input.h"
#include "drivers/coreaudio/audio_driver_coreaudio.h"
#include "drivers/unix/os_unix.h"
-#include "game_center.h"
-#include "icloud.h"
-#include "in_app_store.h"
#include "ios.h"
+#include "joypad_iphone.h"
#include "servers/audio_server.h"
-#include "servers/rendering/rasterizer.h"
-#include "servers/rendering_server.h"
+#include "servers/rendering/renderer_compositor.h"
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
#include "platform/iphone/vulkan_context_iphone.h"
#endif
+extern void godot_ios_plugins_initialize();
+extern void godot_ios_plugins_deinitialize();
+
class OSIPhone : public OS_Unix {
private:
- enum {
- MAX_MOUSE_COUNT = 8,
- MAX_EVENTS = 64,
- };
-
static HashMap<String, void *> dynamic_symbol_lookup_table;
friend void register_dynamic_symbol(char *name, void *address);
- RenderingServer *rendering_server;
-
AudioDriverCoreAudio audio_driver;
-#ifdef GAME_CENTER_ENABLED
- GameCenter *game_center;
-#endif
-#ifdef STOREKIT_ENABLED
- InAppStore *store_kit;
-#endif
-#ifdef ICLOUD_ENABLED
- ICloud *icloud;
-#endif
iOS *ios;
- MainLoop *main_loop;
-
-#if defined(VULKAN_ENABLED)
- VulkanContextIPhone *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
-#endif
- VideoMode video_mode;
-
- virtual int get_video_driver_count() const;
- virtual const char *get_video_driver_name(int p_driver) const;
-
- virtual int get_current_video_driver() const;
-
- virtual void initialize_core();
- virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
-
- virtual void set_main_loop(MainLoop *p_main_loop);
- virtual MainLoop *get_main_loop() const;
-
- virtual void delete_main_loop();
+ JoypadIPhone *joypad_iphone;
- virtual void finalize();
+ MainLoop *main_loop;
- struct MouseList {
- bool pressed[MAX_MOUSE_COUNT];
- MouseList() {
- for (int i = 0; i < MAX_MOUSE_COUNT; i++)
- pressed[i] = false;
- };
- };
+ virtual void initialize_core() override;
+ virtual void initialize() override;
- MouseList touch_list;
+ virtual void initialize_joypads() override {
+ }
- Vector3 last_accel;
+ virtual void set_main_loop(MainLoop *p_main_loop) override;
+ virtual MainLoop *get_main_loop() const override;
- Ref<InputEvent> event_queue[MAX_EVENTS];
- int event_count;
- void queue_event(const Ref<InputEvent> &p_event);
+ virtual void delete_main_loop() override;
- String data_dir;
- String unique_id;
- String locale_code;
+ virtual void finalize() override;
- InputDefault *input;
+ String user_data_dir;
- int virtual_keyboard_height;
+ bool is_focused = false;
- int video_driver_index;
+ void deinitialize_modules();
public:
- bool iterate();
-
- uint8_t get_orientations() const;
-
- void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick);
- void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y);
- void touches_cancelled();
- void key(uint32_t p_key, bool p_pressed);
- void set_virtual_keyboard_height(int p_height);
-
- int set_base_framebuffer(int p_fb);
-
- void update_gravity(float p_x, float p_y, float p_z);
- void update_accelerometer(float p_x, float p_y, float p_z);
- void update_magnetometer(float p_x, float p_y, float p_z);
- void update_gyroscope(float p_x, float p_y, float p_z);
-
- int get_unused_joy_id();
- void joy_connection_changed(int p_idx, bool p_connected, String p_name);
- void joy_button(int p_device, int p_button, bool p_pressed);
- void joy_axis(int p_device, int p_axis, const InputDefault::JoyAxis &p_value);
-
static OSIPhone *get_singleton();
- virtual void set_mouse_show(bool p_show);
- virtual void set_mouse_grab(bool p_grab);
- virtual bool is_mouse_grab_enabled() const;
- virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
- virtual void set_window_title(const String &p_title);
-
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
-
- virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
- virtual Error close_dynamic_library(void *p_library_handle);
- virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false);
-
- virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0);
- virtual VideoMode get_video_mode(int p_screen = 0) const;
- virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const;
+ OSIPhone(String p_data_dir);
+ ~OSIPhone();
- virtual void set_keep_screen_on(bool p_enabled);
+ void initialize_modules();
- virtual bool can_draw() const;
+ bool iterate();
- virtual bool has_virtual_keyboard() const;
- virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1);
- virtual void hide_virtual_keyboard();
- virtual int get_virtual_keyboard_height() const;
+ void start();
- virtual Size2 get_window_size() const;
- virtual Rect2 get_window_safe_area() const;
+ virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override;
+ virtual Error close_dynamic_library(void *p_library_handle) override;
+ virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override;
- virtual bool has_touchscreen_ui_hint() const;
+ virtual void alert(const String &p_alert,
+ const String &p_title = "ALERT!") override;
- void set_data_dir(String p_dir);
+ virtual String get_name() const override;
+ virtual String get_model_name() const override;
- virtual String get_name() const;
- virtual String get_model_name() const;
+ virtual Error shell_open(String p_uri) override;
- Error shell_open(String p_uri);
+ void set_user_data_dir(String p_dir);
+ virtual String get_user_data_dir() const override;
- String get_user_data_dir() const;
+ virtual String get_locale() const override;
- void set_locale(String p_locale);
- String get_locale() const;
+ virtual String get_unique_id() const override;
- void set_unique_id(String p_id);
- String get_unique_id() const;
+ virtual void vibrate_handheld(int p_duration_ms = 500) override;
- virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track);
- virtual bool native_video_is_playing() const;
- virtual void native_video_pause();
- virtual void native_video_unpause();
- virtual void native_video_focus_out();
- virtual void native_video_stop();
- virtual void vibrate_handheld(int p_duration_ms = 500);
+ virtual bool _check_internal_feature_support(const String &p_feature) override;
- virtual bool _check_internal_feature_support(const String &p_feature);
- OSIPhone(int width, int height, String p_data_dir);
- ~OSIPhone();
+ void on_focus_out();
+ void on_focus_in();
};
#endif // OS_IPHONE_H
-#endif
+#endif // IPHONE_ENABLED
diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm
new file mode 100644
index 0000000000..51c4da2960
--- /dev/null
+++ b/platform/iphone/os_iphone.mm
@@ -0,0 +1,330 @@
+/*************************************************************************/
+/* os_iphone.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifdef IPHONE_ENABLED
+
+#include "os_iphone.h"
+#import "app_delegate.h"
+#include "core/config/project_settings.h"
+#include "core/io/file_access_pack.h"
+#include "core/os/dir_access.h"
+#include "core/os/file_access.h"
+#include "display_server_iphone.h"
+#include "drivers/unix/syslog_logger.h"
+#import "godot_view.h"
+#include "main/main.h"
+#import "view_controller.h"
+
+#import <AudioToolbox/AudioServices.h>
+#import <UIKit/UIKit.h>
+#import <dlfcn.h>
+
+#if defined(VULKAN_ENABLED)
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#import <QuartzCore/CAMetalLayer.h>
+#include <vulkan/vulkan_metal.h>
+#endif
+
+// Initialization order between compilation units is not guaranteed,
+// so we use this as a hack to ensure certain code is called before
+// everything else, but after all units are initialized.
+typedef void (*init_callback)();
+static init_callback *ios_init_callbacks = nullptr;
+static int ios_init_callbacks_count = 0;
+static int ios_init_callbacks_capacity = 0;
+HashMap<String, void *> OSIPhone::dynamic_symbol_lookup_table;
+
+void add_ios_init_callback(init_callback cb) {
+ if (ios_init_callbacks_count == ios_init_callbacks_capacity) {
+ void *new_ptr = realloc(ios_init_callbacks, sizeof(cb) * 32);
+ if (new_ptr) {
+ ios_init_callbacks = (init_callback *)(new_ptr);
+ ios_init_callbacks_capacity += 32;
+ }
+ }
+ if (ios_init_callbacks_capacity > ios_init_callbacks_count) {
+ ios_init_callbacks[ios_init_callbacks_count] = cb;
+ ++ios_init_callbacks_count;
+ }
+}
+
+void register_dynamic_symbol(char *name, void *address) {
+ OSIPhone::dynamic_symbol_lookup_table[String(name)] = address;
+}
+
+OSIPhone *OSIPhone::get_singleton() {
+ return (OSIPhone *)OS::get_singleton();
+}
+
+OSIPhone::OSIPhone(String p_data_dir) {
+ for (int i = 0; i < ios_init_callbacks_count; ++i) {
+ ios_init_callbacks[i]();
+ }
+ free(ios_init_callbacks);
+ ios_init_callbacks = nullptr;
+ ios_init_callbacks_count = 0;
+ ios_init_callbacks_capacity = 0;
+
+ main_loop = nullptr;
+
+ // can't call set_data_dir from here, since it requires DirAccess
+ // which is initialized in initialize_core
+ user_data_dir = p_data_dir;
+
+ Vector<Logger *> loggers;
+ loggers.push_back(memnew(SyslogLogger));
+#ifdef DEBUG_ENABLED
+ // it seems iOS app's stdout/stderr is only obtainable if you launch it from
+ // Xcode
+ loggers.push_back(memnew(StdLogger));
+#endif
+ _set_logger(memnew(CompositeLogger(loggers)));
+
+ AudioDriverManager::add_driver(&audio_driver);
+
+ DisplayServerIPhone::register_iphone_driver();
+}
+
+OSIPhone::~OSIPhone() {}
+
+void OSIPhone::initialize_core() {
+ OS_Unix::initialize_core();
+
+ set_user_data_dir(user_data_dir);
+}
+
+void OSIPhone::initialize() {
+ initialize_core();
+}
+
+void OSIPhone::initialize_modules() {
+ ios = memnew(iOS);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", ios));
+
+ joypad_iphone = memnew(JoypadIPhone);
+}
+
+void OSIPhone::deinitialize_modules() {
+ if (joypad_iphone) {
+ memdelete(joypad_iphone);
+ }
+
+ if (ios) {
+ memdelete(ios);
+ }
+
+ godot_ios_plugins_deinitialize();
+}
+
+void OSIPhone::set_main_loop(MainLoop *p_main_loop) {
+ main_loop = p_main_loop;
+
+ if (main_loop) {
+ main_loop->initialize();
+ }
+}
+
+MainLoop *OSIPhone::get_main_loop() const {
+ return main_loop;
+}
+
+void OSIPhone::delete_main_loop() {
+ if (main_loop) {
+ main_loop->finalize();
+ memdelete(main_loop);
+ };
+
+ main_loop = nullptr;
+}
+
+bool OSIPhone::iterate() {
+ if (!main_loop) {
+ return true;
+ }
+
+ if (DisplayServer::get_singleton()) {
+ DisplayServer::get_singleton()->process_events();
+ }
+
+ return Main::iteration();
+}
+
+void OSIPhone::start() {
+ godot_ios_plugins_initialize();
+
+ Main::start();
+
+ if (joypad_iphone) {
+ joypad_iphone->start_processing();
+ }
+}
+
+void OSIPhone::finalize() {
+ deinitialize_modules();
+
+ // Already gets called
+ // delete_main_loop();
+}
+
+// MARK: Dynamic Libraries
+
+Error OSIPhone::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
+ if (p_path.length() == 0) {
+ p_library_handle = RTLD_SELF;
+ return OK;
+ }
+ return OS_Unix::open_dynamic_library(p_path, p_library_handle, p_also_set_library_path);
+}
+
+Error OSIPhone::close_dynamic_library(void *p_library_handle) {
+ if (p_library_handle == RTLD_SELF) {
+ return OK;
+ }
+ return OS_Unix::close_dynamic_library(p_library_handle);
+}
+
+Error OSIPhone::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional) {
+ if (p_library_handle == RTLD_SELF) {
+ void **ptr = OSIPhone::dynamic_symbol_lookup_table.getptr(p_name);
+ if (ptr) {
+ p_symbol_handle = *ptr;
+ return OK;
+ }
+ }
+ return OS_Unix::get_dynamic_library_symbol_handle(p_library_handle, p_name, p_symbol_handle, p_optional);
+}
+
+void OSIPhone::alert(const String &p_alert, const String &p_title) {
+ const CharString utf8_alert = p_alert.utf8();
+ const CharString utf8_title = p_title.utf8();
+ iOS::alert(utf8_alert.get_data(), utf8_title.get_data());
+}
+
+String OSIPhone::get_name() const {
+ return "iOS";
+};
+
+String OSIPhone::get_model_name() const {
+ String model = ios->get_model();
+ if (model != "")
+ return model;
+
+ return OS_Unix::get_model_name();
+}
+
+Error OSIPhone::shell_open(String p_uri) {
+ NSString *urlPath = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()];
+ NSURL *url = [NSURL URLWithString:urlPath];
+
+ if (![[UIApplication sharedApplication] canOpenURL:url]) {
+ return ERR_CANT_OPEN;
+ }
+
+ printf("opening url %s\n", p_uri.utf8().get_data());
+
+ [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
+
+ return OK;
+};
+
+void OSIPhone::set_user_data_dir(String p_dir) {
+ DirAccess *da = DirAccess::open(p_dir);
+
+ user_data_dir = da->get_current_dir();
+ printf("setting data dir to %s from %s\n", user_data_dir.utf8().get_data(), p_dir.utf8().get_data());
+ memdelete(da);
+}
+
+String OSIPhone::get_user_data_dir() const {
+ return user_data_dir;
+}
+
+String OSIPhone::get_locale() const {
+ NSString *preferedLanguage = [NSLocale preferredLanguages].firstObject;
+
+ if (preferedLanguage) {
+ return String::utf8([preferedLanguage UTF8String]).replace("-", "_");
+ }
+
+ NSString *localeIdentifier = [[NSLocale currentLocale] localeIdentifier];
+ return String::utf8([localeIdentifier UTF8String]).replace("-", "_");
+}
+
+String OSIPhone::get_unique_id() const {
+ NSString *uuid = [UIDevice currentDevice].identifierForVendor.UUIDString;
+ return String::utf8([uuid UTF8String]);
+}
+
+void OSIPhone::vibrate_handheld(int p_duration_ms) {
+ // iOS does not support duration for vibration
+ AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
+}
+
+bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
+ return p_feature == "mobile";
+}
+
+void OSIPhone::on_focus_out() {
+ if (is_focused) {
+ is_focused = false;
+
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
+ }
+
+ [AppDelegate.viewController.godotView stopRendering];
+
+ if (DisplayServerIPhone::get_singleton() && DisplayServerIPhone::get_singleton()->native_video_is_playing()) {
+ DisplayServerIPhone::get_singleton()->native_video_pause();
+ }
+
+ audio_driver.stop();
+ }
+}
+
+void OSIPhone::on_focus_in() {
+ if (!is_focused) {
+ is_focused = true;
+
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_IN);
+ }
+
+ [AppDelegate.viewController.godotView startRendering];
+
+ if (DisplayServerIPhone::get_singleton() && DisplayServerIPhone::get_singleton()->native_video_is_playing()) {
+ DisplayServerIPhone::get_singleton()->native_video_unpause();
+ }
+
+ audio_driver.start();
+ }
+}
+
+#endif // IPHONE_ENABLED
diff --git a/platform/iphone/platform_config.h b/platform/iphone/platform_config.h
index bc190ba956..88ad4a3f67 100644
--- a/platform/iphone/platform_config.h
+++ b/platform/iphone/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,8 +30,13 @@
#include <alloca.h>
-#define GLES2_INCLUDE_H <ES2/gl.h>
-
#define PLATFORM_REFCOUNT
#define PTHREAD_RENAME_SELF
+
+#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
+#define _strongify(var) \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wshadow\"") \
+ __strong typeof(var) var = GDWeak_##var; \
+ _Pragma("clang diagnostic pop")
diff --git a/platform/iphone/plugin/godot_plugin_config.h b/platform/iphone/plugin/godot_plugin_config.h
new file mode 100644
index 0000000000..f4e30c8349
--- /dev/null
+++ b/platform/iphone/plugin/godot_plugin_config.h
@@ -0,0 +1,292 @@
+/*************************************************************************/
+/* godot_plugin_config.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifndef GODOT_PLUGIN_CONFIG_H
+#define GODOT_PLUGIN_CONFIG_H
+
+#include "core/error/error_list.h"
+#include "core/io/config_file.h"
+#include "core/string/ustring.h"
+
+/*
+ The `config` section and fields are required and defined as follow:
+- **name**: name of the plugin
+- **binary**: path to static `.a` library
+
+The `dependencies` and fields are optional.
+- **linked**: dependencies that should only be linked.
+- **embedded**: dependencies that should be linked and embedded into application.
+- **system**: system dependencies that should be linked.
+- **capabilities**: capabilities that would be used for `UIRequiredDeviceCapabilities` options in Info.plist file.
+- **files**: files that would be copied into application
+
+The `plist` section are optional.
+- **key**: key and value that would be added in Info.plist file.
+ */
+
+struct PluginConfigIOS {
+ inline static const char *PLUGIN_CONFIG_EXT = ".gdip";
+
+ inline static const char *CONFIG_SECTION = "config";
+ inline static const char *CONFIG_NAME_KEY = "name";
+ inline static const char *CONFIG_BINARY_KEY = "binary";
+ inline static const char *CONFIG_INITIALIZE_KEY = "initialization";
+ inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization";
+
+ inline static const char *DEPENDENCIES_SECTION = "dependencies";
+ inline static const char *DEPENDENCIES_LINKED_KEY = "linked";
+ inline static const char *DEPENDENCIES_EMBEDDED_KEY = "embedded";
+ inline static const char *DEPENDENCIES_SYSTEM_KEY = "system";
+ inline static const char *DEPENDENCIES_CAPABILITIES_KEY = "capabilities";
+ inline static const char *DEPENDENCIES_FILES_KEY = "files";
+ inline static const char *DEPENDENCIES_LINKER_FLAGS = "linker_flags";
+
+ inline static const char *PLIST_SECTION = "plist";
+
+ // Set to true when the config file is properly loaded.
+ bool valid_config = false;
+ bool supports_targets = false;
+ // Unix timestamp of last change to this plugin.
+ uint64_t last_updated = 0;
+
+ // Required config section
+ String name;
+ String binary;
+ String initialization_method;
+ String deinitialization_method;
+
+ // Optional dependencies section
+ Vector<String> linked_dependencies;
+ Vector<String> embedded_dependencies;
+ Vector<String> system_dependencies;
+
+ Vector<String> files_to_copy;
+ Vector<String> capabilities;
+
+ Vector<String> linker_flags;
+
+ // Optional plist section
+ // Supports only string types for now
+ HashMap<String, String> plist;
+};
+
+static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
+ String absolute_path;
+
+ if (dependency_path.is_empty()) {
+ return absolute_path;
+ }
+
+ if (dependency_path.is_abs_path()) {
+ return dependency_path;
+ }
+
+ String res_path = ProjectSettings::get_singleton()->globalize_path("res://");
+ absolute_path = plugin_config_dir.plus_file(dependency_path);
+
+ return absolute_path.replace(res_path, "res://");
+}
+
+static inline String resolve_system_dependency_path(String dependency_path) {
+ String absolute_path;
+
+ if (dependency_path.is_empty()) {
+ return absolute_path;
+ }
+
+ if (dependency_path.is_abs_path()) {
+ return dependency_path;
+ }
+
+ String system_path = "/System/Library/Frameworks";
+
+ return system_path.plus_file(dependency_path);
+}
+
+static inline Vector<String> resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) {
+ Vector<String> paths;
+
+ for (int i = 0; i < p_paths.size(); i++) {
+ String path = resolve_local_dependency_path(plugin_config_dir, p_paths[i]);
+
+ if (path.is_empty()) {
+ continue;
+ }
+
+ paths.push_back(path);
+ }
+
+ return paths;
+}
+
+static inline Vector<String> resolve_system_dependencies(Vector<String> p_paths) {
+ Vector<String> paths;
+
+ for (int i = 0; i < p_paths.size(); i++) {
+ String path = resolve_system_dependency_path(p_paths[i]);
+
+ if (path.is_empty()) {
+ continue;
+ }
+
+ paths.push_back(path);
+ }
+
+ return paths;
+}
+
+static inline bool validate_plugin(PluginConfigIOS &plugin_config) {
+ bool valid_name = !plugin_config.name.is_empty();
+ bool valid_binary_name = !plugin_config.binary.is_empty();
+ bool valid_initialize = !plugin_config.initialization_method.is_empty();
+ bool valid_deinitialize = !plugin_config.deinitialization_method.is_empty();
+
+ bool fields_value = valid_name && valid_binary_name && valid_initialize && valid_deinitialize;
+
+ if (!fields_value) {
+ return false;
+ }
+
+ String plugin_extension = plugin_config.binary.get_extension().to_lower();
+
+ if ((plugin_extension == "a" && FileAccess::exists(plugin_config.binary)) ||
+ (plugin_extension == "xcframework" && DirAccess::exists(plugin_config.binary))) {
+ plugin_config.valid_config = true;
+ plugin_config.supports_targets = false;
+ } else {
+ String file_path = plugin_config.binary.get_base_dir();
+ String file_name = plugin_config.binary.get_basename().get_file();
+ String file_extension = plugin_config.binary.get_extension();
+ String release_file_name = file_path.plus_file(file_name + ".release." + file_extension);
+ String debug_file_name = file_path.plus_file(file_name + ".debug." + file_extension);
+
+ if ((plugin_extension == "a" && FileAccess::exists(release_file_name) && FileAccess::exists(debug_file_name)) ||
+ (plugin_extension == "xcframework" && DirAccess::exists(release_file_name) && DirAccess::exists(debug_file_name))) {
+ plugin_config.valid_config = true;
+ plugin_config.supports_targets = true;
+ }
+ }
+
+ return plugin_config.valid_config;
+}
+
+static inline String get_plugin_main_binary(PluginConfigIOS &plugin_config, bool p_debug) {
+ if (!plugin_config.supports_targets) {
+ return plugin_config.binary;
+ }
+
+ String plugin_binary_dir = plugin_config.binary.get_base_dir();
+ String plugin_name_prefix = plugin_config.binary.get_basename().get_file();
+ String plugin_extension = plugin_config.binary.get_extension();
+ String plugin_file = plugin_name_prefix + "." + (p_debug ? "debug" : "release") + "." + plugin_extension;
+
+ return plugin_binary_dir.plus_file(plugin_file);
+}
+
+static inline uint64_t get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path) {
+ uint64_t last_updated = FileAccess::get_modified_time(config_path);
+
+ if (!plugin_config.supports_targets) {
+ last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary));
+ } else {
+ String file_path = plugin_config.binary.get_base_dir();
+ String file_name = plugin_config.binary.get_basename().get_file();
+ String release_file_name = file_path.plus_file(file_name + ".release.a");
+ String debug_file_name = file_path.plus_file(file_name + ".debug.a");
+
+ last_updated = MAX(last_updated, FileAccess::get_modified_time(release_file_name));
+ last_updated = MAX(last_updated, FileAccess::get_modified_time(debug_file_name));
+ }
+
+ return last_updated;
+}
+
+static inline PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
+ PluginConfigIOS plugin_config = {};
+
+ if (!config_file.is_valid()) {
+ return plugin_config;
+ }
+
+ Error err = config_file->load(path);
+
+ if (err != OK) {
+ return plugin_config;
+ }
+
+ String config_base_dir = path.get_base_dir();
+
+ plugin_config.name = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_NAME_KEY, String());
+ plugin_config.initialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_INITIALIZE_KEY, String());
+ plugin_config.deinitialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_DEINITIALIZE_KEY, String());
+
+ String binary_path = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_BINARY_KEY, String());
+ plugin_config.binary = resolve_local_dependency_path(config_base_dir, binary_path);
+
+ if (config_file->has_section(PluginConfigIOS::DEPENDENCIES_SECTION)) {
+ Vector<String> linked_dependencies = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_LINKED_KEY, Vector<String>());
+ Vector<String> embedded_dependencies = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_EMBEDDED_KEY, Vector<String>());
+ Vector<String> system_dependencies = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_SYSTEM_KEY, Vector<String>());
+ Vector<String> files = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_FILES_KEY, Vector<String>());
+
+ plugin_config.linked_dependencies = resolve_local_dependencies(config_base_dir, linked_dependencies);
+ plugin_config.embedded_dependencies = resolve_local_dependencies(config_base_dir, embedded_dependencies);
+ plugin_config.system_dependencies = resolve_system_dependencies(system_dependencies);
+
+ plugin_config.files_to_copy = resolve_local_dependencies(config_base_dir, files);
+
+ plugin_config.capabilities = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_CAPABILITIES_KEY, Vector<String>());
+
+ plugin_config.linker_flags = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_LINKER_FLAGS, Vector<String>());
+ }
+
+ if (config_file->has_section(PluginConfigIOS::PLIST_SECTION)) {
+ List<String> keys;
+ config_file->get_section_keys(PluginConfigIOS::PLIST_SECTION, &keys);
+
+ for (int i = 0; i < keys.size(); i++) {
+ String value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, keys[i], String());
+
+ if (value.is_empty()) {
+ continue;
+ }
+
+ plugin_config.plist[keys[i]] = value;
+ }
+ }
+
+ if (validate_plugin(plugin_config)) {
+ plugin_config.last_updated = get_plugin_modification_time(plugin_config, path);
+ }
+
+ return plugin_config;
+}
+
+#endif // GODOT_PLUGIN_CONFIG_H
diff --git a/platform/iphone/view_controller.h b/platform/iphone/view_controller.h
index f6bbe11d97..52fb6fbbf2 100644
--- a/platform/iphone/view_controller.h
+++ b/platform/iphone/view_controller.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,23 +28,20 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#import <GameKit/GameKit.h>
#import <UIKit/UIKit.h>
-@interface ViewController : UIViewController <GKGameCenterControllerDelegate> {
-};
+@class GodotView;
+@class GodotNativeVideoView;
+@class GodotKeyboardInputView;
-- (BOOL)shouldAutorotateToInterfaceOrientation:
- (UIInterfaceOrientation)p_orientation;
+@interface ViewController : UIViewController
-- (void)didReceiveMemoryWarning;
+@property(nonatomic, readonly, strong) GodotView *godotView;
+@property(nonatomic, readonly, strong) GodotNativeVideoView *videoView;
+@property(nonatomic, readonly, strong) GodotKeyboardInputView *keyboardView;
-- (void)viewDidLoad;
+// MARK: Native Video Player
-- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures;
-
-- (BOOL)prefersStatusBarHidden;
-
-- (BOOL)prefersHomeIndicatorAutoHidden;
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack;
@end
diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm
index 279bcc1226..6cef244567 100644
--- a/platform/iphone/view_controller.mm
+++ b/platform/iphone/view_controller.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,96 +29,184 @@
/*************************************************************************/
#import "view_controller.h"
-
+#include "core/config/project_settings.h"
+#include "display_server_iphone.h"
+#import "godot_view.h"
+#import "godot_view_renderer.h"
+#import "keyboard_input_view.h"
+#import "native_video_view.h"
#include "os_iphone.h"
-#include "core/project_settings.h"
+#import <AVFoundation/AVFoundation.h>
+#import <GameController/GameController.h>
-extern "C" {
+@interface ViewController () <GodotViewDelegate>
-int add_path(int, char **);
-int add_cmdline(int, char **);
+@property(strong, nonatomic) GodotViewRenderer *renderer;
+@property(strong, nonatomic) GodotNativeVideoView *videoView;
+@property(strong, nonatomic) GodotKeyboardInputView *keyboardView;
-int add_path(int p_argc, char **p_args) {
- NSString *str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"];
- if (!str)
- return p_argc;
+@property(strong, nonatomic) UIView *godotLoadingOverlay;
- p_args[p_argc++] = "--path";
- [str retain]; // memory leak lol (maybe make it static here and delete it in ViewController destructor? @todo
- p_args[p_argc++] = (char *)[str cString];
- p_args[p_argc] = NULL;
+@end
- return p_argc;
-};
+@implementation ViewController
-int add_cmdline(int p_argc, char **p_args) {
- NSArray *arr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_cmdline"];
- if (!arr)
- return p_argc;
+- (GodotView *)godotView {
+ return (GodotView *)self.view;
+}
- for (int i = 0; i < [arr count]; i++) {
- NSString *str = [arr objectAtIndex:i];
- if (!str)
- continue;
- [str retain]; // @todo delete these at some point
- p_args[p_argc++] = (char *)[str cString];
- };
+- (void)loadView {
+ GodotView *view = [[GodotView alloc] init];
+ GodotViewRenderer *renderer = [[GodotViewRenderer alloc] init];
- p_args[p_argc] = NULL;
+ self.renderer = renderer;
+ self.view = view;
- return p_argc;
-};
-}; // extern "C"
+ view.renderer = self.renderer;
+ view.delegate = self;
+}
-@interface ViewController ()
+- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
+ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
-@end
+ if (self) {
+ [self godot_commonInit];
+ }
-@implementation ViewController
+ return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+ self = [super initWithCoder:coder];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ // Initialize view controller values.
+}
- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
printf("*********** did receive memory warning!\n");
-};
+}
- (void)viewDidLoad {
[super viewDidLoad];
+ [self observeKeyboard];
+ [self displayLoadingOverlay];
+
if (@available(iOS 11.0, *)) {
[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
}
+- (void)observeKeyboard {
+ printf("******** setting up keyboard input view\n");
+ self.keyboardView = [GodotKeyboardInputView new];
+ [self.view addSubview:self.keyboardView];
+
+ printf("******** adding observer for keyboard show/hide\n");
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(keyboardOnScreen:)
+ name:UIKeyboardDidShowNotification
+ object:nil];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(keyboardHidden:)
+ name:UIKeyboardDidHideNotification
+ object:nil];
+}
+
+- (void)displayLoadingOverlay {
+ NSBundle *bundle = [NSBundle mainBundle];
+ NSString *storyboardName = @"Launch Screen";
+
+ if ([bundle pathForResource:storyboardName ofType:@"storyboardc"] == nil) {
+ return;
+ }
+
+ UIStoryboard *launchStoryboard = [UIStoryboard storyboardWithName:storyboardName bundle:bundle];
+
+ UIViewController *controller = [launchStoryboard instantiateInitialViewController];
+ self.godotLoadingOverlay = controller.view;
+ self.godotLoadingOverlay.frame = self.view.bounds;
+ self.godotLoadingOverlay.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
+
+ [self.view addSubview:self.godotLoadingOverlay];
+}
+
+- (BOOL)godotViewFinishedSetup:(GodotView *)view {
+ [self.godotLoadingOverlay removeFromSuperview];
+ self.godotLoadingOverlay = nil;
+
+ return YES;
+}
+
+- (void)dealloc {
+ [self.videoView stopVideo];
+
+ self.videoView = nil;
+
+ self.keyboardView = nil;
+
+ self.renderer = nil;
+
+ if (self.godotLoadingOverlay) {
+ [self.godotLoadingOverlay removeFromSuperview];
+ self.godotLoadingOverlay = nil;
+ }
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+// MARK: Orientation
+
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
return UIRectEdgeAll;
}
- (BOOL)shouldAutorotate {
- switch (OS::get_singleton()->get_screen_orientation()) {
- case OS::SCREEN_SENSOR:
- case OS::SCREEN_SENSOR_LANDSCAPE:
- case OS::SCREEN_SENSOR_PORTRAIT:
+ if (!DisplayServerIPhone::get_singleton()) {
+ return NO;
+ }
+
+ switch (DisplayServerIPhone::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
+ case DisplayServer::SCREEN_SENSOR:
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
return YES;
default:
return NO;
}
-};
+}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
- switch (OS::get_singleton()->get_screen_orientation()) {
- case OS::SCREEN_PORTRAIT:
+ if (!DisplayServerIPhone::get_singleton()) {
+ return UIInterfaceOrientationMaskAll;
+ }
+
+ switch (DisplayServerIPhone::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
+ case DisplayServer::SCREEN_PORTRAIT:
return UIInterfaceOrientationMaskPortrait;
- case OS::SCREEN_REVERSE_LANDSCAPE:
+ case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
return UIInterfaceOrientationMaskLandscapeRight;
- case OS::SCREEN_REVERSE_PORTRAIT:
+ case DisplayServer::SCREEN_REVERSE_PORTRAIT:
return UIInterfaceOrientationMaskPortraitUpsideDown;
- case OS::SCREEN_SENSOR_LANDSCAPE:
+ case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
return UIInterfaceOrientationMaskLandscape;
- case OS::SCREEN_SENSOR_PORTRAIT:
+ case DisplayServer::SCREEN_SENSOR_PORTRAIT:
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
- case OS::SCREEN_SENSOR:
+ case DisplayServer::SCREEN_SENSOR:
return UIInterfaceOrientationMaskAll;
- case OS::SCREEN_LANDSCAPE:
+ case DisplayServer::SCREEN_LANDSCAPE:
return UIInterfaceOrientationMaskLandscapeLeft;
}
};
@@ -135,12 +223,42 @@ int add_cmdline(int p_argc, char **p_args) {
}
}
-#ifdef GAME_CENTER_ENABLED
-- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController {
- //[gameCenterViewController dismissViewControllerAnimated:YES completion:^{GameCenter::get_singleton()->game_center_closed();}];//version for signaling when overlay is completely gone
- GameCenter::get_singleton()->game_center_closed();
- [gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
+// MARK: Keyboard
+
+- (void)keyboardOnScreen:(NSNotification *)notification {
+ NSDictionary *info = notification.userInfo;
+ NSValue *value = info[UIKeyboardFrameEndUserInfoKey];
+
+ CGRect rawFrame = [value CGRectValue];
+ CGRect keyboardFrame = [self.view convertRect:rawFrame fromView:nil];
+
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->virtual_keyboard_set_height(keyboardFrame.size.height);
+ }
+}
+
+- (void)keyboardHidden:(NSNotification *)notification {
+ if (DisplayServerIPhone::get_singleton()) {
+ DisplayServerIPhone::get_singleton()->virtual_keyboard_set_height(0);
+ }
+}
+
+// MARK: Native Video Player
+
+- (BOOL)playVideoAtPath:(NSString *)filePath volume:(float)videoVolume audio:(NSString *)audioTrack subtitle:(NSString *)subtitleTrack {
+ // If we are showing some video already, reuse existing view for new video.
+ if (self.videoView) {
+ return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
+ } else {
+ // Create autoresizing view for video playback.
+ GodotNativeVideoView *videoView = [[GodotNativeVideoView alloc] initWithFrame:self.view.bounds];
+ videoView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ [self.view addSubview:videoView];
+
+ self.videoView = videoView;
+
+ return [self.videoView playVideoAtPath:filePath volume:videoVolume audio:audioTrack subtitle:subtitleTrack];
+ }
}
-#endif
@end
diff --git a/platform/iphone/vulkan_context_iphone.h b/platform/iphone/vulkan_context_iphone.h
index cadd701636..88764e270e 100644
--- a/platform/iphone/vulkan_context_iphone.h
+++ b/platform/iphone/vulkan_context_iphone.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,13 +32,14 @@
#define VULKAN_CONTEXT_IPHONE_H
#include "drivers/vulkan/vulkan_context.h"
-// #import <UIKit/UIKit.h>
+
+#import <UIKit/UIKit.h>
class VulkanContextIPhone : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- int window_create(void *p_window, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, CALayer *p_metal_layer, int p_width, int p_height);
VulkanContextIPhone();
~VulkanContextIPhone();
diff --git a/platform/iphone/vulkan_context_iphone.mm b/platform/iphone/vulkan_context_iphone.mm
index 44c940dc3a..b980ae99f0 100644
--- a/platform/iphone/vulkan_context_iphone.mm
+++ b/platform/iphone/vulkan_context_iphone.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,21 +35,21 @@ const char *VulkanContextIPhone::_get_platform_surface_extension() const {
return VK_MVK_IOS_SURFACE_EXTENSION_NAME;
}
-int VulkanContextIPhone::window_create(void *p_window, int p_width, int p_height) {
+Error VulkanContextIPhone::window_create(DisplayServer::WindowID p_window_id, CALayer *p_metal_layer, int p_width, int p_height) {
VkIOSSurfaceCreateInfoMVK createInfo;
- createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+ createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
- createInfo.pView = p_window;
+ createInfo.pView = (__bridge const void *)p_metal_layer;
VkSurfaceKHR surface;
- VkResult err = vkCreateIOSSurfaceMVK(_get_instance(), &createInfo, NULL, &surface);
- ERR_FAIL_COND_V(err, -1);
- return _window_create(surface, p_width, p_height);
-}
+ VkResult err =
+ vkCreateIOSSurfaceMVK(_get_instance(), &createInfo, NULL, &surface);
+ ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
-VulkanContextIPhone::VulkanContextIPhone() {
+ return _window_create(p_window_id, surface, p_width, p_height);
}
-VulkanContextIPhone::~VulkanContextIPhone() {
-}
+VulkanContextIPhone::VulkanContextIPhone() {}
+
+VulkanContextIPhone::~VulkanContextIPhone() {}
diff --git a/platform/javascript/.eslintrc.engine.js b/platform/javascript/.eslintrc.engine.js
new file mode 100644
index 0000000000..3725cf164e
--- /dev/null
+++ b/platform/javascript/.eslintrc.engine.js
@@ -0,0 +1,11 @@
+module.exports = {
+ "extends": [
+ "./.eslintrc.js",
+ ],
+ "globals": {
+ "EngineConfig": true,
+ "Godot": true,
+ "Preloader": true,
+ "Utils": true,
+ },
+};
diff --git a/platform/javascript/.eslintrc.js b/platform/javascript/.eslintrc.js
new file mode 100644
index 0000000000..0ff9d67d26
--- /dev/null
+++ b/platform/javascript/.eslintrc.js
@@ -0,0 +1,43 @@
+module.exports = {
+ "env": {
+ "browser": true,
+ "es2021": true,
+ },
+ "extends": [
+ "airbnb-base",
+ ],
+ "parserOptions": {
+ "ecmaVersion": 12,
+ },
+ "ignorePatterns": "*.externs.js",
+ "rules": {
+ "func-names": "off",
+ // Use tabs for consistency with the C++ codebase.
+ "indent": ["error", "tab"],
+ "max-len": "off",
+ "no-else-return": ["error", {allowElseIf: true}],
+ "curly": ["error", "all"],
+ "brace-style": ["error", "1tbs", { "allowSingleLine": false }],
+ "no-bitwise": "off",
+ "no-continue": "off",
+ "no-self-assign": "off",
+ "no-tabs": "off",
+ "no-param-reassign": ["error", { "props": false }],
+ "no-plusplus": "off",
+ "no-unused-vars": ["error", { "args": "none" }],
+ "prefer-destructuring": "off",
+ "prefer-rest-params": "off",
+ "prefer-spread": "off",
+ "camelcase": "off",
+ "no-underscore-dangle": "off",
+ "max-classes-per-file": "off",
+ "prefer-arrow-callback": "off",
+ // Messes up with copyright headers in source files.
+ "spaced-comment": "off",
+ // Completely breaks emscripten libraries.
+ "object-shorthand": "off",
+ // Closure compiler (exported properties)
+ "quote-props": ["error", "consistent"],
+ "dot-notation": "off",
+ }
+};
diff --git a/platform/javascript/.eslintrc.libs.js b/platform/javascript/.eslintrc.libs.js
new file mode 100644
index 0000000000..81b1b8c864
--- /dev/null
+++ b/platform/javascript/.eslintrc.libs.js
@@ -0,0 +1,25 @@
+module.exports = {
+ "extends": [
+ "./.eslintrc.js",
+ ],
+ "globals": {
+ "LibraryManager": true,
+ "mergeInto": true,
+ "autoAddDeps": true,
+ "HEAP8": true,
+ "HEAPU8": true,
+ "HEAP32": true,
+ "HEAPF32": true,
+ "ERRNO_CODES": true,
+ "FS": true,
+ "IDBFS": true,
+ "GodotOS": true,
+ "GodotConfig": true,
+ "GodotRuntime": true,
+ "GodotFS": true,
+ "IDHandler": true,
+ "Browser": true,
+ "GL": true,
+ "XRWebGLLayer": true,
+ },
+};
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index dcf9a46bf9..ab527ef419 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -9,35 +9,75 @@ javascript_files = [
"javascript_eval.cpp",
"javascript_main.cpp",
"os_javascript.cpp",
+ "api/javascript_tools_editor_plugin.cpp",
]
-build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"]
-if env["threads_enabled"]:
- build_targets.append("#bin/godot${PROGSUFFIX}.worker.js")
+sys_env = env.Clone()
+sys_env.AddJSLibraries(
+ [
+ "js/libs/library_godot_audio.js",
+ "js/libs/library_godot_display.js",
+ "js/libs/library_godot_http_request.js",
+ "js/libs/library_godot_os.js",
+ "js/libs/library_godot_runtime.js",
+ ]
+)
-build = env.add_program(build_targets, javascript_files)
+if env["tools"]:
+ sys_env.AddJSLibraries(["js/libs/library_godot_editor_tools.js"])
+if env["javascript_eval"]:
+ sys_env.AddJSLibraries(["js/libs/library_godot_eval.js"])
-js_libraries = [
- "native/http_request.js",
-]
-for lib in js_libraries:
- env.Append(LINKFLAGS=["--js-library", env.File(lib).path])
-env.Depends(build, js_libraries)
+for lib in sys_env["JS_LIBS"]:
+ sys_env.Append(LINKFLAGS=["--js-library", lib])
+for js in env["JS_PRE"]:
+ sys_env.Append(LINKFLAGS=["--pre-js", env.File(js).path])
+for ext in env["JS_EXTERNS"]:
+ sys_env["ENV"]["EMCC_CLOSURE_ARGS"] += " --externs " + ext.path
-js_pre = [
- "native/id_handler.js",
- "native/utils.js",
-]
-for js in js_pre:
- env.Append(LINKFLAGS=["--pre-js", env.File(js).path])
-env.Depends(build, js_pre)
+build = []
+if env["gdnative_enabled"]:
+ build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"]
+ # Reset libraries. The main runtime will only link emscripten libraries, not godot ones.
+ sys_env["LIBS"] = []
+ # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly.
+ sys_env.Append(LIBS=["idbfs.js"])
+ # Configure it as a main module (dynamic linking support).
+ sys_env.Append(CCFLAGS=["-s", "MAIN_MODULE=1"])
+ sys_env.Append(LINKFLAGS=["-s", "MAIN_MODULE=1"])
+ sys_env.Append(CCFLAGS=["-s", "EXPORT_ALL=1"])
+ sys_env.Append(LINKFLAGS=["-s", "EXPORT_ALL=1"])
+ # Force exporting the standard library (printf, malloc, etc.)
+ sys_env["ENV"]["EMCC_FORCE_STDLIBS"] = "libc,libc++,libc++abi"
+ # The main emscripten runtime, with exported standard libraries.
+ sys = sys_env.Program(build_targets, ["javascript_runtime.cpp"])
+
+ # The side library, containing all Godot code.
+ wasm_env = env.Clone()
+ wasm_env.Append(CPPDEFINES=["WASM_GDNATIVE"]) # So that OS knows it can run GDNative libraries.
+ wasm_env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"])
+ wasm_env.Append(LINKFLAGS=["-s", "SIDE_MODULE=2"])
+ wasm = wasm_env.add_program("#bin/godot.side${PROGSUFFIX}.wasm", javascript_files)
+ build = [sys[0], sys[1], wasm[0]]
+else:
+ build_targets = ["#bin/godot${PROGSUFFIX}.js", "#bin/godot${PROGSUFFIX}.wasm"]
+ if env["threads_enabled"]:
+ build_targets.append("#bin/godot${PROGSUFFIX}.worker.js")
+ # We use IDBFS. Since Emscripten 1.39.1 it needs to be linked explicitly.
+ sys_env.Append(LIBS=["idbfs.js"])
+ build = sys_env.Program(build_targets, javascript_files + ["javascript_runtime.cpp"])
+
+sys_env.Depends(build[0], sys_env["JS_LIBS"])
+sys_env.Depends(build[0], sys_env["JS_PRE"])
+sys_env.Depends(build[0], sys_env["JS_EXTERNS"])
engine = [
- "engine/preloader.js",
- "engine/utils.js",
- "engine/engine.js",
+ "js/engine/preloader.js",
+ "js/engine/utils.js",
+ "js/engine/config.js",
+ "js/engine/engine.js",
]
-externs = [env.File("#platform/javascript/engine/externs.js")]
+externs = [env.File("#platform/javascript/js/engine/engine.externs.js")]
js_engine = env.CreateEngineFile("#bin/godot${PROGSUFFIX}.engine.js", engine, externs)
env.Depends(js_engine, externs)
@@ -53,18 +93,34 @@ out_files = [
zip_dir.File(binary_name + ".js"),
zip_dir.File(binary_name + ".wasm"),
zip_dir.File(binary_name + ".html"),
+ zip_dir.File(binary_name + ".audio.worklet.js"),
]
html_file = "#misc/dist/html/full-size.html"
-in_files = [js_wrapped, build[1], html_file]
-if env["threads_enabled"]:
- in_files.append(build[2])
+if env["tools"]:
+ subst_dict = {"\$GODOT_VERSION": env.GetBuildVersion()}
+ html_file = env.Substfile(
+ target="#bin/godot${PROGSUFFIX}.html", source="#misc/dist/html/editor.html", SUBST_DICT=subst_dict
+ )
+
+in_files = [js_wrapped, build[1], html_file, "#platform/javascript/js/libs/audio.worklet.js"]
+if env["gdnative_enabled"]:
+ in_files.append(build[2]) # Runtime
+ out_files.append(zip_dir.File(binary_name + ".side.wasm"))
+elif env["threads_enabled"]:
+ in_files.append(build[2]) # Worker
out_files.append(zip_dir.File(binary_name + ".worker.js"))
+if env["tools"]:
+ in_files.append("#misc/dist/html/logo.svg")
+ out_files.append(zip_dir.File("logo.svg"))
+ in_files.append("#icon.png")
+ out_files.append(zip_dir.File("favicon.png"))
+
zip_files = env.InstallAs(out_files, in_files)
env.Zip(
"#bin/godot",
zip_files,
ZIPROOT=zip_dir,
ZIPSUFFIX="${PROGSUFFIX}${ZIPSUFFIX}",
- ZIPCOMSTR="Archving $SOURCES as $TARGET",
+ ZIPCOMSTR="Archiving $SOURCES as $TARGET",
)
diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp
index 9c73e5c4c4..2f7bde065f 100644
--- a/platform/javascript/api/api.cpp
+++ b/platform/javascript/api/api.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,12 +29,14 @@
/*************************************************************************/
#include "api.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "javascript_eval.h"
+#include "javascript_tools_editor_plugin.h"
static JavaScript *javascript_eval;
void register_javascript_api() {
+ JavaScriptToolsEditorPlugin::initialize();
ClassDB::register_virtual_class<JavaScript>();
javascript_eval = memnew(JavaScript);
Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScript", javascript_eval));
diff --git a/platform/javascript/api/api.h b/platform/javascript/api/api.h
index 8afe0f33ce..2ac7333cdd 100644
--- a/platform/javascript/api/api.h
+++ b/platform/javascript/api/api.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/javascript/api/javascript_eval.h b/platform/javascript/api/javascript_eval.h
index 29229de8e3..24f7648ed9 100644
--- a/platform/javascript/api/javascript_eval.h
+++ b/platform/javascript/api/javascript_eval.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,7 +31,7 @@
#ifndef JAVASCRIPT_EVAL_H
#define JAVASCRIPT_EVAL_H
-#include "core/object.h"
+#include "core/object/class_db.h"
class JavaScript : public Object {
private:
diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp
new file mode 100644
index 0000000000..8355faccc2
--- /dev/null
+++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp
@@ -0,0 +1,135 @@
+/*************************************************************************/
+/* javascript_tools_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#if defined(TOOLS_ENABLED) && defined(JAVASCRIPT_ENABLED)
+#include "javascript_tools_editor_plugin.h"
+
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
+#include "core/os/dir_access.h"
+#include "core/os/file_access.h"
+#include "editor/editor_node.h"
+
+#include <emscripten/emscripten.h>
+
+// JavaScript functions defined in library_godot_editor_tools.js
+extern "C" {
+extern void godot_js_editor_download_file(const char *p_path, const char *p_name, const char *p_mime);
+}
+
+static void _javascript_editor_init_callback() {
+ EditorNode::get_singleton()->add_editor_plugin(memnew(JavaScriptToolsEditorPlugin(EditorNode::get_singleton())));
+}
+
+void JavaScriptToolsEditorPlugin::initialize() {
+ EditorNode::add_init_callback(_javascript_editor_init_callback);
+}
+
+JavaScriptToolsEditorPlugin::JavaScriptToolsEditorPlugin(EditorNode *p_editor) {
+ add_tool_menu_item("Download Project Source", callable_mp(this, &JavaScriptToolsEditorPlugin::_download_zip));
+}
+
+void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) {
+ if (!Engine::get_singleton() || !Engine::get_singleton()->is_editor_hint()) {
+ WARN_PRINT("Project download is only available in Editor mode");
+ return;
+ }
+ String resource_path = ProjectSettings::get_singleton()->get_resource_path();
+
+ FileAccess *src_f;
+ zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
+ zipFile zip = zipOpen2("/tmp/project.zip", APPEND_STATUS_CREATE, NULL, &io);
+ String base_path = resource_path.substr(0, resource_path.rfind("/")) + "/";
+ _zip_recursive(resource_path, base_path, zip);
+ zipClose(zip, NULL);
+ godot_js_editor_download_file("/tmp/project.zip", "project.zip", "application/zip");
+}
+
+void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, zipFile p_zip) {
+ FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
+ if (!f) {
+ WARN_PRINT("Unable to open file for zipping: " + p_path);
+ return;
+ }
+ Vector<uint8_t> data;
+ int len = f->get_len();
+ data.resize(len);
+ f->get_buffer(data.ptrw(), len);
+ f->close();
+ memdelete(f);
+
+ String path = p_path.replace_first(p_base_path, "");
+ zipOpenNewFileInZip(p_zip,
+ path.utf8().get_data(),
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION);
+ zipWriteInFileInZip(p_zip, data.ptr(), data.size());
+ zipCloseFileInZip(p_zip);
+}
+
+void JavaScriptToolsEditorPlugin::_zip_recursive(String p_path, String p_base_path, zipFile p_zip) {
+ DirAccess *dir = DirAccess::open(p_path);
+ if (!dir) {
+ WARN_PRINT("Unable to open dir for zipping: " + p_path);
+ return;
+ }
+ dir->list_dir_begin();
+ String cur = dir->get_next();
+ while (!cur.is_empty()) {
+ String cs = p_path.plus_file(cur);
+ if (cur == "." || cur == ".." || cur == ".import") {
+ // Skip
+ } else if (dir->current_is_dir()) {
+ String path = cs.replace_first(p_base_path, "") + "/";
+ zipOpenNewFileInZip(p_zip,
+ path.utf8().get_data(),
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION);
+ zipCloseFileInZip(p_zip);
+ _zip_recursive(cs, p_base_path, p_zip);
+ } else {
+ _zip_file(cs, p_base_path, p_zip);
+ }
+ cur = dir->get_next();
+ }
+}
+#endif
diff --git a/platform/iphone/in_app_store.h b/platform/javascript/api/javascript_tools_editor_plugin.h
index 44e65e77ed..557821d627 100644
--- a/platform/iphone/in_app_store.h
+++ b/platform/javascript/api/javascript_tools_editor_plugin.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* in_app_store.h */
+/* javascript_tools_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,40 +28,31 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef STOREKIT_ENABLED
+#ifndef JAVASCRIPT_TOOLS_EDITOR_PLUGIN_H
+#define JAVASCRIPT_TOOLS_EDITOR_PLUGIN_H
-#ifndef IN_APP_STORE_H
-#define IN_APP_STORE_H
+#if defined(TOOLS_ENABLED) && defined(JAVASCRIPT_ENABLED)
+#include "core/io/zip_io.h"
+#include "editor/editor_plugin.h"
-#include "core/object.h"
+class JavaScriptToolsEditorPlugin : public EditorPlugin {
+ GDCLASS(JavaScriptToolsEditorPlugin, EditorPlugin);
-class InAppStore : public Object {
- GDCLASS(InAppStore, Object);
-
- static InAppStore *instance;
- static void _bind_methods();
-
- List<Variant> pending_events;
+private:
+ void _zip_file(String p_path, String p_base_path, zipFile p_zip);
+ void _zip_recursive(String p_path, String p_base_path, zipFile p_zip);
+ void _download_zip(Variant p_v);
public:
- Error request_product_info(Variant p_params);
- Error restore_purchases();
- Error purchase(Variant p_params);
-
- int get_pending_event_count();
- Variant pop_pending_event();
- void finish_transaction(String product_id);
- void set_auto_finish_transaction(bool b);
-
- void _post_event(Variant p_event);
- void _record_purchase(String product_id);
-
- static InAppStore *get_singleton();
+ static void initialize();
- InAppStore();
- ~InAppStore();
+ JavaScriptToolsEditorPlugin(EditorNode *p_editor);
+};
+#else
+class JavaScriptToolsEditorPlugin {
+public:
+ static void initialize() {}
};
-
#endif
-#endif
+#endif // JAVASCRIPT_TOOLS_EDITOR_PLUGIN_H
diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp
index b52bd4ce60..478e848675 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,272 +30,256 @@
#include "audio_driver_javascript.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include <emscripten.h>
AudioDriverJavaScript *AudioDriverJavaScript::singleton = nullptr;
+bool AudioDriverJavaScript::is_available() {
+ return godot_audio_is_available() != 0;
+}
+
const char *AudioDriverJavaScript::get_name() const {
return "JavaScript";
}
-extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_js_mix() {
- AudioDriverJavaScript::singleton->mix_to_js();
+void AudioDriverJavaScript::_state_change_callback(int p_state) {
+ singleton->state = p_state;
}
-extern "C" EMSCRIPTEN_KEEPALIVE void audio_driver_process_capture(float sample) {
- AudioDriverJavaScript::singleton->process_capture(sample);
+void AudioDriverJavaScript::_latency_update_callback(float p_latency) {
+ singleton->output_latency = p_latency;
}
-void AudioDriverJavaScript::mix_to_js() {
- int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode());
- int sample_count = memarr_len(internal_buffer) / channel_count;
- int32_t *stream_buffer = reinterpret_cast<int32_t *>(internal_buffer);
- audio_server_process(sample_count, stream_buffer);
- for (int i = 0; i < sample_count * channel_count; i++) {
- internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.f;
+void AudioDriverJavaScript::_audio_driver_process(int p_from, int p_samples) {
+ int32_t *stream_buffer = reinterpret_cast<int32_t *>(output_rb);
+ const int max_samples = memarr_len(output_rb);
+
+ int write_pos = p_from;
+ int to_write = p_samples;
+ if (to_write == 0) {
+ to_write = max_samples;
+ }
+ // High part
+ if (write_pos + to_write > max_samples) {
+ const int samples_high = max_samples - write_pos;
+ audio_server_process(samples_high / channel_count, &stream_buffer[write_pos]);
+ for (int i = write_pos; i < max_samples; i++) {
+ output_rb[i] = float(stream_buffer[i] >> 16) / 32768.f;
+ }
+ to_write -= samples_high;
+ write_pos = 0;
+ }
+ // Leftover
+ audio_server_process(to_write / channel_count, &stream_buffer[write_pos]);
+ for (int i = write_pos; i < write_pos + to_write; i++) {
+ output_rb[i] = float(stream_buffer[i] >> 16) / 32768.f;
}
}
-void AudioDriverJavaScript::process_capture(float sample) {
- int32_t sample32 = int32_t(sample * 32768.f) * (1U << 16);
- input_buffer_write(sample32);
+void AudioDriverJavaScript::_audio_driver_capture(int p_from, int p_samples) {
+ if (get_input_buffer().size() == 0) {
+ return; // Input capture stopped.
+ }
+ const int max_samples = memarr_len(input_rb);
+
+ int read_pos = p_from;
+ int to_read = p_samples;
+ if (to_read == 0) {
+ to_read = max_samples;
+ }
+ // High part
+ if (read_pos + to_read > max_samples) {
+ const int samples_high = max_samples - read_pos;
+ for (int i = read_pos; i < max_samples; i++) {
+ input_buffer_write(int32_t(input_rb[i] * 32768.f) * (1U << 16));
+ }
+ to_read -= samples_high;
+ read_pos = 0;
+ }
+ // Leftover
+ for (int i = read_pos; i < read_pos + to_read; i++) {
+ input_buffer_write(int32_t(input_rb[i] * 32768.f) * (1U << 16));
+ }
}
Error AudioDriverJavaScript::init() {
- int mix_rate = GLOBAL_GET("audio/mix_rate");
- int latency = GLOBAL_GET("audio/output_latency");
-
- /* clang-format off */
- _driver_id = EM_ASM_INT({
- const MIX_RATE = $0;
- const LATENCY = $1;
- return Module.IDHandler.add({
- 'context': new (window.AudioContext || window.webkitAudioContext)({ sampleRate: MIX_RATE, latencyHint: LATENCY}),
- 'input': null,
- 'stream': null,
- 'script': null
- });
- });
- /* clang-format on */
-
- int channel_count = get_total_channels_by_speaker_mode(get_speaker_mode());
- buffer_length = closest_power_of_2((latency * mix_rate / 1000) * channel_count);
- /* clang-format off */
- buffer_length = EM_ASM_INT({
- var ref = Module.IDHandler.get($0);
- const ctx = ref['context'];
- const BUFFER_LENGTH = $1;
- const CHANNEL_COUNT = $2;
-
- var script = ctx.createScriptProcessor(BUFFER_LENGTH, 2, CHANNEL_COUNT);
- script.connect(ctx.destination);
- ref['script'] = script;
- return script.bufferSize;
- }, _driver_id, buffer_length, channel_count);
- /* clang-format on */
- if (!buffer_length) {
- return FAILED;
+ mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ int latency = GLOBAL_GET("audio/driver/output_latency");
+
+ channel_count = godot_audio_init(mix_rate, latency, &_state_change_callback, &_latency_update_callback);
+ buffer_length = closest_power_of_2((latency * mix_rate / 1000));
+#ifndef NO_THREADS
+ node = memnew(WorkletNode);
+#else
+ node = memnew(ScriptProcessorNode);
+#endif
+ buffer_length = node->create(buffer_length, channel_count);
+ if (output_rb) {
+ memdelete_arr(output_rb);
}
-
- if (!internal_buffer || (int)memarr_len(internal_buffer) != buffer_length * channel_count) {
- if (internal_buffer)
- memdelete_arr(internal_buffer);
- internal_buffer = memnew_arr(float, buffer_length *channel_count);
+ output_rb = memnew_arr(float, buffer_length *channel_count);
+ if (!output_rb) {
+ return ERR_OUT_OF_MEMORY;
}
-
- return internal_buffer ? OK : ERR_OUT_OF_MEMORY;
+ if (input_rb) {
+ memdelete_arr(input_rb);
+ }
+ input_rb = memnew_arr(float, buffer_length *channel_count);
+ if (!input_rb) {
+ return ERR_OUT_OF_MEMORY;
+ }
+ return OK;
}
void AudioDriverJavaScript::start() {
- /* clang-format off */
- EM_ASM({
- const ref = Module.IDHandler.get($0);
- var INTERNAL_BUFFER_PTR = $1;
-
- var audioDriverMixFunction = cwrap('audio_driver_js_mix');
- var audioDriverProcessCapture = cwrap('audio_driver_process_capture', null, ['number']);
- ref['script'].onaudioprocess = function(audioProcessingEvent) {
- audioDriverMixFunction();
-
- var input = audioProcessingEvent.inputBuffer;
- var output = audioProcessingEvent.outputBuffer;
- var internalBuffer = HEAPF32.subarray(
- INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT,
- INTERNAL_BUFFER_PTR / HEAPF32.BYTES_PER_ELEMENT + output.length * output.numberOfChannels);
-
- for (var channel = 0; channel < output.numberOfChannels; channel++) {
- var outputData = output.getChannelData(channel);
- // Loop through samples.
- for (var sample = 0; sample < outputData.length; sample++) {
- outputData[sample] = internalBuffer[sample * output.numberOfChannels + channel];
- }
- }
-
- if (ref['input']) {
- var inputDataL = input.getChannelData(0);
- var inputDataR = input.getChannelData(1);
- for (var i = 0; i < inputDataL.length; i++) {
- audioDriverProcessCapture(inputDataL[i]);
- audioDriverProcessCapture(inputDataR[i]);
- }
- }
- };
- }, _driver_id, internal_buffer);
- /* clang-format on */
+ if (node) {
+ node->start(output_rb, memarr_len(output_rb), input_rb, memarr_len(input_rb));
+ }
}
void AudioDriverJavaScript::resume() {
- /* clang-format off */
- EM_ASM({
- const ref = Module.IDHandler.get($0);
- if (ref && ref['context'] && ref['context'].resume)
- ref['context'].resume();
- }, _driver_id);
- /* clang-format on */
+ if (state == 0) { // 'suspended'
+ godot_audio_resume();
+ }
}
float AudioDriverJavaScript::get_latency() {
- /* clang-format off */
- return EM_ASM_DOUBLE({
- const ref = Module.IDHandler.get($0);
- var latency = 0;
- if (ref && ref['context']) {
- const ctx = ref['context'];
- if (ctx.baseLatency) {
- latency += ctx.baseLatency;
- }
- if (ctx.outputLatency) {
- latency += ctx.outputLatency;
- }
- }
- return latency;
- }, _driver_id);
- /* clang-format on */
+ return output_latency + (float(buffer_length) / mix_rate);
}
int AudioDriverJavaScript::get_mix_rate() const {
- /* clang-format off */
- return EM_ASM_INT({
- const ref = Module.IDHandler.get($0);
- return ref && ref['context'] ? ref['context'].sampleRate : 0;
- }, _driver_id);
- /* clang-format on */
+ return mix_rate;
}
AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
- /* clang-format off */
- return get_speaker_mode_by_total_channels(EM_ASM_INT({
- const ref = Module.IDHandler.get($0);
- return ref && ref['context'] ? ref['context'].destination.channelCount : 0;
- }, _driver_id));
- /* clang-format on */
+ return get_speaker_mode_by_total_channels(channel_count);
}
-// No locking, as threads are not supported.
void AudioDriverJavaScript::lock() {
+ if (node) {
+ node->unlock();
+ }
}
void AudioDriverJavaScript::unlock() {
-}
-
-void AudioDriverJavaScript::finish_async() {
- // Close the context, add the operation to the async_finish list in module.
- int id = _driver_id;
- _driver_id = 0;
-
- /* clang-format off */
- EM_ASM({
- var ref = Module.IDHandler.get($0);
- Module.async_finish.push(new Promise(function(accept, reject) {
- if (!ref) {
- console.log("Ref not found!", $0, Module.IDHandler);
- setTimeout(accept, 0);
- } else {
- const context = ref['context'];
- // Disconnect script and input.
- ref['script'].disconnect();
- if (ref['input'])
- ref['input'].disconnect();
- ref = null;
- context.close().then(function() {
- accept();
- }).catch(function(e) {
- accept();
- });
- }
- }));
- Module.IDHandler.remove($0);
- }, id);
- /* clang-format on */
+ if (node) {
+ node->unlock();
+ }
}
void AudioDriverJavaScript::finish() {
- if (internal_buffer) {
- memdelete_arr(internal_buffer);
- internal_buffer = nullptr;
+ if (node) {
+ node->finish();
+ memdelete(node);
+ node = nullptr;
+ }
+ if (output_rb) {
+ memdelete_arr(output_rb);
+ output_rb = nullptr;
+ }
+ if (input_rb) {
+ memdelete_arr(input_rb);
+ input_rb = nullptr;
}
}
Error AudioDriverJavaScript::capture_start() {
+ lock();
input_buffer_init(buffer_length);
+ unlock();
+ if (godot_audio_capture_start()) {
+ return FAILED;
+ }
+ return OK;
+}
- /* clang-format off */
- EM_ASM({
- function gotMediaInput(stream) {
- var ref = Module.IDHandler.get($0);
- ref['stream'] = stream;
- ref['input'] = ref['context'].createMediaStreamSource(stream);
- ref['input'].connect(ref['script']);
- }
+Error AudioDriverJavaScript::capture_stop() {
+ godot_audio_capture_stop();
+ lock();
+ input_buffer.clear();
+ unlock();
+ return OK;
+}
- function gotMediaInputError(e) {
- out(e);
- }
+AudioDriverJavaScript::AudioDriverJavaScript() {
+ singleton = this;
+}
- if (navigator.mediaDevices.getUserMedia) {
- navigator.mediaDevices.getUserMedia({"audio": true}).then(gotMediaInput, gotMediaInputError);
- } else {
- if (!navigator.getUserMedia)
- navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
- navigator.getUserMedia({"audio": true}, gotMediaInput, gotMediaInputError);
- }
- }, _driver_id);
- /* clang-format on */
+#ifdef NO_THREADS
+/// ScriptProcessorNode implementation
+void AudioDriverJavaScript::ScriptProcessorNode::_process_callback() {
+ AudioDriverJavaScript::singleton->_audio_driver_capture();
+ AudioDriverJavaScript::singleton->_audio_driver_process();
+}
- return OK;
+int AudioDriverJavaScript::ScriptProcessorNode::create(int p_buffer_samples, int p_channels) {
+ return godot_audio_script_create(p_buffer_samples, p_channels);
}
-Error AudioDriverJavaScript::capture_stop() {
- /* clang-format off */
- EM_ASM({
- var ref = Module.IDHandler.get($0);
- if (ref['stream']) {
- const tracks = ref['stream'].getTracks();
- for (var i = 0; i < tracks.length; i++) {
- tracks[i].stop();
+void AudioDriverJavaScript::ScriptProcessorNode::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
+ godot_audio_script_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, &_process_callback);
+}
+#else
+/// AudioWorkletNode implementation
+void AudioDriverJavaScript::WorkletNode::_audio_thread_func(void *p_data) {
+ AudioDriverJavaScript::WorkletNode *obj = static_cast<AudioDriverJavaScript::WorkletNode *>(p_data);
+ AudioDriverJavaScript *driver = AudioDriverJavaScript::singleton;
+ const int out_samples = memarr_len(driver->output_rb);
+ const int in_samples = memarr_len(driver->input_rb);
+ int wpos = 0;
+ int to_write = out_samples;
+ int rpos = 0;
+ int to_read = 0;
+ int32_t step = 0;
+ while (!obj->quit) {
+ if (to_read) {
+ driver->lock();
+ driver->_audio_driver_capture(rpos, to_read);
+ godot_audio_worklet_state_add(obj->state, STATE_SAMPLES_IN, -to_read);
+ driver->unlock();
+ rpos += to_read;
+ if (rpos >= in_samples) {
+ rpos -= in_samples;
}
- ref['stream'] = null;
}
-
- if (ref['input']) {
- ref['input'].disconnect();
- ref['input'] = null;
+ if (to_write) {
+ driver->lock();
+ driver->_audio_driver_process(wpos, to_write);
+ godot_audio_worklet_state_add(obj->state, STATE_SAMPLES_OUT, to_write);
+ driver->unlock();
+ wpos += to_write;
+ if (wpos >= out_samples) {
+ wpos -= out_samples;
+ }
}
+ step = godot_audio_worklet_state_wait(obj->state, STATE_PROCESS, step, 1);
+ to_write = out_samples - godot_audio_worklet_state_get(obj->state, STATE_SAMPLES_OUT);
+ to_read = godot_audio_worklet_state_get(obj->state, STATE_SAMPLES_IN);
+ }
+}
- }, _driver_id);
- /* clang-format on */
+int AudioDriverJavaScript::WorkletNode::create(int p_buffer_size, int p_channels) {
+ godot_audio_worklet_create(p_channels);
+ return p_buffer_size;
+}
- input_buffer.clear();
+void AudioDriverJavaScript::WorkletNode::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
+ godot_audio_worklet_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, state);
+ thread.start(_audio_thread_func, this);
+}
- return OK;
+void AudioDriverJavaScript::WorkletNode::lock() {
+ mutex.lock();
}
-AudioDriverJavaScript::AudioDriverJavaScript() {
- _driver_id = 0;
- internal_buffer = nullptr;
- buffer_length = 0;
+void AudioDriverJavaScript::WorkletNode::unlock() {
+ mutex.unlock();
+}
- singleton = this;
+void AudioDriverJavaScript::WorkletNode::finish() {
+ quit = true; // Ask thread to quit.
+ thread.wait_to_finish();
}
+#endif
diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h
index 9b26be001e..393693640f 100644
--- a/platform/javascript/audio_driver_javascript.h
+++ b/platform/javascript/audio_driver_javascript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,17 +31,78 @@
#ifndef AUDIO_DRIVER_JAVASCRIPT_H
#define AUDIO_DRIVER_JAVASCRIPT_H
+#include "core/os/mutex.h"
+#include "core/os/thread.h"
#include "servers/audio_server.h"
+#include "godot_audio.h"
+
class AudioDriverJavaScript : public AudioDriver {
- float *internal_buffer;
+public:
+ class AudioNode {
+ public:
+ virtual int create(int p_buffer_size, int p_output_channels) = 0;
+ virtual void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) = 0;
+ virtual void finish() {}
+ virtual void lock() {}
+ virtual void unlock() {}
+ virtual ~AudioNode() {}
+ };
+
+ class WorkletNode : public AudioNode {
+ private:
+ enum {
+ STATE_LOCK,
+ STATE_PROCESS,
+ STATE_SAMPLES_IN,
+ STATE_SAMPLES_OUT,
+ STATE_MAX,
+ };
+ Mutex mutex;
+ Thread thread;
+ bool quit = false;
+ int32_t state[STATE_MAX] = { 0 };
+
+ static void _audio_thread_func(void *p_data);
+
+ public:
+ int create(int p_buffer_size, int p_output_channels) override;
+ void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
+ void finish() override;
+ void lock() override;
+ void unlock() override;
+ };
+
+ class ScriptProcessorNode : public AudioNode {
+ private:
+ static void _process_callback();
- int _driver_id;
- int buffer_length;
+ public:
+ int create(int p_buffer_samples, int p_channels) override;
+ void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
+ };
+
+private:
+ AudioNode *node = nullptr;
+
+ float *output_rb = nullptr;
+ float *input_rb = nullptr;
+
+ int buffer_length = 0;
+ int mix_rate = 0;
+ int channel_count = 0;
+ int state = 0;
+ float output_latency = 0.0;
+
+ static void _state_change_callback(int p_state);
+ static void _latency_update_callback(float p_latency);
+
+protected:
+ void _audio_driver_process(int p_from = 0, int p_samples = 0);
+ void _audio_driver_capture(int p_from = 0, int p_samples = 0);
public:
- void mix_to_js();
- void process_capture(float sample);
+ static bool is_available();
static AudioDriverJavaScript *singleton;
@@ -56,12 +117,10 @@ public:
virtual void lock();
virtual void unlock();
virtual void finish();
- void finish_async();
virtual Error capture_start();
virtual Error capture_stop();
AudioDriverJavaScript();
};
-
#endif
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index 81287cead8..4297088c09 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -1,6 +1,16 @@
import os
+import sys
-from emscripten_helpers import parse_config, run_closure_compiler, create_engine_file
+from emscripten_helpers import (
+ run_closure_compiler,
+ create_engine_file,
+ add_js_libraries,
+ add_js_pre,
+ add_js_externs,
+ get_build_version,
+)
+from methods import get_compiler_version
+from SCons.Util import WhereIs
def is_active():
@@ -12,16 +22,24 @@ def get_name():
def can_build():
- return "EM_CONFIG" in os.environ or os.path.exists(os.path.expanduser("~/.emscripten"))
+ return WhereIs("emcc") is not None
def get_opts():
from SCons.Variables import BoolVariable
return [
+ ("initial_memory", "Initial WASM memory (in MiB)", 16),
+ BoolVariable("use_assertions", "Use Emscripten runtime assertions", False),
+ BoolVariable("use_thinlto", "Use ThinLTO", False),
+ BoolVariable("use_ubsan", "Use Emscripten undefined behavior sanitizer (UBSAN)", False),
+ BoolVariable("use_asan", "Use Emscripten address sanitizer (ASAN)", False),
+ BoolVariable("use_lsan", "Use Emscripten leak sanitizer (LSAN)", False),
+ BoolVariable("use_safe_heap", "Use Emscripten SAFE_HEAP sanitizer", False),
# eval() can be a security concern, so it can be disabled.
BoolVariable("javascript_eval", "Enable JavaScript eval interface", True),
BoolVariable("threads_enabled", "Enable WebAssembly Threads support (limited browser support)", False),
+ BoolVariable("gdnative_enabled", "Enable WebAssembly GDNative support (produces bigger binaries)", False),
BoolVariable("use_closure_compiler", "Use closure compiler to minimize JavaScript code", False),
]
@@ -39,9 +57,13 @@ def get_flags():
def configure(env):
+ try:
+ env["initial_memory"] = int(env["initial_memory"])
+ except Exception:
+ print("Initial memory must be a valid integer")
+ sys.exit(255)
## Build type
-
if env["target"] == "release":
# Use -Os to prioritize optimizing for reduced file size. This is
# particularly valuable for the web platform because it directly
@@ -61,15 +83,20 @@ def configure(env):
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(CCFLAGS=["-O1", "-g"])
env.Append(LINKFLAGS=["-O1", "-g"])
+ env["use_assertions"] = True
+
+ if env["use_assertions"]:
env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"])
if env["tools"]:
if not env["threads_enabled"]:
- raise RuntimeError(
- "Threads must be enabled to build the editor. Please add the 'threads_enabled=yes' option"
- )
- # Tools need more memory. Initial stack memory in bytes. See `src/settings.js` in emscripten repository (will be renamed to INITIAL_MEMORY).
- env.Append(LINKFLAGS=["-s", "TOTAL_MEMORY=33554432"])
+ print("Threads must be enabled to build the editor. Please add the 'threads_enabled=yes' option")
+ sys.exit(255)
+ if env["initial_memory"] < 64:
+ print("Editor build requires at least 64MiB of initial memory. Forcing it.")
+ env["initial_memory"] = 64
+ elif env["builtin_icu"]:
+ env.Append(CCFLAGS=["-frtti"])
else:
# Disable exceptions and rtti on non-tools (template) builds
# These flags help keep the file size down.
@@ -77,14 +104,32 @@ def configure(env):
# Don't use dynamic_cast, necessary with no-rtti.
env.Append(CPPDEFINES=["NO_SAFE_CAST"])
+ env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]])
+
## Copy env variables.
env["ENV"] = os.environ
# LTO
- if env["use_lto"]:
- env.Append(CCFLAGS=["-s", "WASM_OBJECT_FILES=0"])
- env.Append(LINKFLAGS=["-s", "WASM_OBJECT_FILES=0"])
- env.Append(LINKFLAGS=["--llvm-lto", "1"])
+ if env["use_thinlto"]:
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ elif env["use_lto"]:
+ env.Append(CCFLAGS=["-flto=full"])
+ env.Append(LINKFLAGS=["-flto=full"])
+
+ # Sanitizers
+ if env["use_ubsan"]:
+ env.Append(CCFLAGS=["-fsanitize=undefined"])
+ env.Append(LINKFLAGS=["-fsanitize=undefined"])
+ if env["use_asan"]:
+ env.Append(CCFLAGS=["-fsanitize=address"])
+ env.Append(LINKFLAGS=["-fsanitize=address"])
+ if env["use_lsan"]:
+ env.Append(CCFLAGS=["-fsanitize=leak"])
+ env.Append(LINKFLAGS=["-fsanitize=leak"])
+ if env["use_safe_heap"]:
+ env.Append(CCFLAGS=["-s", "SAFE_HEAP=1"])
+ env.Append(LINKFLAGS=["-s", "SAFE_HEAP=1"])
# Closure compiler
if env["use_closure_compiler"]:
@@ -94,18 +139,25 @@ def configure(env):
jscc = env.Builder(generator=run_closure_compiler, suffix=".cc.js", src_suffix=".js")
env.Append(BUILDERS={"BuildJS": jscc})
+ # Add helper method for adding libraries, externs, pre-js.
+ env["JS_LIBS"] = []
+ env["JS_PRE"] = []
+ env["JS_EXTERNS"] = []
+ env.AddMethod(add_js_libraries, "AddJSLibraries")
+ env.AddMethod(add_js_pre, "AddJSPre")
+ env.AddMethod(add_js_externs, "AddJSExterns")
+
+ # Add method for getting build version string.
+ env.AddMethod(get_build_version, "GetBuildVersion")
+
# Add method that joins/compiles our Engine files.
env.AddMethod(create_engine_file, "CreateEngineFile")
# Closure compiler extern and support for ecmascript specs (const, let, etc).
env["ENV"]["EMCC_CLOSURE_ARGS"] = "--language_in ECMASCRIPT6"
- em_config = parse_config()
- env.PrependENVPath("PATH", em_config["EMCC_ROOT"])
-
env["CC"] = "emcc"
env["CXX"] = "em++"
- env["LINK"] = "emcc"
env["AR"] = "emar"
env["RANLIB"] = "emranlib"
@@ -132,24 +184,34 @@ def configure(env):
if env["javascript_eval"]:
env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"])
+ if env["threads_enabled"] and env["gdnative_enabled"]:
+ print("Threads and GDNative support can't be both enabled due to WebAssembly limitations")
+ sys.exit(255)
+
# Thread support (via SharedArrayBuffer).
if env["threads_enabled"]:
env.Append(CPPDEFINES=["PTHREAD_NO_RENAME"])
env.Append(CCFLAGS=["-s", "USE_PTHREADS=1"])
env.Append(LINKFLAGS=["-s", "USE_PTHREADS=1"])
- env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=4"])
+ env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
+ env.extra_suffix = ".threads" + env.extra_suffix
else:
env.Append(CPPDEFINES=["NO_THREADS"])
+ if env["gdnative_enabled"]:
+ major, minor, patch = get_compiler_version(env)
+ if major < 2 or (major == 2 and minor == 0 and patch < 10):
+ print("GDNative support requires emscripten >= 2.0.10, detected: %s.%s.%s" % (major, minor, patch))
+ sys.exit(255)
+ env.Append(CCFLAGS=["-s", "RELOCATABLE=1"])
+ env.Append(LINKFLAGS=["-s", "RELOCATABLE=1"])
+ env.extra_suffix = ".gdnative" + env.extra_suffix
+
# Reduce code size by generating less support code (e.g. skip NodeJS support).
env.Append(LINKFLAGS=["-s", "ENVIRONMENT=web,worker"])
- # We use IDBFS in javascript_main.cpp. Since Emscripten 1.39.1 it needs to
- # be linked explicitly.
- env.Append(LIBS=["idbfs.js"])
-
- env.Append(LINKFLAGS=["-s", "BINARYEN=1"])
+ # Wrap the JavaScript support code around a closure named Godot.
env.Append(LINKFLAGS=["-s", "MODULARIZE=1", "-s", "EXPORT_NAME='Godot'"])
# Allow increasing memory buffer size during runtime. This is efficient
@@ -160,12 +222,22 @@ def configure(env):
# This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1.
env.Append(LINKFLAGS=["-s", "USE_WEBGL2=1"])
+ # Do not call main immediately when the support code is ready.
env.Append(LINKFLAGS=["-s", "INVOKE_RUN=0"])
# Allow use to take control of swapping WebGL buffers.
env.Append(LINKFLAGS=["-s", "OFFSCREEN_FRAMEBUFFER=1"])
- # callMain for manual start, FS for preloading, PATH and ERRNO_CODES for BrowserFS.
- env.Append(LINKFLAGS=["-s", "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain', 'FS', 'PATH']"])
+ # callMain for manual start.
+ env.Append(LINKFLAGS=["-s", "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain','cwrap']"])
+
# Add code that allow exiting runtime.
env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"])
+
+ # TODO remove once we have GLES support back (temporary fix undefined symbols due to dead code elimination).
+ env.Append(
+ LINKFLAGS=[
+ "-s",
+ "EXPORTED_FUNCTIONS=['_main', '_emscripten_webgl_get_current_context', '_emscripten_webgl_commit_frame', '_emscripten_webgl_create_context']",
+ ]
+ )
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index b95674efc3..a605f22e16 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,6 +37,7 @@
#include <png.h>
#include "dom_keys.inc"
+#include "godot_js.h"
#define DOM_BUTTON_LEFT 0
#define DOM_BUTTON_MIDDLE 1
@@ -49,55 +50,29 @@ DisplayServerJavaScript *DisplayServerJavaScript::get_singleton() {
}
// Window (canvas)
-extern "C" EMSCRIPTEN_KEEPALIVE void _set_canvas_id(uint8_t *p_data, int p_data_size) {
- DisplayServerJavaScript *display = DisplayServerJavaScript::get_singleton();
- display->canvas_id.parse_utf8((const char *)p_data, p_data_size);
- display->canvas_id = "#" + display->canvas_id;
+void DisplayServerJavaScript::focus_canvas() {
+ godot_js_display_canvas_focus();
}
-static void focus_canvas() {
- /* clang-format off */
- EM_ASM(
- Module['canvas'].focus();
- );
- /* clang-format on */
+bool DisplayServerJavaScript::is_canvas_focused() {
+ return godot_js_display_canvas_is_focused() != 0;
}
-static bool is_canvas_focused() {
- /* clang-format off */
- return EM_ASM_INT_V(
- return document.activeElement == Module['canvas'];
- );
- /* clang-format on */
+bool DisplayServerJavaScript::check_size_force_redraw() {
+ return godot_js_display_size_update() != 0;
}
-static Point2 compute_position_in_canvas(int x, int y) {
- DisplayServerJavaScript *display = DisplayServerJavaScript::get_singleton();
- int canvas_x = EM_ASM_INT({
- return Module['canvas'].getBoundingClientRect().x;
- });
- int canvas_y = EM_ASM_INT({
- return Module['canvas'].getBoundingClientRect().y;
- });
- int canvas_width;
- int canvas_height;
- emscripten_get_canvas_element_size(display->canvas_id.utf8().get_data(), &canvas_width, &canvas_height);
-
- double element_width;
- double element_height;
- emscripten_get_element_css_size(display->canvas_id.utf8().get_data(), &element_width, &element_height);
-
- return Point2((int)(canvas_width / element_width * (x - canvas_x)),
- (int)(canvas_height / element_height * (y - canvas_y)));
+Point2 DisplayServerJavaScript::compute_position_in_canvas(int p_x, int p_y) {
+ int point[2];
+ godot_js_display_compute_position(p_x, p_y, point, point + 1);
+ return Point2(point[0], point[1]);
}
-static bool cursor_inside_canvas = true;
-
EM_BOOL DisplayServerJavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
DisplayServerJavaScript *display = get_singleton();
// Empty ID is canvas.
String target_id = String::utf8(p_event->id);
- if (target_id.empty() || "#" + target_id == display->canvas_id) {
+ if (target_id.is_empty() || target_id == String::utf8(&(display->canvas_id[1]))) {
// This event property is the only reliable data on
// browser fullscreen state.
if (p_event->isFullscreen) {
@@ -109,14 +84,15 @@ EM_BOOL DisplayServerJavaScript::fullscreen_change_callback(int p_event_type, co
return false;
}
-// Drag and drop callback (see native/utils.js).
-extern "C" EMSCRIPTEN_KEEPALIVE void _drop_files_callback(char *p_filev[], int p_filec) {
- DisplayServerJavaScript *ds = DisplayServerJavaScript::get_singleton();
+// Drag and drop callback.
+void DisplayServerJavaScript::drop_files_js_callback(char **p_filev, int p_filec) {
+ DisplayServerJavaScript *ds = get_singleton();
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_null()) {
return;
+ }
Vector<String> files;
for (int i = 0; i < p_filec; i++) {
files.push_back(String::utf8(p_filev[i]));
@@ -128,23 +104,35 @@ extern "C" EMSCRIPTEN_KEEPALIVE void _drop_files_callback(char *p_filev[], int p
ds->drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
}
+// JavaScript quit request callback.
+void DisplayServerJavaScript::request_quit_callback() {
+ DisplayServerJavaScript *ds = get_singleton();
+ if (ds && !ds->window_event_callback.is_null()) {
+ Variant event = int(DisplayServer::WINDOW_EVENT_CLOSE_REQUEST);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ ds->window_event_callback.call((const Variant **)&eventp, 1, ret, ce);
+ }
+}
+
// Keys
template <typename T>
-static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) {
+void DisplayServerJavaScript::dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) {
godot_event->set_shift(emscripten_event_ptr->shiftKey);
godot_event->set_alt(emscripten_event_ptr->altKey);
godot_event->set_control(emscripten_event_ptr->ctrlKey);
godot_event->set_metakey(emscripten_event_ptr->metaKey);
}
-static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
+Ref<InputEventKey> DisplayServerJavaScript::setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
Ref<InputEventKey> ev;
ev.instance();
ev->set_echo(emscripten_event->repeat);
dom2godot_mod(emscripten_event, ev);
- ev->set_keycode(dom2godot_keycode(emscripten_event->keyCode));
- ev->set_physical_keycode(dom2godot_keycode(emscripten_event->keyCode));
+ ev->set_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, false));
+ ev->set_physical_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, true));
String unicode = String::utf8(emscripten_event->key);
// Check if empty or multi-character (e.g. `CapsLock`).
@@ -263,12 +251,13 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E
}
EM_BOOL DisplayServerJavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
+ DisplayServerJavaScript *ds = get_singleton();
Input *input = Input::get_singleton();
int input_mask = input->get_mouse_button_mask();
Point2 pos = compute_position_in_canvas(p_event->clientX, p_event->clientY);
// For motion outside the canvas, only read mouse movement if dragging
// started inside the canvas; imitating desktop app behaviour.
- if (!cursor_inside_canvas && !input_mask)
+ if (!ds->cursor_inside_canvas && !input_mask)
return false;
Ref<InputEventMouseMotion> ev;
@@ -289,7 +278,7 @@ EM_BOOL DisplayServerJavaScript::mousemove_callback(int p_event_type, const Emsc
}
// Cursor
-static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape) {
+const char *DisplayServerJavaScript::godot2dom_cursor(DisplayServer::CursorShape p_shape) {
switch (p_shape) {
case DisplayServer::CURSOR_ARROW:
return "auto";
@@ -330,35 +319,13 @@ static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape) {
}
}
-static void set_css_cursor(const char *p_cursor) {
- /* clang-format off */
- EM_ASM_({
- Module['canvas'].style.cursor = UTF8ToString($0);
- }, p_cursor);
- /* clang-format on */
-}
-
-static bool is_css_cursor_hidden() {
- /* clang-format off */
- return EM_ASM_INT({
- return Module['canvas'].style.cursor === 'none';
- });
- /* clang-format on */
-}
-
void DisplayServerJavaScript::cursor_set_shape(CursorShape p_shape) {
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
-
- if (mouse_get_mode() == MOUSE_MODE_VISIBLE) {
- if (cursors[p_shape] != "") {
- Vector<String> url = cursors[p_shape].split("?");
- set_css_cursor(("url(\"" + url[0] + "\") " + url[1] + ", auto").utf8());
- } else {
- set_css_cursor(godot2dom_cursor(p_shape));
- }
+ if (cursor_shape == p_shape) {
+ return;
}
-
cursor_shape = p_shape;
+ godot_js_display_cursor_set_shape(godot2dom_cursor(cursor_shape));
}
DisplayServer::CursorShape DisplayServerJavaScript::cursor_get_shape() const {
@@ -367,17 +334,6 @@ DisplayServer::CursorShape DisplayServerJavaScript::cursor_get_shape() const {
void DisplayServerJavaScript::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
if (p_cursor.is_valid()) {
- Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
-
- if (cursor_c) {
- if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
- cursor_set_shape(p_shape);
- return;
- }
-
- cursors_cache.erase(p_shape);
- }
-
Ref<Texture2D> texture = p_cursor;
Ref<AtlasTexture> atlas_texture = p_cursor;
Ref<Image> image;
@@ -440,53 +396,10 @@ void DisplayServerJavaScript::cursor_set_custom_image(const RES &p_cursor, Curso
png.resize(len);
ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, png.ptrw(), &len, 0, data.ptr(), 0, nullptr));
- char *object_url;
- /* clang-format off */
- EM_ASM({
- var PNG_PTR = $0;
- var PNG_LEN = $1;
- var PTR = $2;
-
- var png = new Blob([HEAPU8.slice(PNG_PTR, PNG_PTR + PNG_LEN)], { type: 'image/png' });
- var url = URL.createObjectURL(png);
- var length_bytes = lengthBytesUTF8(url) + 1;
- var string_on_wasm_heap = _malloc(length_bytes);
- setValue(PTR, string_on_wasm_heap, '*');
- stringToUTF8(url, string_on_wasm_heap, length_bytes);
- }, png.ptr(), len, &object_url);
- /* clang-format on */
-
- String url = String::utf8(object_url) + "?" + itos(p_hotspot.x) + " " + itos(p_hotspot.y);
-
- /* clang-format off */
- EM_ASM({ _free($0); }, object_url);
- /* clang-format on */
-
- if (cursors[p_shape] != "") {
- /* clang-format off */
- EM_ASM({
- URL.revokeObjectURL(UTF8ToString($0).split('?')[0]);
- }, cursors[p_shape].utf8().get_data());
- /* clang-format on */
- cursors[p_shape] = "";
- }
+ godot_js_display_cursor_set_custom_shape(godot2dom_cursor(p_shape), png.ptr(), len, p_hotspot.x, p_hotspot.y);
- cursors[p_shape] = url;
-
- Vector<Variant> params;
- params.push_back(p_cursor);
- params.push_back(p_hotspot);
- cursors_cache.insert(p_shape, params);
-
- } else if (cursors[p_shape] != "") {
- /* clang-format off */
- EM_ASM({
- URL.revokeObjectURL(UTF8ToString($0).split('?')[0]);
- }, cursors[p_shape].utf8().get_data());
- /* clang-format on */
- cursors[p_shape] = "";
-
- cursors_cache.erase(p_shape);
+ } else {
+ godot_js_display_cursor_set_custom_shape(godot2dom_cursor(p_shape), NULL, 0, 0, 0);
}
cursor_set_shape(cursor_shape);
@@ -499,40 +412,37 @@ void DisplayServerJavaScript::mouse_set_mode(MouseMode p_mode) {
return;
if (p_mode == MOUSE_MODE_VISIBLE) {
- // set_css_cursor must be called before set_cursor_shape to make the cursor visible
- set_css_cursor(godot2dom_cursor(cursor_shape));
- cursor_set_shape(cursor_shape);
+ godot_js_display_cursor_set_visible(1);
emscripten_exit_pointerlock();
} else if (p_mode == MOUSE_MODE_HIDDEN) {
- set_css_cursor("none");
+ godot_js_display_cursor_set_visible(0);
emscripten_exit_pointerlock();
} else if (p_mode == MOUSE_MODE_CAPTURED) {
- EMSCRIPTEN_RESULT result = emscripten_request_pointerlock("canvas", false);
+ godot_js_display_cursor_set_visible(1);
+ EMSCRIPTEN_RESULT result = emscripten_request_pointerlock(canvas_id, false);
ERR_FAIL_COND_MSG(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED, "MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback.");
ERR_FAIL_COND_MSG(result != EMSCRIPTEN_RESULT_SUCCESS, "MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback.");
- // set_css_cursor must be called before cursor_set_shape to make the cursor visible
- set_css_cursor(godot2dom_cursor(cursor_shape));
- cursor_set_shape(cursor_shape);
}
}
DisplayServer::MouseMode DisplayServerJavaScript::mouse_get_mode() const {
- if (is_css_cursor_hidden())
+ if (godot_js_display_cursor_is_hidden()) {
return MOUSE_MODE_HIDDEN;
+ }
EmscriptenPointerlockChangeEvent ev;
emscripten_get_pointerlock_status(&ev);
- return (ev.isActive && String::utf8(ev.id) == "canvas") ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE;
+ return (ev.isActive && String::utf8(ev.id) == String::utf8(&canvas_id[1])) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE;
}
// Wheel
-
EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data) {
ERR_FAIL_COND_V(p_event_type != EMSCRIPTEN_EVENT_WHEEL, false);
+ DisplayServerJavaScript *ds = get_singleton();
if (!is_canvas_focused()) {
- if (cursor_inside_canvas) {
+ if (ds->cursor_inside_canvas) {
focus_canvas();
} else {
return false;
@@ -623,63 +533,55 @@ EM_BOOL DisplayServerJavaScript::touchmove_callback(int p_event_type, const Emsc
}
bool DisplayServerJavaScript::screen_is_touchscreen(int p_screen) const {
- return EM_ASM_INT({ return 'ontouchstart' in window; });
+ return godot_js_display_touchscreen_is_available();
}
// Gamepad
-
-EM_BOOL DisplayServerJavaScript::gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data) {
+void DisplayServerJavaScript::gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid) {
Input *input = Input::get_singleton();
- if (p_event_type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED) {
- String guid = "";
- if (String::utf8(p_event->mapping) == "standard")
- guid = "Default HTML5 Gamepad";
- input->joy_connection_changed(p_event->index, true, String::utf8(p_event->id), guid);
+ if (p_connected) {
+ input->joy_connection_changed(p_index, true, String::utf8(p_id), String::utf8(p_guid));
} else {
- input->joy_connection_changed(p_event->index, false, "");
+ input->joy_connection_changed(p_index, false, "");
}
- return true;
}
void DisplayServerJavaScript::process_joypads() {
- int joypad_count = emscripten_get_num_gamepads();
Input *input = Input::get_singleton();
- for (int joypad = 0; joypad < joypad_count; joypad++) {
- EmscriptenGamepadEvent state;
- EMSCRIPTEN_RESULT query_result = emscripten_get_gamepad_status(joypad, &state);
- // Chromium reserves gamepads slots, so NO_DATA is an expected result.
- ERR_CONTINUE(query_result != EMSCRIPTEN_RESULT_SUCCESS &&
- query_result != EMSCRIPTEN_RESULT_NO_DATA);
- if (query_result == EMSCRIPTEN_RESULT_SUCCESS && state.connected) {
- int button_count = MIN(state.numButtons, 18);
- int axis_count = MIN(state.numAxes, 8);
- for (int button = 0; button < button_count; button++) {
- float value = state.analogButton[button];
- input->joy_button(joypad, button, value);
- }
- for (int axis = 0; axis < axis_count; axis++) {
+ int32_t pads = godot_js_display_gamepad_sample_count();
+ int32_t s_btns_num = 0;
+ int32_t s_axes_num = 0;
+ int32_t s_standard = 0;
+ float s_btns[16];
+ float s_axes[10];
+ for (int idx = 0; idx < pads; idx++) {
+ int err = godot_js_display_gamepad_sample_get(idx, s_btns, &s_btns_num, s_axes, &s_axes_num, &s_standard);
+ if (err) {
+ continue;
+ }
+ for (int b = 0; b < s_btns_num; b++) {
+ float value = s_btns[b];
+ // Buttons 6 and 7 in the standard mapping need to be
+ // axis to be handled as JOY_AXIS_TRIGGER by Godot.
+ if (s_standard && (b == 6 || b == 7)) {
Input::JoyAxis joy_axis;
- joy_axis.min = -1;
- joy_axis.value = state.axis[axis];
- input->joy_axis(joypad, axis, joy_axis);
+ joy_axis.min = 0;
+ joy_axis.value = value;
+ int a = b == 6 ? JOY_AXIS_TRIGGER_LEFT : JOY_AXIS_TRIGGER_RIGHT;
+ input->joy_axis(idx, a, joy_axis);
+ } else {
+ input->joy_button(idx, b, value);
}
}
+ for (int a = 0; a < s_axes_num; a++) {
+ Input::JoyAxis joy_axis;
+ joy_axis.min = -1;
+ joy_axis.value = s_axes[a];
+ input->joy_axis(idx, a, joy_axis);
+ }
}
}
-#if 0
-bool DisplayServerJavaScript::is_joy_known(int p_device) {
-
- return Input::get_singleton()->is_joy_mapped(p_device);
-}
-
-
-String DisplayServerJavaScript::get_joy_guid(int p_device) const {
-
- return Input::get_singleton()->get_joy_guid_remapped(p_device);
-}
-#endif
-
Vector<String> DisplayServerJavaScript::get_rendering_drivers_func() {
Vector<String> drivers;
drivers.push_back("dummy");
@@ -687,53 +589,30 @@ Vector<String> DisplayServerJavaScript::get_rendering_drivers_func() {
}
// Clipboard
-extern "C" EMSCRIPTEN_KEEPALIVE void update_clipboard(const char *p_text) {
- // Only call set_clipboard from OS (sets local clipboard)
- DisplayServerJavaScript::get_singleton()->clipboard = p_text;
+void DisplayServerJavaScript::update_clipboard_callback(const char *p_text) {
+ get_singleton()->clipboard = p_text;
}
void DisplayServerJavaScript::clipboard_set(const String &p_text) {
- /* clang-format off */
- int err = EM_ASM_INT({
- var text = UTF8ToString($0);
- if (!navigator.clipboard || !navigator.clipboard.writeText)
- return 1;
- navigator.clipboard.writeText(text).catch(function(e) {
- // Setting OS clipboard is only possible from an input callback.
- console.error("Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:", e);
- });
- return 0;
- }, p_text.utf8().get_data());
- /* clang-format on */
+ clipboard = p_text;
+ int err = godot_js_display_clipboard_set(p_text.utf8().get_data());
ERR_FAIL_COND_MSG(err, "Clipboard API is not supported.");
}
String DisplayServerJavaScript::clipboard_get() const {
- /* clang-format off */
- EM_ASM({
- try {
- navigator.clipboard.readText().then(function (result) {
- ccall('update_clipboard', 'void', ['string'], [result]);
- }).catch(function (e) {
- // Fail graciously.
- });
- } catch (e) {
- // Fail graciously.
- }
- });
- /* clang-format on */
+ godot_js_display_clipboard_get(update_clipboard_callback);
return clipboard;
}
-extern "C" EMSCRIPTEN_KEEPALIVE void send_window_event(int p_notification) {
+void DisplayServerJavaScript::send_window_event_callback(int p_notification) {
+ DisplayServerJavaScript *ds = get_singleton();
+ if (!ds) {
+ return;
+ }
if (p_notification == DisplayServer::WINDOW_EVENT_MOUSE_ENTER || p_notification == DisplayServer::WINDOW_EVENT_MOUSE_EXIT) {
- cursor_inside_canvas = p_notification == DisplayServer::WINDOW_EVENT_MOUSE_ENTER;
+ ds->cursor_inside_canvas = p_notification == DisplayServer::WINDOW_EVENT_MOUSE_ENTER;
}
- OS_JavaScript *os = OS_JavaScript::get_singleton();
- if (os->is_finalizing())
- return; // We don't want events anymore.
- DisplayServerJavaScript *ds = DisplayServerJavaScript::get_singleton();
- if (ds && !ds->window_event_callback.is_null()) {
+ if (!ds->window_event_callback.is_null()) {
Variant event = int(p_notification);
Variant *eventp = &event;
Variant ret;
@@ -743,11 +622,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE void send_window_event(int p_notification) {
}
void DisplayServerJavaScript::alert(const String &p_alert, const String &p_title) {
- /* clang-format off */
- EM_ASM_({
- window.alert(UTF8ToString($0));
- }, p_alert.utf8().get_data());
- /* clang-format on */
+ godot_js_display_alert(p_alert.utf8().get_data());
}
void DisplayServerJavaScript::set_icon(const Ref<Image> &p_icon) {
@@ -778,29 +653,11 @@ void DisplayServerJavaScript::set_icon(const Ref<Image> &p_icon) {
png.resize(len);
ERR_FAIL_COND(!png_image_write_to_memory(&png_meta, png.ptrw(), &len, 0, data.ptr(), 0, nullptr));
- /* clang-format off */
- EM_ASM({
- var PNG_PTR = $0;
- var PNG_LEN = $1;
-
- var png = new Blob([HEAPU8.slice(PNG_PTR, PNG_PTR + PNG_LEN)], { type: "image/png" });
- var url = URL.createObjectURL(png);
- var link = document.getElementById('-gd-engine-icon');
- if (link === null) {
- link = document.createElement('link');
- link.rel = 'icon';
- link.id = '-gd-engine-icon';
- document.head.appendChild(link);
- }
- link.href = url;
- }, png.ptr(), len);
- /* clang-format on */
+ godot_js_display_window_icon_set(png.ptr(), len);
}
void DisplayServerJavaScript::_dispatch_input_event(const Ref<InputEvent> &p_event) {
OS_JavaScript *os = OS_JavaScript::get_singleton();
- if (os->is_finalizing())
- return; // We don't want events anymore.
// Resume audio context after input in case autoplay was denied.
os->resume_audio();
@@ -820,22 +677,19 @@ DisplayServer *DisplayServerJavaScript::create_func(const String &p_rendering_dr
}
DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- /* clang-format off */
- EM_ASM({
- const canvas = Module['canvas'];
- var enc = new TextEncoder("utf-8");
- var buffer = new Uint8Array(enc.encode(canvas.id));
- var len = buffer.byteLength;
- var out = _malloc(len);
- HEAPU8.set(buffer, out);
- ccall("_set_canvas_id",
- "void",
- ["number", "number"],
- [out, len]
- );
- _free(out);
- });
- /* clang-format on */
+ r_error = OK; // Always succeeds for now.
+
+ // Ensure the canvas ID.
+ godot_js_config_canvas_id_get(canvas_id, 256);
+
+ // Handle contextmenu, webglcontextlost
+ godot_js_display_setup_canvas(p_resolution.x, p_resolution.y, p_mode == WINDOW_MODE_FULLSCREEN);
+
+ // Check if it's windows.
+ swap_cancel_ok = godot_js_display_is_swap_ok_cancel() == 1;
+
+ // Expose method for requesting quit.
+ godot_js_os_request_quit_cb(request_quit_callback);
RasterizerDummy::make_current(); // TODO GLES2 in Godot 4.0... or webgpu?
#if 0
@@ -859,7 +713,7 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
gl_initialization_error = true;
}
- EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(canvas_id.utf8().get_data(), &attributes);
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(canvas_id, &attributes);
if (emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
gl_initialization_error = true;
}
@@ -873,90 +727,48 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
video_driver_index = p_video_driver;
#endif
- /* clang-format off */
- window_set_mode(p_mode);
- if (EM_ASM_INT_V({ return Module['resizeCanvasOnStart'] })) {
- /* clang-format on */
- window_set_size(p_resolution);
- }
-
EMSCRIPTEN_RESULT result;
- CharString id = canvas_id.utf8();
#define EM_CHECK(ev) \
if (result != EMSCRIPTEN_RESULT_SUCCESS) \
ERR_PRINT("Error while setting " #ev " callback: Code " + itos(result));
#define SET_EM_CALLBACK(target, ev, cb) \
result = emscripten_set_##ev##_callback(target, nullptr, true, &cb); \
EM_CHECK(ev)
-#define SET_EM_CALLBACK_NOTARGET(ev, cb) \
- result = emscripten_set_##ev##_callback(nullptr, true, &cb); \
+#define SET_EM_WINDOW_CALLBACK(ev, cb) \
+ result = emscripten_set_##ev##_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, &cb); \
EM_CHECK(ev)
// These callbacks from Emscripten's html5.h suffice to access most
- // JavaScript APIs. For APIs that are not (sufficiently) exposed, EM_ASM
- // is used below.
- SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mousemove, mousemove_callback)
- SET_EM_CALLBACK(id.get_data(), mousedown, mouse_button_callback)
- SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mouseup, mouse_button_callback)
- SET_EM_CALLBACK(id.get_data(), wheel, wheel_callback)
- SET_EM_CALLBACK(id.get_data(), touchstart, touch_press_callback)
- SET_EM_CALLBACK(id.get_data(), touchmove, touchmove_callback)
- SET_EM_CALLBACK(id.get_data(), touchend, touch_press_callback)
- SET_EM_CALLBACK(id.get_data(), touchcancel, touch_press_callback)
- SET_EM_CALLBACK(id.get_data(), keydown, keydown_callback)
- SET_EM_CALLBACK(id.get_data(), keypress, keypress_callback)
- SET_EM_CALLBACK(id.get_data(), keyup, keyup_callback)
+ // JavaScript APIs.
+ SET_EM_CALLBACK(canvas_id, mousedown, mouse_button_callback)
+ SET_EM_WINDOW_CALLBACK(mousemove, mousemove_callback)
+ SET_EM_WINDOW_CALLBACK(mouseup, mouse_button_callback)
+ SET_EM_CALLBACK(canvas_id, wheel, wheel_callback)
+ SET_EM_CALLBACK(canvas_id, touchstart, touch_press_callback)
+ SET_EM_CALLBACK(canvas_id, touchmove, touchmove_callback)
+ SET_EM_CALLBACK(canvas_id, touchend, touch_press_callback)
+ SET_EM_CALLBACK(canvas_id, touchcancel, touch_press_callback)
+ SET_EM_CALLBACK(canvas_id, keydown, keydown_callback)
+ SET_EM_CALLBACK(canvas_id, keypress, keypress_callback)
+ SET_EM_CALLBACK(canvas_id, keyup, keyup_callback)
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, fullscreenchange, fullscreen_change_callback)
- SET_EM_CALLBACK_NOTARGET(gamepadconnected, gamepad_change_callback)
- SET_EM_CALLBACK_NOTARGET(gamepaddisconnected, gamepad_change_callback)
-#undef SET_EM_CALLBACK_NOTARGET
#undef SET_EM_CALLBACK
#undef EM_CHECK
- /* clang-format off */
- EM_ASM_ARGS({
- Module.listeners = {};
- const canvas = Module['canvas'];
- const send_window_event = cwrap('send_window_event', null, ['number']);
- const notifications = arguments;
- (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, index) {
- Module.listeners[event] = send_window_event.bind(null, notifications[index]);
- canvas.addEventListener(event, Module.listeners[event]);
- });
- // Clipboard
- const update_clipboard = cwrap('update_clipboard', null, ['string']);
- Module.listeners['paste'] = function(evt) {
- update_clipboard(evt.clipboardData.getData('text'));
- };
- window.addEventListener('paste', Module.listeners['paste'], false);
- Module.listeners['dragover'] = function(ev) {
- // Prevent default behavior (which would try to open the file(s))
- ev.preventDefault();
- };
- Module.listeners['drop'] = Module.drop_handler; // Defined in native/utils.js
- canvas.addEventListener('dragover', Module.listeners['dragover'], false);
- canvas.addEventListener('drop', Module.listeners['drop'], false);
- },
- WINDOW_EVENT_MOUSE_ENTER,
- WINDOW_EVENT_MOUSE_EXIT,
- WINDOW_EVENT_FOCUS_IN,
- WINDOW_EVENT_FOCUS_OUT
- );
- /* clang-format on */
+ // For APIs that are not (sufficiently) exposed, a
+ // library is used below (implemented in library_godot_display.js).
+ godot_js_display_notification_cb(&send_window_event_callback,
+ WINDOW_EVENT_MOUSE_ENTER,
+ WINDOW_EVENT_MOUSE_EXIT,
+ WINDOW_EVENT_FOCUS_IN,
+ WINDOW_EVENT_FOCUS_OUT);
+ godot_js_display_paste_cb(update_clipboard_callback);
+ godot_js_display_drop_files_cb(drop_files_js_callback);
+ godot_js_display_gamepad_cb(&DisplayServerJavaScript::gamepad_callback);
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_event);
}
DisplayServerJavaScript::~DisplayServerJavaScript() {
- EM_ASM({
- Object.entries(Module.listeners).forEach(function(kv) {
- if (kv[0] == 'paste') {
- window.removeEventListener(kv[0], kv[1], true);
- } else {
- Module['canvas'].removeEventListener(kv[0], kv[1]);
- }
- });
- Module.listeners = {};
- });
//emscripten_webgl_commit_frame();
//emscripten_webgl_destroy_context(webgl_ctx);
}
@@ -1004,20 +816,23 @@ Point2i DisplayServerJavaScript::screen_get_position(int p_screen) const {
}
Size2i DisplayServerJavaScript::screen_get_size(int p_screen) const {
- EmscriptenFullscreenChangeEvent ev;
- EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status(&ev);
- ERR_FAIL_COND_V(result != EMSCRIPTEN_RESULT_SUCCESS, Size2i());
- return Size2i(ev.screenWidth, ev.screenHeight);
+ int size[2];
+ godot_js_display_screen_size_get(size, size + 1);
+ return Size2(size[0], size[1]);
}
Rect2i DisplayServerJavaScript::screen_get_usable_rect(int p_screen) const {
- int canvas[2];
- emscripten_get_canvas_element_size(canvas_id.utf8().get_data(), canvas, canvas + 1);
- return Rect2i(0, 0, canvas[0], canvas[1]);
+ int size[2];
+ godot_js_display_window_size_get(size, size + 1);
+ return Rect2i(0, 0, size[0], size[1]);
}
int DisplayServerJavaScript::screen_get_dpi(int p_screen) const {
- return 96; // TODO maybe check pixel ratio via window.devicePixelRatio * 96? Inexact.
+ return godot_js_display_screen_dpi_get();
+}
+
+float DisplayServerJavaScript::screen_get_scale(int p_screen) const {
+ return godot_js_display_pixel_ratio_get();
}
Vector<DisplayServer::WindowID> DisplayServerJavaScript::get_window_list() const {
@@ -1059,11 +874,7 @@ void DisplayServerJavaScript::window_set_drop_files_callback(const Callable &p_c
}
void DisplayServerJavaScript::window_set_title(const String &p_title, WindowID p_window) {
- /* clang-format off */
- EM_ASM_({
- document.title = UTF8ToString($0);
- }, p_title.utf8().get_data());
- /* clang-format on */
+ godot_js_display_window_title_set(p_title.utf8().get_data());
}
int DisplayServerJavaScript::window_get_current_screen(WindowID p_window) const {
@@ -1103,13 +914,13 @@ Size2i DisplayServerJavaScript::window_get_min_size(WindowID p_window) const {
}
void DisplayServerJavaScript::window_set_size(const Size2i p_size, WindowID p_window) {
- emscripten_set_canvas_element_size(canvas_id.utf8().get_data(), p_size.x, p_size.y);
+ godot_js_display_desired_size_set(p_size.x, p_size.y);
}
Size2i DisplayServerJavaScript::window_get_size(WindowID p_window) const {
- int canvas[2];
- emscripten_get_canvas_element_size(canvas_id.utf8().get_data(), canvas, canvas + 1);
- return Size2(canvas[0], canvas[1]);
+ int size[2];
+ godot_js_display_window_size_get(size, size + 1);
+ return Size2i(size[0], size[1]);
}
Size2i DisplayServerJavaScript::window_get_real_size(WindowID p_window) const {
@@ -1123,20 +934,13 @@ void DisplayServerJavaScript::window_set_mode(WindowMode p_mode, WindowID p_wind
switch (p_mode) {
case WINDOW_MODE_WINDOWED: {
if (window_mode == WINDOW_MODE_FULLSCREEN) {
- emscripten_exit_fullscreen();
+ godot_js_display_fullscreen_exit();
}
window_mode = WINDOW_MODE_WINDOWED;
- window_set_size(windowed_size);
} break;
case WINDOW_MODE_FULLSCREEN: {
- EmscriptenFullscreenStrategy strategy;
- strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
- strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
- strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- strategy.canvasResizedCallback = nullptr;
- EMSCRIPTEN_RESULT result = emscripten_request_fullscreen_strategy(canvas_id.utf8().get_data(), false, &strategy);
- ERR_FAIL_COND_MSG(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED, "Enabling fullscreen is only possible from an input callback for the HTML5 platform.");
- ERR_FAIL_COND_MSG(result != EMSCRIPTEN_RESULT_SUCCESS, "Enabling fullscreen is only possible from an input callback for the HTML5 platform.");
+ int result = godot_js_display_fullscreen_request();
+ ERR_FAIL_COND_MSG(result, "The request was denied. Remember that enabling fullscreen is only possible from an input callback for the HTML5 platform.");
} break;
case WINDOW_MODE_MAXIMIZED:
case WINDOW_MODE_MINIMIZED:
@@ -1180,14 +984,19 @@ bool DisplayServerJavaScript::can_any_window_draw() const {
}
void DisplayServerJavaScript::process_events() {
- if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS)
+ if (godot_js_display_gamepad_sample() == OK) {
process_joypads();
+ }
}
int DisplayServerJavaScript::get_current_video_driver() const {
return 1;
}
+bool DisplayServerJavaScript::get_swap_cancel_ok() {
+ return swap_cancel_ok;
+}
+
void DisplayServerJavaScript::swap_buffers() {
//emscripten_webgl_commit_frame();
}
diff --git a/platform/javascript/display_server_javascript.h b/platform/javascript/display_server_javascript.h
index 9860ecdf98..47e25ab2a0 100644
--- a/platform/javascript/display_server_javascript.h
+++ b/platform/javascript/display_server_javascript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,22 +37,38 @@
#include <emscripten/html5.h>
class DisplayServerJavaScript : public DisplayServer {
- //int video_driver_index;
-
- Vector2 windowed_size;
-
+private:
+ WindowMode window_mode = WINDOW_MODE_WINDOWED;
ObjectID window_attached_instance_id = {};
+ Callable window_event_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+ Callable drop_files_callback;
+
+ String clipboard;
Ref<InputEventKey> deferred_key_event;
- CursorShape cursor_shape = CURSOR_ARROW;
- String cursors[CURSOR_MAX];
- Map<CursorShape, Vector<Variant>> cursors_cache;
Point2 touches[32];
+ char canvas_id[256] = { 0 };
+ bool cursor_inside_canvas = true;
+ CursorShape cursor_shape = CURSOR_ARROW;
Point2i last_click_pos = Point2(-100, -100); // TODO check this again.
double last_click_ms = 0;
int last_click_button_index = -1;
+ bool swap_cancel_ok = false;
+
+ // utilities
+ static Point2 compute_position_in_canvas(int p_x, int p_y);
+ static void focus_canvas();
+ static bool is_canvas_focused();
+ template <typename T>
+ static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event);
+ static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event);
+ static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape);
+
+ // events
static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
@@ -67,7 +83,7 @@ class DisplayServerJavaScript : public DisplayServer {
static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
- static EM_BOOL gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data);
+ static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid);
void process_joypads();
static Vector<String> get_rendering_drivers_func();
@@ -75,109 +91,109 @@ class DisplayServerJavaScript : public DisplayServer {
static void _dispatch_input_event(const Ref<InputEvent> &p_event);
+ static void request_quit_callback();
+ static void update_clipboard_callback(const char *p_text);
+ static void send_window_event_callback(int p_notification);
+ static void drop_files_js_callback(char **p_filev, int p_filec);
+
protected:
- virtual int get_current_video_driver() const;
+ int get_current_video_driver() const;
public:
// Override return type to make writing static callbacks less tedious.
static DisplayServerJavaScript *get_singleton();
- WindowMode window_mode = WINDOW_MODE_WINDOWED;
-
- String clipboard;
- String canvas_id;
-
- Callable window_event_callback;
- Callable input_event_callback;
- Callable input_text_callback;
- Callable drop_files_callback;
+ // utilities
+ bool check_size_force_redraw();
// from DisplayServer
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
- virtual bool has_feature(Feature p_feature) const;
- virtual String get_name() const;
+ void alert(const String &p_alert, const String &p_title = "ALERT!") override;
+ bool has_feature(Feature p_feature) const override;
+ String get_name() const override;
// cursor
- virtual void cursor_set_shape(CursorShape p_shape);
- virtual CursorShape cursor_get_shape() const;
- virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
+ void cursor_set_shape(CursorShape p_shape) override;
+ CursorShape cursor_get_shape() const override;
+ void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
// mouse
- virtual void mouse_set_mode(MouseMode p_mode);
- virtual MouseMode mouse_get_mode() const;
+ void mouse_set_mode(MouseMode p_mode) override;
+ MouseMode mouse_get_mode() const override;
// touch
- virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
// clipboard
- virtual void clipboard_set(const String &p_text);
- virtual String clipboard_get() const;
+ void clipboard_set(const String &p_text) override;
+ String clipboard_get() const override;
// screen
- virtual int get_screen_count() const;
- virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
- virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
- virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
- virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ int get_screen_count() const override;
+ Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
// windows
- virtual Vector<DisplayServer::WindowID> get_window_list() const;
- virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
+ Vector<DisplayServer::WindowID> get_window_list() const override;
+ WindowID get_window_at_screen_position(const Point2i &p_position) const override;
- virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID);
- virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const;
+ void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
+ ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
- virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
- virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
+ void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const;
- virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID);
+ int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
+ void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const;
- virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID);
+ Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override;
+ void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual void window_set_transient(WindowID p_window, WindowID p_parent);
+ void window_set_transient(WindowID p_window, WindowID p_parent) override;
- virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
- virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const;
+ void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
- virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const;
+ void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
- virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const;
- virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const; // FIXME: Find clearer name for this.
+ void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+ Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID);
- virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const;
+ void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+ bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
- virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const;
+ void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
+ bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID);
- virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+ void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
+ void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
- virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+ bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual bool can_any_window_draw() const;
+ bool can_any_window_draw() const override;
// events
- virtual void process_events();
+ void process_events() override;
// icon
- virtual void set_icon(const Ref<Image> &p_icon);
+ void set_icon(const Ref<Image> &p_icon) override;
// others
- virtual void swap_buffers();
+ bool get_swap_cancel_ok() override;
+ void swap_buffers() override;
static void register_javascript_driver();
DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
diff --git a/platform/javascript/dom_keys.inc b/platform/javascript/dom_keys.inc
index 882e943471..7902efafe0 100644
--- a/platform/javascript/dom_keys.inc
+++ b/platform/javascript/dom_keys.inc
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,400 +30,203 @@
#include "core/os/keyboard.h"
-// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value
-#define DOM_VK_CANCEL 0x03
-#define DOM_VK_HELP 0x06
-#define DOM_VK_BACK_SPACE 0x08
-#define DOM_VK_TAB 0x09
-#define DOM_VK_CLEAR 0x0C
-#define DOM_VK_RETURN 0x0D
-#define DOM_VK_ENTER 0x0E // "Reserved, but not used."
-#define DOM_VK_SHIFT 0x10
-#define DOM_VK_CONTROL 0x11
-#define DOM_VK_ALT 0x12
-#define DOM_VK_PAUSE 0x13
-#define DOM_VK_CAPS_LOCK 0x14
-#define DOM_VK_KANA 0x15
-#define DOM_VK_HANGUL 0x15
-#define DOM_VK_EISU 0x16
-#define DOM_VK_JUNJA 0x17
-#define DOM_VK_FINAL 0x18
-#define DOM_VK_HANJA 0x19
-#define DOM_VK_KANJI 0x19
-#define DOM_VK_ESCAPE 0x1B
-#define DOM_VK_CONVERT 0x1C
-#define DOM_VK_NONCONVERT 0x1D
-#define DOM_VK_ACCEPT 0x1E
-#define DOM_VK_MODECHANGE 0x1F
-#define DOM_VK_SPACE 0x20
-#define DOM_VK_PAGE_UP 0x21
-#define DOM_VK_PAGE_DOWN 0x22
-#define DOM_VK_END 0x23
-#define DOM_VK_HOME 0x24
-#define DOM_VK_LEFT 0x25
-#define DOM_VK_UP 0x26
-#define DOM_VK_RIGHT 0x27
-#define DOM_VK_DOWN 0x28
-#define DOM_VK_SELECT 0x29
-#define DOM_VK_PRINT 0x2A
-#define DOM_VK_EXECUTE 0x2B
-#define DOM_VK_PRINTSCREEN 0x2C
-#define DOM_VK_INSERT 0x2D
-#define DOM_VK_DELETE 0x2E
-#define DOM_VK_0 0x30
-#define DOM_VK_1 0x31
-#define DOM_VK_2 0x32
-#define DOM_VK_3 0x33
-#define DOM_VK_4 0x34
-#define DOM_VK_5 0x35
-#define DOM_VK_6 0x36
-#define DOM_VK_7 0x37
-#define DOM_VK_8 0x38
-#define DOM_VK_9 0x39
-#define DOM_VK_COLON 0x3A
-#define DOM_VK_SEMICOLON 0x3B
-#define DOM_VK_LESS_THAN 0x3C
-#define DOM_VK_EQUALS 0x3D
-#define DOM_VK_GREATER_THAN 0x3E
-#define DOM_VK_QUESTION_MARK 0x3F
-#define DOM_VK_AT 0x40
-#define DOM_VK_A 0x41
-#define DOM_VK_B 0x42
-#define DOM_VK_C 0x43
-#define DOM_VK_D 0x44
-#define DOM_VK_E 0x45
-#define DOM_VK_F 0x46
-#define DOM_VK_G 0x47
-#define DOM_VK_H 0x48
-#define DOM_VK_I 0x49
-#define DOM_VK_J 0x4A
-#define DOM_VK_K 0x4B
-#define DOM_VK_L 0x4C
-#define DOM_VK_M 0x4D
-#define DOM_VK_N 0x4E
-#define DOM_VK_O 0x4F
-#define DOM_VK_P 0x50
-#define DOM_VK_Q 0x51
-#define DOM_VK_R 0x52
-#define DOM_VK_S 0x53
-#define DOM_VK_T 0x54
-#define DOM_VK_U 0x55
-#define DOM_VK_V 0x56
-#define DOM_VK_W 0x57
-#define DOM_VK_X 0x58
-#define DOM_VK_Y 0x59
-#define DOM_VK_Z 0x5A
-#define DOM_VK_WIN 0x5B
-#define DOM_VK_CONTEXT_MENU 0x5D
-#define DOM_VK_SLEEP 0x5F
-#define DOM_VK_NUMPAD0 0x60
-#define DOM_VK_NUMPAD1 0x61
-#define DOM_VK_NUMPAD2 0x62
-#define DOM_VK_NUMPAD3 0x63
-#define DOM_VK_NUMPAD4 0x64
-#define DOM_VK_NUMPAD5 0x65
-#define DOM_VK_NUMPAD6 0x66
-#define DOM_VK_NUMPAD7 0x67
-#define DOM_VK_NUMPAD8 0x68
-#define DOM_VK_NUMPAD9 0x69
-#define DOM_VK_MULTIPLY 0x6A
-#define DOM_VK_ADD 0x6B
-#define DOM_VK_SEPARATOR 0x6C
-#define DOM_VK_SUBTRACT 0x6D
-#define DOM_VK_DECIMAL 0x6E
-#define DOM_VK_DIVIDE 0x6F
-#define DOM_VK_F1 0x70
-#define DOM_VK_F2 0x71
-#define DOM_VK_F3 0x72
-#define DOM_VK_F4 0x73
-#define DOM_VK_F5 0x74
-#define DOM_VK_F6 0x75
-#define DOM_VK_F7 0x76
-#define DOM_VK_F8 0x77
-#define DOM_VK_F9 0x78
-#define DOM_VK_F10 0x79
-#define DOM_VK_F11 0x7A
-#define DOM_VK_F12 0x7B
-#define DOM_VK_F13 0x7C
-#define DOM_VK_F14 0x7D
-#define DOM_VK_F15 0x7E
-#define DOM_VK_F16 0x7F
-#define DOM_VK_F17 0x80
-#define DOM_VK_F18 0x81
-#define DOM_VK_F19 0x82
-#define DOM_VK_F20 0x83
-#define DOM_VK_F21 0x84
-#define DOM_VK_F22 0x85
-#define DOM_VK_F23 0x86
-#define DOM_VK_F24 0x87
-#define DOM_VK_NUM_LOCK 0x90
-#define DOM_VK_SCROLL_LOCK 0x91
-#define DOM_VK_WIN_OEM_FJ_JISHO 0x92
-#define DOM_VK_WIN_OEM_FJ_MASSHOU 0x93
-#define DOM_VK_WIN_OEM_FJ_TOUROKU 0x94
-#define DOM_VK_WIN_OEM_FJ_LOYA 0x95
-#define DOM_VK_WIN_OEM_FJ_ROYA 0x96
-#define DOM_VK_CIRCUMFLEX 0xA0
-#define DOM_VK_EXCLAMATION 0xA1
-#define DOM_VK_DOUBLE_QUOTE 0xA2
-#define DOM_VK_HASH 0xA3
-#define DOM_VK_DOLLAR 0xA4
-#define DOM_VK_PERCENT 0xA5
-#define DOM_VK_AMPERSAND 0xA6
-#define DOM_VK_UNDERSCORE 0xA7
-#define DOM_VK_OPEN_PAREN 0xA8
-#define DOM_VK_CLOSE_PAREN 0xA9
-#define DOM_VK_ASTERISK 0xAA
-#define DOM_VK_PLUS 0xAB
-#define DOM_VK_PIPE 0xAC
-#define DOM_VK_HYPHEN_MINUS 0xAD
-#define DOM_VK_OPEN_CURLY_BRACKET 0xAE
-#define DOM_VK_CLOSE_CURLY_BRACKET 0xAF
-#define DOM_VK_TILDE 0xB0
-#define DOM_VK_VOLUME_MUTE 0xB5
-#define DOM_VK_VOLUME_DOWN 0xB6
-#define DOM_VK_VOLUME_UP 0xB7
-#define DOM_VK_COMMA 0xBC
-#define DOM_VK_PERIOD 0xBE
-#define DOM_VK_SLASH 0xBF
-#define DOM_VK_BACK_QUOTE 0xC0
-#define DOM_VK_OPEN_BRACKET 0xDB
-#define DOM_VK_BACK_SLASH 0xDC
-#define DOM_VK_CLOSE_BRACKET 0xDD
-#define DOM_VK_QUOTE 0xDE
-#define DOM_VK_META 0xE0
-#define DOM_VK_ALTGR 0xE1
-#define DOM_VK_WIN_ICO_HELP 0xE3
-#define DOM_VK_WIN_ICO_00 0xE4
-#define DOM_VK_WIN_ICO_CLEAR 0xE6
-#define DOM_VK_WIN_OEM_RESET 0xE9
-#define DOM_VK_WIN_OEM_JUMP 0xEA
-#define DOM_VK_WIN_OEM_PA1 0xEB
-#define DOM_VK_WIN_OEM_PA2 0xEC
-#define DOM_VK_WIN_OEM_PA3 0xED
-#define DOM_VK_WIN_OEM_WSCTRL 0xEE
-#define DOM_VK_WIN_OEM_CUSEL 0xEF
-#define DOM_VK_WIN_OEM_ATTN 0xF0
-#define DOM_VK_WIN_OEM_FINISH 0xF1
-#define DOM_VK_WIN_OEM_COPY 0xF2
-#define DOM_VK_WIN_OEM_AUTO 0xF3
-#define DOM_VK_WIN_OEM_ENLW 0xF4
-#define DOM_VK_WIN_OEM_BACKTAB 0xF5
-#define DOM_VK_ATTN 0xF6
-#define DOM_VK_CRSEL 0xF7
-#define DOM_VK_EXSEL 0xF8
-#define DOM_VK_EREOF 0xF9
-#define DOM_VK_PLAY 0xFA
-#define DOM_VK_ZOOM 0xFB
-#define DOM_VK_PA1 0xFD
-#define DOM_VK_WIN_OEM_CLEAR 0xFE
-
-int dom2godot_keycode(int dom_keycode) {
- if (DOM_VK_0 <= dom_keycode && dom_keycode <= DOM_VK_Z) {
- // ASCII intersection
- return dom_keycode;
- }
-
- if (DOM_VK_NUMPAD0 <= dom_keycode && dom_keycode <= DOM_VK_NUMPAD9) {
- // Numpad numbers
- return KEY_KP_0 + (dom_keycode - DOM_VK_NUMPAD0);
+// See https://w3c.github.io/uievents-code/#code-value-tables
+int dom_code2godot_scancode(EM_UTF8 const p_code[32], EM_UTF8 const p_key[32], bool p_physical) {
+#define DOM2GODOT(p_str, p_godot_code) \
+ if (memcmp((const void *)p_str, (void *)p_code, strlen(p_str) + 1) == 0) { \
+ return KEY_##p_godot_code; \
}
- if (DOM_VK_F1 <= dom_keycode && dom_keycode <= DOM_VK_F16) {
- // F1-F16
- return KEY_F1 + (dom_keycode - DOM_VK_F1);
+ // Numpad section.
+ DOM2GODOT("NumLock", NUMLOCK);
+ DOM2GODOT("Numpad0", KP_0);
+ DOM2GODOT("Numpad1", KP_1);
+ DOM2GODOT("Numpad2", KP_2);
+ DOM2GODOT("Numpad3", KP_3);
+ DOM2GODOT("Numpad4", KP_4);
+ DOM2GODOT("Numpad5", KP_5);
+ DOM2GODOT("Numpad6", KP_6);
+ DOM2GODOT("Numpad7", KP_7);
+ DOM2GODOT("Numpad8", KP_8);
+ DOM2GODOT("Numpad9", KP_9);
+ DOM2GODOT("NumpadAdd", KP_ADD);
+ DOM2GODOT("NumpadBackspace", BACKSPACE);
+ DOM2GODOT("NumpadClear", CLEAR);
+ DOM2GODOT("NumpadClearEntry", CLEAR);
+ //DOM2GODOT("NumpadComma", UNKNOWN);
+ DOM2GODOT("NumpadDecimal", KP_PERIOD);
+ DOM2GODOT("NumpadDivide", KP_DIVIDE);
+ DOM2GODOT("NumpadEnter", KP_ENTER);
+ DOM2GODOT("NumpadEqual", EQUAL);
+ //DOM2GODOT("NumpadHash", UNKNOWN);
+ //DOM2GODOT("NumpadMemoryAdd", UNKNOWN);
+ //DOM2GODOT("NumpadMemoryClear", UNKNOWN);
+ //DOM2GODOT("NumpadMemoryRecall", UNKNOWN);
+ //DOM2GODOT("NumpadMemoryStore", UNKNOWN);
+ //DOM2GODOT("NumpadMemorySubtract", UNKNOWN);
+ DOM2GODOT("NumpadMultiply", KP_MULTIPLY);
+ DOM2GODOT("NumpadParenLeft", PARENLEFT);
+ DOM2GODOT("NumpadParenRight", PARENRIGHT);
+ DOM2GODOT("NumpadStar", KP_MULTIPLY); // or ASTERISK ?
+ DOM2GODOT("NumpadSubtract", KP_SUBTRACT);
+
+ // Printable ASCII.
+ if (!p_physical) {
+ uint8_t b0 = (uint8_t)p_key[0];
+ uint8_t b1 = (uint8_t)p_key[1];
+ uint8_t b2 = (uint8_t)p_key[2];
+ if (b1 == 0 && b0 > 0x1F && b0 < 0x7F) { // ASCII.
+ if (b0 > 0x60 && b0 < 0x7B) { // Lowercase ASCII.
+ b0 -= 32;
+ }
+ return b0;
+ }
+
+#define _U_2BYTES_MASK 0xE0
+#define _U_2BYTES 0xC0
+ // Latin-1 codes.
+ if (b2 == 0 && (b0 & _U_2BYTES_MASK) == _U_2BYTES) { // 2-bytes utf8, only known latin.
+ uint32_t key = ((b0 & ~_U_2BYTES_MASK) << 6) | (b1 & 0x3F);
+ if (key >= 0xA0 && key <= 0xDF) {
+ return key;
+ }
+ if (key >= 0xE0 && key <= 0xFF) { // Lowercase known latin.
+ key -= 0x20;
+ return key;
+ }
+ }
+#undef _U_2BYTES_MASK
+#undef _U_2BYTES
}
- switch (dom_keycode) {
- //case DOM_VK_CANCEL: return KEY_UNKNOWN;
- case DOM_VK_HELP:
- return KEY_HELP;
- case DOM_VK_BACK_SPACE:
- return KEY_BACKSPACE;
- case DOM_VK_TAB:
- return KEY_TAB;
-
- case DOM_VK_CLEAR:
- case DOM_VK_WIN_OEM_CLEAR: // OEM duplicate
- return KEY_CLEAR;
-
- case DOM_VK_RETURN:
- case DOM_VK_ENTER: // unused according to MDN
- return KEY_ENTER;
-
- case DOM_VK_SHIFT:
- return KEY_SHIFT;
- case DOM_VK_CONTROL:
- return KEY_CONTROL;
-
- case DOM_VK_ALT:
- case DOM_VK_ALTGR:
- return KEY_ALT;
-
- case DOM_VK_PAUSE:
- return KEY_PAUSE;
- case DOM_VK_CAPS_LOCK:
- return KEY_CAPSLOCK;
-
- /*
- case DOM_VK_KANA: return KEY_UNKNOWN;
- case DOM_VK_HANGUL: return KEY_UNKNOWN;
- case DOM_VK_EISU: return KEY_UNKNOWN;
- case DOM_VK_JUNJA: return KEY_UNKNOWN;
- case DOM_VK_FINAL: return KEY_UNKNOWN;
- case DOM_VK_HANJA: return KEY_UNKNOWN;
- case DOM_VK_KANJI: return KEY_UNKNOWN;
- */
-
- case DOM_VK_ESCAPE:
- return KEY_ESCAPE;
- /*
- case DOM_VK_CONVERT: return KEY_UNKNOWN;
- case DOM_VK_NONCONVERT: return KEY_UNKNOWN;
- case DOM_VK_ACCEPT: return KEY_UNKNOWN;
- case DOM_VK_MODECHANGE: return KEY_UNKNOWN;
- */
-
- case DOM_VK_SPACE:
- return KEY_SPACE;
- case DOM_VK_PAGE_UP:
- return KEY_PAGEUP;
- case DOM_VK_PAGE_DOWN:
- return KEY_PAGEDOWN;
- case DOM_VK_END:
- return KEY_END;
- case DOM_VK_HOME:
- return KEY_HOME;
- case DOM_VK_LEFT:
- return KEY_LEFT;
- case DOM_VK_UP:
- return KEY_UP;
- case DOM_VK_RIGHT:
- return KEY_RIGHT;
- case DOM_VK_DOWN:
- return KEY_DOWN;
-
- //case DOM_VK_SELECT: return KEY_UNKNOWN;
-
- case DOM_VK_PRINTSCREEN:
- case DOM_VK_PRINT:
- return KEY_PRINT;
-
- //case DOM_VK_EXECUTE: return KEY_UNKNOWN;
- case DOM_VK_INSERT:
- return KEY_INSERT;
- case DOM_VK_DELETE:
- return KEY_DELETE;
-
- case DOM_VK_META:
- case DOM_VK_WIN:
- return KEY_META;
-
- case DOM_VK_CONTEXT_MENU:
- return KEY_MENU;
- case DOM_VK_SLEEP:
- return KEY_STANDBY;
-
- // Numpad keys
- case DOM_VK_MULTIPLY:
- return KEY_KP_MULTIPLY;
- case DOM_VK_ADD:
- return KEY_KP_ADD;
- case DOM_VK_SEPARATOR:
- return KEY_KP_PERIOD; // Good enough?
- case DOM_VK_SUBTRACT:
- return KEY_KP_SUBTRACT;
- case DOM_VK_DECIMAL:
- return KEY_KP_PERIOD;
- case DOM_VK_DIVIDE:
- return KEY_KP_DIVIDE;
-
- /*
- case DOM_VK_F17: return KEY_UNKNOWN;
- case DOM_VK_F18: return KEY_UNKNOWN;
- case DOM_VK_F19: return KEY_UNKNOWN;
- case DOM_VK_F20: return KEY_UNKNOWN;
- case DOM_VK_F21: return KEY_UNKNOWN;
- case DOM_VK_F22: return KEY_UNKNOWN;
- case DOM_VK_F23: return KEY_UNKNOWN;
- case DOM_VK_F24: return KEY_UNKNOWN;
- */
-
- case DOM_VK_NUM_LOCK:
- return KEY_NUMLOCK;
- case DOM_VK_SCROLL_LOCK:
- return KEY_SCROLLLOCK;
-
- /*
- case DOM_VK_WIN_OEM_FJ_JISHO: return KEY_UNKNOWN;
- case DOM_VK_WIN_OEM_FJ_MASSHOU: return KEY_UNKNOWN;
- case DOM_VK_WIN_OEM_FJ_TOUROKU: return KEY_UNKNOWN;
- case DOM_VK_WIN_OEM_FJ_LOYA: return KEY_UNKNOWN;
- case DOM_VK_WIN_OEM_FJ_ROYA: return KEY_UNKNOWN;
- */
-
- case DOM_VK_CIRCUMFLEX:
- return KEY_ASCIICIRCUM;
- case DOM_VK_EXCLAMATION:
- return KEY_EXCLAM;
- case DOM_VK_DOUBLE_QUOTE:
- return KEY_QUOTEDBL;
- case DOM_VK_HASH:
- return KEY_NUMBERSIGN;
- case DOM_VK_DOLLAR:
- return KEY_DOLLAR;
- case DOM_VK_PERCENT:
- return KEY_PERCENT;
- case DOM_VK_AMPERSAND:
- return KEY_AMPERSAND;
- case DOM_VK_UNDERSCORE:
- return KEY_UNDERSCORE;
- case DOM_VK_OPEN_PAREN:
- return KEY_PARENLEFT;
- case DOM_VK_CLOSE_PAREN:
- return KEY_PARENRIGHT;
- case DOM_VK_ASTERISK:
- return KEY_ASTERISK;
- case DOM_VK_PLUS:
- return KEY_PLUS;
- case DOM_VK_PIPE:
- return KEY_BAR;
- case DOM_VK_HYPHEN_MINUS:
- return KEY_MINUS;
- case DOM_VK_OPEN_CURLY_BRACKET:
- return KEY_BRACELEFT;
- case DOM_VK_CLOSE_CURLY_BRACKET:
- return KEY_BRACERIGHT;
- case DOM_VK_TILDE:
- return KEY_ASCIITILDE;
-
- case DOM_VK_VOLUME_MUTE:
- return KEY_VOLUMEMUTE;
- case DOM_VK_VOLUME_DOWN:
- return KEY_VOLUMEDOWN;
- case DOM_VK_VOLUME_UP:
- return KEY_VOLUMEUP;
-
- case DOM_VK_COMMA:
- return KEY_COMMA;
- case DOM_VK_PERIOD:
- return KEY_PERIOD;
- case DOM_VK_SLASH:
- return KEY_SLASH;
- case DOM_VK_BACK_QUOTE:
- return KEY_QUOTELEFT;
- case DOM_VK_OPEN_BRACKET:
- return KEY_BRACKETLEFT;
- case DOM_VK_BACK_SLASH:
- return KEY_BACKSLASH;
- case DOM_VK_CLOSE_BRACKET:
- return KEY_BRACKETRIGHT;
- case DOM_VK_QUOTE:
- return KEY_APOSTROPHE;
-
- // The rest is OEM/unusual.
-
- default:
- return KEY_UNKNOWN;
- };
+ // Alphanumeric section.
+ DOM2GODOT("Backquote", QUOTELEFT);
+ DOM2GODOT("Backslash", BACKSLASH);
+ DOM2GODOT("BracketLeft", BRACKETLEFT);
+ DOM2GODOT("BracketRight", BRACKETRIGHT);
+ DOM2GODOT("Comma", COMMA);
+ DOM2GODOT("Digit0", 0);
+ DOM2GODOT("Digit1", 1);
+ DOM2GODOT("Digit2", 2);
+ DOM2GODOT("Digit3", 3);
+ DOM2GODOT("Digit4", 4);
+ DOM2GODOT("Digit5", 5);
+ DOM2GODOT("Digit6", 6);
+ DOM2GODOT("Digit7", 7);
+ DOM2GODOT("Digit8", 8);
+ DOM2GODOT("Digit9", 9);
+ DOM2GODOT("Equal", EQUAL);
+ DOM2GODOT("IntlBackslash", BACKSLASH);
+ //DOM2GODOT("IntlRo", UNKNOWN);
+ DOM2GODOT("IntlYen", YEN);
+
+ DOM2GODOT("KeyA", A);
+ DOM2GODOT("KeyB", B);
+ DOM2GODOT("KeyC", C);
+ DOM2GODOT("KeyD", D);
+ DOM2GODOT("KeyE", E);
+ DOM2GODOT("KeyF", F);
+ DOM2GODOT("KeyG", G);
+ DOM2GODOT("KeyH", H);
+ DOM2GODOT("KeyI", I);
+ DOM2GODOT("KeyJ", J);
+ DOM2GODOT("KeyK", K);
+ DOM2GODOT("KeyL", L);
+ DOM2GODOT("KeyM", M);
+ DOM2GODOT("KeyN", N);
+ DOM2GODOT("KeyO", O);
+ DOM2GODOT("KeyP", P);
+ DOM2GODOT("KeyQ", Q);
+ DOM2GODOT("KeyR", R);
+ DOM2GODOT("KeyS", S);
+ DOM2GODOT("KeyT", T);
+ DOM2GODOT("KeyU", U);
+ DOM2GODOT("KeyV", V);
+ DOM2GODOT("KeyW", W);
+ DOM2GODOT("KeyX", X);
+ DOM2GODOT("KeyY", Y);
+ DOM2GODOT("KeyZ", Z);
+
+ DOM2GODOT("Minus", MINUS);
+ DOM2GODOT("Period", PERIOD);
+ DOM2GODOT("Quote", APOSTROPHE);
+ DOM2GODOT("Semicolon", SEMICOLON);
+ DOM2GODOT("Slash", SLASH);
+
+ // Functional keys in the Alphanumeric section.
+ DOM2GODOT("AltLeft", ALT);
+ DOM2GODOT("AltRight", ALT);
+ DOM2GODOT("Backspace", BACKSPACE);
+ DOM2GODOT("CapsLock", CAPSLOCK);
+ DOM2GODOT("ContextMenu", MENU);
+ DOM2GODOT("ControlLeft", CONTROL);
+ DOM2GODOT("ControlRight", CONTROL);
+ DOM2GODOT("Enter", ENTER);
+ DOM2GODOT("MetaLeft", SUPER_L);
+ DOM2GODOT("MetaRight", SUPER_R);
+ DOM2GODOT("ShiftLeft", SHIFT);
+ DOM2GODOT("ShiftRight", SHIFT);
+ DOM2GODOT("Space", SPACE);
+ DOM2GODOT("Tab", TAB);
+
+ // ControlPad section.
+ DOM2GODOT("Delete", DELETE);
+ DOM2GODOT("End", END);
+ DOM2GODOT("Help", HELP);
+ DOM2GODOT("Home", HOME);
+ DOM2GODOT("Insert", INSERT);
+ DOM2GODOT("PageDown", PAGEDOWN);
+ DOM2GODOT("PageUp", PAGEUP);
+
+ // ArrowPad section.
+ DOM2GODOT("ArrowDown", DOWN);
+ DOM2GODOT("ArrowLeft", LEFT);
+ DOM2GODOT("ArrowRight", RIGHT);
+ DOM2GODOT("ArrowUp", UP);
+
+ // Function section.
+ DOM2GODOT("Escape", ESCAPE);
+ DOM2GODOT("F1", F1);
+ DOM2GODOT("F2", F2);
+ DOM2GODOT("F3", F3);
+ DOM2GODOT("F4", F4);
+ DOM2GODOT("F5", F5);
+ DOM2GODOT("F6", F6);
+ DOM2GODOT("F7", F7);
+ DOM2GODOT("F8", F8);
+ DOM2GODOT("F9", F9);
+ DOM2GODOT("F10", F10);
+ DOM2GODOT("F11", F11);
+ DOM2GODOT("F12", F12);
+ //DOM2GODOT("Fn", UNKNOWN); // never actually fired, but included in the standard draft.
+ //DOM2GODOT("FnLock", UNKNOWN);
+ DOM2GODOT("PrintScreen", PRINT);
+ DOM2GODOT("ScrollLock", SCROLLLOCK);
+ DOM2GODOT("Pause", PAUSE);
+
+ // Media keys section.
+ DOM2GODOT("BrowserBack", BACK);
+ DOM2GODOT("BrowserFavorites", FAVORITES);
+ DOM2GODOT("BrowserForward", FORWARD);
+ DOM2GODOT("BrowserHome", OPENURL);
+ DOM2GODOT("BrowserRefresh", REFRESH);
+ DOM2GODOT("BrowserSearch", SEARCH);
+ DOM2GODOT("BrowserStop", STOP);
+ //DOM2GODOT("Eject", UNKNOWN);
+ DOM2GODOT("LaunchApp1", LAUNCH0);
+ DOM2GODOT("LaunchApp2", LAUNCH1);
+ DOM2GODOT("LaunchMail", LAUNCHMAIL);
+ DOM2GODOT("MediaPlayPause", MEDIAPLAY);
+ DOM2GODOT("MediaSelect", LAUNCHMEDIA);
+ DOM2GODOT("MediaStop", MEDIASTOP);
+ DOM2GODOT("MediaTrackNext", MEDIANEXT);
+ DOM2GODOT("MediaTrackPrevious", MEDIAPREVIOUS);
+ //DOM2GODOT("Power", UNKNOWN);
+ //DOM2GODOT("Sleep", UNKNOWN);
+ DOM2GODOT("AudioVolumeDown", VOLUMEDOWN);
+ DOM2GODOT("AudioVolumeMute", VOLUMEMUTE);
+ DOM2GODOT("AudioVolumeUp", VOLUMEUP);
+ //DOM2GODOT("WakeUp", UNKNOWN);
+ return KEY_UNKNOWN;
+#undef DOM2GODOT
}
diff --git a/platform/javascript/emscripten_helpers.py b/platform/javascript/emscripten_helpers.py
index a55c9d3f48..d08555916b 100644
--- a/platform/javascript/emscripten_helpers.py
+++ b/platform/javascript/emscripten_helpers.py
@@ -1,28 +1,11 @@
import os
-
-def parse_config():
- em_config_file = os.getenv("EM_CONFIG") or os.path.expanduser("~/.emscripten")
- if not os.path.exists(em_config_file):
- raise RuntimeError("Emscripten configuration file '%s' does not exist" % em_config_file)
-
- normalized = {}
- em_config = {}
- with open(em_config_file) as f:
- try:
- # Emscripten configuration file is a Python file with simple assignments.
- exec(f.read(), em_config)
- except StandardError as e:
- raise RuntimeError("Emscripten configuration file '%s' is invalid:\n%s" % (em_config_file, e))
- normalized["EMCC_ROOT"] = em_config.get("EMSCRIPTEN_ROOT")
- normalized["NODE_JS"] = em_config.get("NODE_JS")
- normalized["CLOSURE_BIN"] = os.path.join(normalized["EMCC_ROOT"], "node_modules", ".bin", "google-closure-compiler")
- return normalized
+from SCons.Util import WhereIs
def run_closure_compiler(target, source, env, for_signature):
- cfg = parse_config()
- cmd = [cfg["NODE_JS"], cfg["CLOSURE_BIN"]]
+ closure_bin = os.path.join(os.path.dirname(WhereIs("emcc")), "node_modules", ".bin", "google-closure-compiler")
+ cmd = [WhereIs("node"), closure_bin]
cmd.extend(["--compilation_level", "ADVANCED_OPTIMIZATIONS"])
for f in env["JSEXTERNS"]:
cmd.extend(["--externs", f.get_abspath()])
@@ -32,7 +15,28 @@ def run_closure_compiler(target, source, env, for_signature):
return " ".join(cmd)
+def get_build_version(env):
+ import version
+
+ name = "custom_build"
+ if os.getenv("BUILD_NAME") != None:
+ name = os.getenv("BUILD_NAME")
+ return "%d.%d.%d.%s.%s" % (version.major, version.minor, version.patch, version.status, name)
+
+
def create_engine_file(env, target, source, externs):
if env["use_closure_compiler"]:
return env.BuildJS(target, source, JSEXTERNS=externs)
return env.Textfile(target, [env.File(s) for s in source])
+
+
+def add_js_libraries(env, libraries):
+ env.Append(JS_LIBS=env.File(libraries))
+
+
+def add_js_pre(env, js_pre):
+ env.Append(JS_PRE=env.File(js_pre))
+
+
+def add_js_externs(env, externs):
+ env.Append(JS_EXTERNS=env.File(externs))
diff --git a/platform/javascript/engine/engine.js b/platform/javascript/engine/engine.js
deleted file mode 100644
index d709422abb..0000000000
--- a/platform/javascript/engine/engine.js
+++ /dev/null
@@ -1,245 +0,0 @@
-Function('return this')()['Engine'] = (function() {
- var preloader = new Preloader();
-
- var wasmExt = '.wasm';
- var unloadAfterInit = true;
- var loadPath = '';
- var loadPromise = null;
- var initPromise = null;
- var stderr = null;
- var stdout = null;
- var progressFunc = null;
-
- function load(basePath) {
- if (loadPromise == null) {
- loadPath = basePath;
- loadPromise = preloader.loadPromise(basePath + wasmExt);
- preloader.setProgressFunc(progressFunc);
- requestAnimationFrame(preloader.animateProgress);
- }
- return loadPromise;
- };
-
- function unload() {
- loadPromise = null;
- };
-
- /** @constructor */
- function Engine() {
- this.canvas = null;
- this.executableName = '';
- this.rtenv = null;
- this.customLocale = null;
- this.resizeCanvasOnStart = false;
- this.onExecute = null;
- this.onExit = null;
- };
-
- Engine.prototype.init = /** @param {string=} basePath */ function(basePath) {
- if (initPromise) {
- return initPromise;
- }
- if (loadPromise == null) {
- if (!basePath) {
- initPromise = Promise.reject(new Error("A base path must be provided when calling `init` and the engine is not loaded."));
- return initPromise;
- }
- load(basePath);
- }
- var config = {};
- if (typeof stdout === 'function')
- config.print = stdout;
- if (typeof stderr === 'function')
- config.printErr = stderr;
- var me = this;
- initPromise = new Promise(function(resolve, reject) {
- config['locateFile'] = Utils.createLocateRewrite(loadPath);
- config['instantiateWasm'] = Utils.createInstantiatePromise(loadPromise);
- Godot(config).then(function(module) {
- me.rtenv = module;
- if (unloadAfterInit) {
- unload();
- }
- resolve();
- config = null;
- });
- });
- return initPromise;
- };
-
- /** @type {function(string, string):Object} */
- Engine.prototype.preloadFile = function(file, path) {
- return preloader.preload(file, path);
- };
-
- /** @type {function(...string):Object} */
- Engine.prototype.start = function() {
- // Start from arguments.
- var args = [];
- for (var i = 0; i < arguments.length; i++) {
- args.push(arguments[i]);
- }
- var me = this;
- return me.init().then(function() {
- if (!me.rtenv) {
- return Promise.reject(new Error('The engine must be initialized before it can be started'));
- }
-
- if (!(me.canvas instanceof HTMLCanvasElement)) {
- me.canvas = Utils.findCanvas();
- }
-
- // Canvas can grab focus on click, or key events won't work.
- if (me.canvas.tabIndex < 0) {
- me.canvas.tabIndex = 0;
- }
-
- // Disable right-click context menu.
- me.canvas.addEventListener('contextmenu', function(ev) {
- ev.preventDefault();
- }, false);
-
- // Until context restoration is implemented warn the user of context loss.
- me.canvas.addEventListener('webglcontextlost', function(ev) {
- alert("WebGL context lost, please reload the page");
- ev.preventDefault();
- }, false);
-
- // Browser locale, or custom one if defined.
- var locale = me.customLocale;
- if (!locale) {
- locale = navigator.languages ? navigator.languages[0] : navigator.language;
- locale = locale.split('.')[0];
- }
- me.rtenv['locale'] = locale;
- me.rtenv['canvas'] = me.canvas;
- me.rtenv['thisProgram'] = me.executableName;
- me.rtenv['resizeCanvasOnStart'] = me.resizeCanvasOnStart;
- me.rtenv['noExitRuntime'] = true;
- me.rtenv['onExecute'] = me.onExecute;
- me.rtenv['onExit'] = function(code) {
- if (me.onExit)
- me.onExit(code);
- me.rtenv = null;
- }
- return new Promise(function(resolve, reject) {
- preloader.preloadedFiles.forEach(function(file) {
- me.rtenv['copyToFS'](file.path, file.buffer);
- });
- preloader.preloadedFiles.length = 0; // Clear memory
- me.rtenv['callMain'](args);
- initPromise = null;
- resolve();
- });
- });
- };
-
- Engine.prototype.startGame = function(execName, mainPack, extraArgs) {
- // Start and init with execName as loadPath if not inited.
- this.executableName = execName;
- var me = this;
- return Promise.all([
- this.init(execName),
- this.preloadFile(mainPack, mainPack)
- ]).then(function() {
- var args = ['--main-pack', mainPack];
- if (extraArgs)
- args = args.concat(extraArgs);
- return me.start.apply(me, args);
- });
- };
-
- Engine.prototype.setWebAssemblyFilenameExtension = function(override) {
- if (String(override).length === 0) {
- throw new Error('Invalid WebAssembly filename extension override');
- }
- wasmExt = String(override);
- };
-
- Engine.prototype.setUnloadAfterInit = function(enabled) {
- unloadAfterInit = enabled;
- };
-
- Engine.prototype.setCanvas = function(canvasElem) {
- this.canvas = canvasElem;
- };
-
- Engine.prototype.setCanvasResizedOnStart = function(enabled) {
- this.resizeCanvasOnStart = enabled;
- };
-
- Engine.prototype.setLocale = function(locale) {
- this.customLocale = locale;
- };
-
- Engine.prototype.setExecutableName = function(newName) {
- this.executableName = newName;
- };
-
- Engine.prototype.setProgressFunc = function(func) {
- progressFunc = func;
- };
-
- Engine.prototype.setStdoutFunc = function(func) {
- var print = function(text) {
- if (arguments.length > 1) {
- text = Array.prototype.slice.call(arguments).join(" ");
- }
- func(text);
- };
- if (this.rtenv)
- this.rtenv.print = print;
- stdout = print;
- };
-
- Engine.prototype.setStderrFunc = function(func) {
- var printErr = function(text) {
- if (arguments.length > 1)
- text = Array.prototype.slice.call(arguments).join(" ");
- func(text);
- };
- if (this.rtenv)
- this.rtenv.printErr = printErr;
- stderr = printErr;
- };
-
- Engine.prototype.setOnExecute = function(onExecute) {
- if (this.rtenv)
- this.rtenv.onExecute = onExecute;
- this.onExecute = onExecute;
- }
-
- Engine.prototype.setOnExit = function(onExit) {
- this.onExit = onExit;
- }
-
- Engine.prototype.copyToFS = function(path, buffer) {
- if (this.rtenv == null) {
- throw new Error("Engine must be inited before copying files");
- }
- this.rtenv['copyToFS'](path, buffer);
- }
-
- // Closure compiler exported engine methods.
- /** @export */
- Engine['isWebGLAvailable'] = Utils.isWebGLAvailable;
- Engine['load'] = load;
- Engine['unload'] = unload;
- Engine.prototype['init'] = Engine.prototype.init;
- Engine.prototype['preloadFile'] = Engine.prototype.preloadFile;
- Engine.prototype['start'] = Engine.prototype.start;
- Engine.prototype['startGame'] = Engine.prototype.startGame;
- Engine.prototype['setWebAssemblyFilenameExtension'] = Engine.prototype.setWebAssemblyFilenameExtension;
- Engine.prototype['setUnloadAfterInit'] = Engine.prototype.setUnloadAfterInit;
- Engine.prototype['setCanvas'] = Engine.prototype.setCanvas;
- Engine.prototype['setCanvasResizedOnStart'] = Engine.prototype.setCanvasResizedOnStart;
- Engine.prototype['setLocale'] = Engine.prototype.setLocale;
- Engine.prototype['setExecutableName'] = Engine.prototype.setExecutableName;
- Engine.prototype['setProgressFunc'] = Engine.prototype.setProgressFunc;
- Engine.prototype['setStdoutFunc'] = Engine.prototype.setStdoutFunc;
- Engine.prototype['setStderrFunc'] = Engine.prototype.setStderrFunc;
- Engine.prototype['setOnExecute'] = Engine.prototype.setOnExecute;
- Engine.prototype['setOnExit'] = Engine.prototype.setOnExit;
- Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
- return Engine;
-})();
diff --git a/platform/javascript/engine/preloader.js b/platform/javascript/engine/preloader.js
deleted file mode 100644
index 17918eae38..0000000000
--- a/platform/javascript/engine/preloader.js
+++ /dev/null
@@ -1,139 +0,0 @@
-var Preloader = /** @constructor */ function() {
-
- var DOWNLOAD_ATTEMPTS_MAX = 4;
- var progressFunc = null;
- var lastProgress = { loaded: 0, total: 0 };
-
- var loadingFiles = {};
- this.preloadedFiles = [];
-
- function loadXHR(resolve, reject, file, tracker) {
- var xhr = new XMLHttpRequest;
- xhr.open('GET', file);
- if (!file.endsWith('.js')) {
- xhr.responseType = 'arraybuffer';
- }
- ['loadstart', 'progress', 'load', 'error', 'abort'].forEach(function(ev) {
- xhr.addEventListener(ev, onXHREvent.bind(xhr, resolve, reject, file, tracker));
- });
- xhr.send();
- }
-
- function onXHREvent(resolve, reject, file, tracker, ev) {
-
- if (this.status >= 400) {
-
- if (this.status < 500 || ++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) {
- reject(new Error("Failed loading file '" + file + "': " + this.statusText));
- this.abort();
- return;
- } else {
- setTimeout(loadXHR.bind(null, resolve, reject, file, tracker), 1000);
- }
- }
-
- switch (ev.type) {
- case 'loadstart':
- if (tracker[file] === undefined) {
- tracker[file] = {
- total: ev.total,
- loaded: ev.loaded,
- attempts: 0,
- final: false,
- };
- }
- break;
-
- case 'progress':
- tracker[file].loaded = ev.loaded;
- tracker[file].total = ev.total;
- break;
-
- case 'load':
- tracker[file].final = true;
- resolve(this);
- break;
-
- case 'error':
- if (++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) {
- tracker[file].final = true;
- reject(new Error("Failed loading file '" + file + "'"));
- } else {
- setTimeout(loadXHR.bind(null, resolve, reject, file, tracker), 1000);
- }
- break;
-
- case 'abort':
- tracker[file].final = true;
- reject(new Error("Loading file '" + file + "' was aborted."));
- break;
- }
- }
-
- this.loadPromise = function(file) {
- return new Promise(function(resolve, reject) {
- loadXHR(resolve, reject, file, loadingFiles);
- });
- }
-
- this.preload = function(pathOrBuffer, destPath) {
- if (pathOrBuffer instanceof ArrayBuffer) {
- pathOrBuffer = new Uint8Array(pathOrBuffer);
- } else if (ArrayBuffer.isView(pathOrBuffer)) {
- pathOrBuffer = new Uint8Array(pathOrBuffer.buffer);
- }
- if (pathOrBuffer instanceof Uint8Array) {
- this.preloadedFiles.push({
- path: destPath,
- buffer: pathOrBuffer
- });
- return Promise.resolve();
- } else if (typeof pathOrBuffer === 'string') {
- var me = this;
- return this.loadPromise(pathOrBuffer).then(function(xhr) {
- me.preloadedFiles.push({
- path: destPath || pathOrBuffer,
- buffer: xhr.response
- });
- return Promise.resolve();
- });
- } else {
- throw Promise.reject("Invalid object for preloading");
- }
- };
-
- var animateProgress = function() {
-
- var loaded = 0;
- var total = 0;
- var totalIsValid = true;
- var progressIsFinal = true;
-
- Object.keys(loadingFiles).forEach(function(file) {
- const stat = loadingFiles[file];
- if (!stat.final) {
- progressIsFinal = false;
- }
- if (!totalIsValid || stat.total === 0) {
- totalIsValid = false;
- total = 0;
- } else {
- total += stat.total;
- }
- loaded += stat.loaded;
- });
- if (loaded !== lastProgress.loaded || total !== lastProgress.total) {
- lastProgress.loaded = loaded;
- lastProgress.total = total;
- if (typeof progressFunc === 'function')
- progressFunc(loaded, total);
- }
- if (!progressIsFinal)
- requestAnimationFrame(animateProgress);
- }
- this.animateProgress = animateProgress; // Also exposed to start it.
-
- this.setProgressFunc = function(callback) {
- progressFunc = callback;
- }
-};
diff --git a/platform/javascript/engine/utils.js b/platform/javascript/engine/utils.js
deleted file mode 100644
index 0c97b38199..0000000000
--- a/platform/javascript/engine/utils.js
+++ /dev/null
@@ -1,51 +0,0 @@
-var Utils = {
-
- createLocateRewrite: function(execName) {
- function rw(path) {
- if (path.endsWith('.worker.js')) {
- return execName + '.worker.js';
- } else if (path.endsWith('.js')) {
- return execName + '.js';
- } else if (path.endsWith('.wasm')) {
- return execName + '.wasm';
- }
- }
- return rw;
- },
-
- createInstantiatePromise: function(wasmLoader) {
- function instantiateWasm(imports, onSuccess) {
- wasmLoader.then(function(xhr) {
- WebAssembly.instantiate(xhr.response, imports).then(function(result) {
- onSuccess(result['instance'], result['module']);
- });
- });
- wasmLoader = null;
- return {};
- };
-
- return instantiateWasm;
- },
-
- findCanvas: function() {
- var nodes = document.getElementsByTagName('canvas');
- if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
- return nodes[0];
- }
- throw new Error("No canvas found");
- },
-
- isWebGLAvailable: function(majorVersion = 1) {
-
- var testContext = false;
- try {
- var testCanvas = document.createElement('canvas');
- if (majorVersion === 1) {
- testContext = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');
- } else if (majorVersion === 2) {
- testContext = testCanvas.getContext('webgl2') || testCanvas.getContext('experimental-webgl2');
- }
- } catch (e) {}
- return !!testContext;
- }
-};
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 3573ddac95..353cc49ef8 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,16 +37,13 @@
#include "platform/javascript/logo.gen.h"
#include "platform/javascript/run_icon.gen.h"
-#define EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE "webassembly_release.zip"
-#define EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG "webassembly_debug.zip"
-
class EditorHTTPServer : public Reference {
private:
Ref<TCP_Server> server;
Ref<StreamPeerTCP> connection;
- uint64_t time;
+ uint64_t time = 0;
uint8_t req_buf[4096];
- int req_pos;
+ int req_pos = 0;
void _clear_client() {
connection = Ref<StreamPeerTCP>();
@@ -85,33 +82,44 @@ public:
// Wrong protocol
ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version.");
- String filepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_js_export");
+ const String cache_path = EditorSettings::get_singleton()->get_cache_dir();
const String basereq = "/tmp_js_export";
- String ctype = "";
+ String filepath;
+ String ctype;
if (req[1] == basereq + ".html") {
- filepath += ".html";
+ filepath = cache_path.plus_file(req[1].get_file());
ctype = "text/html";
} else if (req[1] == basereq + ".js") {
- filepath += ".js";
+ filepath = cache_path.plus_file(req[1].get_file());
+ ctype = "application/javascript";
+ } else if (req[1] == basereq + ".audio.worklet.js") {
+ filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/javascript";
} else if (req[1] == basereq + ".worker.js") {
- filepath += ".worker.js";
+ filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/javascript";
} else if (req[1] == basereq + ".pck") {
- filepath += ".pck";
+ filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/octet-stream";
} else if (req[1] == basereq + ".png" || req[1] == "/favicon.png") {
// Also allow serving the generated favicon for a smoother loading experience.
if (req[1] == "/favicon.png") {
filepath = EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png");
} else {
- filepath += ".png";
+ filepath = basereq + ".png";
}
ctype = "image/png";
+ } else if (req[1] == basereq + ".side.wasm") {
+ filepath = cache_path.plus_file(req[1].get_file());
+ ctype = "application/wasm";
} else if (req[1] == basereq + ".wasm") {
- filepath += ".wasm";
+ filepath = cache_path.plus_file(req[1].get_file());
ctype = "application/wasm";
- } else {
+ } else if (req[1].ends_with(".wasm")) {
+ filepath = cache_path.plus_file(req[1].get_file()); // TODO dangerous?
+ ctype = "application/wasm";
+ }
+ if (filepath.is_empty() || !FileAccess::exists(filepath)) {
String s = "HTTP/1.1 404 Not Found\r\n";
s += "Connection: Close\r\n";
s += "\r\n";
@@ -124,6 +132,10 @@ public:
String s = "HTTP/1.1 200 OK\r\n";
s += "Connection: Close\r\n";
s += "Content-Type: " + ctype + "\r\n";
+ s += "Access-Control-Allow-Origin: *\r\n";
+ s += "Cross-Origin-Opener-Policy: same-origin\r\n";
+ s += "Cross-Origin-Embedder-Policy: require-corp\r\n";
+ s += "Cache-Control: no-store, max-age=0\r\n";
s += "\r\n";
CharString cs = s.utf8();
Error err = connection->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
@@ -197,69 +209,105 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform {
Ref<ImageTexture> logo;
Ref<ImageTexture> run_icon;
Ref<ImageTexture> stop_icon;
- int menu_options;
-
- void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags);
+ int menu_options = 0;
-private:
Ref<EditorHTTPServer> server;
- bool server_quit;
+ bool server_quit = false;
Mutex server_lock;
- Thread *server_thread;
+ Thread server_thread;
+
+ enum ExportMode {
+ EXPORT_MODE_NORMAL = 0,
+ EXPORT_MODE_THREADS = 1,
+ EXPORT_MODE_GDNATIVE = 2,
+ };
+
+ String _get_template_name(ExportMode p_mode, bool p_debug) const {
+ String name = "webassembly";
+ switch (p_mode) {
+ case EXPORT_MODE_THREADS:
+ name += "_threads";
+ break;
+ case EXPORT_MODE_GDNATIVE:
+ name += "_gdnative";
+ break;
+ default:
+ break;
+ }
+ if (p_debug) {
+ name += "_debug.zip";
+ } else {
+ name += "_release.zip";
+ }
+ return name;
+ }
+
+ void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects);
static void _server_thread_poll(void *data);
public:
- virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
+ virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override;
- virtual void get_export_options(List<ExportOption> *r_options);
+ virtual void get_export_options(List<ExportOption> *r_options) override;
- virtual String get_name() const;
- virtual String get_os_name() const;
- virtual Ref<Texture2D> get_logo() const;
+ virtual String get_name() const override;
+ virtual String get_os_name() const override;
+ virtual Ref<Texture2D> get_logo() const override;
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
- virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const;
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
- virtual bool poll_export();
- virtual int get_options_count() const;
- virtual String get_option_label(int p_index) const { return p_index ? TTR("Stop HTTP Server") : TTR("Run in Browser"); }
- virtual String get_option_tooltip(int p_index) const { return p_index ? TTR("Stop HTTP Server") : TTR("Run exported HTML in the system's default browser."); }
- virtual Ref<ImageTexture> get_option_icon(int p_index) const;
- virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags);
- virtual Ref<Texture2D> get_run_icon() const;
+ virtual bool poll_export() override;
+ virtual int get_options_count() const override;
+ virtual String get_option_label(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run in Browser"); }
+ virtual String get_option_tooltip(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run exported HTML in the system's default browser."); }
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) override;
+ virtual Ref<Texture2D> get_run_icon() const override;
- virtual void get_platform_features(List<String> *r_features) {
+ virtual void get_platform_features(List<String> *r_features) override {
r_features->push_back("web");
r_features->push_back(get_os_name());
}
- virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
}
- String get_debug_protocol() const { return "ws://"; }
+ String get_debug_protocol() const override { return "ws://"; }
EditorExportPlatformJavaScript();
~EditorExportPlatformJavaScript();
};
-void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags) {
+void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects) {
String str_template = String::utf8(reinterpret_cast<const char *>(p_html.ptr()), p_html.size());
String str_export;
Vector<String> lines = str_template.split("\n");
+ Array libs;
+ for (int i = 0; i < p_shared_objects.size(); i++) {
+ libs.push_back(p_shared_objects[i].path.get_file());
+ }
Vector<String> flags;
- String flags_json;
- gen_export_flags(flags, p_flags);
- flags_json = JSON::print(flags);
+ gen_export_flags(flags, p_flags & (~DEBUG_FLAG_DUMB_CLIENT));
+ Array args;
+ for (int i = 0; i < flags.size(); i++) {
+ args.push_back(flags[i]);
+ }
+ Dictionary config;
+ config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy");
+ config["gdnativeLibs"] = libs;
+ config["executable"] = p_name;
+ config["args"] = args;
+ const String str_config = JSON::print(config);
for (int i = 0; i < lines.size(); i++) {
String current_line = lines[i];
- current_line = current_line.replace("$GODOT_BASENAME", p_name);
+ current_line = current_line.replace("$GODOT_URL", p_name + ".js");
current_line = current_line.replace("$GODOT_PROJECT_NAME", ProjectSettings::get_singleton()->get_setting("application/config/name"));
current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include"));
- current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false");
- current_line = current_line.replace("$GODOT_ARGS", flags_json);
+ current_line = current_line.replace("$GODOT_CONFIG", str_config);
str_export += current_line + "\n";
}
@@ -276,7 +324,7 @@ void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportP
}
if (p_preset->get("vram_texture_compression/for_mobile")) {
- String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name");
+ String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
if (driver == "GLES2") {
r_features->push_back("etc");
} else if (driver == "Vulkan") {
@@ -284,15 +332,25 @@ void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportP
r_features->push_back("etc2");
}
}
+ ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
+ if (mode == EXPORT_MODE_THREADS) {
+ r_features->push_back("threads");
+ } else if (mode == EXPORT_MODE_GDNATIVE) {
+ r_features->push_back("wasm32");
+ }
}
void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/export_type", PROPERTY_HINT_ENUM, "Regular,Threads,GDNative"), 0)); // Export type.
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2));
}
String EditorExportPlatformJavaScript::get_name() const {
@@ -310,11 +368,11 @@ Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const {
bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
+ ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
// Look for export templates (first official, and if defined custom templates).
-
- bool dvalid = exists_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG, &err);
- bool rvalid = exists_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE, &err);
+ bool dvalid = exists_export_template(_get_template_name(mode, true), &err);
+ bool rvalid = exists_export_template(_get_template_name(mode, false), &err);
if (p_preset->get("custom_template/debug") != "") {
dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
@@ -342,7 +400,7 @@ bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p
}
}
- if (!err.empty()) {
+ if (!err.is_empty()) {
r_error = err;
}
@@ -367,11 +425,8 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
template_path = template_path.strip_edges();
if (template_path == String()) {
- if (p_debug) {
- template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG);
- } else {
- template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE);
- }
+ ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
+ template_path = find_export_template(_get_template_name(mode, p_debug));
}
if (!DirAccess::exists(p_path.get_base_dir())) {
@@ -383,12 +438,24 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
return ERR_FILE_NOT_FOUND;
}
+ Vector<SharedObject> shared_objects;
String pck_path = p_path.get_basename() + ".pck";
- Error error = save_pack(p_preset, pck_path);
+ Error error = save_pack(p_preset, pck_path, &shared_objects);
if (error != OK) {
EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path);
return error;
}
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ for (int i = 0; i < shared_objects.size(); i++) {
+ String dst = p_path.get_base_dir().plus_file(shared_objects[i].path.get_file());
+ error = da->copy(shared_objects[i].path, dst);
+ if (error != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + shared_objects[i].path.get_file());
+ memdelete(da);
+ return error;
+ }
+ }
+ memdelete(da);
FileAccess *src_f = nullptr;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
@@ -424,17 +491,24 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
//write
if (file == "godot.html") {
- if (!custom_html.empty()) {
+ if (!custom_html.is_empty()) {
continue;
}
- _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug, p_flags);
+ _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects);
file = p_path.get_file();
} else if (file == "godot.js") {
file = p_path.get_file().get_basename() + ".js";
+
} else if (file == "godot.worker.js") {
file = p_path.get_file().get_basename() + ".worker.js";
+ } else if (file == "godot.side.wasm") {
+ file = p_path.get_file().get_basename() + ".side.wasm";
+
+ } else if (file == "godot.audio.worklet.js") {
+ file = p_path.get_file().get_basename() + ".audio.worklet.js";
+
} else if (file == "godot.wasm") {
file = p_path.get_file().get_basename() + ".wasm";
}
@@ -452,7 +526,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
} while (unzGoToNextFile(pkg) == UNZ_OK);
unzClose(pkg);
- if (!custom_html.empty()) {
+ if (!custom_html.is_empty()) {
FileAccess *f = FileAccess::open(custom_html, FileAccess::READ);
if (!f) {
EditorNode::get_singleton()->show_warning(TTR("Could not read custom HTML shell:") + "\n" + custom_html);
@@ -462,7 +536,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
buf.resize(f->get_len());
f->get_buffer(buf.ptrw(), buf.size());
memdelete(f);
- _fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug, p_flags);
+ _fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug, p_flags, shared_objects);
f = FileAccess::open(p_path, FileAccess::WRITE);
if (!f) {
@@ -475,7 +549,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
Ref<Image> splash;
const String splash_path = String(GLOBAL_GET("application/boot_splash/image")).strip_edges();
- if (!splash_path.empty()) {
+ if (!splash_path.is_empty()) {
splash.instance();
const Error err = splash->load(splash_path);
if (err) {
@@ -496,7 +570,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
// This way, the favicon can be displayed immediately when loading the page.
Ref<Image> favicon;
const String favicon_path = String(GLOBAL_GET("application/config/icon")).strip_edges();
- if (!favicon_path.empty()) {
+ if (!favicon_path.is_empty()) {
favicon.instance();
const Error err = favicon->load(favicon_path);
if (err) {
@@ -561,8 +635,10 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese
DirAccess::remove_file_or_error(basepath + ".html");
DirAccess::remove_file_or_error(basepath + ".js");
DirAccess::remove_file_or_error(basepath + ".worker.js");
+ DirAccess::remove_file_or_error(basepath + ".audio.worklet.js");
DirAccess::remove_file_or_error(basepath + ".pck");
DirAccess::remove_file_or_error(basepath + ".png");
+ DirAccess::remove_file_or_error(basepath + ".side.wasm");
DirAccess::remove_file_or_error(basepath + ".wasm");
DirAccess::remove_file_or_error(EditorSettings::get_singleton()->get_cache_dir().plus_file("favicon.png"));
return err;
@@ -611,8 +687,7 @@ void EditorExportPlatformJavaScript::_server_thread_poll(void *data) {
EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
server.instance();
- server_quit = false;
- server_thread = Thread::create(_server_thread_poll, this);
+ server_thread.start(_server_thread_poll, this);
Ref<Image> img = memnew(Image(_javascript_logo));
logo.instance();
@@ -628,15 +703,12 @@ EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
} else {
stop_icon.instance();
}
-
- menu_options = 0;
}
EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() {
server->stop();
server_quit = true;
- Thread::wait_to_finish(server_thread);
- memdelete(server_thread);
+ server_thread.wait_to_finish();
}
void register_javascript_exporter() {
diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h
index 30c5c855d1..e641339f55 100644
--- a/platform/javascript/export/export.h
+++ b/platform/javascript/export/export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/iphone/game_center.h b/platform/javascript/godot_audio.h
index 0d3ef5b696..54fc8fa3b5 100644
--- a/platform/iphone/game_center.h
+++ b/platform/javascript/godot_audio.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* game_center.h */
+/* godot_audio.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,48 +28,36 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef GAME_CENTER_ENABLED
+#ifndef GODOT_AUDIO_H
+#define GODOT_AUDIO_H
-#ifndef GAME_CENTER_H
-#define GAME_CENTER_H
-
-#include "core/object.h"
-
-class GameCenter : public Object {
- GDCLASS(GameCenter, Object);
-
- static GameCenter *instance;
- static void _bind_methods();
-
- List<Variant> pending_events;
-
- bool authenticated;
-
- void return_connect_error(const char *p_error_description);
-
-public:
- void connect();
- bool is_authenticated();
+#ifdef __cplusplus
+extern "C" {
+#endif
- Error post_score(Variant p_score);
- Error award_achievement(Variant p_params);
- void reset_achievements();
- void request_achievements();
- void request_achievement_descriptions();
- Error show_game_center(Variant p_params);
- Error request_identity_verification_signature();
+#include "stddef.h"
- void game_center_closed();
+extern int godot_audio_is_available();
+extern int godot_audio_init(int p_mix_rate, int p_latency, void (*_state_cb)(int), void (*_latency_cb)(float));
+extern void godot_audio_resume();
- int get_pending_event_count();
- Variant pop_pending_event();
+extern int godot_audio_capture_start();
+extern void godot_audio_capture_stop();
- static GameCenter *get_singleton();
+// Worklet
+typedef int32_t GodotAudioState[4];
+extern void godot_audio_worklet_create(int p_channels);
+extern void godot_audio_worklet_start(float *p_in_buf, int p_in_size, float *p_out_buf, int p_out_size, GodotAudioState p_state);
+extern int godot_audio_worklet_state_add(GodotAudioState p_state, int p_idx, int p_value);
+extern int godot_audio_worklet_state_get(GodotAudioState p_state, int p_idx);
+extern int godot_audio_worklet_state_wait(int32_t *p_state, int p_idx, int32_t p_expected, int p_timeout);
- GameCenter();
- ~GameCenter();
-};
+// Script
+extern int godot_audio_script_create(int p_buffer_size, int p_channels);
+extern void godot_audio_script_start(float *p_in_buf, int p_in_size, float *p_out_buf, int p_out_size, void (*p_cb)());
+#ifdef __cplusplus
+}
#endif
-#endif
+#endif /* GODOT_AUDIO_H */
diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h
new file mode 100644
index 0000000000..5aa8677a54
--- /dev/null
+++ b/platform/javascript/godot_js.h
@@ -0,0 +1,100 @@
+/*************************************************************************/
+/* godot_js.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+#ifndef GODOT_JS_H
+#define GODOT_JS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "stddef.h"
+
+// Config
+extern void godot_js_config_locale_get(char *p_ptr, int p_ptr_max);
+extern void godot_js_config_canvas_id_get(char *p_ptr, int p_ptr_max);
+
+// OS
+extern void godot_js_os_finish_async(void (*p_callback)());
+extern void godot_js_os_request_quit_cb(void (*p_callback)());
+extern int godot_js_os_fs_is_persistent();
+extern void godot_js_os_fs_sync(void (*p_callback)());
+extern int godot_js_os_execute(const char *p_json);
+extern void godot_js_os_shell_open(const char *p_uri);
+extern int godot_js_os_hw_concurrency_get();
+
+// Display
+extern int godot_js_display_screen_dpi_get();
+extern double godot_js_display_pixel_ratio_get();
+extern void godot_js_display_alert(const char *p_text);
+extern int godot_js_display_touchscreen_is_available();
+extern int godot_js_display_is_swap_ok_cancel();
+
+// Display canvas
+extern void godot_js_display_canvas_focus();
+extern int godot_js_display_canvas_is_focused();
+
+// Display window
+extern void godot_js_display_desired_size_set(int p_width, int p_height);
+extern int godot_js_display_size_update();
+extern void godot_js_display_window_size_get(int32_t *p_x, int32_t *p_y);
+extern void godot_js_display_screen_size_get(int32_t *p_x, int32_t *p_y);
+extern int godot_js_display_fullscreen_request();
+extern int godot_js_display_fullscreen_exit();
+extern void godot_js_display_compute_position(int p_x, int p_y, int32_t *r_x, int32_t *r_y);
+extern void godot_js_display_window_title_set(const char *p_text);
+extern void godot_js_display_window_icon_set(const uint8_t *p_ptr, int p_len);
+
+// Display clipboard
+extern int godot_js_display_clipboard_set(const char *p_text);
+extern int godot_js_display_clipboard_get(void (*p_callback)(const char *p_text));
+
+// Display cursor
+extern void godot_js_display_cursor_set_shape(const char *p_cursor);
+extern int godot_js_display_cursor_is_hidden();
+extern void godot_js_display_cursor_set_custom_shape(const char *p_shape, const uint8_t *p_ptr, int p_len, int p_hotspot_x, int p_hotspot_y);
+extern void godot_js_display_cursor_set_visible(int p_visible);
+
+// Display gamepad
+extern char *godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid));
+extern int godot_js_display_gamepad_sample();
+extern int godot_js_display_gamepad_sample_count();
+extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard);
+
+// Display listeners
+extern void godot_js_display_notification_cb(void (*p_callback)(int p_notification), int p_enter, int p_exit, int p_in, int p_out);
+extern void godot_js_display_paste_cb(void (*p_callback)(const char *p_text));
+extern void godot_js_display_drop_files_cb(void (*p_callback)(char **p_filev, int p_filec));
+extern void godot_js_display_setup_canvas(int p_width, int p_height, int p_fullscreen);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GODOT_JS_H */
diff --git a/platform/javascript/http_client.h.inc b/platform/javascript/http_client.h.inc
index 4d5ff88bdd..2ef1ad5b83 100644
--- a/platform/javascript/http_client.h.inc
+++ b/platform/javascript/http_client.h.inc
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp
index cb0e48b8a9..1136ea299d 100644
--- a/platform/javascript/http_client_javascript.cpp
+++ b/platform/javascript/http_client_javascript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -78,15 +78,15 @@ Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Ve
ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(p_method == METHOD_TRACE || p_method == METHOD_CONNECT, ERR_UNAVAILABLE, "HTTP methods TRACE and CONNECT are not supported for the HTML5 platform.");
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(host.empty(), ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(host.is_empty(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(port < 0, ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER);
String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + p_url;
godot_xhr_reset(xhr_id);
godot_xhr_open(xhr_id, _methods[p_method], url.utf8().get_data(),
- username.empty() ? nullptr : username.utf8().get_data(),
- password.empty() ? nullptr : password.utf8().get_data());
+ username.is_empty() ? nullptr : username.utf8().get_data(),
+ password.is_empty() ? nullptr : password.utf8().get_data());
for (int i = 0; i < p_headers.size(); i++) {
int header_separator = p_headers[i].find(": ");
@@ -104,7 +104,11 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector
Error err = prepare_request(p_method, p_url, p_headers);
if (err != OK)
return err;
- godot_xhr_send_data(xhr_id, p_body.ptr(), p_body.size());
+ if (p_body.is_empty()) {
+ godot_xhr_send(xhr_id, nullptr, 0);
+ } else {
+ godot_xhr_send(xhr_id, p_body.ptr(), p_body.size());
+ }
return OK;
}
@@ -112,7 +116,12 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str
Error err = prepare_request(p_method, p_url, p_headers);
if (err != OK)
return err;
- godot_xhr_send_string(xhr_id, p_body.utf8().get_data());
+ if (p_body.is_empty()) {
+ godot_xhr_send(xhr_id, nullptr, 0);
+ } else {
+ const CharString cs = p_body.utf8();
+ godot_xhr_send(xhr_id, cs.get_data(), cs.length());
+ }
return OK;
}
@@ -132,7 +141,7 @@ HTTPClient::Status HTTPClient::get_status() const {
}
bool HTTPClient::has_response() const {
- return !polled_response_header.empty();
+ return !polled_response_header.is_empty();
}
bool HTTPClient::is_response_chunked() const {
@@ -145,7 +154,7 @@ int HTTPClient::get_response_code() const {
}
Error HTTPClient::get_response_headers(List<String> *r_response) {
- if (polled_response_header.empty())
+ if (polled_response_header.is_empty())
return ERR_INVALID_PARAMETER;
Vector<String> header_lines = polled_response_header.split("\r\n", false);
@@ -220,13 +229,13 @@ Error HTTPClient::poll() {
has_polled = true;
} else {
// forcing synchronous requests is not possible on the web
- if (last_polling_frame == Engine::get_singleton()->get_idle_frames()) {
+ if (last_polling_frame == Engine::get_singleton()->get_process_frames()) {
WARN_PRINT("HTTPClient polled multiple times in one frame, "
"but request cannot progress more than once per "
"frame on the HTML5 platform.");
}
}
- last_polling_frame = Engine::get_singleton()->get_idle_frames();
+ last_polling_frame = Engine::get_singleton()->get_process_frames();
#endif
polled_response_code = godot_xhr_get_status(xhr_id);
diff --git a/platform/javascript/http_request.h b/platform/javascript/http_request.h
index 54e98c1927..a9d6faade2 100644
--- a/platform/javascript/http_request.h
+++ b/platform/javascript/http_request.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -47,15 +47,13 @@ typedef enum {
extern int godot_xhr_new();
extern void godot_xhr_reset(int p_xhr_id);
-extern bool godot_xhr_free(int p_xhr_id);
+extern void godot_xhr_free(int p_xhr_id);
extern int godot_xhr_open(int p_xhr_id, const char *p_method, const char *p_url, const char *p_user = nullptr, const char *p_password = nullptr);
extern void godot_xhr_set_request_header(int p_xhr_id, const char *p_header, const char *p_value);
-extern void godot_xhr_send_null(int p_xhr_id);
-extern void godot_xhr_send_string(int p_xhr_id, const char *p_data);
-extern void godot_xhr_send_data(int p_xhr_id, const void *p_data, int p_len);
+extern void godot_xhr_send(int p_xhr_id, const void *p_data, int p_len);
extern void godot_xhr_abort(int p_xhr_id);
/* this is an HTTPClient::ResponseCode, not ::Status */
diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp
index 3a72b10dd4..cb19dd20d4 100644
--- a/platform/javascript/javascript_eval.cpp
+++ b/platform/javascript/javascript_eval.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,95 +33,30 @@
#include "api/javascript_eval.h"
#include "emscripten.h"
-extern "C" EMSCRIPTEN_KEEPALIVE uint8_t *resize_PackedByteArray_and_open_write(PackedByteArray *p_arr, VectorWriteProxy<uint8_t> *r_write, int p_len) {
- p_arr->resize(p_len);
- *r_write = p_arr->write;
- return p_arr->ptrw();
+extern "C" {
+union js_eval_ret {
+ uint32_t b;
+ double d;
+ char *s;
+};
+
+extern int godot_js_eval(const char *p_js, int p_use_global_ctx, union js_eval_ret *p_union_ptr, void *p_byte_arr, void *p_byte_arr_write, void *(*p_callback)(void *p_ptr, void *p_ptr2, int p_len));
}
-Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
- union {
- bool b;
- double d;
- char *s;
- } js_data;
+void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_len) {
+ PackedByteArray *arr = (PackedByteArray *)p_arr;
+ VectorWriteProxy<uint8_t> *write = (VectorWriteProxy<uint8_t> *)r_write;
+ arr->resize(p_len);
+ *write = arr->write;
+ return arr->ptrw();
+}
+Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
+ union js_eval_ret js_data;
PackedByteArray arr;
VectorWriteProxy<uint8_t> arr_write;
- /* clang-format off */
- Variant::Type return_type = static_cast<Variant::Type>(EM_ASM_INT({
-
- const CODE = $0;
- const USE_GLOBAL_EXEC_CONTEXT = $1;
- const PTR = $2;
- const BYTEARRAY_PTR = $3;
- const BYTEARRAY_WRITE_PTR = $4;
- var eval_ret;
- try {
- if (USE_GLOBAL_EXEC_CONTEXT) {
- // indirect eval call grants global execution context
- var global_eval = eval;
- eval_ret = global_eval(UTF8ToString(CODE));
- } else {
- eval_ret = eval(UTF8ToString(CODE));
- }
- } catch (e) {
- err(e);
- eval_ret = null;
- }
-
- switch (typeof eval_ret) {
-
- case 'boolean':
- setValue(PTR, eval_ret, 'i32');
- return 1; // BOOL
-
- case 'number':
- setValue(PTR, eval_ret, 'double');
- return 3; // FLOAT
-
- case 'string':
- var array_len = lengthBytesUTF8(eval_ret)+1;
- var array_ptr = _malloc(array_len);
- try {
- if (array_ptr===0) {
- throw new Error('String allocation failed (probably out of memory)');
- }
- setValue(PTR, array_ptr , '*');
- stringToUTF8(eval_ret, array_ptr, array_len);
- return 4; // STRING
- } catch (e) {
- if (array_ptr!==0) {
- _free(array_ptr)
- }
- err(e);
- // fall through
- }
- break;
-
- case 'object':
- if (eval_ret === null) {
- break;
- }
-
- if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) {
- eval_ret = new Uint8Array(eval_ret.buffer);
- }
- else if (eval_ret instanceof ArrayBuffer) {
- eval_ret = new Uint8Array(eval_ret);
- }
- if (eval_ret instanceof Uint8Array) {
- var bytes_ptr = ccall('resize_PackedByteArray_and_open_write', 'number', ['number', 'number' ,'number'], [BYTEARRAY_PTR, BYTEARRAY_WRITE_PTR, eval_ret.length]);
- HEAPU8.set(eval_ret, bytes_ptr);
- return 20; // PACKED_BYTE_ARRAY
- }
- break;
- }
- return 0; // NIL
-
- }, p_code.utf8().get_data(), p_use_global_exec_context, &js_data, &arr, &arr_write));
- /* clang-format on */
+ Variant::Type return_type = static_cast<Variant::Type>(godot_js_eval(p_code.utf8().get_data(), p_use_global_exec_context, &js_data, &arr, &arr_write, resize_PackedByteArray_and_open_write));
switch (return_type) {
case Variant::BOOL:
@@ -130,9 +65,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
return js_data.d;
case Variant::STRING: {
String str = String::utf8(js_data.s);
- /* clang-format off */
- EM_ASM_({ _free($0); }, js_data.s);
- /* clang-format on */
+ free(js_data.s); // Must free the string allocated in JS.
return str;
}
case Variant::PACKED_BYTE_ARRAY:
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index 740a72fafa..0fe95b0a8f 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,11 +30,16 @@
#include "core/io/resource_loader.h"
#include "main/main.h"
-#include "os_javascript.h"
+#include "platform/javascript/display_server_javascript.h"
+#include "platform/javascript/os_javascript.h"
#include <emscripten/emscripten.h>
+#include <stdlib.h>
+
+#include "godot_js.h"
static OS_JavaScript *os = nullptr;
+static uint64_t target_ticks = 0;
void exit_callback() {
emscripten_cancel_main_loop(); // After this, we can exit!
@@ -45,62 +50,55 @@ void exit_callback() {
emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing.
}
+void cleanup_after_sync() {
+ emscripten_set_main_loop(exit_callback, -1, false);
+}
+
void main_loop_callback() {
+ uint64_t current_ticks = os->get_ticks_usec();
+
+ bool force_draw = DisplayServerJavaScript::get_singleton()->check_size_force_redraw();
+ if (force_draw) {
+ Main::force_redraw();
+ } else if (current_ticks < target_ticks) {
+ return; // Skip frame.
+ }
+
+ int target_fps = Engine::get_singleton()->get_target_fps();
+ if (target_fps > 0) {
+ target_ticks += (uint64_t)(1000000 / target_fps);
+ }
if (os->main_loop_iterate()) {
- emscripten_cancel_main_loop(); // Cancel current loop and wait for finalize_async.
- EM_ASM({
- // This will contain the list of operations that need to complete before cleanup.
- Module.async_finish = [];
- });
- os->get_main_loop()->finish();
- os->finalize_async(); // Will add all the async finish functions.
- EM_ASM({
- Promise.all(Module.async_finish).then(function() {
- Module.async_finish = [];
- ccall("cleanup_after_sync", null, []);
- });
- });
+ emscripten_cancel_main_loop(); // Cancel current loop and wait for cleanup_after_sync.
+ godot_js_os_finish_async(cleanup_after_sync);
}
}
-extern "C" EMSCRIPTEN_KEEPALIVE void cleanup_after_sync() {
- emscripten_set_main_loop(exit_callback, -1, false);
-}
+/// When calling main, it is assumed FS is setup and synced.
+extern EMSCRIPTEN_KEEPALIVE int godot_js_main(int argc, char *argv[]) {
+ os = new OS_JavaScript();
+
+ // We must override main when testing is enabled
+ TEST_MAIN_OVERRIDE
+
+ Main::setup(argv[0], argc - 1, &argv[1]);
-extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
- String idbfs_err = String::utf8(p_idbfs_err);
- if (!idbfs_err.empty()) {
- print_line("IndexedDB not available: " + idbfs_err);
- }
- os->set_idb_available(idbfs_err.empty());
- // TODO: Check error return value.
- Main::setup2(); // Manual second phase.
// Ease up compatibility.
ResourceLoader::set_abort_on_missing_resources(false);
- Main::start();
- os->get_main_loop()->init();
- emscripten_resume_main_loop();
-}
-int main(int argc, char *argv[]) {
- os = new OS_JavaScript();
- Main::setup(argv[0], argc - 1, &argv[1], false);
+ Main::start();
+ os->get_main_loop()->initialize();
+#ifdef TOOLS_ENABLED
+ if (Main::is_project_manager() && FileAccess::exists("/tmp/preload.zip")) {
+ PackedStringArray ps;
+ ps.push_back("/tmp/preload.zip");
+ os->get_main_loop()->emit_signal("files_dropped", ps, -1);
+ }
+#endif
emscripten_set_main_loop(main_loop_callback, -1, false);
- emscripten_pause_main_loop(); // Will need to wait for FS sync.
-
- // Sync from persistent state into memory and then
- // run the 'main_after_fs_sync' function.
- /* clang-format off */
- EM_ASM(
- FS.mkdir('/userfs');
- FS.mount(IDBFS, {}, '/userfs');
- FS.syncfs(true, function(err) {
- ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""])
- });
-
- );
- /* clang-format on */
+ // Immediately run the first iteration.
+ // We are inside an animation frame, we want to immediately draw on the newly setup canvas.
+ main_loop_callback();
return 0;
- // Continued async in main_after_fs_sync() from the syncfs() callback.
}
diff --git a/platform/haiku/haiku_application.cpp b/platform/javascript/javascript_runtime.cpp
index 82d9c093e1..2996e95a95 100644
--- a/platform/haiku/haiku_application.cpp
+++ b/platform/javascript/javascript_runtime.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* haiku_application.cpp */
+/* javascript_runtime.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "haiku_application.h"
+extern int godot_js_main(int argc, char *argv[]);
-HaikuApplication::HaikuApplication() :
- BApplication("application/x-vnd.godot") {
+int main(int argc, char *argv[]) {
+ return godot_js_main(argc, argv);
}
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js
new file mode 100644
index 0000000000..97fd718815
--- /dev/null
+++ b/platform/javascript/js/engine/config.js
@@ -0,0 +1,100 @@
+/** @constructor */
+function EngineConfig(opts) {
+ // Module config
+ this.unloadAfterInit = true;
+ this.onPrintError = function () {
+ console.error.apply(console, Array.from(arguments)); // eslint-disable-line no-console
+ };
+ this.onPrint = function () {
+ console.log.apply(console, Array.from(arguments)); // eslint-disable-line no-console
+ };
+ this.onProgress = null;
+
+ // Godot Config
+ this.canvas = null;
+ this.executable = '';
+ this.mainPack = null;
+ this.locale = null;
+ this.canvasResizePolicy = false;
+ this.persistentPaths = ['/userfs'];
+ this.gdnativeLibs = [];
+ this.args = [];
+ this.onExecute = null;
+ this.onExit = null;
+ this.update(opts);
+}
+
+EngineConfig.prototype.update = function (opts) {
+ const config = opts || {};
+ function parse(key, def) {
+ if (typeof (config[key]) === 'undefined') {
+ return def;
+ }
+ return config[key];
+ }
+ // Module config
+ this.unloadAfterInit = parse('unloadAfterInit', this.unloadAfterInit);
+ this.onPrintError = parse('onPrintError', this.onPrintError);
+ this.onPrint = parse('onPrint', this.onPrint);
+ this.onProgress = parse('onProgress', this.onProgress);
+
+ // Godot config
+ this.canvas = parse('canvas', this.canvas);
+ this.executable = parse('executable', this.executable);
+ this.mainPack = parse('mainPack', this.mainPack);
+ this.locale = parse('locale', this.locale);
+ this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy);
+ this.persistentPaths = parse('persistentPaths', this.persistentPaths);
+ this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
+ this.args = parse('args', this.args);
+ this.onExecute = parse('onExecute', this.onExecute);
+ this.onExit = parse('onExit', this.onExit);
+};
+
+EngineConfig.prototype.getModuleConfig = function (loadPath, loadPromise) {
+ const me = this;
+ return {
+ 'print': this.onPrint,
+ 'printErr': this.onPrintError,
+ 'locateFile': Utils.createLocateRewrite(loadPath),
+ 'instantiateWasm': Utils.createInstantiatePromise(loadPromise),
+ 'thisProgram': me.executable,
+ 'noExitRuntime': true,
+ 'dynamicLibraries': [`${me.executable}.side.wasm`],
+ };
+};
+
+EngineConfig.prototype.getGodotConfig = function (cleanup) {
+ if (!(this.canvas instanceof HTMLCanvasElement)) {
+ this.canvas = Utils.findCanvas();
+ if (!this.canvas) {
+ throw new Error('No canvas found in page');
+ }
+ }
+
+ // Canvas can grab focus on click, or key events won't work.
+ if (this.canvas.tabIndex < 0) {
+ this.canvas.tabIndex = 0;
+ }
+
+ // Browser locale, or custom one if defined.
+ let locale = this.locale;
+ if (!locale) {
+ locale = navigator.languages ? navigator.languages[0] : navigator.language;
+ locale = locale.split('.')[0];
+ }
+ const onExit = this.onExit;
+ // Godot configuration.
+ return {
+ 'canvas': this.canvas,
+ 'canvasResizePolicy': this.canvasResizePolicy,
+ 'locale': locale,
+ 'onExecute': this.onExecute,
+ 'onExit': function (p_code) {
+ cleanup(); // We always need to call the cleanup callback to free memory.
+ if (typeof (onExit) === 'function') {
+ onExit(p_code);
+ }
+ },
+ };
+};
diff --git a/platform/javascript/engine/externs.js b/platform/javascript/js/engine/engine.externs.js
index 1a94dd15ec..1a94dd15ec 100644
--- a/platform/javascript/engine/externs.js
+++ b/platform/javascript/js/engine/engine.externs.js
diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js
new file mode 100644
index 0000000000..d14e0e5806
--- /dev/null
+++ b/platform/javascript/js/engine/engine.js
@@ -0,0 +1,144 @@
+const Engine = (function () {
+ const preloader = new Preloader();
+
+ let loadPromise = null;
+ let loadPath = '';
+ let initPromise = null;
+
+ function load(basePath) {
+ if (loadPromise == null) {
+ loadPath = basePath;
+ loadPromise = preloader.loadPromise(`${loadPath}.wasm`);
+ requestAnimationFrame(preloader.animateProgress);
+ }
+ return loadPromise;
+ }
+
+ function unload() {
+ loadPromise = null;
+ }
+
+ /** @constructor */
+ function Engine(opts) { // eslint-disable-line no-shadow
+ this.config = new EngineConfig(opts);
+ this.rtenv = null;
+ }
+
+ Engine.prototype.init = /** @param {string=} basePath */ function (basePath) {
+ if (initPromise) {
+ return initPromise;
+ }
+ if (loadPromise == null) {
+ if (!basePath) {
+ initPromise = Promise.reject(new Error('A base path must be provided when calling `init` and the engine is not loaded.'));
+ return initPromise;
+ }
+ load(basePath);
+ }
+ preloader.setProgressFunc(this.config.onProgress);
+ let config = this.config.getModuleConfig(loadPath, loadPromise);
+ const me = this;
+ initPromise = new Promise(function (resolve, reject) {
+ Godot(config).then(function (module) {
+ module['initFS'](me.config.persistentPaths).then(function (fs_err) {
+ me.rtenv = module;
+ if (me.config.unloadAfterInit) {
+ unload();
+ }
+ resolve();
+ config = null;
+ });
+ });
+ });
+ return initPromise;
+ };
+
+ /** @type {function(string, string):Object} */
+ Engine.prototype.preloadFile = function (file, path) {
+ return preloader.preload(file, path);
+ };
+
+ /** @type {function(...string):Object} */
+ Engine.prototype.start = function (override) {
+ this.config.update(override);
+ const me = this;
+ return me.init().then(function () {
+ if (!me.rtenv) {
+ return Promise.reject(new Error('The engine must be initialized before it can be started'));
+ }
+
+ let config = {};
+ try {
+ config = me.config.getGodotConfig(function () {
+ me.rtenv = null;
+ });
+ } catch (e) {
+ return Promise.reject(e);
+ }
+ // Godot configuration.
+ me.rtenv['initConfig'](config);
+
+ // Preload GDNative libraries.
+ const libs = [];
+ me.config.gdnativeLibs.forEach(function (lib) {
+ libs.push(me.rtenv['loadDynamicLibrary'](lib, { 'loadAsync': true }));
+ });
+ return Promise.all(libs).then(function () {
+ return new Promise(function (resolve, reject) {
+ preloader.preloadedFiles.forEach(function (file) {
+ me.rtenv['copyToFS'](file.path, file.buffer);
+ });
+ preloader.preloadedFiles.length = 0; // Clear memory
+ me.rtenv['callMain'](me.config.args);
+ initPromise = null;
+ resolve();
+ });
+ });
+ });
+ };
+
+ Engine.prototype.startGame = function (override) {
+ this.config.update(override);
+ // Add main-pack argument.
+ const exe = this.config.executable;
+ const pack = this.config.mainPack || `${exe}.pck`;
+ this.config.args = ['--main-pack', pack].concat(this.config.args);
+ // Start and init with execName as loadPath if not inited.
+ const me = this;
+ return Promise.all([
+ this.init(exe),
+ this.preloadFile(pack, pack),
+ ]).then(function () {
+ return me.start.apply(me);
+ });
+ };
+
+ Engine.prototype.copyToFS = function (path, buffer) {
+ if (this.rtenv == null) {
+ throw new Error('Engine must be inited before copying files');
+ }
+ this.rtenv['copyToFS'](path, buffer);
+ };
+
+ Engine.prototype.requestQuit = function () {
+ if (this.rtenv) {
+ this.rtenv['request_quit']();
+ }
+ };
+
+ // Closure compiler exported engine methods.
+ /** @export */
+ Engine['isWebGLAvailable'] = Utils.isWebGLAvailable;
+ Engine['load'] = load;
+ Engine['unload'] = unload;
+ Engine.prototype['init'] = Engine.prototype.init;
+ Engine.prototype['preloadFile'] = Engine.prototype.preloadFile;
+ Engine.prototype['start'] = Engine.prototype.start;
+ Engine.prototype['startGame'] = Engine.prototype.startGame;
+ Engine.prototype['copyToFS'] = Engine.prototype.copyToFS;
+ Engine.prototype['requestQuit'] = Engine.prototype.requestQuit;
+ return Engine;
+}());
+if (typeof window !== 'undefined') {
+ window['Engine'] = Engine;
+}
diff --git a/platform/javascript/js/engine/preloader.js b/platform/javascript/js/engine/preloader.js
new file mode 100644
index 0000000000..ec34fb93f2
--- /dev/null
+++ b/platform/javascript/js/engine/preloader.js
@@ -0,0 +1,127 @@
+const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars
+ const loadXHR = function (resolve, reject, file, tracker, attempts) {
+ const xhr = new XMLHttpRequest();
+ tracker[file] = {
+ total: 0,
+ loaded: 0,
+ final: false,
+ };
+ xhr.onerror = function () {
+ if (attempts <= 1) {
+ reject(new Error(`Failed loading file '${file}'`));
+ } else {
+ setTimeout(function () {
+ loadXHR(resolve, reject, file, tracker, attempts - 1);
+ }, 1000);
+ }
+ };
+ xhr.onabort = function () {
+ tracker[file].final = true;
+ reject(new Error(`Loading file '${file}' was aborted.`));
+ };
+ xhr.onloadstart = function (ev) {
+ tracker[file].total = ev.total;
+ tracker[file].loaded = ev.loaded;
+ };
+ xhr.onprogress = function (ev) {
+ tracker[file].loaded = ev.loaded;
+ tracker[file].total = ev.total;
+ };
+ xhr.onload = function () {
+ if (xhr.status >= 400) {
+ if (xhr.status < 500 || attempts <= 1) {
+ reject(new Error(`Failed loading file '${file}': ${xhr.statusText}`));
+ xhr.abort();
+ } else {
+ setTimeout(function () {
+ loadXHR(resolve, reject, file, tracker, attempts - 1);
+ }, 1000);
+ }
+ } else {
+ tracker[file].final = true;
+ resolve(xhr);
+ }
+ };
+ // Make request.
+ xhr.open('GET', file);
+ if (!file.endsWith('.js')) {
+ xhr.responseType = 'arraybuffer';
+ }
+ xhr.send();
+ };
+
+ const DOWNLOAD_ATTEMPTS_MAX = 4;
+ const loadingFiles = {};
+ const lastProgress = { loaded: 0, total: 0 };
+ let progressFunc = null;
+
+ const animateProgress = function () {
+ let loaded = 0;
+ let total = 0;
+ let totalIsValid = true;
+ let progressIsFinal = true;
+
+ Object.keys(loadingFiles).forEach(function (file) {
+ const stat = loadingFiles[file];
+ if (!stat.final) {
+ progressIsFinal = false;
+ }
+ if (!totalIsValid || stat.total === 0) {
+ totalIsValid = false;
+ total = 0;
+ } else {
+ total += stat.total;
+ }
+ loaded += stat.loaded;
+ });
+ if (loaded !== lastProgress.loaded || total !== lastProgress.total) {
+ lastProgress.loaded = loaded;
+ lastProgress.total = total;
+ if (typeof progressFunc === 'function') {
+ progressFunc(loaded, total);
+ }
+ }
+ if (!progressIsFinal) {
+ requestAnimationFrame(animateProgress);
+ }
+ };
+
+ this.animateProgress = animateProgress;
+
+ this.setProgressFunc = function (callback) {
+ progressFunc = callback;
+ };
+
+ this.loadPromise = function (file) {
+ return new Promise(function (resolve, reject) {
+ loadXHR(resolve, reject, file, loadingFiles, DOWNLOAD_ATTEMPTS_MAX);
+ });
+ };
+
+ this.preloadedFiles = [];
+ this.preload = function (pathOrBuffer, destPath) {
+ let buffer = null;
+ if (typeof pathOrBuffer === 'string') {
+ const me = this;
+ return this.loadPromise(pathOrBuffer).then(function (xhr) {
+ me.preloadedFiles.push({
+ path: destPath || pathOrBuffer,
+ buffer: xhr.response,
+ });
+ return Promise.resolve();
+ });
+ } else if (pathOrBuffer instanceof ArrayBuffer) {
+ buffer = new Uint8Array(pathOrBuffer);
+ } else if (ArrayBuffer.isView(pathOrBuffer)) {
+ buffer = new Uint8Array(pathOrBuffer.buffer);
+ }
+ if (buffer) {
+ this.preloadedFiles.push({
+ path: destPath,
+ buffer: pathOrBuffer,
+ });
+ return Promise.resolve();
+ }
+ return Promise.reject(new Error('Invalid object for preloading'));
+ };
+};
diff --git a/platform/javascript/js/engine/utils.js b/platform/javascript/js/engine/utils.js
new file mode 100644
index 0000000000..9273bbad42
--- /dev/null
+++ b/platform/javascript/js/engine/utils.js
@@ -0,0 +1,58 @@
+const Utils = { // eslint-disable-line no-unused-vars
+
+ createLocateRewrite: function (execName) {
+ function rw(path) {
+ if (path.endsWith('.worker.js')) {
+ return `${execName}.worker.js`;
+ } else if (path.endsWith('.audio.worklet.js')) {
+ return `${execName}.audio.worklet.js`;
+ } else if (path.endsWith('.js')) {
+ return `${execName}.js`;
+ } else if (path.endsWith('.side.wasm')) {
+ return `${execName}.side.wasm`;
+ } else if (path.endsWith('.wasm')) {
+ return `${execName}.wasm`;
+ }
+ return path;
+ }
+ return rw;
+ },
+
+ createInstantiatePromise: function (wasmLoader) {
+ let loader = wasmLoader;
+ function instantiateWasm(imports, onSuccess) {
+ loader.then(function (xhr) {
+ WebAssembly.instantiate(xhr.response, imports).then(function (result) {
+ onSuccess(result['instance'], result['module']);
+ });
+ });
+ loader = null;
+ return {};
+ }
+
+ return instantiateWasm;
+ },
+
+ findCanvas: function () {
+ const nodes = document.getElementsByTagName('canvas');
+ if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
+ return nodes[0];
+ }
+ return null;
+ },
+
+ isWebGLAvailable: function (majorVersion = 1) {
+ let testContext = false;
+ try {
+ const testCanvas = document.createElement('canvas');
+ if (majorVersion === 1) {
+ testContext = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');
+ } else if (majorVersion === 2) {
+ testContext = testCanvas.getContext('webgl2') || testCanvas.getContext('experimental-webgl2');
+ }
+ } catch (e) {
+ // Not available
+ }
+ return !!testContext;
+ },
+};
diff --git a/platform/javascript/js/libs/audio.worklet.js b/platform/javascript/js/libs/audio.worklet.js
new file mode 100644
index 0000000000..6b3f80c6a9
--- /dev/null
+++ b/platform/javascript/js/libs/audio.worklet.js
@@ -0,0 +1,186 @@
+/*************************************************************************/
+/* audio.worklet.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+class RingBuffer {
+ constructor(p_buffer, p_state) {
+ this.buffer = p_buffer;
+ this.avail = p_state;
+ this.rpos = 0;
+ this.wpos = 0;
+ }
+
+ data_left() {
+ return Atomics.load(this.avail, 0);
+ }
+
+ space_left() {
+ return this.buffer.length - this.data_left();
+ }
+
+ read(output) {
+ const size = this.buffer.length;
+ let from = 0;
+ let to_write = output.length;
+ if (this.rpos + to_write > size) {
+ const high = size - this.rpos;
+ output.set(this.buffer.subarray(this.rpos, size));
+ from = high;
+ to_write -= high;
+ this.rpos = 0;
+ }
+ output.set(this.buffer.subarray(this.rpos, this.rpos + to_write), from);
+ this.rpos += to_write;
+ Atomics.add(this.avail, 0, -output.length);
+ Atomics.notify(this.avail, 0);
+ }
+
+ write(p_buffer) {
+ const to_write = p_buffer.length;
+ const mw = this.buffer.length - this.wpos;
+ if (mw >= to_write) {
+ this.buffer.set(p_buffer, this.wpos);
+ } else {
+ const high = p_buffer.subarray(0, to_write - mw);
+ const low = p_buffer.subarray(to_write - mw);
+ this.buffer.set(high, this.wpos);
+ this.buffer.set(low);
+ }
+ let diff = to_write;
+ if (this.wpos + diff >= this.buffer.length) {
+ diff -= this.buffer.length;
+ }
+ this.wpos += diff;
+ Atomics.add(this.avail, 0, to_write);
+ Atomics.notify(this.avail, 0);
+ }
+}
+
+class GodotProcessor extends AudioWorkletProcessor {
+ constructor() {
+ super();
+ this.running = true;
+ this.lock = null;
+ this.notifier = null;
+ this.output = null;
+ this.output_buffer = new Float32Array();
+ this.input = null;
+ this.input_buffer = new Float32Array();
+ this.port.onmessage = (event) => {
+ const cmd = event.data['cmd'];
+ const data = event.data['data'];
+ this.parse_message(cmd, data);
+ };
+ }
+
+ process_notify() {
+ Atomics.add(this.notifier, 0, 1);
+ Atomics.notify(this.notifier, 0);
+ }
+
+ parse_message(p_cmd, p_data) {
+ if (p_cmd === 'start' && p_data) {
+ const state = p_data[0];
+ let idx = 0;
+ this.lock = state.subarray(idx, ++idx);
+ this.notifier = state.subarray(idx, ++idx);
+ const avail_in = state.subarray(idx, ++idx);
+ const avail_out = state.subarray(idx, ++idx);
+ this.input = new RingBuffer(p_data[1], avail_in);
+ this.output = new RingBuffer(p_data[2], avail_out);
+ } else if (p_cmd === 'stop') {
+ this.runing = false;
+ this.output = null;
+ this.input = null;
+ }
+ }
+
+ static array_has_data(arr) {
+ return arr.length && arr[0].length && arr[0][0].length;
+ }
+
+ process(inputs, outputs, parameters) {
+ if (!this.running) {
+ return false; // Stop processing.
+ }
+ if (this.output === null) {
+ return true; // Not ready yet, keep processing.
+ }
+ const process_input = GodotProcessor.array_has_data(inputs);
+ if (process_input) {
+ const input = inputs[0];
+ const chunk = input[0].length * input.length;
+ if (this.input_buffer.length !== chunk) {
+ this.input_buffer = new Float32Array(chunk);
+ }
+ if (this.input.space_left() >= chunk) {
+ GodotProcessor.write_input(this.input_buffer, input);
+ this.input.write(this.input_buffer);
+ } else {
+ this.port.postMessage('Input buffer is full! Skipping input frame.');
+ }
+ }
+ const process_output = GodotProcessor.array_has_data(outputs);
+ if (process_output) {
+ const output = outputs[0];
+ const chunk = output[0].length * output.length;
+ if (this.output_buffer.length !== chunk) {
+ this.output_buffer = new Float32Array(chunk);
+ }
+ if (this.output.data_left() >= chunk) {
+ this.output.read(this.output_buffer);
+ GodotProcessor.write_output(output, this.output_buffer);
+ } else {
+ this.port.postMessage('Output buffer has not enough frames! Skipping output frame.');
+ }
+ }
+ this.process_notify();
+ return true;
+ }
+
+ static write_output(dest, source) {
+ const channels = dest.length;
+ for (let ch = 0; ch < channels; ch++) {
+ for (let sample = 0; sample < dest[ch].length; sample++) {
+ dest[ch][sample] = source[sample * channels + ch];
+ }
+ }
+ }
+
+ static write_input(dest, source) {
+ const channels = source.length;
+ for (let ch = 0; ch < channels; ch++) {
+ for (let sample = 0; sample < source[ch].length; sample++) {
+ dest[sample * channels + ch] = source[ch][sample];
+ }
+ }
+ }
+}
+
+registerProcessor('godot-processor', GodotProcessor);
diff --git a/platform/javascript/js/libs/library_godot_audio.js b/platform/javascript/js/libs/library_godot_audio.js
new file mode 100644
index 0000000000..8e385e9176
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_audio.js
@@ -0,0 +1,362 @@
+/*************************************************************************/
+/* library_godot_audio.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+const GodotAudio = {
+ $GodotAudio__deps: ['$GodotRuntime', '$GodotOS'],
+ $GodotAudio: {
+ ctx: null,
+ input: null,
+ driver: null,
+ interval: 0,
+
+ init: function (mix_rate, latency, onstatechange, onlatencyupdate) {
+ const ctx = new (window.AudioContext || window.webkitAudioContext)({
+ sampleRate: mix_rate,
+ // latencyHint: latency / 1000 // Do not specify, leave 'interactive' for good performance.
+ });
+ GodotAudio.ctx = ctx;
+ ctx.onstatechange = function () {
+ let state = 0;
+ switch (ctx.state) {
+ case 'suspended':
+ state = 0;
+ break;
+ case 'running':
+ state = 1;
+ break;
+ case 'closed':
+ state = 2;
+ break;
+
+ // no default
+ }
+ onstatechange(state);
+ };
+ ctx.onstatechange(); // Immeditately notify state.
+ // Update computed latency
+ GodotAudio.interval = setInterval(function () {
+ let computed_latency = 0;
+ if (ctx.baseLatency) {
+ computed_latency += GodotAudio.ctx.baseLatency;
+ }
+ if (ctx.outputLatency) {
+ computed_latency += GodotAudio.ctx.outputLatency;
+ }
+ onlatencyupdate(computed_latency);
+ }, 1000);
+ GodotOS.atexit(GodotAudio.close_async);
+ return ctx.destination.channelCount;
+ },
+
+ create_input: function (callback) {
+ if (GodotAudio.input) {
+ return 0; // Already started.
+ }
+ function gotMediaInput(stream) {
+ try {
+ GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream);
+ callback(GodotAudio.input);
+ } catch (e) {
+ GodotRuntime.error('Failed creaating input.', e);
+ }
+ }
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
+ navigator.mediaDevices.getUserMedia({
+ 'audio': true,
+ }).then(gotMediaInput, function (e) {
+ GodotRuntime.error('Error getting user media.', e);
+ });
+ } else {
+ if (!navigator.getUserMedia) {
+ navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
+ }
+ if (!navigator.getUserMedia) {
+ GodotRuntime.error('getUserMedia not available.');
+ return 1;
+ }
+ navigator.getUserMedia({
+ 'audio': true,
+ }, gotMediaInput, function (e) {
+ GodotRuntime.print(e);
+ });
+ }
+ return 0;
+ },
+
+ close_async: function (resolve, reject) {
+ const ctx = GodotAudio.ctx;
+ GodotAudio.ctx = null;
+ // Audio was not initialized.
+ if (!ctx) {
+ resolve();
+ return;
+ }
+ // Remove latency callback
+ if (GodotAudio.interval) {
+ clearInterval(GodotAudio.interval);
+ GodotAudio.interval = 0;
+ }
+ // Disconnect input, if it was started.
+ if (GodotAudio.input) {
+ GodotAudio.input.disconnect();
+ GodotAudio.input = null;
+ }
+ // Disconnect output
+ let closed = Promise.resolve();
+ if (GodotAudio.driver) {
+ closed = GodotAudio.driver.close();
+ }
+ closed.then(function () {
+ return ctx.close();
+ }).then(function () {
+ ctx.onstatechange = null;
+ resolve();
+ }).catch(function (e) {
+ ctx.onstatechange = null;
+ GodotRuntime.error('Error closing AudioContext', e);
+ resolve();
+ });
+ },
+ },
+
+ godot_audio_is_available__sig: 'i',
+ godot_audio_is_available__proxy: 'sync',
+ godot_audio_is_available: function () {
+ if (!(window.AudioContext || window.webkitAudioContext)) {
+ return 0;
+ }
+ return 1;
+ },
+
+ godot_audio_init__sig: 'iiiii',
+ godot_audio_init: function (p_mix_rate, p_latency, p_state_change, p_latency_update) {
+ const statechange = GodotRuntime.get_func(p_state_change);
+ const latencyupdate = GodotRuntime.get_func(p_latency_update);
+ return GodotAudio.init(p_mix_rate, p_latency, statechange, latencyupdate);
+ },
+
+ godot_audio_resume__sig: 'v',
+ godot_audio_resume: function () {
+ if (GodotAudio.ctx && GodotAudio.ctx.state !== 'running') {
+ GodotAudio.ctx.resume();
+ }
+ },
+
+ godot_audio_capture_start__proxy: 'sync',
+ godot_audio_capture_start__sig: 'i',
+ godot_audio_capture_start: function () {
+ return GodotAudio.create_input(function (input) {
+ input.connect(GodotAudio.driver.get_node());
+ });
+ },
+
+ godot_audio_capture_stop__proxy: 'sync',
+ godot_audio_capture_stop__sig: 'v',
+ godot_audio_capture_stop: function () {
+ if (GodotAudio.input) {
+ const tracks = GodotAudio.input['mediaStream']['getTracks']();
+ for (let i = 0; i < tracks.length; i++) {
+ tracks[i]['stop']();
+ }
+ GodotAudio.input.disconnect();
+ GodotAudio.input = null;
+ }
+ },
+};
+
+autoAddDeps(GodotAudio, '$GodotAudio');
+mergeInto(LibraryManager.library, GodotAudio);
+
+/**
+ * The AudioWorklet API driver, used when threads are available.
+ */
+const GodotAudioWorklet = {
+ $GodotAudioWorklet__deps: ['$GodotAudio', '$GodotConfig'],
+ $GodotAudioWorklet: {
+ promise: null,
+ worklet: null,
+
+ create: function (channels) {
+ const path = GodotConfig.locate_file('godot.audio.worklet.js');
+ GodotAudioWorklet.promise = GodotAudio.ctx.audioWorklet.addModule(path).then(function () {
+ GodotAudioWorklet.worklet = new AudioWorkletNode(
+ GodotAudio.ctx,
+ 'godot-processor',
+ {
+ 'outputChannelCount': [channels],
+ },
+ );
+ return Promise.resolve();
+ });
+ GodotAudio.driver = GodotAudioWorklet;
+ },
+
+ start: function (in_buf, out_buf, state) {
+ GodotAudioWorklet.promise.then(function () {
+ const node = GodotAudioWorklet.worklet;
+ node.connect(GodotAudio.ctx.destination);
+ node.port.postMessage({
+ 'cmd': 'start',
+ 'data': [state, in_buf, out_buf],
+ });
+ node.port.onmessage = function (event) {
+ GodotRuntime.error(event.data);
+ };
+ });
+ },
+
+ get_node: function () {
+ return GodotAudioWorklet.worklet;
+ },
+
+ close: function () {
+ return new Promise(function (resolve, reject) {
+ GodotAudioWorklet.promise.then(function () {
+ GodotAudioWorklet.worklet.port.postMessage({
+ 'cmd': 'stop',
+ 'data': null,
+ });
+ GodotAudioWorklet.worklet.disconnect();
+ GodotAudioWorklet.worklet = null;
+ GodotAudioWorklet.promise = null;
+ resolve();
+ });
+ });
+ },
+ },
+
+ godot_audio_worklet_create__sig: 'vi',
+ godot_audio_worklet_create: function (channels) {
+ GodotAudioWorklet.create(channels);
+ },
+
+ godot_audio_worklet_start__sig: 'viiiii',
+ godot_audio_worklet_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_state) {
+ const out_buffer = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size);
+ const in_buffer = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size);
+ const state = GodotRuntime.heapSub(HEAP32, p_state, 4);
+ GodotAudioWorklet.start(in_buffer, out_buffer, state);
+ },
+
+ godot_audio_worklet_state_wait__sig: 'iiii',
+ godot_audio_worklet_state_wait: function (p_state, p_idx, p_expected, p_timeout) {
+ Atomics.wait(HEAP32, (p_state >> 2) + p_idx, p_expected, p_timeout);
+ return Atomics.load(HEAP32, (p_state >> 2) + p_idx);
+ },
+
+ godot_audio_worklet_state_add__sig: 'iiii',
+ godot_audio_worklet_state_add: function (p_state, p_idx, p_value) {
+ return Atomics.add(HEAP32, (p_state >> 2) + p_idx, p_value);
+ },
+
+ godot_audio_worklet_state_get__sig: 'iii',
+ godot_audio_worklet_state_get: function (p_state, p_idx) {
+ return Atomics.load(HEAP32, (p_state >> 2) + p_idx);
+ },
+};
+
+autoAddDeps(GodotAudioWorklet, '$GodotAudioWorklet');
+mergeInto(LibraryManager.library, GodotAudioWorklet);
+
+/*
+ * The deprecated ScriptProcessorNode API, used when threads are disabled.
+ */
+const GodotAudioScript = {
+ $GodotAudioScript__deps: ['$GodotAudio'],
+ $GodotAudioScript: {
+ script: null,
+
+ create: function (buffer_length, channel_count) {
+ GodotAudioScript.script = GodotAudio.ctx.createScriptProcessor(buffer_length, 2, channel_count);
+ GodotAudio.driver = GodotAudioScript;
+ return GodotAudioScript.script.bufferSize;
+ },
+
+ start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess) {
+ GodotAudioScript.script.onaudioprocess = function (event) {
+ // Read input
+ const inb = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size);
+ const input = event.inputBuffer;
+ if (GodotAudio.input) {
+ const inlen = input.getChannelData(0).length;
+ for (let ch = 0; ch < 2; ch++) {
+ const data = input.getChannelData(ch);
+ for (let s = 0; s < inlen; s++) {
+ inb[s * 2 + ch] = data[s];
+ }
+ }
+ }
+
+ // Let Godot process the input/output.
+ onprocess();
+
+ // Write the output.
+ const outb = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size);
+ const output = event.outputBuffer;
+ const channels = output.numberOfChannels;
+ for (let ch = 0; ch < channels; ch++) {
+ const data = output.getChannelData(ch);
+ // Loop through samples and assign computed values.
+ for (let sample = 0; sample < data.length; sample++) {
+ data[sample] = outb[sample * channels + ch];
+ }
+ }
+ };
+ GodotAudioScript.script.connect(GodotAudio.ctx.destination);
+ },
+
+ get_node: function () {
+ return GodotAudioScript.script;
+ },
+
+ close: function () {
+ return new Promise(function (resolve, reject) {
+ GodotAudioScript.script.disconnect();
+ GodotAudioScript.script.onaudioprocess = null;
+ GodotAudioScript.script = null;
+ resolve();
+ });
+ },
+ },
+
+ godot_audio_script_create__sig: 'iii',
+ godot_audio_script_create: function (buffer_length, channel_count) {
+ return GodotAudioScript.create(buffer_length, channel_count);
+ },
+
+ godot_audio_script_start__sig: 'viiiii',
+ godot_audio_script_start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, p_cb) {
+ const onprocess = GodotRuntime.get_func(p_cb);
+ GodotAudioScript.start(p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess);
+ },
+};
+
+autoAddDeps(GodotAudioScript, '$GodotAudioScript');
+mergeInto(LibraryManager.library, GodotAudioScript);
diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js
new file mode 100644
index 0000000000..fdb5cc0ec2
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_display.js
@@ -0,0 +1,853 @@
+/*************************************************************************/
+/* library_godot_display.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+/*
+ * Display Server listeners.
+ * Keeps track of registered event listeners so it can remove them on shutdown.
+ */
+const GodotDisplayListeners = {
+ $GodotDisplayListeners__deps: ['$GodotOS'],
+ $GodotDisplayListeners__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayListeners.clear(); resolve(); });',
+ $GodotDisplayListeners: {
+ handlers: [],
+
+ has: function (target, event, method, capture) {
+ return GodotDisplayListeners.handlers.findIndex(function (e) {
+ return e.target === target && e.event === event && e.method === method && e.capture === capture;
+ }) !== -1;
+ },
+
+ add: function (target, event, method, capture) {
+ if (GodotDisplayListeners.has(target, event, method, capture)) {
+ return;
+ }
+ function Handler(p_target, p_event, p_method, p_capture) {
+ this.target = p_target;
+ this.event = p_event;
+ this.method = p_method;
+ this.capture = p_capture;
+ }
+ GodotDisplayListeners.handlers.push(new Handler(target, event, method, capture));
+ target.addEventListener(event, method, capture);
+ },
+
+ clear: function () {
+ GodotDisplayListeners.handlers.forEach(function (h) {
+ h.target.removeEventListener(h.event, h.method, h.capture);
+ });
+ GodotDisplayListeners.handlers.length = 0;
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotDisplayListeners);
+
+/*
+ * Drag and drop handler.
+ * This is pretty big, but basically detect dropped files on GodotConfig.canvas,
+ * process them one by one (recursively for directories), and copies them to
+ * the temporary FS path '/tmp/drop-[random]/' so it can be emitted as a godot
+ * event (that requires a string array of paths).
+ *
+ * NOTE: The temporary files are removed after the callback. This means that
+ * deferred callbacks won't be able to access the files.
+ */
+const GodotDisplayDragDrop = {
+ $GodotDisplayDragDrop__deps: ['$FS', '$GodotFS'],
+ $GodotDisplayDragDrop: {
+ promises: [],
+ pending_files: [],
+
+ add_entry: function (entry) {
+ if (entry.isDirectory) {
+ GodotDisplayDragDrop.add_dir(entry);
+ } else if (entry.isFile) {
+ GodotDisplayDragDrop.add_file(entry);
+ } else {
+ GodotRuntime.error('Unrecognized entry...', entry);
+ }
+ },
+
+ add_dir: function (entry) {
+ GodotDisplayDragDrop.promises.push(new Promise(function (resolve, reject) {
+ const reader = entry.createReader();
+ reader.readEntries(function (entries) {
+ for (let i = 0; i < entries.length; i++) {
+ GodotDisplayDragDrop.add_entry(entries[i]);
+ }
+ resolve();
+ });
+ }));
+ },
+
+ add_file: function (entry) {
+ GodotDisplayDragDrop.promises.push(new Promise(function (resolve, reject) {
+ entry.file(function (file) {
+ const reader = new FileReader();
+ reader.onload = function () {
+ const f = {
+ 'path': file.relativePath || file.webkitRelativePath,
+ 'name': file.name,
+ 'type': file.type,
+ 'size': file.size,
+ 'data': reader.result,
+ };
+ if (!f['path']) {
+ f['path'] = f['name'];
+ }
+ GodotDisplayDragDrop.pending_files.push(f);
+ resolve();
+ };
+ reader.onerror = function () {
+ GodotRuntime.print('Error reading file');
+ reject();
+ };
+ reader.readAsArrayBuffer(file);
+ }, function (err) {
+ GodotRuntime.print('Error!');
+ reject();
+ });
+ }));
+ },
+
+ process: function (resolve, reject) {
+ if (GodotDisplayDragDrop.promises.length === 0) {
+ resolve();
+ return;
+ }
+ GodotDisplayDragDrop.promises.pop().then(function () {
+ setTimeout(function () {
+ GodotDisplayDragDrop.process(resolve, reject);
+ }, 0);
+ });
+ },
+
+ _process_event: function (ev, callback) {
+ ev.preventDefault();
+ if (ev.dataTransfer.items) {
+ // Use DataTransferItemList interface to access the file(s)
+ for (let i = 0; i < ev.dataTransfer.items.length; i++) {
+ const item = ev.dataTransfer.items[i];
+ let entry = null;
+ if ('getAsEntry' in item) {
+ entry = item.getAsEntry();
+ } else if ('webkitGetAsEntry' in item) {
+ entry = item.webkitGetAsEntry();
+ }
+ if (entry) {
+ GodotDisplayDragDrop.add_entry(entry);
+ }
+ }
+ } else {
+ GodotRuntime.error('File upload not supported');
+ }
+ new Promise(GodotDisplayDragDrop.process).then(function () {
+ const DROP = `/tmp/drop-${parseInt(Math.random() * (1 << 30), 10)}/`;
+ const drops = [];
+ const files = [];
+ FS.mkdir(DROP);
+ GodotDisplayDragDrop.pending_files.forEach((elem) => {
+ const path = elem['path'];
+ GodotFS.copy_to_fs(DROP + path, elem['data']);
+ let idx = path.indexOf('/');
+ if (idx === -1) {
+ // Root file
+ drops.push(DROP + path);
+ } else {
+ // Subdir
+ const sub = path.substr(0, idx);
+ idx = sub.indexOf('/');
+ if (idx < 0 && drops.indexOf(DROP + sub) === -1) {
+ drops.push(DROP + sub);
+ }
+ }
+ files.push(DROP + path);
+ });
+ GodotDisplayDragDrop.promises = [];
+ GodotDisplayDragDrop.pending_files = [];
+ callback(drops);
+ const dirs = [DROP.substr(0, DROP.length - 1)];
+ // Remove temporary files
+ files.forEach(function (file) {
+ FS.unlink(file);
+ let dir = file.replace(DROP, '');
+ let idx = dir.lastIndexOf('/');
+ while (idx > 0) {
+ dir = dir.substr(0, idx);
+ if (dirs.indexOf(DROP + dir) === -1) {
+ dirs.push(DROP + dir);
+ }
+ idx = dir.lastIndexOf('/');
+ }
+ });
+ // Remove dirs.
+ dirs.sort(function (a, b) {
+ const al = (a.match(/\//g) || []).length;
+ const bl = (b.match(/\//g) || []).length;
+ if (al > bl) {
+ return -1;
+ } else if (al < bl) {
+ return 1;
+ }
+ return 0;
+ }).forEach(function (dir) {
+ FS.rmdir(dir);
+ });
+ });
+ },
+
+ handler: function (callback) {
+ return function (ev) {
+ GodotDisplayDragDrop._process_event(ev, callback);
+ };
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotDisplayDragDrop);
+
+/*
+ * Display server cursor helper.
+ * Keeps track of cursor status and custom shapes.
+ */
+const GodotDisplayCursor = {
+ $GodotDisplayCursor__deps: ['$GodotOS', '$GodotConfig'],
+ $GodotDisplayCursor__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayCursor.clear(); resolve(); });',
+ $GodotDisplayCursor: {
+ shape: 'auto',
+ visible: true,
+ cursors: {},
+ set_style: function (style) {
+ GodotConfig.canvas.style.cursor = style;
+ },
+ set_shape: function (shape) {
+ GodotDisplayCursor.shape = shape;
+ let css = shape;
+ if (shape in GodotDisplayCursor.cursors) {
+ const c = GodotDisplayCursor.cursors[shape];
+ css = `url("${c.url}") ${c.x} ${c.y}, auto`;
+ }
+ if (GodotDisplayCursor.visible) {
+ GodotDisplayCursor.set_style(css);
+ }
+ },
+ clear: function () {
+ GodotDisplayCursor.set_style('');
+ GodotDisplayCursor.shape = 'auto';
+ GodotDisplayCursor.visible = true;
+ Object.keys(GodotDisplayCursor.cursors).forEach(function (key) {
+ URL.revokeObjectURL(GodotDisplayCursor.cursors[key]);
+ delete GodotDisplayCursor.cursors[key];
+ });
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotDisplayCursor);
+
+/*
+ * Display Gamepad API helper.
+ */
+const GodotDisplayGamepads = {
+ $GodotDisplayGamepads__deps: ['$GodotRuntime', '$GodotDisplayListeners'],
+ $GodotDisplayGamepads: {
+ samples: [],
+
+ get_pads: function () {
+ try {
+ // Will throw in iframe when permission is denied.
+ // Will throw/warn in the future for insecure contexts.
+ // See https://github.com/w3c/gamepad/pull/120
+ const pads = navigator.getGamepads();
+ if (pads) {
+ return pads;
+ }
+ return [];
+ } catch (e) {
+ return [];
+ }
+ },
+
+ get_samples: function () {
+ return GodotDisplayGamepads.samples;
+ },
+
+ get_sample: function (index) {
+ const samples = GodotDisplayGamepads.samples;
+ return index < samples.length ? samples[index] : null;
+ },
+
+ sample: function () {
+ const pads = GodotDisplayGamepads.get_pads();
+ const samples = [];
+ for (let i = 0; i < pads.length; i++) {
+ const pad = pads[i];
+ if (!pad) {
+ samples.push(null);
+ continue;
+ }
+ const s = {
+ standard: pad.mapping === 'standard',
+ buttons: [],
+ axes: [],
+ connected: pad.connected,
+ };
+ for (let b = 0; b < pad.buttons.length; b++) {
+ s.buttons.push(pad.buttons[b].value);
+ }
+ for (let a = 0; a < pad.axes.length; a++) {
+ s.axes.push(pad.axes[a]);
+ }
+ samples.push(s);
+ }
+ GodotDisplayGamepads.samples = samples;
+ },
+
+ init: function (onchange) {
+ GodotDisplayListeners.samples = [];
+ function add(pad) {
+ const guid = GodotDisplayGamepads.get_guid(pad);
+ const c_id = GodotRuntime.allocString(pad.id);
+ const c_guid = GodotRuntime.allocString(guid);
+ onchange(pad.index, 1, c_id, c_guid);
+ GodotRuntime.free(c_id);
+ GodotRuntime.free(c_guid);
+ }
+ const pads = GodotDisplayGamepads.get_pads();
+ for (let i = 0; i < pads.length; i++) {
+ // Might be reserved space.
+ if (pads[i]) {
+ add(pads[i]);
+ }
+ }
+ GodotDisplayListeners.add(window, 'gamepadconnected', function (evt) {
+ add(evt.gamepad);
+ }, false);
+ GodotDisplayListeners.add(window, 'gamepaddisconnected', function (evt) {
+ onchange(evt.gamepad.index, 0);
+ }, false);
+ },
+
+ get_guid: function (pad) {
+ if (pad.mapping) {
+ return pad.mapping;
+ }
+ const ua = navigator.userAgent;
+ let os = 'Unknown';
+ if (ua.indexOf('Android') >= 0) {
+ os = 'Android';
+ } else if (ua.indexOf('Linux') >= 0) {
+ os = 'Linux';
+ } else if (ua.indexOf('iPhone') >= 0) {
+ os = 'iOS';
+ } else if (ua.indexOf('Macintosh') >= 0) {
+ // Updated iPads will fall into this category.
+ os = 'MacOSX';
+ } else if (ua.indexOf('Windows') >= 0) {
+ os = 'Windows';
+ }
+
+ const id = pad.id;
+ // Chrom* style: NAME (Vendor: xxxx Product: xxxx)
+ const exp1 = /vendor: ([0-9a-f]{4}) product: ([0-9a-f]{4})/i;
+ // Firefox/Safari style (safari may remove leading zeores)
+ const exp2 = /^([0-9a-f]+)-([0-9a-f]+)-/i;
+ let vendor = '';
+ let product = '';
+ if (exp1.test(id)) {
+ const match = exp1.exec(id);
+ vendor = match[1].padStart(4, '0');
+ product = match[2].padStart(4, '0');
+ } else if (exp2.test(id)) {
+ const match = exp2.exec(id);
+ vendor = match[1].padStart(4, '0');
+ product = match[2].padStart(4, '0');
+ }
+ if (!vendor || !product) {
+ return `${os}Unknown`;
+ }
+ return os + vendor + product;
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotDisplayGamepads);
+
+const GodotDisplayScreen = {
+ $GodotDisplayScreen__deps: ['$GodotConfig', '$GodotOS', '$GL', 'emscripten_webgl_get_current_context'],
+ $GodotDisplayScreen: {
+ desired_size: [0, 0],
+ isFullscreen: function () {
+ const elem = document.fullscreenElement || document.mozFullscreenElement
+ || document.webkitFullscreenElement || document.msFullscreenElement;
+ if (elem) {
+ return elem === GodotConfig.canvas;
+ }
+ // But maybe knowing the element is not supported.
+ return document.fullscreen || document.mozFullScreen
+ || document.webkitIsFullscreen;
+ },
+ hasFullscreen: function () {
+ return document.fullscreenEnabled || document.mozFullScreenEnabled
+ || document.webkitFullscreenEnabled;
+ },
+ requestFullscreen: function () {
+ if (!GodotDisplayScreen.hasFullscreen()) {
+ return 1;
+ }
+ const canvas = GodotConfig.canvas;
+ try {
+ const promise = (canvas.requestFullscreen || canvas.msRequestFullscreen
+ || canvas.mozRequestFullScreen || canvas.mozRequestFullscreen
+ || canvas.webkitRequestFullscreen
+ ).call(canvas);
+ // Some browsers (Safari) return undefined.
+ // For the standard ones, we need to catch it.
+ if (promise) {
+ promise.catch(function () {
+ // nothing to do.
+ });
+ }
+ } catch (e) {
+ return 1;
+ }
+ return 0;
+ },
+ exitFullscreen: function () {
+ if (!GodotDisplayScreen.isFullscreen()) {
+ return 0;
+ }
+ try {
+ const promise = document.exitFullscreen();
+ if (promise) {
+ promise.catch(function () {
+ // nothing to do.
+ });
+ }
+ } catch (e) {
+ return 1;
+ }
+ return 0;
+ },
+ _updateGL: function () {
+ const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef
+ const gl = GL.getContext(gl_context_handle);
+ if (gl) {
+ GL.resizeOffscreenFramebuffer(gl);
+ }
+ },
+ updateSize: function () {
+ const isFullscreen = GodotDisplayScreen.isFullscreen();
+ const wantsFullWindow = GodotConfig.canvas_resize_policy === 2;
+ const noResize = GodotConfig.canvas_resize_policy === 0;
+ const wwidth = GodotDisplayScreen.desired_size[0];
+ const wheight = GodotDisplayScreen.desired_size[1];
+ const canvas = GodotConfig.canvas;
+ let width = wwidth;
+ let height = wheight;
+ if (noResize) {
+ // Don't resize canvas, just update GL if needed.
+ if (canvas.width !== width || canvas.height !== height) {
+ GodotDisplayScreen.desired_size = [canvas.width, canvas.height];
+ GodotDisplayScreen._updateGL();
+ return 1;
+ }
+ return 0;
+ }
+ const scale = window.devicePixelRatio || 1;
+ if (isFullscreen || wantsFullWindow) {
+ // We need to match screen size.
+ width = window.innerWidth * scale;
+ height = window.innerHeight * scale;
+ }
+ const csw = `${width / scale}px`;
+ const csh = `${height / scale}px`;
+ if (canvas.style.width !== csw || canvas.style.height !== csh || canvas.width !== width || canvas.height !== height) {
+ // Size doesn't match.
+ // Resize canvas, set correct CSS pixel size, update GL.
+ canvas.width = width;
+ canvas.height = height;
+ canvas.style.width = csw;
+ canvas.style.height = csh;
+ GodotDisplayScreen._updateGL();
+ return 1;
+ }
+ return 0;
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotDisplayScreen);
+
+/**
+ * Display server interface.
+ *
+ * Exposes all the functions needed by DisplayServer implementation.
+ */
+const GodotDisplay = {
+ $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop', '$GodotDisplayGamepads', '$GodotDisplayScreen'],
+ $GodotDisplay: {
+ window_icon: '',
+ findDPI: function () {
+ function testDPI(dpi) {
+ return window.matchMedia(`(max-resolution: ${dpi}dpi)`).matches;
+ }
+ function bisect(low, high, func) {
+ const mid = parseInt(((high - low) / 2) + low, 10);
+ if (high - low <= 1) {
+ return func(high) ? high : low;
+ }
+ if (func(mid)) {
+ return bisect(low, mid, func);
+ }
+ return bisect(mid, high, func);
+ }
+ try {
+ const dpi = bisect(0, 800, testDPI);
+ return dpi >= 96 ? dpi : 96;
+ } catch (e) {
+ return 96;
+ }
+ },
+ },
+
+ godot_js_display_is_swap_ok_cancel__sig: 'i',
+ godot_js_display_is_swap_ok_cancel: function () {
+ const win = (['Windows', 'Win64', 'Win32', 'WinCE']);
+ const plat = navigator.platform || '';
+ if (win.indexOf(plat) !== -1) {
+ return 1;
+ }
+ return 0;
+ },
+
+ godot_js_display_alert__sig: 'vi',
+ godot_js_display_alert: function (p_text) {
+ window.alert(GodotRuntime.parseString(p_text)); // eslint-disable-line no-alert
+ },
+
+ godot_js_display_screen_dpi_get__sig: 'i',
+ godot_js_display_screen_dpi_get: function () {
+ return GodotDisplay.findDPI();
+ },
+
+ godot_js_display_pixel_ratio_get__sig: 'f',
+ godot_js_display_pixel_ratio_get: function () {
+ return window.devicePixelRatio || 1;
+ },
+
+ godot_js_display_fullscreen_request__sig: 'i',
+ godot_js_display_fullscreen_request: function () {
+ return GodotDisplayScreen.requestFullscreen();
+ },
+
+ godot_js_display_fullscreen_exit__sig: 'i',
+ godot_js_display_fullscreen_exit: function () {
+ return GodotDisplayScreen.exitFullscreen();
+ },
+
+ godot_js_display_desired_size_set__sig: 'v',
+ godot_js_display_desired_size_set: function (width, height) {
+ GodotDisplayScreen.desired_size = [width, height];
+ GodotDisplayScreen.updateSize();
+ },
+
+ godot_js_display_size_update__sig: 'i',
+ godot_js_display_size_update: function () {
+ return GodotDisplayScreen.updateSize();
+ },
+
+ godot_js_display_screen_size_get__sig: 'vii',
+ godot_js_display_screen_size_get: function (width, height) {
+ const scale = window.devicePixelRatio || 1;
+ GodotRuntime.setHeapValue(width, window.screen.width * scale, 'i32');
+ GodotRuntime.setHeapValue(height, window.screen.height * scale, 'i32');
+ },
+
+ godot_js_display_window_size_get: function (p_width, p_height) {
+ GodotRuntime.setHeapValue(p_width, GodotConfig.canvas.width, 'i32');
+ GodotRuntime.setHeapValue(p_height, GodotConfig.canvas.height, 'i32');
+ },
+
+ godot_js_display_compute_position: function (x, y, r_x, r_y) {
+ const canvas = GodotConfig.canvas;
+ const rect = canvas.getBoundingClientRect();
+ const rw = canvas.width / rect.width;
+ const rh = canvas.height / rect.height;
+ GodotRuntime.setHeapValue(r_x, (x - rect.x) * rw, 'i32');
+ GodotRuntime.setHeapValue(r_y, (y - rect.y) * rh, 'i32');
+ },
+
+ /*
+ * Canvas
+ */
+ godot_js_display_canvas_focus__sig: 'v',
+ godot_js_display_canvas_focus: function () {
+ GodotConfig.canvas.focus();
+ },
+
+ godot_js_display_canvas_is_focused__sig: 'i',
+ godot_js_display_canvas_is_focused: function () {
+ return document.activeElement === GodotConfig.canvas;
+ },
+
+ /*
+ * Touchscreen
+ */
+ godot_js_display_touchscreen_is_available__sig: 'i',
+ godot_js_display_touchscreen_is_available: function () {
+ return 'ontouchstart' in window;
+ },
+
+ /*
+ * Clipboard
+ */
+ godot_js_display_clipboard_set__sig: 'ii',
+ godot_js_display_clipboard_set: function (p_text) {
+ const text = GodotRuntime.parseString(p_text);
+ if (!navigator.clipboard || !navigator.clipboard.writeText) {
+ return 1;
+ }
+ navigator.clipboard.writeText(text).catch(function (e) {
+ // Setting OS clipboard is only possible from an input callback.
+ GodotRuntime.error('Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:', e);
+ });
+ return 0;
+ },
+
+ godot_js_display_clipboard_get__sig: 'ii',
+ godot_js_display_clipboard_get: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ try {
+ navigator.clipboard.readText().then(function (result) {
+ const ptr = GodotRuntime.allocString(result);
+ func(ptr);
+ GodotRuntime.free(ptr);
+ }).catch(function (e) {
+ // Fail graciously.
+ });
+ } catch (e) {
+ // Fail graciously.
+ }
+ },
+
+ /*
+ * Window
+ */
+ godot_js_display_window_title_set__sig: 'vi',
+ godot_js_display_window_title_set: function (p_data) {
+ document.title = GodotRuntime.parseString(p_data);
+ },
+
+ godot_js_display_window_icon_set__sig: 'vii',
+ godot_js_display_window_icon_set: function (p_ptr, p_len) {
+ let link = document.getElementById('-gd-engine-icon');
+ if (link === null) {
+ link = document.createElement('link');
+ link.rel = 'icon';
+ link.id = '-gd-engine-icon';
+ document.head.appendChild(link);
+ }
+ const old_icon = GodotDisplay.window_icon;
+ const png = new Blob([GodotRuntime.heapCopy(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
+ GodotDisplay.window_icon = URL.createObjectURL(png);
+ link.href = GodotDisplay.window_icon;
+ if (old_icon) {
+ URL.revokeObjectURL(old_icon);
+ }
+ },
+
+ /*
+ * Cursor
+ */
+ godot_js_display_cursor_set_visible__sig: 'vi',
+ godot_js_display_cursor_set_visible: function (p_visible) {
+ const visible = p_visible !== 0;
+ if (visible === GodotDisplayCursor.visible) {
+ return;
+ }
+ GodotDisplayCursor.visible = visible;
+ if (visible) {
+ GodotDisplayCursor.set_shape(GodotDisplayCursor.shape);
+ } else {
+ GodotDisplayCursor.set_style('none');
+ }
+ },
+
+ godot_js_display_cursor_is_hidden__sig: 'i',
+ godot_js_display_cursor_is_hidden: function () {
+ return !GodotDisplayCursor.visible;
+ },
+
+ godot_js_display_cursor_set_shape__sig: 'vi',
+ godot_js_display_cursor_set_shape: function (p_string) {
+ GodotDisplayCursor.set_shape(GodotRuntime.parseString(p_string));
+ },
+
+ godot_js_display_cursor_set_custom_shape__sig: 'viiiii',
+ godot_js_display_cursor_set_custom_shape: function (p_shape, p_ptr, p_len, p_hotspot_x, p_hotspot_y) {
+ const shape = GodotRuntime.parseString(p_shape);
+ const old_shape = GodotDisplayCursor.cursors[shape];
+ if (p_len > 0) {
+ const png = new Blob([GodotRuntime.heapCopy(HEAPU8, p_ptr, p_len)], { type: 'image/png' });
+ const url = URL.createObjectURL(png);
+ GodotDisplayCursor.cursors[shape] = {
+ url: url,
+ x: p_hotspot_x,
+ y: p_hotspot_y,
+ };
+ } else {
+ delete GodotDisplayCursor.cursors[shape];
+ }
+ if (shape === GodotDisplayCursor.shape) {
+ GodotDisplayCursor.set_shape(GodotDisplayCursor.shape);
+ }
+ if (old_shape) {
+ URL.revokeObjectURL(old_shape.url);
+ }
+ },
+
+ /*
+ * Listeners
+ */
+ godot_js_display_notification_cb__sig: 'viiiii',
+ godot_js_display_notification_cb: function (callback, p_enter, p_exit, p_in, p_out) {
+ const canvas = GodotConfig.canvas;
+ const func = GodotRuntime.get_func(callback);
+ const notif = [p_enter, p_exit, p_in, p_out];
+ ['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function (evt_name, idx) {
+ GodotDisplayListeners.add(canvas, evt_name, function () {
+ func.bind(null, notif[idx]);
+ }, true);
+ });
+ },
+
+ godot_js_display_paste_cb__sig: 'vi',
+ godot_js_display_paste_cb: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ GodotDisplayListeners.add(window, 'paste', function (evt) {
+ const text = evt.clipboardData.getData('text');
+ const ptr = GodotRuntime.allocString(text);
+ func(ptr);
+ GodotRuntime.free(ptr);
+ }, false);
+ },
+
+ godot_js_display_drop_files_cb__sig: 'vi',
+ godot_js_display_drop_files_cb: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ const dropFiles = function (files) {
+ const args = files || [];
+ if (!args.length) {
+ return;
+ }
+ const argc = args.length;
+ const argv = GodotRuntime.allocStringArray(args);
+ func(argv, argc);
+ GodotRuntime.freeStringArray(argv, argc);
+ };
+ const canvas = GodotConfig.canvas;
+ GodotDisplayListeners.add(canvas, 'dragover', function (ev) {
+ // Prevent default behavior (which would try to open the file(s))
+ ev.preventDefault();
+ }, false);
+ GodotDisplayListeners.add(canvas, 'drop', GodotDisplayDragDrop.handler(dropFiles));
+ },
+
+ godot_js_display_setup_canvas__sig: 'viii',
+ godot_js_display_setup_canvas: function (p_width, p_height, p_fullscreen) {
+ const canvas = GodotConfig.canvas;
+ GodotDisplayListeners.add(canvas, 'contextmenu', function (ev) {
+ ev.preventDefault();
+ }, false);
+ GodotDisplayListeners.add(canvas, 'webglcontextlost', function (ev) {
+ alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert
+ ev.preventDefault();
+ }, false);
+ switch (GodotConfig.canvas_resize_policy) {
+ case 0: // None
+ GodotDisplayScreen.desired_size = [canvas.width, canvas.height];
+ break;
+ case 1: // Project
+ GodotDisplayScreen.desired_size = [p_width, p_height];
+ break;
+ default: // Full window
+ // Ensure we display in the right place, the size will be handled by updateSize
+ canvas.style.position = 'absolute';
+ canvas.style.top = 0;
+ canvas.style.left = 0;
+ break;
+ }
+ if (p_fullscreen) {
+ GodotDisplayScreen.requestFullscreen();
+ }
+ },
+
+ /*
+ * Gamepads
+ */
+ godot_js_display_gamepad_cb__sig: 'vi',
+ godot_js_display_gamepad_cb: function (change_cb) {
+ const onchange = GodotRuntime.get_func(change_cb);
+ GodotDisplayGamepads.init(onchange);
+ },
+
+ godot_js_display_gamepad_sample_count__sig: 'i',
+ godot_js_display_gamepad_sample_count: function () {
+ return GodotDisplayGamepads.get_samples().length;
+ },
+
+ godot_js_display_gamepad_sample__sig: 'i',
+ godot_js_display_gamepad_sample: function () {
+ GodotDisplayGamepads.sample();
+ return 0;
+ },
+
+ godot_js_display_gamepad_sample_get__sig: 'iiiiiii',
+ godot_js_display_gamepad_sample_get: function (p_index, r_btns, r_btns_num, r_axes, r_axes_num, r_standard) {
+ const sample = GodotDisplayGamepads.get_sample(p_index);
+ if (!sample || !sample.connected) {
+ return 1;
+ }
+ const btns = sample.buttons;
+ const btns_len = btns.length < 16 ? btns.length : 16;
+ for (let i = 0; i < btns_len; i++) {
+ GodotRuntime.setHeapValue(r_btns + (i << 2), btns[i], 'float');
+ }
+ GodotRuntime.setHeapValue(r_btns_num, btns_len, 'i32');
+ const axes = sample.axes;
+ const axes_len = axes.length < 10 ? axes.length : 10;
+ for (let i = 0; i < axes_len; i++) {
+ GodotRuntime.setHeapValue(r_axes + (i << 2), axes[i], 'float');
+ }
+ GodotRuntime.setHeapValue(r_axes_num, axes_len, 'i32');
+ const is_standard = sample.standard ? 1 : 0;
+ GodotRuntime.setHeapValue(r_standard, is_standard, 'i32');
+ return 0;
+ },
+};
+
+autoAddDeps(GodotDisplay, '$GodotDisplay');
+mergeInto(LibraryManager.library, GodotDisplay);
diff --git a/platform/haiku/audio_driver_media_kit.h b/platform/javascript/js/libs/library_godot_editor_tools.js
index 8272780fa7..d7f1ad5ea1 100644
--- a/platform/haiku/audio_driver_media_kit.h
+++ b/platform/javascript/js/libs/library_godot_editor_tools.js
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* audio_driver_media_kit.h */
+/* library_godot_editor_tools.js */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,47 +28,30 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "servers/audio_server.h"
-
-#ifdef MEDIA_KIT_ENABLED
-
-#include "core/os/mutex.h"
-#include "core/os/thread.h"
-
-#include <kernel/image.h> // needed for image_id
-
-#include <SoundPlayer.h>
-
-class AudioDriverMediaKit : public AudioDriver {
- Mutex mutex;
-
- BSoundPlayer *player;
- static int32_t *samples_in;
-
- static void PlayBuffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format);
-
- unsigned int mix_rate;
- SpeakerMode speaker_mode;
- unsigned int buffer_size;
- int channels;
-
- bool active;
-
-public:
- const char *get_name() const {
- return "MediaKit";
- };
-
- virtual Error init();
- virtual void start();
- virtual int get_mix_rate() const;
- virtual SpeakerMode get_speaker_mode() const;
- virtual void lock();
- virtual void unlock();
- virtual void finish();
-
- AudioDriverMediaKit();
- ~AudioDriverMediaKit();
+const GodotEditorTools = {
+ godot_js_editor_download_file__deps: ['$FS'],
+ godot_js_editor_download_file__sig: 'viii',
+ godot_js_editor_download_file: function (p_path, p_name, p_mime) {
+ const path = GodotRuntime.parseString(p_path);
+ const name = GodotRuntime.parseString(p_name);
+ const mime = GodotRuntime.parseString(p_mime);
+ const size = FS.stat(path)['size'];
+ const buf = new Uint8Array(size);
+ const fd = FS.open(path, 'r');
+ FS.read(fd, buf, 0, size);
+ FS.close(fd);
+ FS.unlink(path);
+ const blob = new Blob([buf], { type: mime });
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = name;
+ a.style.display = 'none';
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ window.URL.revokeObjectURL(url);
+ },
};
-#endif
+mergeInto(LibraryManager.library, GodotEditorTools);
diff --git a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java b/platform/javascript/js/libs/library_godot_eval.js
index 6b66c7e474..9ab392b813 100644
--- a/platform/android/java/plugins/godotpayment/src/main/java/org/godotengine/godot/plugin/payment/utils/RequestParams.java
+++ b/platform/javascript/js/libs/library_godot_eval.js
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* RequestParams.java */
+/* library_godot_eval.js */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,57 +28,59 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.plugin.payment.utils;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import org.apache.http.NameValuePair;
-import org.apache.http.message.BasicNameValuePair;
-
-/**
- *
- * @author Luis Linietsky <luis.linietsky@gmail.com>
- */
-public class RequestParams {
- private HashMap<String, String> params;
- private String url;
-
- public RequestParams() {
- params = new HashMap<String, String>();
- }
+const GodotEval = {
+ godot_js_eval__deps: ['$GodotRuntime'],
+ godot_js_eval__sig: 'iiiiiii',
+ godot_js_eval: function (p_js, p_use_global_ctx, p_union_ptr, p_byte_arr, p_byte_arr_write, p_callback) {
+ const js_code = GodotRuntime.parseString(p_js);
+ let eval_ret = null;
+ try {
+ if (p_use_global_ctx) {
+ // indirect eval call grants global execution context
+ const global_eval = eval; // eslint-disable-line no-eval
+ eval_ret = global_eval(js_code);
+ } else {
+ eval_ret = eval(js_code); // eslint-disable-line no-eval
+ }
+ } catch (e) {
+ GodotRuntime.error(e);
+ }
- public void put(String key, String value) {
- params.put(key, value);
- }
+ switch (typeof eval_ret) {
+ case 'boolean':
+ GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'i32');
+ return 1; // BOOL
- public String get(String key) {
- return params.get(key);
- }
+ case 'number':
+ GodotRuntime.setHeapValue(p_union_ptr, eval_ret, 'double');
+ return 3; // REAL
- public void remove(Object key) {
- params.remove(key);
- }
+ case 'string':
+ GodotRuntime.setHeapValue(p_union_ptr, GodotRuntime.allocString(eval_ret), '*');
+ return 4; // STRING
- public boolean has(String key) {
- return params.containsKey(key);
- }
+ case 'object':
+ if (eval_ret === null) {
+ break;
+ }
- public List<NameValuePair> toPairsList() {
- List<NameValuePair> fields = new ArrayList<NameValuePair>();
+ if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) {
+ eval_ret = new Uint8Array(eval_ret.buffer);
+ } else if (eval_ret instanceof ArrayBuffer) {
+ eval_ret = new Uint8Array(eval_ret);
+ }
+ if (eval_ret instanceof Uint8Array) {
+ const func = GodotRuntime.get_func(p_callback);
+ const bytes_ptr = func(p_byte_arr, p_byte_arr_write, eval_ret.length);
+ HEAPU8.set(eval_ret, bytes_ptr);
+ return 20; // POOL_BYTE_ARRAY
+ }
+ break;
- for (String key : params.keySet()) {
- fields.add(new BasicNameValuePair(key, this.get(key)));
+ // no default
}
- return fields;
- }
-
- public String getUrl() {
- return url;
- }
+ return 0; // NIL
+ },
+};
- public void setUrl(String url) {
- this.url = url;
- }
-}
+mergeInto(LibraryManager.library, GodotEval);
diff --git a/platform/javascript/native/http_request.js b/platform/javascript/js/libs/library_godot_http_request.js
index f621689f9d..7dc2a2df29 100644
--- a/platform/javascript/native/http_request.js
+++ b/platform/javascript/js/libs/library_godot_http_request.js
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,120 +27,116 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-var GodotHTTPRequest = {
+const GodotHTTPRequest = {
+ $GodotHTTPRequest__deps: ['$GodotRuntime'],
$GodotHTTPRequest: {
-
requests: [],
- getUnusedRequestId: function() {
- var idMax = GodotHTTPRequest.requests.length;
- for (var potentialId = 0; potentialId < idMax; ++potentialId) {
+ getUnusedRequestId: function () {
+ const idMax = GodotHTTPRequest.requests.length;
+ for (let potentialId = 0; potentialId < idMax; ++potentialId) {
if (GodotHTTPRequest.requests[potentialId] instanceof XMLHttpRequest) {
continue;
}
return potentialId;
}
- GodotHTTPRequest.requests.push(null)
+ GodotHTTPRequest.requests.push(null);
return idMax;
},
- setupRequest: function(xhr) {
+ setupRequest: function (xhr) {
xhr.responseType = 'arraybuffer';
},
},
- godot_xhr_new: function() {
- var newId = GodotHTTPRequest.getUnusedRequestId();
- GodotHTTPRequest.requests[newId] = new XMLHttpRequest;
+ godot_xhr_new__sig: 'i',
+ godot_xhr_new: function () {
+ const newId = GodotHTTPRequest.getUnusedRequestId();
+ GodotHTTPRequest.requests[newId] = new XMLHttpRequest();
GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[newId]);
return newId;
},
- godot_xhr_reset: function(xhrId) {
- GodotHTTPRequest.requests[xhrId] = new XMLHttpRequest;
+ godot_xhr_reset__sig: 'vi',
+ godot_xhr_reset: function (xhrId) {
+ GodotHTTPRequest.requests[xhrId] = new XMLHttpRequest();
GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[xhrId]);
},
- godot_xhr_free: function(xhrId) {
+ godot_xhr_free__sig: 'vi',
+ godot_xhr_free: function (xhrId) {
GodotHTTPRequest.requests[xhrId].abort();
GodotHTTPRequest.requests[xhrId] = null;
},
- godot_xhr_open: function(xhrId, method, url, user, password) {
- user = user > 0 ? UTF8ToString(user) : null;
- password = password > 0 ? UTF8ToString(password) : null;
- GodotHTTPRequest.requests[xhrId].open(UTF8ToString(method), UTF8ToString(url), true, user, password);
- },
-
- godot_xhr_set_request_header: function(xhrId, header, value) {
- GodotHTTPRequest.requests[xhrId].setRequestHeader(UTF8ToString(header), UTF8ToString(value));
+ godot_xhr_open__sig: 'viiiii',
+ godot_xhr_open: function (xhrId, method, url, p_user, p_password) {
+ const user = p_user > 0 ? GodotRuntime.parseString(p_user) : null;
+ const password = p_password > 0 ? GodotRuntime.parseString(p_password) : null;
+ GodotHTTPRequest.requests[xhrId].open(GodotRuntime.parseString(method), GodotRuntime.parseString(url), true, user, password);
},
- godot_xhr_send_null: function(xhrId) {
- GodotHTTPRequest.requests[xhrId].send();
+ godot_xhr_set_request_header__sig: 'viii',
+ godot_xhr_set_request_header: function (xhrId, header, value) {
+ GodotHTTPRequest.requests[xhrId].setRequestHeader(GodotRuntime.parseString(header), GodotRuntime.parseString(value));
},
- godot_xhr_send_string: function(xhrId, strPtr) {
- if (!strPtr) {
- err("Failed to send string per XHR: null pointer");
- return;
+ godot_xhr_send__sig: 'viii',
+ godot_xhr_send: function (xhrId, p_ptr, p_len) {
+ let data = null;
+ if (p_ptr && p_len) {
+ data = GodotRuntime.heapCopy(HEAP8, p_ptr, p_len);
}
- GodotHTTPRequest.requests[xhrId].send(UTF8ToString(strPtr));
+ GodotHTTPRequest.requests[xhrId].send(data);
},
- godot_xhr_send_data: function(xhrId, ptr, len) {
- if (!ptr) {
- err("Failed to send data per XHR: null pointer");
- return;
- }
- if (len < 0) {
- err("Failed to send data per XHR: buffer length less than 0");
- return;
- }
- GodotHTTPRequest.requests[xhrId].send(HEAPU8.subarray(ptr, ptr + len));
- },
-
- godot_xhr_abort: function(xhrId) {
+ godot_xhr_abort__sig: 'vi',
+ godot_xhr_abort: function (xhrId) {
GodotHTTPRequest.requests[xhrId].abort();
},
- godot_xhr_get_status: function(xhrId) {
+ godot_xhr_get_status__sig: 'ii',
+ godot_xhr_get_status: function (xhrId) {
return GodotHTTPRequest.requests[xhrId].status;
},
- godot_xhr_get_ready_state: function(xhrId) {
+ godot_xhr_get_ready_state__sig: 'ii',
+ godot_xhr_get_ready_state: function (xhrId) {
return GodotHTTPRequest.requests[xhrId].readyState;
},
- godot_xhr_get_response_headers_length: function(xhrId) {
- var headers = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
- return headers === null ? 0 : lengthBytesUTF8(headers);
+ godot_xhr_get_response_headers_length__sig: 'ii',
+ godot_xhr_get_response_headers_length: function (xhrId) {
+ const headers = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
+ return headers === null ? 0 : GodotRuntime.strlen(headers);
},
- godot_xhr_get_response_headers: function(xhrId, dst, len) {
- var str = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
- if (str === null)
+ godot_xhr_get_response_headers__sig: 'viii',
+ godot_xhr_get_response_headers: function (xhrId, dst, len) {
+ const str = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders();
+ if (str === null) {
return;
- var buf = new Uint8Array(len + 1);
- stringToUTF8Array(str, buf, 0, buf.length);
- buf = buf.subarray(0, -1);
- HEAPU8.set(buf, dst);
+ }
+ GodotRuntime.stringToHeap(str, dst, len);
},
- godot_xhr_get_response_length: function(xhrId) {
- var body = GodotHTTPRequest.requests[xhrId].response;
+ godot_xhr_get_response_length__sig: 'ii',
+ godot_xhr_get_response_length: function (xhrId) {
+ const body = GodotHTTPRequest.requests[xhrId].response;
return body === null ? 0 : body.byteLength;
},
- godot_xhr_get_response: function(xhrId, dst, len) {
- var buf = GodotHTTPRequest.requests[xhrId].response;
- if (buf === null)
+ godot_xhr_get_response__sig: 'viii',
+ godot_xhr_get_response: function (xhrId, dst, len) {
+ let buf = GodotHTTPRequest.requests[xhrId].response;
+ if (buf === null) {
return;
+ }
buf = new Uint8Array(buf).subarray(0, len);
HEAPU8.set(buf, dst);
},
};
-autoAddDeps(GodotHTTPRequest, "$GodotHTTPRequest");
+autoAddDeps(GodotHTTPRequest, '$GodotHTTPRequest');
mergeInto(LibraryManager.library, GodotHTTPRequest);
diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js
new file mode 100644
index 0000000000..0f189b013c
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_os.js
@@ -0,0 +1,304 @@
+/*************************************************************************/
+/* library_godot_os.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+const IDHandler = {
+ $IDHandler: {
+ _last_id: 0,
+ _references: {},
+
+ get: function (p_id) {
+ return IDHandler._references[p_id];
+ },
+
+ add: function (p_data) {
+ const id = ++IDHandler._last_id;
+ IDHandler._references[id] = p_data;
+ return id;
+ },
+
+ remove: function (p_id) {
+ delete IDHandler._references[p_id];
+ },
+ },
+};
+
+autoAddDeps(IDHandler, '$IDHandler');
+mergeInto(LibraryManager.library, IDHandler);
+
+const GodotConfig = {
+ $GodotConfig__postset: 'Module["initConfig"] = GodotConfig.init_config;',
+ $GodotConfig__deps: ['$GodotRuntime'],
+ $GodotConfig: {
+ canvas: null,
+ locale: 'en',
+ canvas_resize_policy: 2, // Adaptive
+ on_execute: null,
+ on_exit: null,
+
+ init_config: function (p_opts) {
+ GodotConfig.canvas_resize_policy = p_opts['canvasResizePolicy'];
+ GodotConfig.canvas = p_opts['canvas'];
+ GodotConfig.locale = p_opts['locale'] || GodotConfig.locale;
+ GodotConfig.on_execute = p_opts['onExecute'];
+ GodotConfig.on_exit = p_opts['onExit'];
+ },
+
+ locate_file: function (file) {
+ return Module['locateFile'](file); // eslint-disable-line no-undef
+ },
+ clear: function () {
+ GodotConfig.canvas = null;
+ GodotConfig.locale = 'en';
+ GodotConfig.canvas_resize_policy = 2;
+ GodotConfig.on_execute = null;
+ GodotConfig.on_exit = null;
+ },
+ },
+
+ godot_js_config_canvas_id_get__sig: 'vii',
+ godot_js_config_canvas_id_get: function (p_ptr, p_ptr_max) {
+ GodotRuntime.stringToHeap(`#${GodotConfig.canvas.id}`, p_ptr, p_ptr_max);
+ },
+
+ godot_js_config_locale_get__sig: 'vii',
+ godot_js_config_locale_get: function (p_ptr, p_ptr_max) {
+ GodotRuntime.stringToHeap(GodotConfig.locale, p_ptr, p_ptr_max);
+ },
+};
+
+autoAddDeps(GodotConfig, '$GodotConfig');
+mergeInto(LibraryManager.library, GodotConfig);
+
+const GodotFS = {
+ $GodotFS__deps: ['$FS', '$IDBFS', '$GodotRuntime'],
+ $GodotFS__postset: [
+ 'Module["initFS"] = GodotFS.init;',
+ 'Module["copyToFS"] = GodotFS.copy_to_fs;',
+ ].join(''),
+ $GodotFS: {
+ _idbfs: false,
+ _syncing: false,
+ _mount_points: [],
+
+ is_persistent: function () {
+ return GodotFS._idbfs ? 1 : 0;
+ },
+
+ // Initialize godot file system, setting up persistent paths.
+ // Returns a promise that resolves when the FS is ready.
+ // We keep track of mount_points, so that we can properly close the IDBFS
+ // since emscripten is not doing it by itself. (emscripten GH#12516).
+ init: function (persistentPaths) {
+ GodotFS._idbfs = false;
+ if (!Array.isArray(persistentPaths)) {
+ return Promise.reject(new Error('Persistent paths must be an array'));
+ }
+ if (!persistentPaths.length) {
+ return Promise.resolve();
+ }
+ GodotFS._mount_points = persistentPaths.slice();
+
+ function createRecursive(dir) {
+ try {
+ FS.stat(dir);
+ } catch (e) {
+ if (e.errno !== ERRNO_CODES.ENOENT) {
+ throw e;
+ }
+ FS.mkdirTree(dir);
+ }
+ }
+
+ GodotFS._mount_points.forEach(function (path) {
+ createRecursive(path);
+ FS.mount(IDBFS, {}, path);
+ });
+ return new Promise(function (resolve, reject) {
+ FS.syncfs(true, function (err) {
+ if (err) {
+ GodotFS._mount_points = [];
+ GodotFS._idbfs = false;
+ GodotRuntime.print(`IndexedDB not available: ${err.message}`);
+ } else {
+ GodotFS._idbfs = true;
+ }
+ resolve(err);
+ });
+ });
+ },
+
+ // Deinit godot file system, making sure to unmount file systems, and close IDBFS(s).
+ deinit: function () {
+ GodotFS._mount_points.forEach(function (path) {
+ try {
+ FS.unmount(path);
+ } catch (e) {
+ GodotRuntime.print('Already unmounted', e);
+ }
+ if (GodotFS._idbfs && IDBFS.dbs[path]) {
+ IDBFS.dbs[path].close();
+ delete IDBFS.dbs[path];
+ }
+ });
+ GodotFS._mount_points = [];
+ GodotFS._idbfs = false;
+ GodotFS._syncing = false;
+ },
+
+ sync: function () {
+ if (GodotFS._syncing) {
+ GodotRuntime.error('Already syncing!');
+ return Promise.resolve();
+ }
+ GodotFS._syncing = true;
+ return new Promise(function (resolve, reject) {
+ FS.syncfs(false, function (error) {
+ if (error) {
+ GodotRuntime.error(`Failed to save IDB file system: ${error.message}`);
+ }
+ GodotFS._syncing = false;
+ resolve(error);
+ });
+ });
+ },
+
+ // Copies a buffer to the internal file system. Creating directories recursively.
+ copy_to_fs: function (path, buffer) {
+ const idx = path.lastIndexOf('/');
+ let dir = '/';
+ if (idx > 0) {
+ dir = path.slice(0, idx);
+ }
+ try {
+ FS.stat(dir);
+ } catch (e) {
+ if (e.errno !== ERRNO_CODES.ENOENT) {
+ throw e;
+ }
+ FS.mkdirTree(dir);
+ }
+ FS.writeFile(path, new Uint8Array(buffer));
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotFS);
+
+const GodotOS = {
+ $GodotOS__deps: ['$GodotRuntime', '$GodotConfig', '$GodotFS'],
+ $GodotOS__postset: [
+ 'Module["request_quit"] = function() { GodotOS.request_quit() };',
+ 'Module["onExit"] = GodotOS.cleanup;',
+ 'GodotOS._fs_sync_promise = Promise.resolve();',
+ ].join(''),
+ $GodotOS: {
+ request_quit: function () {},
+ _async_cbs: [],
+ _fs_sync_promise: null,
+
+ atexit: function (p_promise_cb) {
+ GodotOS._async_cbs.push(p_promise_cb);
+ },
+
+ cleanup: function (exit_code) {
+ const cb = GodotConfig.on_exit;
+ GodotFS.deinit();
+ GodotConfig.clear();
+ if (cb) {
+ cb(exit_code);
+ }
+ },
+
+ finish_async: function (callback) {
+ GodotOS._fs_sync_promise.then(function (err) {
+ const promises = [];
+ GodotOS._async_cbs.forEach(function (cb) {
+ promises.push(new Promise(cb));
+ });
+ return Promise.all(promises);
+ }).then(function () {
+ return GodotFS.sync(); // Final FS sync.
+ }).then(function (err) {
+ // Always deferred.
+ setTimeout(function () {
+ callback();
+ }, 0);
+ });
+ },
+ },
+
+ godot_js_os_finish_async__sig: 'vi',
+ godot_js_os_finish_async: function (p_callback) {
+ const func = GodotRuntime.get_func(p_callback);
+ GodotOS.finish_async(func);
+ },
+
+ godot_js_os_request_quit_cb__sig: 'vi',
+ godot_js_os_request_quit_cb: function (p_callback) {
+ GodotOS.request_quit = GodotRuntime.get_func(p_callback);
+ },
+
+ godot_js_os_fs_is_persistent__sig: 'i',
+ godot_js_os_fs_is_persistent: function () {
+ return GodotFS.is_persistent();
+ },
+
+ godot_js_os_fs_sync__sig: 'vi',
+ godot_js_os_fs_sync: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ GodotOS._fs_sync_promise = GodotFS.sync();
+ GodotOS._fs_sync_promise.then(function (err) {
+ func();
+ });
+ },
+
+ godot_js_os_execute__sig: 'ii',
+ godot_js_os_execute: function (p_json) {
+ const json_args = GodotRuntime.parseString(p_json);
+ const args = JSON.parse(json_args);
+ if (GodotConfig.on_execute) {
+ GodotConfig.on_execute(args);
+ return 0;
+ }
+ return 1;
+ },
+
+ godot_js_os_shell_open__sig: 'vi',
+ godot_js_os_shell_open: function (p_uri) {
+ window.open(GodotRuntime.parseString(p_uri), '_blank');
+ },
+
+ godot_js_os_hw_concurrency_get__sig: 'i',
+ godot_js_os_hw_concurrency_get: function () {
+ return navigator.hardwareConcurrency || 1;
+ },
+};
+
+autoAddDeps(GodotOS, '$GodotOS');
+mergeInto(LibraryManager.library, GodotOS);
diff --git a/platform/javascript/js/libs/library_godot_runtime.js b/platform/javascript/js/libs/library_godot_runtime.js
new file mode 100644
index 0000000000..7e36ff8ab5
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_runtime.js
@@ -0,0 +1,120 @@
+/*************************************************************************/
+/* library_godot_runtime.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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. */
+/*************************************************************************/
+
+const GodotRuntime = {
+ $GodotRuntime: {
+ /*
+ * Functions
+ */
+ get_func: function (ptr) {
+ return wasmTable.get(ptr); // eslint-disable-line no-undef
+ },
+
+ /*
+ * Prints
+ */
+ error: function () {
+ err.apply(null, Array.from(arguments)); // eslint-disable-line no-undef
+ },
+
+ print: function () {
+ out.apply(null, Array.from(arguments)); // eslint-disable-line no-undef
+ },
+
+ /*
+ * Memory
+ */
+ malloc: function (p_size) {
+ return _malloc(p_size); // eslint-disable-line no-undef
+ },
+
+ free: function (p_ptr) {
+ _free(p_ptr); // eslint-disable-line no-undef
+ },
+
+ getHeapValue: function (p_ptr, p_type) {
+ return getValue(p_ptr, p_type); // eslint-disable-line no-undef
+ },
+
+ setHeapValue: function (p_ptr, p_value, p_type) {
+ setValue(p_ptr, p_value, p_type); // eslint-disable-line no-undef
+ },
+
+ heapSub: function (p_heap, p_ptr, p_len) {
+ const bytes = p_heap.BYTES_PER_ELEMENT;
+ return p_heap.subarray(p_ptr / bytes, p_ptr / bytes + p_len);
+ },
+
+ heapCopy: function (p_heap, p_ptr, p_len) {
+ const bytes = p_heap.BYTES_PER_ELEMENT;
+ return p_heap.slice(p_ptr / bytes, p_ptr / bytes + p_len);
+ },
+
+ /*
+ * Strings
+ */
+ parseString: function (p_ptr) {
+ return UTF8ToString(p_ptr); // eslint-disable-line no-undef
+ },
+
+ strlen: function (p_str) {
+ return lengthBytesUTF8(p_str); // eslint-disable-line no-undef
+ },
+
+ allocString: function (p_str) {
+ const length = GodotRuntime.strlen(p_str) + 1;
+ const c_str = GodotRuntime.malloc(length);
+ stringToUTF8(p_str, c_str, length); // eslint-disable-line no-undef
+ return c_str;
+ },
+
+ allocStringArray: function (p_strings) {
+ const size = p_strings.length;
+ const c_ptr = GodotRuntime.malloc(size * 4);
+ for (let i = 0; i < size; i++) {
+ HEAP32[(c_ptr >> 2) + i] = GodotRuntime.allocString(p_strings[i]);
+ }
+ return c_ptr;
+ },
+
+ freeStringArray: function (p_ptr, p_len) {
+ for (let i = 0; i < p_len; i++) {
+ GodotRuntime.free(HEAP32[(p_ptr >> 2) + i]);
+ }
+ GodotRuntime.free(p_ptr);
+ },
+
+ stringToHeap: function (p_str, p_ptr, p_len) {
+ return stringToUTF8Array(p_str, HEAP8, p_ptr, p_len); // eslint-disable-line no-undef
+ },
+ },
+};
+autoAddDeps(GodotRuntime, '$GodotRuntime');
+mergeInto(LibraryManager.library, GodotRuntime);
diff --git a/platform/javascript/native/utils.js b/platform/javascript/native/utils.js
deleted file mode 100644
index 95585d26ae..0000000000
--- a/platform/javascript/native/utils.js
+++ /dev/null
@@ -1,204 +0,0 @@
-/*************************************************************************/
-/* utils.js */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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. */
-/*************************************************************************/
-
-Module['copyToFS'] = function(path, buffer) {
- var p = path.lastIndexOf("/");
- var dir = "/";
- if (p > 0) {
- dir = path.slice(0, path.lastIndexOf("/"));
- }
- try {
- FS.stat(dir);
- } catch (e) {
- if (e.errno !== ERRNO_CODES.ENOENT) { // 'ENOENT', see https://github.com/emscripten-core/emscripten/blob/master/system/lib/libc/musl/arch/emscripten/bits/errno.h
- throw e;
- }
- FS.mkdirTree(dir);
- }
- // With memory growth, canOwn should be false.
- FS.writeFile(path, new Uint8Array(buffer), {'flags': 'wx+'});
-}
-
-Module.drop_handler = (function() {
- var upload = [];
- var uploadPromises = [];
- var uploadCallback = null;
-
- function readFilePromise(entry, path) {
- return new Promise(function(resolve, reject) {
- entry.file(function(file) {
- var reader = new FileReader();
- reader.onload = function() {
- var f = {
- "path": file.relativePath || file.webkitRelativePath,
- "name": file.name,
- "type": file.type,
- "size": file.size,
- "data": reader.result
- };
- if (!f['path'])
- f['path'] = f['name'];
- upload.push(f);
- resolve()
- };
- reader.onerror = function() {
- console.log("Error reading file");
- reject();
- }
-
- reader.readAsArrayBuffer(file);
-
- }, function(err) {
- console.log("Error!");
- reject();
- });
- });
- }
-
- function readDirectoryPromise(entry) {
- return new Promise(function(resolve, reject) {
- var reader = entry.createReader();
- reader.readEntries(function(entries) {
- for (var i = 0; i < entries.length; i++) {
- var ent = entries[i];
- if (ent.isDirectory) {
- uploadPromises.push(readDirectoryPromise(ent));
- } else if (ent.isFile) {
- uploadPromises.push(readFilePromise(ent));
- }
- }
- resolve();
- });
- });
- }
-
- function processUploadsPromises(resolve, reject) {
- if (uploadPromises.length == 0) {
- resolve();
- return;
- }
- uploadPromises.pop().then(function() {
- setTimeout(function() {
- processUploadsPromises(resolve, reject);
- //processUploadsPromises.bind(null, resolve, reject)
- }, 0);
- });
- }
-
- function dropFiles(files) {
- var args = files || [];
- var argc = args.length;
- var argv = stackAlloc((argc + 1) * 4);
- for (var i = 0; i < argc; i++) {
- HEAP32[(argv >> 2) + i] = allocateUTF8OnStack(args[i]);
- }
- HEAP32[(argv >> 2) + argc] = 0;
- // Defined in display_server_javascript.cpp
- ccall('_drop_files_callback', 'void', ['number', 'number'], [argv, argc]);
- }
-
- return function(ev) {
- ev.preventDefault();
- if (ev.dataTransfer.items) {
- // Use DataTransferItemList interface to access the file(s)
- for (var i = 0; i < ev.dataTransfer.items.length; i++) {
- const item = ev.dataTransfer.items[i];
- var entry = null;
- if ("getAsEntry" in item) {
- entry = item.getAsEntry();
- } else if ("webkitGetAsEntry" in item) {
- entry = item.webkitGetAsEntry();
- }
- if (!entry) {
- console.error("File upload not supported");
- } else if (entry.isDirectory) {
- uploadPromises.push(readDirectoryPromise(entry));
- } else if (entry.isFile) {
- uploadPromises.push(readFilePromise(entry));
- } else {
- console.error("Unrecognized entry...", entry);
- }
- }
- } else {
- console.error("File upload not supported");
- }
- uploadCallback = new Promise(processUploadsPromises).then(function() {
- const DROP = "/tmp/drop-" + parseInt(Math.random() * Math.pow(2, 31)) + "/";
- var drops = [];
- var files = [];
- upload.forEach((elem) => {
- var path = elem['path'];
- Module['copyToFS'](DROP + path, elem['data']);
- var idx = path.indexOf("/");
- if (idx == -1) {
- // Root file
- drops.push(DROP + path);
- } else {
- // Subdir
- var sub = path.substr(0, idx);
- idx = sub.indexOf("/");
- if (idx < 0 && drops.indexOf(DROP + sub) == -1) {
- drops.push(DROP + sub);
- }
- }
- files.push(DROP + path);
- });
- uploadPromises = [];
- upload = [];
- dropFiles(drops);
- var dirs = [DROP.substr(0, DROP.length -1)];
- files.forEach(function (file) {
- FS.unlink(file);
- var dir = file.replace(DROP, "");
- var idx = dir.lastIndexOf("/");
- while (idx > 0) {
- dir = dir.substr(0, idx);
- if (dirs.indexOf(DROP + dir) == -1) {
- dirs.push(DROP + dir);
- }
- idx = dir.lastIndexOf("/");
- }
- });
- // Remove dirs.
- dirs = dirs.sort(function(a, b) {
- var al = (a.match(/\//g) || []).length;
- var bl = (b.match(/\//g) || []).length;
- if (al > bl)
- return -1;
- else if (al < bl)
- return 1;
- return 0;
- });
- dirs.forEach(function(dir) {
- FS.rmdir(dir);
- });
- });
- }
-})();
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index ad4b5a5afa..0b1650076c 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,7 +31,6 @@
#include "os_javascript.h"
#include "core/debugger/engine_debugger.h"
-#include "core/io/file_access_buffered_fa.h"
#include "core/io/json.h"
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
@@ -43,49 +42,27 @@
#include "modules/websocket/remote_debugger_peer_websocket.h"
#endif
+#include <dlfcn.h>
#include <emscripten.h>
#include <stdlib.h>
-bool OS_JavaScript::has_touchscreen_ui_hint() const {
- /* clang-format off */
- return EM_ASM_INT_V(
- return 'ontouchstart' in window;
- );
- /* clang-format on */
-}
-
-// Audio
-
-int OS_JavaScript::get_audio_driver_count() const {
- return 1;
-}
-
-const char *OS_JavaScript::get_audio_driver_name(int p_driver) const {
- return "JavaScript";
-}
+#include "godot_js.h"
// Lifecycle
void OS_JavaScript::initialize() {
OS_Unix::initialize_core();
- FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix>>(FileAccess::ACCESS_RESOURCES);
DisplayServerJavaScript::register_javascript_driver();
#ifdef MODULE_WEBSOCKET_ENABLED
EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create);
EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create);
#endif
-
- char locale_ptr[16];
- /* clang-format off */
- EM_ASM({
- stringToUTF8(Module['locale'], $0, 16);
- }, locale_ptr);
- /* clang-format on */
- setenv("LANG", locale_ptr, true);
}
void OS_JavaScript::resume_audio() {
- audio_driver_javascript.resume();
+ if (audio_driver_javascript) {
+ audio_driver_javascript->resume();
+ }
}
void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) {
@@ -96,27 +73,15 @@ MainLoop *OS_JavaScript::get_main_loop() const {
return main_loop;
}
-void OS_JavaScript::main_loop_callback() {
- get_singleton()->main_loop_iterate();
+void OS_JavaScript::fs_sync_callback() {
+ get_singleton()->idb_is_syncing = false;
}
bool OS_JavaScript::main_loop_iterate() {
- if (is_userfs_persistent() && sync_wait_time >= 0) {
- int64_t current_time = get_ticks_msec();
- int64_t elapsed_time = current_time - last_sync_check_time;
- last_sync_check_time = current_time;
-
- sync_wait_time -= elapsed_time;
-
- if (sync_wait_time < 0) {
- /* clang-format off */
- EM_ASM(
- FS.syncfs(function(error) {
- if (error) { err('Failed to save IDB file system: ' + error.message); }
- });
- );
- /* clang-format on */
- }
+ if (is_userfs_persistent() && idb_needs_sync && !idb_is_syncing) {
+ idb_is_syncing = true;
+ idb_needs_sync = false;
+ godot_js_os_fs_sync(&fs_sync_callback);
}
DisplayServer::get_singleton()->process_events();
@@ -131,35 +96,28 @@ void OS_JavaScript::delete_main_loop() {
main_loop = nullptr;
}
-void OS_JavaScript::finalize_async() {
- finalizing = true;
- audio_driver_javascript.finish_async();
-}
-
void OS_JavaScript::finalize() {
delete_main_loop();
+ if (audio_driver_javascript) {
+ memdelete(audio_driver_javascript);
+ audio_driver_javascript = nullptr;
+ }
}
// Miscellaneous
-Error OS_JavaScript::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+Error OS_JavaScript::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+ return create_process(p_path, p_arguments);
+}
+
+Error OS_JavaScript::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
Array args;
for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) {
args.push_back(E->get());
}
String json_args = JSON::print(args);
- /* clang-format off */
- int failed = EM_ASM_INT({
- const json_args = UTF8ToString($0);
- const args = JSON.parse(json_args);
- if (Module["onExecute"]) {
- Module["onExecute"](args);
- return 0;
- }
- return 1;
- }, json_args.utf8().get_data());
- /* clang-format on */
- ERR_FAIL_COND_V_MSG(failed, ERR_UNAVAILABLE, "OS::execute() must be implemented in JavaScript via 'engine.setOnExecute' if required.");
+ int failed = godot_js_os_execute(json_args.utf8().get_data());
+ ERR_FAIL_COND_V_MSG(failed, ERR_UNAVAILABLE, "OS::execute() or create_process() must be implemented in JavaScript via 'engine.setOnExecute' if required.");
return OK;
}
@@ -171,13 +129,29 @@ int OS_JavaScript::get_process_id() const {
ERR_FAIL_V_MSG(0, "OS::get_process_id() is not available on the HTML5 platform.");
}
+int OS_JavaScript::get_processor_count() const {
+ return godot_js_os_hw_concurrency_get();
+}
+
bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) {
- if (p_feature == "HTML5" || p_feature == "web")
+ if (p_feature == "HTML5" || p_feature == "web") {
return true;
+ }
#ifdef JAVASCRIPT_EVAL_ENABLED
- if (p_feature == "JavaScript")
+ if (p_feature == "JavaScript") {
return true;
+ }
+#endif
+#ifndef NO_THREADS
+ if (p_feature == "threads") {
+ return true;
+ }
+#endif
+#if WASM_GDNATIVE
+ if (p_feature == "wasm32") {
+ return true;
+ }
#endif
return false;
@@ -189,11 +163,7 @@ String OS_JavaScript::get_executable_path() const {
Error OS_JavaScript::shell_open(String p_uri) {
// Open URI in a new tab, browser will deal with it by protocol.
- /* clang-format off */
- EM_ASM_({
- window.open(UTF8ToString($0), '_blank');
- }, p_uri.utf8().get_data());
- /* clang-format on */
+ godot_js_os_shell_open(p_uri.utf8().get_data());
return OK;
}
@@ -201,10 +171,6 @@ String OS_JavaScript::get_name() const {
return "HTML5";
}
-bool OS_JavaScript::can_draw() const {
- return true; // Always?
-}
-
String OS_JavaScript::get_user_data_dir() const {
return "/userfs";
};
@@ -222,22 +188,31 @@ String OS_JavaScript::get_data_path() const {
}
void OS_JavaScript::file_access_close_callback(const String &p_file, int p_flags) {
- OS_JavaScript *os = get_singleton();
- if (os->is_userfs_persistent() && p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) {
- os->last_sync_check_time = OS::get_singleton()->get_ticks_msec();
- // Wait five seconds in case more files are about to be closed.
- os->sync_wait_time = 5000;
+ OS_JavaScript *os = OS_JavaScript::get_singleton();
+ if (!(os->is_userfs_persistent() && (p_flags & FileAccess::WRITE))) {
+ return; // FS persistence is not working or we are not writing.
+ }
+ bool is_file_persistent = p_file.begins_with("/userfs");
+#ifdef TOOLS_ENABLED
+ // Hack for editor persistence (can we track).
+ is_file_persistent = is_file_persistent || p_file.begins_with("/home/web_user/");
+#endif
+ if (is_file_persistent) {
+ os->idb_needs_sync = true;
}
-}
-
-void OS_JavaScript::set_idb_available(bool p_idb_available) {
- idb_available = p_idb_available;
}
bool OS_JavaScript::is_userfs_persistent() const {
return idb_available;
}
+Error OS_JavaScript::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
+ String path = p_path.get_file();
+ p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
+ ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ". Error: " + dlerror());
+ return OK;
+}
+
OS_JavaScript *OS_JavaScript::get_singleton() {
return static_cast<OS_JavaScript *>(OS::get_singleton());
}
@@ -246,7 +221,16 @@ void OS_JavaScript::initialize_joypads() {
}
OS_JavaScript::OS_JavaScript() {
- AudioDriverManager::add_driver(&audio_driver_javascript);
+ char locale_ptr[16];
+ godot_js_config_locale_get(locale_ptr, 16);
+ setenv("LANG", locale_ptr, true);
+
+ if (AudioDriverJavaScript::is_available()) {
+ audio_driver_javascript = memnew(AudioDriverJavaScript);
+ AudioDriverManager::add_driver(audio_driver_javascript);
+ }
+
+ idb_available = godot_js_os_fs_is_persistent();
Vector<Logger *> loggers;
loggers.push_back(memnew(StdLogger));
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index f0f18b44f8..81bb9c5f3d 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -40,61 +40,58 @@
class OS_JavaScript : public OS_Unix {
MainLoop *main_loop = nullptr;
- AudioDriverJavaScript audio_driver_javascript;
+ AudioDriverJavaScript *audio_driver_javascript = nullptr;
- bool finalizing = false;
+ bool idb_is_syncing = false;
bool idb_available = false;
- int64_t sync_wait_time = -1;
- int64_t last_sync_check_time = -1;
+ bool idb_needs_sync = false;
static void main_loop_callback();
static void file_access_close_callback(const String &p_file, int p_flags);
+ static void fs_sync_callback();
protected:
- virtual void initialize();
+ void initialize() override;
- virtual void set_main_loop(MainLoop *p_main_loop);
- virtual void delete_main_loop();
+ void set_main_loop(MainLoop *p_main_loop) override;
+ void delete_main_loop() override;
- virtual void finalize();
+ void finalize() override;
- virtual bool _check_internal_feature_support(const String &p_feature);
+ bool _check_internal_feature_support(const String &p_feature) override;
public:
// Override return type to make writing static callbacks less tedious.
static OS_JavaScript *get_singleton();
- virtual void initialize_joypads();
+ void initialize_joypads() override;
- virtual bool has_touchscreen_ui_hint() const;
-
- virtual int get_audio_driver_count() const;
- virtual const char *get_audio_driver_name(int p_driver) const;
-
- virtual MainLoop *get_main_loop() const;
- void finalize_async();
+ MainLoop *get_main_loop() const override;
bool main_loop_iterate();
- virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr);
- virtual Error kill(const ProcessID &p_pid);
- virtual int get_process_id() const;
+ Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override;
+ Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+ Error kill(const ProcessID &p_pid) override;
+ int get_process_id() const override;
+ int get_processor_count() const override;
- String get_executable_path() const;
- virtual Error shell_open(String p_uri);
- virtual String get_name() const;
- virtual bool can_draw() const;
+ String get_executable_path() const override;
+ Error shell_open(String p_uri) override;
+ String get_name() const override;
+ // Override default OS implementation which would block the main thread with delay_usec.
+ // Implemented in javascript_main.cpp loop callback instead.
+ void add_frame_delay(bool p_can_draw) override {}
- virtual String get_cache_path() const;
- virtual String get_config_path() const;
- virtual String get_data_path() const;
- virtual String get_user_data_dir() const;
+ String get_cache_path() const override;
+ String get_config_path() const override;
+ String get_data_path() const override;
+ String get_user_data_dir() const override;
- void set_idb_available(bool p_idb_available);
- virtual bool is_userfs_persistent() const;
+ bool is_userfs_persistent() const override;
+ Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) override;
void resume_audio();
- bool is_finalizing() { return finalizing; }
OS_JavaScript();
};
diff --git a/platform/javascript/package-lock.json b/platform/javascript/package-lock.json
new file mode 100644
index 0000000000..8e298a495e
--- /dev/null
+++ b/platform/javascript/package-lock.json
@@ -0,0 +1,1605 @@
+{
+ "name": "godot",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ }
+ }
+ },
+ "@eslint/eslintrc": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
+ "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.1.1",
+ "espree": "^7.3.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^3.13.1",
+ "lodash": "^4.17.19",
+ "minimatch": "^3.0.4",
+ "strip-json-comments": "^3.1.1"
+ }
+ },
+ "@types/color-name": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+ "dev": true
+ },
+ "@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+ "dev": true
+ },
+ "acorn": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
+ "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
+ "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.12.5",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
+ "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array-includes": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
+ "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0",
+ "is-string": "^1.0.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "array.prototype.flat": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
+ "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "confusing-browser-globals": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
+ "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==",
+ "dev": true
+ },
+ "contains-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^4.1.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-abstract": {
+ "version": "1.18.0-next.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz",
+ "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eslint": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.9.0.tgz",
+ "integrity": "sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@eslint/eslintrc": "^0.1.3",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "enquirer": "^2.3.5",
+ "eslint-scope": "^5.1.0",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^1.3.0",
+ "espree": "^7.3.0",
+ "esquery": "^1.2.0",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^5.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash": "^4.17.19",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^5.2.3",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ }
+ },
+ "eslint-config-airbnb-base": {
+ "version": "14.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz",
+ "integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==",
+ "dev": true,
+ "requires": {
+ "confusing-browser-globals": "^1.0.9",
+ "object.assign": "^4.1.0",
+ "object.entries": "^1.1.2"
+ }
+ },
+ "eslint-import-resolver-node": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
+ "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.9",
+ "resolve": "^1.13.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-module-utils": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
+ "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.9",
+ "pkg-dir": "^2.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "2.22.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz",
+ "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.1.1",
+ "array.prototype.flat": "^1.2.3",
+ "contains-path": "^0.1.0",
+ "debug": "^2.6.9",
+ "doctrine": "1.5.0",
+ "eslint-import-resolver-node": "^0.3.3",
+ "eslint-module-utils": "^2.6.0",
+ "has": "^1.0.3",
+ "minimatch": "^3.0.4",
+ "object.values": "^1.1.1",
+ "read-pkg-up": "^2.0.0",
+ "resolve": "^1.17.0",
+ "tsconfig-paths": "^3.9.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "isarray": "^1.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ },
+ "espree": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
+ "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.2.0",
+ "eslint-visitor-keys": "^1.3.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
+ "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "file-entry-cache": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+ "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^2.0.1"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "flat-cache": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "dev": true,
+ "requires": {
+ "flatted": "^2.0.0",
+ "rimraf": "2.6.3",
+ "write": "1.0.3"
+ }
+ },
+ "flatted": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "12.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+ "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
+ "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz",
+ "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==",
+ "dev": true
+ },
+ "is-date-object": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-negative-zero": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+ "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-string": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+ "dev": true
+ },
+ "is-symbol": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+ "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "load-json-file": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "object-inspect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
+ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.0",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "object.entries": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz",
+ "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "has": "^1.0.3"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "object.values": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
+ "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ }
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.1.0"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^2.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^2.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^2.0.0"
+ }
+ },
+ "regexpp": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
+ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "semver": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+ "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
+ "spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
+ "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+ "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+ "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "table": {
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "tsconfig-paths": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
+ "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
+ "dev": true,
+ "requires": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+ "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "v8-compile-cache": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
+ "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+ "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ }
+ }
+}
diff --git a/platform/javascript/package.json b/platform/javascript/package.json
new file mode 100644
index 0000000000..630b584f5b
--- /dev/null
+++ b/platform/javascript/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "godot",
+ "private": true,
+ "version": "1.0.0",
+ "description": "Linting setup for Godot's HTML5 platform code",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules",
+ "lint:engine": "eslint \"js/engine/*.js\" --no-eslintrc -c .eslintrc.engine.js",
+ "lint:libs": "eslint \"js/libs/*.js\" --no-eslintrc -c .eslintrc.libs.js",
+ "lint:modules": "eslint \"../../modules/**/*.js\" --no-eslintrc -c .eslintrc.libs.js",
+ "format": "npm run format:engine && npm run format:libs && npm run format:modules",
+ "format:engine": "npm run lint:engine -- --fix",
+ "format:libs": "npm run lint:libs -- --fix",
+ "format:modules": "npm run lint:modules -- --fix"
+ },
+ "author": "Godot Engine contributors",
+ "license": "MIT",
+ "devDependencies": {
+ "eslint": "^7.9.0",
+ "eslint-config-airbnb-base": "^14.2.0",
+ "eslint-plugin-import": "^2.22.0"
+ }
+}
diff --git a/platform/javascript/platform_config.h b/platform/javascript/platform_config.h
index e2200376d3..65df34902e 100644
--- a/platform/javascript/platform_config.h
+++ b/platform/javascript/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub
index ae75a75830..46714e9502 100644
--- a/platform/linuxbsd/SCsub
+++ b/platform/linuxbsd/SCsub
@@ -16,7 +16,10 @@ common_x11 = [
"key_mapping_x11.cpp",
]
+if "udev" in env and env["udev"]:
+ common_x11.append("libudev-so_wrap.c")
+
prog = env.add_program("#bin/godot", ["godot_linuxbsd.cpp"] + common_x11)
-if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]:
+if env["debug_symbols"] and env["separate_debug_symbols"]:
env.AddPostAction(prog, run_in_subprocess(platform_linuxbsd_builders.make_debug_linuxbsd))
diff --git a/platform/linuxbsd/context_gl_x11.cpp b/platform/linuxbsd/context_gl_x11.cpp
index 71dc9602b3..1f92370ab7 100644
--- a/platform/linuxbsd/context_gl_x11.cpp
+++ b/platform/linuxbsd/context_gl_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/context_gl_x11.h b/platform/linuxbsd/context_gl_x11.h
index 7aed280c98..d089886f4d 100644
--- a/platform/linuxbsd/context_gl_x11.h
+++ b/platform/linuxbsd/context_gl_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index b3553e961a..ea0222cb19 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,8 +30,8 @@
#include "crash_handler_linuxbsd.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "main/main.h"
#ifdef DEBUG_ENABLED
@@ -67,7 +67,7 @@ static void handle_crash(int sig) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
}
- fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
+ fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data());
char **strings = backtrace_symbols(bt_buffer, size);
if (strings) {
for (size_t i = 1; i < size; i++) {
@@ -104,12 +104,12 @@ static void handle_crash(int sig) {
// Try to get the file/line number using addr2line
int ret;
- Error err = OS::get_singleton()->execute(String("addr2line"), args, true, nullptr, &output, &ret);
+ Error err = OS::get_singleton()->execute(String("addr2line"), args, &output, &ret);
if (err == OK) {
output.erase(output.length() - 1, 1);
}
- fprintf(stderr, "[%ld] %s (%ls)\n", (long int)i, fname, output.c_str());
+ fprintf(stderr, "[%ld] %s (%s)\n", (long int)i, fname, output.utf8().get_data());
}
free(strings);
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.h b/platform/linuxbsd/crash_handler_linuxbsd.h
index 9bb03579bc..a3dae0cc22 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.h
+++ b/platform/linuxbsd/crash_handler_linuxbsd.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 07fa06bc06..8f1afe0e66 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -12,7 +12,6 @@ def get_name():
def can_build():
-
if os.name != "posix" or sys.platform == "darwin":
return False
@@ -35,6 +34,11 @@ def can_build():
print("xinerama not found.. x11 disabled.")
return False
+ x11_error = os.system("pkg-config xext --modversion > /dev/null ")
+ if x11_error:
+ print("xext not found.. x11 disabled.")
+ return False
+
x11_error = os.system("pkg-config xrandr --modversion > /dev/null ")
if x11_error:
print("xrandr not found.. x11 disabled.")
@@ -60,15 +64,15 @@ def get_opts():
BoolVariable("use_llvm", "Use the LLVM compiler", False),
BoolVariable("use_lld", "Use the LLD linker", False),
BoolVariable("use_thinlto", "Use ThinLTO", False),
- BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", False),
+ BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True),
BoolVariable("use_coverage", "Test Godot coverage", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
BoolVariable("pulseaudio", "Detect and use PulseAudio", True),
- BoolVariable("udev", "Use udev for gamepad connection callbacks", False),
- EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
+ BoolVariable("udev", "Use udev for gamepad connection callbacks", True),
+ BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("touch", "Enable touch events", True),
BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False),
@@ -76,12 +80,10 @@ def get_opts():
def get_flags():
-
return []
def configure(env):
-
## Build type
if env["target"] == "release":
@@ -90,9 +92,7 @@ def configure(env):
else: # optimize for size
env.Prepend(CCFLAGS=["-Os"])
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
+ if env["debug_symbols"]:
env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "release_debug":
@@ -102,14 +102,12 @@ def configure(env):
env.Prepend(CCFLAGS=["-Os"])
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
+ if env["debug_symbols"]:
env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "debug":
env.Prepend(CCFLAGS=["-g3"])
- env.Prepend(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(LINKFLAGS=["-rdynamic"])
## Architecture
@@ -128,8 +126,6 @@ def configure(env):
if "clang++" not in os.path.basename(env["CXX"]):
env["CC"] = "clang"
env["CXX"] = "clang++"
- env["LINK"] = "clang++"
- env.Append(CPPDEFINES=["TYPED_METHOD_BIND"])
env.extra_suffix = ".llvm" + env.extra_suffix
if env["use_lld"]:
@@ -194,6 +190,7 @@ def configure(env):
env.ParseConfig("pkg-config x11 --cflags --libs")
env.ParseConfig("pkg-config xcursor --cflags --libs")
env.ParseConfig("pkg-config xinerama --cflags --libs")
+ env.ParseConfig("pkg-config xext --cflags --libs")
env.ParseConfig("pkg-config xrandr --cflags --libs")
env.ParseConfig("pkg-config xrender --cflags --libs")
env.ParseConfig("pkg-config xi --cflags --libs")
@@ -205,14 +202,31 @@ def configure(env):
# freetype depends on libpng and zlib, so bundling one of them while keeping others
# as shared libraries leads to weird issues
- if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]:
+ if (
+ env["builtin_freetype"]
+ or env["builtin_libpng"]
+ or env["builtin_zlib"]
+ or env["builtin_graphite"]
+ or env["builtin_harfbuzz"]
+ ):
env["builtin_freetype"] = True
env["builtin_libpng"] = True
env["builtin_zlib"] = True
+ env["builtin_graphite"] = True
+ env["builtin_harfbuzz"] = True
if not env["builtin_freetype"]:
env.ParseConfig("pkg-config freetype2 --cflags --libs")
+ if not env["builtin_graphite"]:
+ env.ParseConfig("pkg-config graphite2 --cflags --libs")
+
+ if not env["builtin_icu"]:
+ env.ParseConfig("pkg-config icu-uc --cflags --libs")
+
+ if not env["builtin_harfbuzz"]:
+ env.ParseConfig("pkg-config harfbuzz harfbuzz-icu --cflags --libs")
+
if not env["builtin_libpng"]:
env.ParseConfig("pkg-config libpng16 --cflags --libs")
@@ -296,9 +310,8 @@ def configure(env):
if os.system("pkg-config --exists alsa") == 0: # 0 means found
print("Enabling ALSA")
+ env["alsa"] = True
env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"])
- # Don't parse --cflags, we don't need to add /usr/include/alsa to include path
- env.ParseConfig("pkg-config alsa --libs")
else:
print("ALSA libraries not found, disabling driver")
@@ -306,20 +319,20 @@ def configure(env):
if os.system("pkg-config --exists libpulse") == 0: # 0 means found
print("Enabling PulseAudio")
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED"])
- env.ParseConfig("pkg-config --cflags --libs libpulse")
+ env.ParseConfig("pkg-config --cflags libpulse")
else:
print("PulseAudio development libraries not found, disabling driver")
if platform.system() == "Linux":
env.Append(CPPDEFINES=["JOYDEV_ENABLED"])
-
if env["udev"]:
if os.system("pkg-config --exists libudev") == 0: # 0 means found
print("Enabling udev support")
env.Append(CPPDEFINES=["UDEV_ENABLED"])
- env.ParseConfig("pkg-config libudev --cflags --libs")
else:
print("libudev development libraries not found, disabling udev support")
+ else:
+ env["udev"] = False # Linux specific
# Linkflags below this line should typically stay the last ones
if not env["builtin_zlib"]:
@@ -376,4 +389,13 @@ def configure(env):
# Link those statically for portability
if env["use_static_cpp"]:
- env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])
+ # Workaround for GH-31743, Ubuntu 18.04 i386 crashes when it's used.
+ # That doesn't make any sense but it's likely a Ubuntu bug?
+ if is64 or env["bits"] == "64":
+ env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"])
+ if env["use_llvm"]:
+ env["LINKCOM"] = env["LINKCOM"] + " -l:libatomic.a"
+
+ else:
+ if env["use_llvm"]:
+ env.Append(LIBS=["atomic"])
diff --git a/platform/linuxbsd/detect_prime_x11.cpp b/platform/linuxbsd/detect_prime_x11.cpp
index 1e46d3222d..da1c95a593 100644
--- a/platform/linuxbsd/detect_prime_x11.cpp
+++ b/platform/linuxbsd/detect_prime_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,8 +33,8 @@
#include "detect_prime.h"
-#include "core/print_string.h"
-#include "core/ustring.h"
+#include "core/string/print_string.h"
+#include "core/string/ustring.h"
#include <stdlib.h>
@@ -56,11 +56,12 @@ typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLX
struct vendor {
const char *glxvendor;
- int priority;
+ int priority = 0;
};
vendor vendormap[] = {
{ "Advanced Micro Devices, Inc.", 30 },
+ { "AMD", 30 },
{ "NVIDIA Corporation", 30 },
{ "X.Org", 30 },
{ "Intel Open Source Technology Center", 20 },
@@ -128,7 +129,7 @@ void create_context() {
int detect_prime() {
pid_t p;
- int priorities[2];
+ int priorities[2] = {};
String vendors[2];
String renderers[2];
diff --git a/platform/linuxbsd/detect_prime_x11.h b/platform/linuxbsd/detect_prime_x11.h
index 039bdee76b..0b548b849e 100644
--- a/platform/linuxbsd/detect_prime_x11.h
+++ b/platform/linuxbsd/detect_prime_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index ef5ac66b34..fceeb82325 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,35 +32,27 @@
#ifdef X11_ENABLED
+#include "core/config/project_settings.h"
+#include "core/string/print_string.h"
#include "detect_prime_x11.h"
-
-#include "core/os/dir_access.h"
-#include "core/print_string.h"
-#include "errno.h"
#include "key_mapping_x11.h"
-
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
-#endif
-
+#include "main/main.h"
#include "scene/resources/texture.h"
-#ifdef HAVE_MNTENT
-#include <mntent.h>
+#if defined(VULKAN_ENABLED)
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#endif
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "X11/Xutil.h"
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xinerama.h>
+#include <X11/extensions/shape.h>
-#include "X11/Xatom.h"
-#include "X11/extensions/Xinerama.h"
// ICCCM
#define WM_NormalState 1L // window normal state
#define WM_IconicState 3L // window minimized
@@ -69,8 +61,6 @@
#define _NET_WM_STATE_ADD 1L // add/set property
#define _NET_WM_STATE_TOGGLE 2L // toggle property
-#include "main/main.h"
-
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -82,14 +72,9 @@
#undef KEY_TAB
#endif
-#include <X11/Xatom.h>
-
#undef CursorShape
-
#include <X11/XKBlib.h>
-#include "core/project_settings.h"
-
// 2.2 is the first release with multitouch
#define XINPUT_CLIENT_VERSION_MAJOR 2
#define XINPUT_CLIENT_VERSION_MINOR 2
@@ -100,9 +85,25 @@
#define VALUATOR_TILTX 3
#define VALUATOR_TILTY 4
+//#define DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+#define DEBUG_LOG_X11(...) printf(__VA_ARGS__)
+#else
+#define DEBUG_LOG_X11(...)
+#endif
+
static const double abs_resolution_mult = 10000.0;
static const double abs_resolution_range_mult = 10.0;
+// Hints for X11 fullscreen
+struct Hints {
+ unsigned long flags = 0;
+ unsigned long functions = 0;
+ unsigned long decorations = 0;
+ long inputMode = 0;
+ unsigned long status = 0;
+};
+
bool DisplayServerX11::has_feature(Feature p_feature) const {
switch (p_feature) {
case FEATURE_SUBWINDOWS:
@@ -190,7 +191,7 @@ void DisplayServerX11::alert(const String &p_alert, const String &p_title) {
}
if (program.length()) {
- OS::get_singleton()->execute(program, args, true);
+ OS::get_singleton()->execute(program, args);
} else {
print_line(p_alert);
}
@@ -331,23 +332,19 @@ bool DisplayServerX11::_refresh_device_info() {
}
void DisplayServerX11::_flush_mouse_motion() {
- while (true) {
- if (XPending(x11_display) > 0) {
- XEvent event;
- XPeekEvent(x11_display, &event);
-
- if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
- XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
-
- if (event_data->evtype == XI_RawMotion) {
- XNextEvent(x11_display, &event);
- } else {
- break;
- }
- } else {
- break;
+ // Block events polling while flushing motion events.
+ MutexLock mutex_lock(events_mutex);
+
+ for (uint32_t event_index = 0; event_index < polled_events.size(); ++event_index) {
+ XEvent &event = polled_events[event_index];
+ if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
+ XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
+ if (event_data->evtype == XI_RawMotion) {
+ XFreeEventData(x11_display, &event.xcookie);
+ polled_events.remove(event_index--);
+ continue;
}
- } else {
+ XFreeEventData(x11_display, &event.xcookie);
break;
}
}
@@ -417,17 +414,24 @@ void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) {
if (mouse_mode == MOUSE_MODE_CAPTURED) {
last_mouse_pos = p_to;
} else {
- /*XWindowAttributes xwa;
- XGetWindowAttributes(x11_display, x11_window, &xwa);
- printf("%d %d\n", xwa.x, xwa.y); needed? */
-
XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window,
0, 0, 0, 0, (int)p_to.x, (int)p_to.y);
}
}
Point2i DisplayServerX11::mouse_get_position() const {
- return last_mouse_pos;
+ int root_x, root_y;
+ int win_x, win_y;
+ unsigned int mask_return;
+ Window window_returned;
+
+ Bool result = XQueryPointer(x11_display, RootWindow(x11_display, DefaultScreen(x11_display)), &window_returned,
+ &window_returned, &root_x, &root_y, &win_x, &win_y,
+ &mask_return);
+ if (result == True) {
+ return Point2i(root_x, root_y);
+ }
+ return Point2i();
}
Point2i DisplayServerX11::mouse_get_absolute_position() const {
@@ -453,60 +457,156 @@ int DisplayServerX11::mouse_get_button_state() const {
void DisplayServerX11::clipboard_set(const String &p_text) {
_THREAD_SAFE_METHOD_
- internal_clipboard = p_text;
+ {
+ // The clipboard content can be accessed while polling for events.
+ MutexLock mutex_lock(events_mutex);
+ internal_clipboard = p_text;
+ }
+
XSetSelectionOwner(x11_display, XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
}
-static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
+Bool DisplayServerX11::_predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg) {
+ if (event->type == SelectionNotify && event->xselection.requestor == *(Window *)arg) {
+ return True;
+ } else {
+ return False;
+ }
+}
+
+Bool DisplayServerX11::_predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg) {
+ if (event->type == PropertyNotify && event->xproperty.state == PropertyNewValue) {
+ return True;
+ } else {
+ return False;
+ }
+}
+
+String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const {
String ret;
- Atom type;
- Atom selection = XA_PRIMARY;
- int format, result;
- unsigned long len, bytes_left, dummy;
- unsigned char *data;
- Window Sown = XGetSelectionOwner(x11_display, p_source);
-
- if (Sown == x11_window) {
- return p_internal_clipboard;
- };
+ Window selection_owner = XGetSelectionOwner(x11_display, p_source);
+ if (selection_owner == x11_window) {
+ return internal_clipboard;
+ }
- if (Sown != None) {
+ if (selection_owner != None) {
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
+
+ Atom selection = XA_PRIMARY;
XConvertSelection(x11_display, p_source, target, selection,
x11_window, CurrentTime);
+
XFlush(x11_display);
- while (true) {
- XEvent event;
- XNextEvent(x11_display, &event);
- if (event.type == SelectionNotify && event.xselection.requestor == x11_window) {
- break;
- };
- };
- //
- // Do not get any data, see how much data is there
- //
+ // Blocking wait for predicate to be True and remove the event from the queue.
+ XEvent event;
+ XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window);
+
+ // Do not get any data, see how much data is there.
+ Atom type;
+ int format, result;
+ unsigned long len, bytes_left, dummy;
+ unsigned char *data;
XGetWindowProperty(x11_display, x11_window,
selection, // Tricky..
0, 0, // offset - len
0, // Delete 0==FALSE
- AnyPropertyType, //flag
+ AnyPropertyType, // flag
&type, // return type
&format, // return format
- &len, &bytes_left, //that
+ &len, &bytes_left, // data length
&data);
- // DATA is There
- if (bytes_left > 0) {
+
+ if (data) {
+ XFree(data);
+ }
+
+ if (type == XInternAtom(x11_display, "INCR", 0)) {
+ // Data is going to be received incrementally.
+ DEBUG_LOG_X11("INCR selection started.\n");
+
+ LocalVector<uint8_t> incr_data;
+ uint32_t data_size = 0;
+ bool success = false;
+
+ // Delete INCR property to notify the owner.
+ XDeleteProperty(x11_display, x11_window, type);
+
+ // Process events from the queue.
+ bool done = false;
+ while (!done) {
+ if (!_wait_for_events()) {
+ // Error or timeout, abort.
+ break;
+ }
+
+ // Non-blocking wait for next event and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, nullptr)) {
+ result = XGetWindowProperty(x11_display, x11_window,
+ selection, // selection type
+ 0, LONG_MAX, // offset - len
+ True, // delete property to notify the owner
+ AnyPropertyType, // flag
+ &type, // return type
+ &format, // return format
+ &len, &bytes_left, // data length
+ &data);
+
+ DEBUG_LOG_X11("PropertyNotify: len=%lu, format=%i\n", len, format);
+
+ if (result == Success) {
+ if (data && (len > 0)) {
+ uint32_t prev_size = incr_data.size();
+ if (prev_size == 0) {
+ // First property contains initial data size.
+ unsigned long initial_size = *(unsigned long *)data;
+ incr_data.resize(initial_size);
+ } else {
+ // New chunk, resize to be safe and append data.
+ incr_data.resize(MAX(data_size + len, prev_size));
+ memcpy(incr_data.ptr() + data_size, data, len);
+ data_size += len;
+ }
+ } else {
+ // Last chunk, process finished.
+ done = true;
+ success = true;
+ }
+ } else {
+ printf("Failed to get selection data chunk.\n");
+ done = true;
+ }
+
+ if (data) {
+ XFree(data);
+ }
+
+ if (done) {
+ break;
+ }
+ }
+ }
+
+ if (success && (data_size > 0)) {
+ ret.parse_utf8((const char *)incr_data.ptr(), data_size);
+ }
+ } else if (bytes_left > 0) {
+ // Data is ready and can be processed all at once.
result = XGetWindowProperty(x11_display, x11_window,
selection, 0, bytes_left, 0,
AnyPropertyType, &type, &format,
&len, &dummy, &data);
+
if (result == Success) {
ret.parse_utf8((const char *)data);
} else {
- printf("FAIL\n");
+ printf("Failed to get selection data.\n");
}
+
if (data) {
XFree(data);
}
@@ -516,14 +616,14 @@ static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x
return ret;
}
-static String _clipboard_get(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
+String DisplayServerX11::_clipboard_get(Atom p_source, Window x11_window) const {
String ret;
Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
if (utf8_atom != None) {
- ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom);
+ ret = _clipboard_get_impl(p_source, x11_window, utf8_atom);
}
- if (ret == "") {
- ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
+ if (ret.is_empty()) {
+ ret = _clipboard_get_impl(p_source, x11_window, XA_STRING);
}
return ret;
}
@@ -532,15 +632,69 @@ String DisplayServerX11::clipboard_get() const {
_THREAD_SAFE_METHOD_
String ret;
- ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
+ ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window);
- if (ret == "") {
- ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
- };
+ if (ret.is_empty()) {
+ ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window);
+ }
return ret;
}
+Bool DisplayServerX11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) {
+ if (event->xany.window == *(Window *)arg) {
+ return (event->type == SelectionRequest) ||
+ (event->type == SelectionNotify);
+ } else {
+ return False;
+ }
+}
+
+void DisplayServerX11::_clipboard_transfer_ownership(Atom p_source, Window x11_window) const {
+ _THREAD_SAFE_METHOD_
+
+ Window selection_owner = XGetSelectionOwner(x11_display, p_source);
+
+ if (selection_owner != x11_window) {
+ return;
+ }
+
+ // Block events polling while processing selection events.
+ MutexLock mutex_lock(events_mutex);
+
+ Atom clipboard_manager = XInternAtom(x11_display, "CLIPBOARD_MANAGER", False);
+ Atom save_targets = XInternAtom(x11_display, "SAVE_TARGETS", False);
+ XConvertSelection(x11_display, clipboard_manager, save_targets, None,
+ x11_window, CurrentTime);
+
+ // Process events from the queue.
+ while (true) {
+ if (!_wait_for_events()) {
+ // Error or timeout, abort.
+ break;
+ }
+
+ // Non-blocking wait for next event and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_save_targets, (XPointer)&x11_window)) {
+ switch (ev.type) {
+ case SelectionRequest:
+ _handle_selection_request_event(&(ev.xselectionrequest));
+ break;
+
+ case SelectionNotify: {
+ if (ev.xselection.target == save_targets) {
+ // Once SelectionNotify is received, we're done whether it succeeded or not.
+ return;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+}
+
int DisplayServerX11::get_screen_count() const {
_THREAD_SAFE_METHOD_
@@ -691,6 +845,14 @@ DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, u
return id;
}
+void DisplayServerX11::show_window(WindowID p_id) {
+ _THREAD_SAFE_METHOD_
+
+ WindowData &wd = windows[p_id];
+
+ XMapWindow(x11_display, wd.x11_window);
+}
+
void DisplayServerX11::delete_sub_window(WindowID p_id) {
_THREAD_SAFE_METHOD_
@@ -699,6 +861,8 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
WindowData &wd = windows[p_id];
+ DEBUG_LOG_X11("delete_sub_window: %lu (%u) \n", wd.x11_window, p_id);
+
while (wd.transient_children.size()) {
window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
}
@@ -716,6 +880,7 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
XDestroyWindow(x11_display, wd.x11_window);
if (wd.xic) {
XDestroyIC(wd.xic);
+ wd.xic = nullptr;
}
windows.erase(p_id);
@@ -735,7 +900,31 @@ ObjectID DisplayServerX11::window_get_attached_instance_id(WindowID p_window) co
}
DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const Point2i &p_position) const {
- return INVALID_WINDOW_ID;
+ WindowID found_window = INVALID_WINDOW_ID;
+ WindowID parent_window = INVALID_WINDOW_ID;
+ unsigned int focus_order = 0;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ const WindowData &wd = E->get();
+
+ // Discard windows with no focus.
+ if (wd.focus_order == 0) {
+ continue;
+ }
+
+ // Find topmost window which contains the given position.
+ WindowID window_id = E->key();
+ Rect2i win_rect = Rect2i(window_get_position(window_id), window_get_size(window_id));
+ if (win_rect.has_point(p_position)) {
+ // For siblings, pick the window which was focused last.
+ if ((parent_window != wd.transient_parent) || (wd.focus_order > focus_order)) {
+ found_window = window_id;
+ parent_window = wd.transient_parent;
+ focus_order = wd.focus_order;
+ }
+ }
+ }
+
+ return found_window;
}
void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window) {
@@ -748,7 +937,41 @@ void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window
Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false);
Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false);
- XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
+ if (_net_wm_name != None && utf8_string != None) {
+ XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
+ }
+}
+
+void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ const WindowData &wd = windows[p_window];
+
+ int event_base, error_base;
+ const Bool ext_okay = XShapeQueryExtension(x11_display, &event_base, &error_base);
+ if (ext_okay) {
+ Region region;
+ if (p_region.size() == 0) {
+ region = XCreateRegion();
+ XRectangle rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = window_get_real_size(p_window).x;
+ rect.height = window_get_real_size(p_window).y;
+ XUnionRectWithRegion(&rect, region, region);
+ } else {
+ XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * p_region.size());
+ for (int i = 0; i < p_region.size(); i++) {
+ points[i].x = p_region[i].x;
+ points[i].y = p_region[i].y;
+ }
+ region = XPolygonRegion(points, p_region.size(), EvenOddRule);
+ memfree(points);
+ }
+ XShapeCombineRegion(x11_display, wd.x11_window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
+ }
}
void DisplayServerX11::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
@@ -844,24 +1067,34 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd_window = windows[p_window];
- ERR_FAIL_COND(wd_window.transient_parent == p_parent);
+ WindowID prev_parent = wd_window.transient_parent;
+ ERR_FAIL_COND(prev_parent == p_parent);
ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient.");
if (p_parent == INVALID_WINDOW_ID) {
//remove transient
- ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
- ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
+ ERR_FAIL_COND(prev_parent == INVALID_WINDOW_ID);
+ ERR_FAIL_COND(!windows.has(prev_parent));
- WindowData &wd_parent = windows[wd_window.transient_parent];
+ WindowData &wd_parent = windows[prev_parent];
wd_window.transient_parent = INVALID_WINDOW_ID;
wd_parent.transient_children.erase(p_window);
XSetTransientForHint(x11_display, wd_window.x11_window, None);
+
+ // Set focus to parent sub window to avoid losing all focus with nested menus.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (wd_window.menu_type && !wd_window.no_focus) {
+ if (!wd_parent.no_focus) {
+ XSetInputFocus(x11_display, wd_parent.x11_window, RevertToPointerRoot, CurrentTime);
+ }
+ }
} else {
ERR_FAIL_COND(!windows.has(p_parent));
- ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
+ ERR_FAIL_COND_MSG(prev_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
WindowData &wd_parent = windows[p_parent];
wd_window.transient_parent = p_parent;
@@ -871,6 +1104,46 @@ void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent
}
}
+// Helper method. Assumes that the window id has already been checked and exists.
+void DisplayServerX11::_update_size_hints(WindowID p_window) {
+ WindowData &wd = windows[p_window];
+ WindowMode window_mode = window_get_mode(p_window);
+ XSizeHints *xsh = XAllocSizeHints();
+
+ // Always set the position and size hints - they should be synchronized with the actual values after the window is mapped anyway
+ xsh->flags |= PPosition | PSize;
+ xsh->x = wd.position.x;
+ xsh->y = wd.position.y;
+ xsh->width = wd.size.width;
+ xsh->height = wd.size.height;
+
+ if (window_mode == WINDOW_MODE_FULLSCREEN) {
+ // Do not set any other hints to prevent the window manager from ignoring the fullscreen flags
+ } else if (window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
+ // If resizing is disabled, use the forced size
+ xsh->flags |= PMinSize | PMaxSize;
+ xsh->min_width = wd.size.x;
+ xsh->max_width = wd.size.x;
+ xsh->min_height = wd.size.y;
+ xsh->max_height = wd.size.y;
+ } else {
+ // Otherwise, just respect min_size and max_size
+ if (wd.min_size != Size2i()) {
+ xsh->flags |= PMinSize;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
+ }
+ if (wd.max_size != Size2i()) {
+ xsh->flags |= PMaxSize;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
+ }
+ }
+
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
+ XFree(xsh);
+}
+
Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
_THREAD_SAFE_METHOD_
@@ -924,25 +1197,8 @@ void DisplayServerX11::window_set_max_size(const Size2i p_size, WindowID p_windo
}
wd.max_size = p_size;
- if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
-
- XFlush(x11_display);
- }
+ _update_size_hints(p_window);
+ XFlush(x11_display);
}
Size2i DisplayServerX11::window_get_max_size(WindowID p_window) const {
@@ -966,25 +1222,8 @@ void DisplayServerX11::window_set_min_size(const Size2i p_size, WindowID p_windo
}
wd.min_size = p_size;
- if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
-
- XFlush(x11_display);
- }
+ _update_size_hints(p_window);
+ XFlush(x11_display);
}
Size2i DisplayServerX11::window_get_min_size(WindowID p_window) const {
@@ -1017,37 +1256,15 @@ void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
int old_w = xwa.width;
int old_h = xwa.height;
- // If window resizable is disabled we need to update the attributes first
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = size.x;
- xsh->max_width = size.x;
- xsh->min_height = size.y;
- xsh->max_height = size.y;
- } else {
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- }
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
+ // Update our videomode width and height
+ wd.size = size;
+
+ // Update the size hints first to make sure the window size can be set
+ _update_size_hints(p_window);
// Resize the window
XResizeWindow(x11_display, wd.x11_window, size.x, size.y);
- // Update our videomode width and height
- wd.size = size;
-
for (int timeout = 0; timeout < 50; ++timeout) {
XSync(x11_display, False);
XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
@@ -1098,18 +1315,23 @@ Size2i DisplayServerX11::window_get_real_size(WindowID p_window) const {
return Size2i(w, h);
}
-bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const {
- _THREAD_SAFE_METHOD_
-
+// Just a helper to reduce code duplication in `window_is_maximize_allowed`
+// and `_set_wm_maximized`.
+bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_atom_name) const {
ERR_FAIL_COND_V(!windows.has(p_window), false);
const WindowData &wd = windows[p_window];
- Atom property = XInternAtom(x11_display, "_NET_WM_ALLOWED_ACTIONS", False);
+ Atom property = XInternAtom(x11_display, p_atom_name, False);
Atom type;
int format;
unsigned long len;
unsigned long remaining;
unsigned char *data = nullptr;
+ bool retval = false;
+
+ if (property == None) {
+ return false;
+ }
int result = XGetWindowProperty(
x11_display,
@@ -1141,13 +1363,20 @@ bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const {
}
if (found_wm_act_max_horz || found_wm_act_max_vert) {
- return true;
+ retval = true;
+ break;
}
}
- XFree(atoms);
+
+ XFree(data);
}
- return false;
+ return retval;
+}
+
+bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+ return _window_maximize_check(p_window, "_NET_WM_ALLOWED_ACTIONS");
}
void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) {
@@ -1192,17 +1421,14 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
hints.flags = 2;
hints.decorations = 0;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
}
- if (p_enabled && window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
+ if (p_enabled) {
// Set the window as resizable to prevent window managers to ignore the fullscreen state flag.
- XSizeHints *xsh;
-
- xsh = XAllocSizeHints();
- xsh->flags = 0L;
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
+ _update_size_hints(p_window);
}
// Using EWMH -- Extended Window Manager Hints
@@ -1224,36 +1450,15 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
// set bypass compositor hint
Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False);
unsigned long compositing_disable_on = p_enabled ? 1 : 0;
- XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
+ if (bypass_compositor != None) {
+ XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
+ }
XFlush(x11_display);
if (!p_enabled) {
// Reset the non-resizable flags if we un-set these before.
- Size2i size = window_get_size(p_window);
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = size.x;
- xsh->max_width = size.x;
- xsh->min_height = size.y;
- xsh->max_height = size.y;
- } else {
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- }
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
+ _update_size_hints(p_window);
// put back or remove decorations according to the last set borderless state
Hints hints;
@@ -1261,7 +1466,9 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
hints.flags = 2;
hints.decorations = window_get_flag(WINDOW_FLAG_BORDERLESS, p_window) ? 0 : 1;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
}
}
@@ -1311,13 +1518,13 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
} break;
case WINDOW_MODE_FULLSCREEN: {
//Remove full-screen
+ wd.fullscreen = false;
+
_set_wm_fullscreen(p_window, false);
//un-maximize required for always on top
bool on_top = window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window);
- wd.fullscreen = false;
-
window_set_position(wd.last_position_before_fs, p_window);
if (on_top) {
@@ -1363,15 +1570,16 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
} break;
case WINDOW_MODE_FULLSCREEN: {
wd.last_position_before_fs = wd.position;
+
if (window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window)) {
_set_wm_maximized(p_window, true);
}
- _set_wm_fullscreen(p_window, true);
+
wd.fullscreen = true;
+ _set_wm_fullscreen(p_window, true);
} break;
case WINDOW_MODE_MAXIMIZED: {
_set_wm_maximized(p_window, true);
-
} break;
}
}
@@ -1385,62 +1593,20 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c
if (wd.fullscreen) { //if fullscreen, it's not in another mode
return WINDOW_MODE_FULLSCREEN;
}
- { //test maximized
- // Using EWMH -- Extended Window Manager Hints
- Atom property = XInternAtom(x11_display, "_NET_WM_STATE", False);
- Atom type;
- int format;
- unsigned long len;
- unsigned long remaining;
- unsigned char *data = nullptr;
- bool retval = false;
- int result = XGetWindowProperty(
- x11_display,
- wd.x11_window,
- property,
- 0,
- 1024,
- False,
- XA_ATOM,
- &type,
- &format,
- &len,
- &remaining,
- &data);
-
- if (result == Success && data) {
- Atom *atoms = (Atom *)data;
- Atom wm_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
- Atom wm_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
- bool found_wm_max_horz = false;
- bool found_wm_max_vert = false;
-
- for (uint64_t i = 0; i < len; i++) {
- if (atoms[i] == wm_max_horz) {
- found_wm_max_horz = true;
- }
- if (atoms[i] == wm_max_vert) {
- found_wm_max_vert = true;
- }
-
- if (found_wm_max_horz && found_wm_max_vert) {
- retval = true;
- break;
- }
- }
-
- XFree(data);
- }
-
- if (retval) {
- return WINDOW_MODE_MAXIMIZED;
- }
+ // Test maximized.
+ // Using EWMH -- Extended Window Manager Hints
+ if (_window_maximize_check(p_window, "_NET_WM_STATE")) {
+ return WINDOW_MODE_MAXIMIZED;
}
- { // test minimzed
+ { // Test minimized.
// Using ICCCM -- Inter-Client Communication Conventions Manual
Atom property = XInternAtom(x11_display, "WM_STATE", True);
+ if (property == None) {
+ return WINDOW_MODE_WINDOWED;
+ }
+
Atom type;
int format;
unsigned long len;
@@ -1471,7 +1637,7 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c
}
}
- // all other discarded, return windowed.
+ // All other discarded, return windowed.
return WINDOW_MODE_WINDOWED;
}
@@ -1484,37 +1650,11 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
switch (p_flag) {
case WINDOW_FLAG_RESIZE_DISABLED: {
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (p_enabled) {
- Size2i size = window_get_size(p_window);
-
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = size.x;
- xsh->max_width = size.x;
- xsh->min_height = size.y;
- xsh->max_height = size.y;
- } else {
- xsh->flags = 0L;
- if (wd.min_size != Size2i()) {
- xsh->flags |= PMinSize;
- xsh->min_width = wd.min_size.x;
- xsh->min_height = wd.min_size.y;
- }
- if (wd.max_size != Size2i()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = wd.max_size.x;
- xsh->max_height = wd.max_size.y;
- }
- }
-
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
-
wd.resize_disabled = p_enabled;
- XFlush(x11_display);
+ _update_size_hints(p_window);
+ XFlush(x11_display);
} break;
case WINDOW_FLAG_BORDERLESS: {
Hints hints;
@@ -1522,7 +1662,9 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
hints.flags = 2;
hints.decorations = p_enabled ? 0 : 1;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ if (property != None) {
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
// Preserve window size
window_set_size(window_get_size(p_window), p_window);
@@ -1682,10 +1824,16 @@ void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_win
return;
}
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
if (p_active) {
- XSetICFocus(wd.xic);
+ {
+ MutexLock mutex_lock(events_mutex);
+ XSetICFocus(wd.xic);
+ }
window_set_ime_position(wd.im_position, p_window);
} else {
+ MutexLock mutex_lock(events_mutex);
XUnsetICFocus(wd.xic);
}
}
@@ -1706,7 +1854,14 @@ void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_
spot.x = short(p_pos.x);
spot.y = short(p_pos.y);
XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, nullptr);
- XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, nullptr);
+
+ {
+ // Block events polling during this call
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, nullptr);
+ }
+
XFree(preedit_attr);
}
@@ -1846,62 +2001,132 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape
}
}
-DisplayServerX11::LatinKeyboardVariant DisplayServerX11::get_latin_keyboard_variant() const {
- _THREAD_SAFE_METHOD_
-
- XkbDescRec *xkbdesc = XkbAllocKeyboard();
- ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY);
+int DisplayServerX11::keyboard_get_layout_count() const {
+ int _group_count = 0;
+ XkbDescRec *kbd = XkbAllocKeyboard();
+ if (kbd) {
+ kbd->dpy = x11_display;
+ XkbGetControls(x11_display, XkbAllControlsMask, kbd);
+ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
+
+ const Atom *groups = kbd->names->groups;
+ if (kbd->ctrls != NULL) {
+ _group_count = kbd->ctrls->num_groups;
+ } else {
+ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
+ _group_count++;
+ }
+ }
+ XkbFreeKeyboard(kbd, 0, true);
+ }
+ return _group_count;
+}
- XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc);
- ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY);
- ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY);
+int DisplayServerX11::keyboard_get_current_layout() const {
+ XkbStateRec state;
+ XkbGetState(x11_display, XkbUseCoreKbd, &state);
+ return state.group;
+}
- char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols);
- ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY);
+void DisplayServerX11::keyboard_set_current_layout(int p_index) {
+ ERR_FAIL_INDEX(p_index, keyboard_get_layout_count());
+ XkbLockGroup(x11_display, XkbUseCoreKbd, p_index);
+}
- Vector<String> info = String(layout).split("+");
- ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY);
+String DisplayServerX11::keyboard_get_layout_language(int p_index) const {
+ String ret;
+ XkbDescRec *kbd = XkbAllocKeyboard();
+ if (kbd) {
+ kbd->dpy = x11_display;
+ XkbGetControls(x11_display, XkbAllControlsMask, kbd);
+ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
+ XkbGetNames(x11_display, XkbGroupNamesMask, kbd);
+
+ int _group_count = 0;
+ const Atom *groups = kbd->names->groups;
+ if (kbd->ctrls != NULL) {
+ _group_count = kbd->ctrls->num_groups;
+ } else {
+ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
+ _group_count++;
+ }
+ }
- if (info[1].find("colemak") != -1) {
- return LATIN_KEYBOARD_COLEMAK;
- } else if (info[1].find("qwertz") != -1) {
- return LATIN_KEYBOARD_QWERTZ;
- } else if (info[1].find("azerty") != -1) {
- return LATIN_KEYBOARD_AZERTY;
- } else if (info[1].find("qzerty") != -1) {
- return LATIN_KEYBOARD_QZERTY;
- } else if (info[1].find("dvorak") != -1) {
- return LATIN_KEYBOARD_DVORAK;
- } else if (info[1].find("neo") != -1) {
- return LATIN_KEYBOARD_NEO;
+ Atom names = kbd->names->symbols;
+ if (names != None) {
+ char *name = XGetAtomName(x11_display, names);
+ Vector<String> info = String(name).split("+");
+ if (p_index >= 0 && p_index < _group_count) {
+ if (p_index + 1 < info.size()) {
+ ret = info[p_index + 1]; // Skip "pc" at the start and "inet"/"group" at the end of symbols.
+ } else {
+ ret = "en"; // No symbol for layout fallback to "en".
+ }
+ } else {
+ ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ").");
+ }
+ XFree(name);
+ }
+ XkbFreeKeyboard(kbd, 0, true);
}
+ return ret.substr(0, 2);
+}
+
+String DisplayServerX11::keyboard_get_layout_name(int p_index) const {
+ String ret;
+ XkbDescRec *kbd = XkbAllocKeyboard();
+ if (kbd) {
+ kbd->dpy = x11_display;
+ XkbGetControls(x11_display, XkbAllControlsMask, kbd);
+ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd);
+ XkbGetNames(x11_display, XkbGroupNamesMask, kbd);
+
+ int _group_count = 0;
+ const Atom *groups = kbd->names->groups;
+ if (kbd->ctrls != NULL) {
+ _group_count = kbd->ctrls->num_groups;
+ } else {
+ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) {
+ _group_count++;
+ }
+ }
- return LATIN_KEYBOARD_QWERTY;
+ if (p_index >= 0 && p_index < _group_count) {
+ char *full_name = XGetAtomName(x11_display, groups[p_index]);
+ ret.parse_utf8(full_name);
+ XFree(full_name);
+ } else {
+ ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ").");
+ }
+ XkbFreeKeyboard(kbd, 0, true);
+ }
+ return ret;
}
DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
- Atom actual_type;
- int actual_format;
- unsigned long nitems;
- unsigned long bytes_after;
+ Atom actual_type = None;
+ int actual_format = 0;
+ unsigned long nitems = 0;
+ unsigned long bytes_after = 0;
unsigned char *ret = nullptr;
int read_bytes = 1024;
- //Keep trying to read the property until there are no
- //bytes unread.
- do {
- if (ret != nullptr) {
- XFree(ret);
- }
+ // Keep trying to read the property until there are no bytes unread.
+ if (p_property != None) {
+ do {
+ if (ret != nullptr) {
+ XFree(ret);
+ }
- XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
- &actual_type, &actual_format, &nitems, &bytes_after,
- &ret);
+ XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
+ &actual_type, &actual_format, &nitems, &bytes_after,
+ &ret);
- read_bytes *= 2;
+ read_bytes *= 2;
- } while (bytes_after != 0);
+ } while (bytes_after != 0);
+ }
Property p = { ret, actual_format, (int)nitems, actual_type };
@@ -1957,7 +2182,7 @@ unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button
return last_button_state;
}
-void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo) {
+void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo) {
WindowData wd = windows[p_window];
// X11 functions don't know what const is
XKeyEvent *xkeyevent = p_event;
@@ -2094,7 +2319,7 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
/* Phase 4, determine if event must be filtered */
// This seems to be a side-effect of using XIM.
- // XEventFilter looks like a core X11 function,
+ // XFilterEvent looks like a core X11 function,
// but it's actually just used to see if we must
// ignore a deadkey, or events XIM determines
// must not reach the actual gui.
@@ -2128,17 +2353,16 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
// Echo characters in X11 are a keyrelease and a keypress
// one after the other with the (almot) same timestamp.
- // To detect them, i use XPeekEvent and check that their
- // difference in time is below a threshold.
+ // To detect them, i compare to the next event in list and
+ // check that their difference in time is below a threshold.
if (xkeyevent->type != KeyPress) {
p_echo = false;
// make sure there are events pending,
// so this call won't block.
- if (XPending(x11_display) > 0) {
- XEvent peek_event;
- XPeekEvent(x11_display, &peek_event);
+ if (p_event_index + 1 < p_events.size()) {
+ XEvent &peek_event = p_events[p_event_index + 1];
// I'm using a threshold of 5 msecs,
// since sometimes there seems to be a little
@@ -2153,9 +2377,9 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
KeySym rk;
XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, nullptr);
if (rk == keysym_keycode) {
- XEvent event;
- XNextEvent(x11_display, &event); //erase next event
- _handle_key_event(p_window, (XKeyEvent *)&event, true);
+ // Consume to next event.
+ ++p_event_index;
+ _handle_key_event(p_window, (XKeyEvent *)&peek_event, p_events, p_event_index, true);
return; //ignore current, echo next
}
}
@@ -2210,6 +2434,118 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event,
Input::get_singleton()->accumulate_input_event(k);
}
+Atom DisplayServerX11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const {
+ if (p_target == XInternAtom(x11_display, "TARGETS", 0)) {
+ // Request to list all supported targets.
+ Atom data[9];
+ data[0] = XInternAtom(x11_display, "TARGETS", 0);
+ data[1] = XInternAtom(x11_display, "SAVE_TARGETS", 0);
+ data[2] = XInternAtom(x11_display, "MULTIPLE", 0);
+ data[3] = XInternAtom(x11_display, "UTF8_STRING", 0);
+ data[4] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
+ data[5] = XInternAtom(x11_display, "TEXT", 0);
+ data[6] = XA_STRING;
+ data[7] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
+ data[8] = XInternAtom(x11_display, "text/plain", 0);
+
+ XChangeProperty(x11_display,
+ p_requestor,
+ p_property,
+ XA_ATOM,
+ 32,
+ PropModeReplace,
+ (unsigned char *)&data,
+ sizeof(data) / sizeof(data[0]));
+ return p_property;
+ } else if (p_target == XInternAtom(x11_display, "SAVE_TARGETS", 0)) {
+ // Request to check if SAVE_TARGETS is supported, nothing special to do.
+ XChangeProperty(x11_display,
+ p_requestor,
+ p_property,
+ XInternAtom(x11_display, "NULL", False),
+ 32,
+ PropModeReplace,
+ nullptr,
+ 0);
+ return p_property;
+ } else if (p_target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
+ p_target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
+ p_target == XInternAtom(x11_display, "TEXT", 0) ||
+ p_target == XA_STRING ||
+ p_target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
+ p_target == XInternAtom(x11_display, "text/plain", 0)) {
+ // Directly using internal clipboard because we know our window
+ // is the owner during a selection request.
+ CharString clip = internal_clipboard.utf8();
+ XChangeProperty(x11_display,
+ p_requestor,
+ p_property,
+ p_target,
+ 8,
+ PropModeReplace,
+ (unsigned char *)clip.get_data(),
+ clip.length());
+ return p_property;
+ } else {
+ char *target_name = XGetAtomName(x11_display, p_target);
+ printf("Target '%s' not supported.\n", target_name);
+ if (target_name) {
+ XFree(target_name);
+ }
+ return None;
+ }
+}
+
+void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p_event) const {
+ XEvent respond;
+ if (p_event->target == XInternAtom(x11_display, "MULTIPLE", 0)) {
+ // Request for multiple target conversions at once.
+ Atom atom_pair = XInternAtom(x11_display, "ATOM_PAIR", False);
+ respond.xselection.property = None;
+
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = nullptr;
+ if (XGetWindowProperty(x11_display, p_event->requestor, p_event->property, 0, LONG_MAX, False, atom_pair, &type, &format, &len, &remaining, &data) == Success) {
+ if ((len >= 2) && data) {
+ Atom *targets = (Atom *)data;
+ for (uint64_t i = 0; i < len; i += 2) {
+ Atom target = targets[i];
+ Atom &property = targets[i + 1];
+ property = _process_selection_request_target(target, p_event->requestor, property);
+ }
+
+ XChangeProperty(x11_display,
+ p_event->requestor,
+ p_event->property,
+ atom_pair,
+ 32,
+ PropModeReplace,
+ (unsigned char *)targets,
+ len);
+
+ respond.xselection.property = p_event->property;
+ }
+ XFree(data);
+ }
+ } else {
+ // Request for target conversion.
+ respond.xselection.property = _process_selection_request_target(p_event->target, p_event->requestor, p_event->property);
+ }
+
+ respond.xselection.type = SelectionNotify;
+ respond.xselection.display = p_event->display;
+ respond.xselection.requestor = p_event->requestor;
+ respond.xselection.selection = p_event->selection;
+ respond.xselection.target = p_event->target;
+ respond.xselection.time = p_event->time;
+
+ XSendEvent(x11_display, p_event->requestor, True, NoEventMask, &respond);
+ XFlush(x11_display);
+}
+
void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
::XPointer call_data) {
WARN_PRINT("Input method stopped");
@@ -2322,9 +2658,110 @@ void DisplayServerX11::_send_window_event(const WindowData &wd, WindowEvent p_ev
}
}
+void DisplayServerX11::_poll_events_thread(void *ud) {
+ DisplayServerX11 *display_server = (DisplayServerX11 *)ud;
+ display_server->_poll_events();
+}
+
+Bool DisplayServerX11::_predicate_all_events(Display *display, XEvent *event, XPointer arg) {
+ // Just accept all events.
+ return True;
+}
+
+bool DisplayServerX11::_wait_for_events() const {
+ int x11_fd = ConnectionNumber(x11_display);
+ fd_set in_fds;
+
+ XFlush(x11_display);
+
+ FD_ZERO(&in_fds);
+ FD_SET(x11_fd, &in_fds);
+
+ struct timeval tv;
+ tv.tv_usec = 0;
+ tv.tv_sec = 1;
+
+ // Wait for next event or timeout.
+ int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
+
+ if (num_ready_fds > 0) {
+ // Event received.
+ return true;
+ } else {
+ // Error or timeout.
+ if (num_ready_fds < 0) {
+ ERR_PRINT("_wait_for_events: select error: " + itos(errno));
+ }
+ return false;
+ }
+}
+
+void DisplayServerX11::_poll_events() {
+ while (!events_thread_done.is_set()) {
+ _wait_for_events();
+
+ // Process events from the queue.
+ {
+ MutexLock mutex_lock(events_mutex);
+
+ // Non-blocking wait for next event and remove it from the queue.
+ XEvent ev;
+ while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, nullptr)) {
+ // Check if the input manager wants to process the event.
+ if (XFilterEvent(&ev, None)) {
+ // Event has been filtered by the Input Manager,
+ // it has to be ignored and a new one will be received.
+ continue;
+ }
+
+ // Handle selection request events directly in the event thread, because
+ // communication through the x server takes several events sent back and forth
+ // and we don't want to block other programs while processing only one each frame.
+ if (ev.type == SelectionRequest) {
+ _handle_selection_request_event(&(ev.xselectionrequest));
+ continue;
+ }
+
+ polled_events.push_back(ev);
+ }
+ }
+ }
+}
+
void DisplayServerX11::process_events() {
_THREAD_SAFE_METHOD_
+#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+ static int frame = 0;
+ ++frame;
+#endif
+
+ if (app_focused) {
+ //verify that one of the windows has focus, else send focus out notification
+ bool focus_found = false;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (E->get().focused) {
+ focus_found = true;
+ break;
+ }
+ }
+
+ if (!focus_found) {
+ uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_no_focus;
+
+ if (delta > 250) {
+ //X11 can go between windows and have no focus for a while, when creating them or something else. Use this as safety to avoid unnecessary focus in/outs.
+ if (OS::get_singleton()->get_main_loop()) {
+ DEBUG_LOG_X11("All focus lost, triggering NOTIFICATION_APPLICATION_FOCUS_OUT\n");
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
+ }
+ app_focused = false;
+ }
+ } else {
+ time_since_no_focus = OS::get_singleton()->get_ticks_msec();
+ }
+ }
+
do_mouse_warp = false;
// Is the current mouse mode one where it needs to be grabbed.
@@ -2334,9 +2771,16 @@ void DisplayServerX11::process_events() {
xi.tilt = Vector2();
xi.pressure_supported = false;
- while (XPending(x11_display) > 0) {
- XEvent event;
- XNextEvent(x11_display, &event);
+ LocalVector<XEvent> events;
+ {
+ // Block events polling while flushing events.
+ MutexLock mutex_lock(events_mutex);
+ events = polled_events;
+ polled_events.clear();
+ }
+
+ for (uint32_t event_index = 0; event_index < events.size(); ++event_index) {
+ XEvent &event = events[event_index];
WindowID window_id = MAIN_WINDOW_ID;
@@ -2348,10 +2792,6 @@ void DisplayServerX11::process_events() {
}
}
- if (XFilterEvent(&event, None)) {
- continue;
- }
-
if (XGetEventData(x11_display, &event.xcookie)) {
if (event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
@@ -2514,34 +2954,74 @@ void DisplayServerX11::process_events() {
XFreeEventData(x11_display, &event.xcookie);
switch (event.type) {
- case Expose:
+ case MapNotify: {
+ DEBUG_LOG_X11("[%u] MapNotify window=%lu (%u) \n", frame, event.xmap.window, window_id);
+
+ const WindowData &wd = windows[window_id];
+
+ // Set focus when menu window is started.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (wd.menu_type && !wd.no_focus) {
+ XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
+ }
+ } break;
+
+ case Expose: {
+ DEBUG_LOG_X11("[%u] Expose window=%lu (%u), count='%u' \n", frame, event.xexpose.window, window_id, event.xexpose.count);
+
Main::force_redraw();
- break;
+ } break;
- case NoExpose:
- minimized = true;
- break;
+ case NoExpose: {
+ DEBUG_LOG_X11("[%u] NoExpose drawable=%lu (%u) \n", frame, event.xnoexpose.drawable, window_id);
+
+ windows[window_id].minimized = true;
+ } break;
case VisibilityNotify: {
+ DEBUG_LOG_X11("[%u] VisibilityNotify window=%lu (%u), state=%u \n", frame, event.xvisibility.window, window_id, event.xvisibility.state);
+
XVisibilityEvent *visibility = (XVisibilityEvent *)&event;
- minimized = (visibility->state == VisibilityFullyObscured);
+ windows[window_id].minimized = (visibility->state == VisibilityFullyObscured);
} break;
+
case LeaveNotify: {
+ DEBUG_LOG_X11("[%u] LeaveNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
+
if (!mouse_mode_grab) {
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
}
} break;
+
case EnterNotify: {
+ DEBUG_LOG_X11("[%u] EnterNotify window=%lu (%u), mode='%u' \n", frame, event.xcrossing.window, window_id, event.xcrossing.mode);
+
if (!mouse_mode_grab) {
_send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
}
} break;
- case FocusIn:
- minimized = false;
- window_has_focus = true;
- _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_IN);
- window_focused = true;
+
+ case FocusIn: {
+ DEBUG_LOG_X11("[%u] FocusIn window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode);
+
+ WindowData &wd = windows[window_id];
+
+ wd.focused = true;
+
+ if (wd.xic) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XSetICFocus(wd.xic);
+ }
+
+ // Keep track of focus order for overlapping windows.
+ static unsigned int focus_order = 0;
+ wd.focus_order = ++focus_order;
+
+ _send_window_event(wd, WINDOW_EVENT_FOCUS_IN);
if (mouse_mode_grab) {
// Show and update the cursor if confined and the window regained focus.
@@ -2565,16 +3045,31 @@ void DisplayServerX11::process_events() {
XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
}*/
#endif
- if (windows[window_id].xic) {
- XSetICFocus(windows[window_id].xic);
+
+ if (!app_focused) {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
+ }
+ app_focused = true;
+ }
+ } break;
+
+ case FocusOut: {
+ DEBUG_LOG_X11("[%u] FocusOut window=%lu (%u), mode='%u' \n", frame, event.xfocus.window, window_id, event.xfocus.mode);
+
+ WindowData &wd = windows[window_id];
+
+ wd.focused = false;
+
+ if (wd.xic) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+ XUnsetICFocus(wd.xic);
}
- break;
- case FocusOut:
- window_has_focus = false;
Input::get_singleton()->release_pressed_events();
- _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT);
- window_focused = false;
+ _send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);
if (mouse_mode_grab) {
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
@@ -2603,14 +3098,23 @@ void DisplayServerX11::process_events() {
}
xi.state.clear();
#endif
- if (windows[window_id].xic) {
- XSetICFocus(windows[window_id].xic);
+ } break;
+
+ case ConfigureNotify: {
+ DEBUG_LOG_X11("[%u] ConfigureNotify window=%lu (%u), event=%lu, above=%lu, override_redirect=%u \n", frame, event.xconfigure.window, window_id, event.xconfigure.event, event.xconfigure.above, event.xconfigure.override_redirect);
+
+ const WindowData &wd = windows[window_id];
+
+ // Set focus when menu window is re-used.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (wd.menu_type && !wd.no_focus) {
+ XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
}
- break;
- case ConfigureNotify:
_window_changed(&event);
- break;
+ } break;
+
case ButtonPress:
case ButtonRelease: {
/* exit in case of a mouse button press */
@@ -2637,7 +3141,18 @@ void DisplayServerX11::process_events() {
mb->set_pressed((event.type == ButtonPress));
+ const WindowData &wd = windows[window_id];
+
if (event.type == ButtonPress) {
+ DEBUG_LOG_X11("[%u] ButtonPress window=%lu (%u), button_index=%u \n", frame, event.xbutton.window, window_id, mb->get_button_index());
+
+ // Ensure window focus on click.
+ // RevertToPointerRoot is used to make sure we don't lose all focus in case
+ // a subwindow and its parent are both destroyed.
+ if (!wd.no_focus) {
+ XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime);
+ }
+
uint64_t diff = OS::get_singleton()->get_ticks_usec() / 1000 - last_click_ms;
if (mb->get_button_index() == last_click_button_index) {
@@ -2656,6 +3171,33 @@ void DisplayServerX11::process_events() {
last_click_ms += diff;
last_click_pos = Point2i(event.xbutton.x, event.xbutton.y);
}
+ } else {
+ DEBUG_LOG_X11("[%u] ButtonRelease window=%lu (%u), button_index=%u \n", frame, event.xbutton.window, window_id, mb->get_button_index());
+
+ if (!wd.focused) {
+ // Propagate the event to the focused window,
+ // because it's received only on the topmost window.
+ // Note: This is needed for drag & drop to work between windows,
+ // because the engine expects events to keep being processed
+ // on the same window dragging started.
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ const WindowData &wd_other = E->get();
+ WindowID window_id_other = E->key();
+ if (wd_other.focused) {
+ if (window_id_other != window_id) {
+ int x, y;
+ Window child;
+ XTranslateCoordinates(x11_display, wd.x11_window, wd_other.x11_window, event.xbutton.x, event.xbutton.y, &x, &y, &child);
+
+ mb->set_window_id(window_id_other);
+ mb->set_position(Vector2(x, y));
+ mb->set_global_position(mb->get_position());
+ Input::get_singleton()->accumulate_input_event(mb);
+ }
+ break;
+ }
+ }
+ }
}
Input::get_singleton()->accumulate_input_event(mb);
@@ -2673,11 +3215,11 @@ void DisplayServerX11::process_events() {
break;
}
- if (XPending(x11_display) > 0) {
- XEvent tevent;
- XPeekEvent(x11_display, &tevent);
- if (tevent.type == MotionNotify) {
- XNextEvent(x11_display, &event);
+ if (event_index + 1 < events.size()) {
+ const XEvent &next_event = events[event_index + 1];
+ if (next_event.type == MotionNotify) {
+ ++event_index;
+ event = next_event;
} else {
break;
}
@@ -2705,6 +3247,9 @@ void DisplayServerX11::process_events() {
break;
}
+ const WindowData &wd = windows[window_id];
+ bool focused = wd.focused;
+
if (mouse_mode == MOUSE_MODE_CAPTURED) {
if (xi.relative_motion.x == 0 && xi.relative_motion.y == 0) {
break;
@@ -2713,7 +3258,7 @@ void DisplayServerX11::process_events() {
Point2i new_center = pos;
pos = last_mouse_pos + xi.relative_motion;
center = new_center;
- do_mouse_warp = window_has_focus; // warp the cursor if we're focused in
+ do_mouse_warp = focused; // warp the cursor if we're focused in
}
if (!last_mouse_pos_valid) {
@@ -2755,14 +3300,11 @@ void DisplayServerX11::process_events() {
}
mm->set_tilt(xi.tilt);
- // Make the absolute position integral so it doesn't look _too_ weird :)
- Point2i posi(pos);
-
_get_key_modifier_state(event.xmotion.state, mm);
mm->set_button_mask(mouse_get_button_state());
- mm->set_position(posi);
- mm->set_global_position(posi);
- Input::get_singleton()->set_mouse_position(posi);
+ mm->set_position(pos);
+ mm->set_global_position(pos);
+ Input::get_singleton()->set_mouse_position(pos);
mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
mm->set_relative(rel);
@@ -2773,8 +3315,32 @@ void DisplayServerX11::process_events() {
// Don't propagate the motion event unless we have focus
// this is so that the relative motion doesn't get messed up
// after we regain focus.
- if (window_has_focus || !mouse_mode_grab) {
+ if (focused) {
Input::get_singleton()->accumulate_input_event(mm);
+ } else {
+ // Propagate the event to the focused window,
+ // because it's received only on the topmost window.
+ // Note: This is needed for drag & drop to work between windows,
+ // because the engine expects events to keep being processed
+ // on the same window dragging started.
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ const WindowData &wd_other = E->get();
+ if (wd_other.focused) {
+ int x, y;
+ Window child;
+ XTranslateCoordinates(x11_display, wd.x11_window, wd_other.x11_window, event.xmotion.x, event.xmotion.y, &x, &y, &child);
+
+ Point2i pos_focused(x, y);
+
+ mm->set_window_id(E->key());
+ mm->set_position(pos_focused);
+ mm->set_global_position(pos_focused);
+ mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
+ Input::get_singleton()->accumulate_input_event(mm);
+
+ break;
+ }
+ }
}
} break;
@@ -2784,67 +3350,7 @@ void DisplayServerX11::process_events() {
// key event is a little complex, so
// it will be handled in its own function.
- _handle_key_event(window_id, (XKeyEvent *)&event);
- } break;
- case SelectionRequest: {
- XSelectionRequestEvent *req;
- XEvent e, respond;
- e = event;
-
- req = &(e.xselectionrequest);
- if (req->target == XInternAtom(x11_display, "UTF8_STRING", 0) ||
- req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) ||
- req->target == XInternAtom(x11_display, "TEXT", 0) ||
- req->target == XA_STRING ||
- req->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
- req->target == XInternAtom(x11_display, "text/plain", 0)) {
- CharString clip = clipboard_get().utf8();
- XChangeProperty(x11_display,
- req->requestor,
- req->property,
- req->target,
- 8,
- PropModeReplace,
- (unsigned char *)clip.get_data(),
- clip.length());
- respond.xselection.property = req->property;
- } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) {
- Atom data[7];
- data[0] = XInternAtom(x11_display, "TARGETS", 0);
- data[1] = XInternAtom(x11_display, "UTF8_STRING", 0);
- data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0);
- data[3] = XInternAtom(x11_display, "TEXT", 0);
- data[4] = XA_STRING;
- data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0);
- data[6] = XInternAtom(x11_display, "text/plain", 0);
-
- XChangeProperty(x11_display,
- req->requestor,
- req->property,
- XA_ATOM,
- 32,
- PropModeReplace,
- (unsigned char *)&data,
- sizeof(data) / sizeof(data[0]));
- respond.xselection.property = req->property;
-
- } else {
- char *targetname = XGetAtomName(x11_display, req->target);
- printf("No Target '%s'\n", targetname);
- if (targetname) {
- XFree(targetname);
- }
- respond.xselection.property = None;
- }
-
- respond.xselection.type = SelectionNotify;
- respond.xselection.display = req->display;
- respond.xselection.requestor = req->requestor;
- respond.xselection.selection = req->selection;
- respond.xselection.target = req->target;
- respond.xselection.time = req->time;
- XSendEvent(x11_display, req->requestor, True, NoEventMask, &respond);
- XFlush(x11_display);
+ _handle_key_event(window_id, (XKeyEvent *)&event, events, event_index);
} break;
case SelectionNotify:
@@ -2854,7 +3360,7 @@ void DisplayServerX11::process_events() {
Vector<String> files = String((char *)p.data).split("\n", false);
for (int i = 0; i < files.size(); i++) {
- files.write[i] = files[i].replace("file://", "").http_unescape().strip_edges();
+ files.write[i] = files[i].replace("file://", "").uri_decode().strip_edges();
}
if (!windows[window_id].drop_files_callback.is_null()) {
@@ -3095,7 +3601,9 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
pr += 4;
}
- XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
+ if (net_wm_icon != None) {
+ XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
+ }
if (!g_set_icon_error) {
break;
@@ -3123,7 +3631,13 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() {
}
DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- return memnew(DisplayServerX11(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ if (r_error != OK) {
+ ds->alert("Your video card driver does not support any of the supported Vulkan versions.\n"
+ "Please update your drivers or if you have a very old or integrated GPU upgrade it.",
+ "Unable to initialize Video driver");
+ }
+ return ds;
}
DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
@@ -3145,19 +3659,46 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
- WindowID id;
+ WindowID id = window_id_counter++;
+ WindowData &wd = windows[id];
+
+ if ((id != MAIN_WINDOW_ID) && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) {
+ wd.menu_type = true;
+ }
+
+ if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
+ wd.menu_type = true;
+ wd.no_focus = true;
+ }
+
+ // Setup for menu subwindows:
+ // - override_redirect forces the WM not to interfere with the window, to avoid delays due to
+ // handling decorations and placement.
+ // On the other hand, focus changes need to be handled manually when this is set.
+ // - save_under is a hint for the WM to keep the content of windows behind to avoid repaint.
+ if (wd.menu_type) {
+ windowAttributes.override_redirect = True;
+ windowAttributes.save_under = True;
+ valuemask |= CWOverrideRedirect | CWSaveUnder;
+ }
+
{
- WindowData wd;
wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo->screen), p_rect.position.x, p_rect.position.y, p_rect.size.width > 0 ? p_rect.size.width : 1, p_rect.size.height > 0 ? p_rect.size.height : 1, 0, visualInfo->depth, InputOutput, visualInfo->visual, valuemask, &windowAttributes);
- XMapWindow(x11_display, wd.x11_window);
+ // Enable receiving notification when the window is initialized (MapNotify)
+ // so the focus can be set at the right time.
+ if (wd.menu_type && !wd.no_focus) {
+ XSelectInput(x11_display, wd.x11_window, StructureNotifyMask);
+ }
//associate PID
// make PID known to X11
{
const long pid = OS::get_singleton()->get_process_id();
Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False);
- XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+ if (net_wm_pid != None) {
+ XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+ }
}
long im_event_mask = 0;
@@ -3205,9 +3746,15 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
/* set the titlebar name */
XStoreName(x11_display, wd.x11_window, "Godot");
XSetWMProtocols(x11_display, wd.x11_window, &wm_delete, 1);
- XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1);
+ if (xdnd_aware != None) {
+ XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1);
+ }
if (xim && xim_style) {
+ // Block events polling while changing input focus
+ // because it triggers some event polling internally.
+ MutexLock mutex_lock(events_mutex);
+
wd.xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, wd.x11_window, XNFocusWindow, wd.x11_window, (char *)nullptr);
if (XGetICValues(wd.xic, XNFilterEvents, &im_event_mask, nullptr) != nullptr) {
WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
@@ -3226,86 +3773,34 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
_update_context(wd);
- id = window_id_counter++;
-
- windows[id] = wd;
-
- {
- if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
-
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = p_rect.size.width;
- xsh->max_width = p_rect.size.width;
- xsh->min_height = p_rect.size.height;
- xsh->max_height = p_rect.size.height;
-
- XSetWMNormalHints(x11_display, wd.x11_window, xsh);
- XFree(xsh);
- }
-
- bool make_utility = false;
-
- if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
- Hints hints;
- Atom property;
- hints.flags = 2;
- hints.decorations = 0;
- property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
+ Hints hints;
+ Atom property;
+ hints.flags = 2;
+ hints.decorations = 0;
+ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ if (property != None) {
XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
-
- make_utility = true;
- }
- if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
- make_utility = true;
}
+ }
- if (make_utility) {
- //this one seems to disable the fade animations for regular windows
- //but has the drawback that will not get focus by default, so
- //we need fo force it, unless no focus requested
-
- Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
- Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
-
+ if (wd.menu_type) {
+ // Set Utility type to disable fade animations.
+ Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
+ Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
+ if (wt_atom != None && type_atom != None) {
XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ }
+ } else {
+ Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
+ Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
- if (!(p_flags & WINDOW_FLAG_NO_FOCUS_BIT)) {
- //but as utility appears unfocused, it needs to be forcefuly focused, unless no focus requested
- XEvent xev;
- Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False);
-
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = wd.x11_window;
- xev.xclient.message_type = net_active_window;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = 1;
- xev.xclient.data.l[1] = CurrentTime;
-
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- }
- } else {
- Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
- Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
-
+ if (wt_atom != None && type_atom != None) {
XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
}
}
- if (id != MAIN_WINDOW_ID) {
- XSizeHints my_hints = XSizeHints();
-
- my_hints.flags = PPosition | PSize; /* I want to specify position and size */
- my_hints.x = p_rect.position.x; /* The origin and size coords I want */
- my_hints.y = p_rect.position.y;
- my_hints.width = p_rect.size.width;
- my_hints.height = p_rect.size.height;
-
- XSetNormalHints(x11_display, wd.x11_window, &my_hints);
- XMoveWindow(x11_display, wd.x11_window, p_rect.position.x, p_rect.position.y);
- }
+ _update_size_hints(id);
#if defined(VULKAN_ENABLED)
if (context_vulkan) {
@@ -3323,8 +3818,6 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
XFree(visualInfo);
}
- WindowData &wd = windows[id];
-
window_set_mode(p_mode, id);
//sync size
@@ -3346,6 +3839,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u
if (cursors[current_cursor] != None) {
XDefineCursor(x11_display, wd.x11_window, cursors[current_cursor]);
}
+
return id;
}
@@ -3417,7 +3911,12 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
xrandr_handle = dlopen("libXrandr.so.2", RTLD_LAZY);
if (!xrandr_handle) {
err = dlerror();
- fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
+ // For some arcane reason, NetBSD now ships libXrandr.so.3 while the rest of the world has libXrandr.so.2...
+ // In case this happens for other X11 platforms in the future, let's give it a try too before failing.
+ xrandr_handle = dlopen("libXrandr.so.3", RTLD_LAZY);
+ if (!xrandr_handle) {
+ fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
+ }
} else {
XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
@@ -3580,11 +4079,16 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
WindowID main_window = _create_window(p_mode, p_flags, Rect2i(window_position, p_resolution));
+ if (main_window == INVALID_WINDOW_ID) {
+ r_error = ERR_CANT_CREATE;
+ return;
+ }
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, main_window);
}
}
+ show_window(main_window);
//create RenderingDevice if used
#if defined(VULKAN_ENABLED)
@@ -3593,12 +4097,12 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
rendering_device_vulkan = memnew(RenderingDeviceVulkan);
rendering_device_vulkan->initialize(context_vulkan);
- RasterizerRD::make_current();
+ RendererCompositorRD::make_current();
}
#endif
/*
- rendering_server = memnew(RenderingServerRaster);
+ rendering_server = memnew(RenderingServerDefault);
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
rendering_server = memnew(RenderingServerWrapMT(rendering_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
}
@@ -3750,8 +4254,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
requested = None;
- window_has_focus = true; // Set focus to true at init
-
/*if (p_desired.layered) {
set_window_per_pixel_transparency_enabled(true);
}*/
@@ -3764,12 +4266,22 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
}
}
+ events_thread.start(_poll_events_thread, this);
+
_update_real_mouse_position(windows[MAIN_WINDOW_ID]);
r_error = OK;
}
DisplayServerX11::~DisplayServerX11() {
+ // Send owned clipboard data to clipboard manager before exit.
+ Window x11_main_window = windows[MAIN_WINDOW_ID].x11_window;
+ _clipboard_transfer_ownership(XA_PRIMARY, x11_main_window);
+ _clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_main_window);
+
+ events_thread_done.set();
+ events_thread.wait_to_finish();
+
//destroy all windows
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
#ifdef VULKAN_ENABLED
@@ -3778,11 +4290,13 @@ DisplayServerX11::~DisplayServerX11() {
}
#endif
- if (E->get().xic) {
- XDestroyIC(E->get().xic);
+ WindowData &wd = E->get();
+ if (wd.xic) {
+ XDestroyIC(wd.xic);
+ wd.xic = nullptr;
}
- XUnmapWindow(x11_display, E->get().x11_window);
- XDestroyWindow(x11_display, E->get().x11_window);
+ XUnmapWindow(x11_display, wd.x11_window);
+ XDestroyWindow(x11_display, wd.x11_window);
}
//destroy drivers
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index b5ea71f72a..10686d8424 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -36,14 +36,14 @@
#include "servers/display_server.h"
#include "core/input/input.h"
-
+#include "core/templates/local_vector.h"
#include "drivers/alsa/audio_driver_alsa.h"
#include "drivers/alsamidi/midi_driver_alsamidi.h"
#include "drivers/pulseaudio/audio_driver_pulseaudio.h"
#include "drivers/unix/os_unix.h"
#include "joypad_linux.h"
#include "servers/audio_server.h"
-#include "servers/rendering/rasterizer.h"
+#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
#if defined(OPENGL_ENABLED)
@@ -61,27 +61,18 @@
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
-// Hints for X11 fullscreen
-typedef struct {
- unsigned long flags;
- unsigned long functions;
- unsigned long decorations;
- long inputMode;
- unsigned long status;
-} Hints;
-
typedef struct _xrr_monitor_info {
Atom name;
- Bool primary;
- Bool automatic;
- int noutput;
- int x;
- int y;
- int width;
- int height;
- int mwidth;
- int mheight;
- RROutput *outputs;
+ Bool primary = false;
+ Bool automatic = false;
+ int noutput = 0;
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+ int mwidth = 0;
+ int mheight = 0;
+ RROutput *outputs = nullptr;
} xrr_monitor_info;
#undef CursorShape
@@ -133,6 +124,9 @@ class DisplayServerX11 : public DisplayServer {
ObjectID instance_id;
+ bool menu_type = false;
+ bool no_focus = false;
+
//better to guess on the fly, given WM can change it
//WindowMode mode;
bool fullscreen = false; //OS can't exit from this mode
@@ -140,6 +134,10 @@ class DisplayServerX11 : public DisplayServer {
bool borderless = false;
bool resize_disabled = false;
Vector2i last_position_before_fs;
+ bool focused = false;
+ bool minimized = false;
+
+ unsigned int focus_order = 0;
};
Map<WindowID, WindowData> windows;
@@ -165,6 +163,8 @@ class DisplayServerX11 : public DisplayServer {
uint64_t last_click_ms;
int last_click_button_index;
uint32_t last_button_state;
+ bool app_focused = false;
+ uint64_t time_since_no_focus = 0;
struct {
int opcode;
@@ -194,10 +194,17 @@ class DisplayServerX11 : public DisplayServer {
MouseMode mouse_mode;
Point2i center;
- void _handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo = false);
+ void _handle_key_event(WindowID p_window, XKeyEvent *p_event, LocalVector<XEvent> &p_events, uint32_t &p_event_index, bool p_echo = false);
+
+ Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const;
+ void _handle_selection_request_event(XSelectionRequestEvent *p_event) const;
- bool minimized;
- bool window_has_focus;
+ String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
+ String _clipboard_get(Atom p_source, Window x11_window) const;
+ void _clipboard_transfer_ownership(Atom p_source, Window x11_window) const;
+
+ //bool minimized;
+ //bool window_has_focus;
bool do_mouse_warp;
const char *cursor_theme;
@@ -211,7 +218,7 @@ class DisplayServerX11 : public DisplayServer {
bool layered_window;
String rendering_driver;
- bool window_focused;
+ //bool window_focused;
//void set_wm_border(bool p_enabled);
void set_wm_fullscreen(bool p_enabled);
void set_wm_above(bool p_enabled);
@@ -231,6 +238,8 @@ class DisplayServerX11 : public DisplayServer {
static Property _read_property(Display *p_display, Window p_window, Atom p_property);
void _update_real_mouse_position(const WindowData &wd);
+ bool _window_maximize_check(WindowID p_window, const char *p_atom_name) const;
+ void _update_size_hints(WindowID p_window);
void _set_wm_fullscreen(WindowID p_window, bool p_enabled);
void _set_wm_maximized(WindowID p_window, bool p_enabled);
@@ -242,6 +251,19 @@ class DisplayServerX11 : public DisplayServer {
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
+ mutable Mutex events_mutex;
+ Thread events_thread;
+ SafeFlag events_thread_done;
+ LocalVector<XEvent> polled_events;
+ static void _poll_events_thread(void *ud);
+ bool _wait_for_events() const;
+ void _poll_events();
+
+ static Bool _predicate_all_events(Display *display, XEvent *event, XPointer arg);
+ static Bool _predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg);
+ static Bool _predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg);
+ static Bool _predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg);
+
protected:
void _window_changed(XEvent *event);
@@ -272,6 +294,7 @@ public:
virtual Vector<DisplayServer::WindowID> get_window_list() const;
virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
+ virtual void show_window(WindowID p_id);
virtual void delete_sub_window(WindowID p_id);
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
@@ -280,6 +303,8 @@ public:
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const;
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID);
+
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
@@ -327,7 +352,11 @@ public:
virtual CursorShape cursor_get_shape() const;
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+ virtual int keyboard_get_layout_count() const;
+ virtual int keyboard_get_current_layout() const;
+ virtual void keyboard_set_current_layout(int p_index);
+ virtual String keyboard_get_layout_language(int p_index) const;
+ virtual String keyboard_get_layout_name(int p_index) const;
virtual void process_events();
diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp
index 86ea95c563..cb95068314 100644
--- a/platform/linuxbsd/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/export/export.h b/platform/linuxbsd/export/export.h
index 5ee81f485e..61e96aa2f6 100644
--- a/platform/linuxbsd/export/export.h
+++ b/platform/linuxbsd/export/export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/godot_linuxbsd.cpp b/platform/linuxbsd/godot_linuxbsd.cpp
index 3ed64e9d46..6f5c46b59c 100644
--- a/platform/linuxbsd/godot_linuxbsd.cpp
+++ b/platform/linuxbsd/godot_linuxbsd.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -41,6 +41,9 @@ int main(int argc, char *argv[]) {
setlocale(LC_CTYPE, "");
+ // We must override main when testing is enabled
+ TEST_MAIN_OVERRIDE
+
char *cwd = (char *)malloc(PATH_MAX);
ERR_FAIL_COND_V(!cwd, ERR_OUT_OF_MEMORY);
char *ret = getcwd(cwd, PATH_MAX);
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 5edaf35c50..471259e50f 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,13 +32,14 @@
#include "joypad_linux.h"
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#ifdef UDEV_ENABLED
-#include <libudev.h>
+#include "libudev-so_wrap.h"
#endif
#define LONG_BITS (sizeof(long) * 8)
@@ -49,15 +50,6 @@
static const char *ignore_str = "/dev/input/js";
#endif
-JoypadLinux::Joypad::Joypad() {
- fd = -1;
- dpad = 0;
- devpath = "";
- for (int i = 0; i < MAX_ABS; i++) {
- abs_info[i] = nullptr;
- }
-}
-
JoypadLinux::Joypad::~Joypad() {
for (int i = 0; i < MAX_ABS; i++) {
if (abs_info[i]) {
@@ -80,15 +72,28 @@ void JoypadLinux::Joypad::reset() {
}
JoypadLinux::JoypadLinux(Input *in) {
- exit_udev = false;
+#ifdef UDEV_ENABLED
+#ifdef DEBUG_ENABLED
+ int dylibloader_verbose = 1;
+#else
+ int dylibloader_verbose = 0;
+#endif
+ use_udev = initialize_libudev(dylibloader_verbose) == 0;
+ if (use_udev) {
+ print_verbose("JoypadLinux: udev enabled and loaded successfully.");
+ } else {
+ print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
+ }
+#else
+ print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
+#endif
input = in;
- joy_thread = Thread::create(joy_thread_func, this);
+ joy_thread.start(joy_thread_func, this);
}
JoypadLinux::~JoypadLinux() {
- exit_udev = true;
- Thread::wait_to_finish(joy_thread);
- memdelete(joy_thread);
+ exit_monitor.set();
+ joy_thread.wait_to_finish();
close_joypad();
}
@@ -101,11 +106,20 @@ void JoypadLinux::joy_thread_func(void *p_user) {
void JoypadLinux::run_joypad_thread() {
#ifdef UDEV_ENABLED
- udev *_udev = udev_new();
- ERR_FAIL_COND(!_udev);
- enumerate_joypads(_udev);
- monitor_joypads(_udev);
- udev_unref(_udev);
+ if (use_udev) {
+ udev *_udev = udev_new();
+ if (!_udev) {
+ use_udev = false;
+ ERR_PRINT("Failed getting an udev context, falling back to parsing /dev/input.");
+ monitor_joypads();
+ } else {
+ enumerate_joypads(_udev);
+ monitor_joypads(_udev);
+ udev_unref(_udev);
+ }
+ } else {
+ monitor_joypads();
+ }
#else
monitor_joypads();
#endif
@@ -146,7 +160,7 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
udev_monitor_enable_receiving(mon);
int fd = udev_monitor_get_fd(mon);
- while (!exit_udev) {
+ while (!exit_monitor.is_set()) {
fd_set fds;
struct timeval tv;
int ret;
@@ -188,17 +202,27 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
#endif
void JoypadLinux::monitor_joypads() {
- while (!exit_udev) {
+ while (!exit_monitor.is_set()) {
{
MutexLock lock(joy_mutex);
- for (int i = 0; i < 32; i++) {
+ DIR *input_directory;
+ input_directory = opendir("/dev/input");
+ if (input_directory) {
+ struct dirent *current;
char fname[64];
- sprintf(fname, "/dev/input/event%d", i);
- if (attached_devices.find(fname) == -1) {
- open_joypad(fname);
+
+ while ((current = readdir(input_directory)) != NULL) {
+ if (strncmp(current->d_name, "event", 5) != 0) {
+ continue;
+ }
+ sprintf(fname, "/dev/input/%.*s", 16, current->d_name);
+ if (attached_devices.find(fname) == -1) {
+ open_joypad(fname);
+ }
}
}
+ closedir(input_directory);
}
usleep(1000000); // 1s
}
@@ -311,16 +335,9 @@ void JoypadLinux::open_joypad(const char *p_path) {
return;
}
- //check if the device supports basic gamepad events, prevents certain keyboards from
- //being detected as joypads
+ // Check if the device supports basic gamepad events
if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
- (test_bit(ABS_X, absbit) || test_bit(ABS_Y, absbit) || test_bit(ABS_HAT0X, absbit) ||
- test_bit(ABS_GAS, absbit) || test_bit(ABS_RUDDER, absbit)) &&
- (test_bit(BTN_A, keybit) || test_bit(BTN_THUMBL, keybit) ||
- test_bit(BTN_TRIGGER, keybit) || test_bit(BTN_1, keybit))) &&
- !(test_bit(EV_ABS, evbit) &&
- test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) &&
- test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit))) {
+ test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
close(fd);
return;
}
@@ -466,9 +483,9 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0X:
if (ev.value != 0) {
if (ev.value < 0) {
- joy->dpad |= Input::HAT_MASK_LEFT;
+ joy->dpad = (joy->dpad | Input::HAT_MASK_LEFT) & ~Input::HAT_MASK_RIGHT;
} else {
- joy->dpad |= Input::HAT_MASK_RIGHT;
+ joy->dpad = (joy->dpad | Input::HAT_MASK_RIGHT) & ~Input::HAT_MASK_LEFT;
}
} else {
joy->dpad &= ~(Input::HAT_MASK_LEFT | Input::HAT_MASK_RIGHT);
@@ -480,9 +497,9 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0Y:
if (ev.value != 0) {
if (ev.value < 0) {
- joy->dpad |= Input::HAT_MASK_UP;
+ joy->dpad = (joy->dpad | Input::HAT_MASK_UP) & ~Input::HAT_MASK_DOWN;
} else {
- joy->dpad |= Input::HAT_MASK_DOWN;
+ joy->dpad = (joy->dpad | Input::HAT_MASK_DOWN) & ~Input::HAT_MASK_UP;
}
} else {
joy->dpad &= ~(Input::HAT_MASK_UP | Input::HAT_MASK_DOWN);
diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h
index 0d175193a5..b0d0db047b 100644
--- a/platform/linuxbsd/joypad_linux.h
+++ b/platform/linuxbsd/joypad_linux.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -56,25 +56,27 @@ private:
Input::JoyAxis curr_axis[MAX_ABS];
int key_map[MAX_KEY];
int abs_map[MAX_ABS];
- int dpad;
- int fd;
+ int dpad = 0;
+ int fd = -1;
String devpath;
- input_absinfo *abs_info[MAX_ABS];
+ input_absinfo *abs_info[MAX_ABS] = {};
- bool force_feedback;
- int ff_effect_id;
- uint64_t ff_effect_timestamp;
+ bool force_feedback = false;
+ int ff_effect_id = 0;
+ uint64_t ff_effect_timestamp = 0;
- Joypad();
~Joypad();
void reset();
};
- bool exit_udev;
+#ifdef UDEV_ENABLED
+ bool use_udev = false;
+#endif
+ SafeFlag exit_monitor;
Mutex joy_mutex;
- Thread *joy_thread;
- Input *input;
+ Thread joy_thread;
+ Input *input = nullptr;
Joypad joypads[JOYPADS_MAX];
Vector<String> attached_devices;
diff --git a/platform/linuxbsd/key_mapping_x11.cpp b/platform/linuxbsd/key_mapping_x11.cpp
index 77512b1a9e..f9f612fa74 100644
--- a/platform/linuxbsd/key_mapping_x11.cpp
+++ b/platform/linuxbsd/key_mapping_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -185,7 +185,6 @@ struct _TranslatePair {
};
static _TranslatePair _scancode_to_keycode[] = {
-
{ KEY_ESCAPE, 0x09 },
{ KEY_1, 0x0A },
{ KEY_2, 0x0B },
@@ -354,7 +353,6 @@ struct _XTranslateUnicodePair {
};
enum {
-
_KEYSYM_MAX = 759
};
@@ -1160,7 +1158,6 @@ struct _XTranslateUnicodePairReverse {
};
enum {
-
_UNICODE_MAX = 750
};
diff --git a/platform/linuxbsd/key_mapping_x11.h b/platform/linuxbsd/key_mapping_x11.h
index 8f5e01a3c2..163a8e21db 100644
--- a/platform/linuxbsd/key_mapping_x11.h
+++ b/platform/linuxbsd/key_mapping_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/libudev-so_wrap.c b/platform/linuxbsd/libudev-so_wrap.c
new file mode 100644
index 0000000000..a9fa4a494a
--- /dev/null
+++ b/platform/linuxbsd/libudev-so_wrap.c
@@ -0,0 +1,1013 @@
+// This file is generated. Do not edit!
+// see https://github.com/hpvb/dynload-wrapper for details
+// generated by /home/hp/Projects/godot/pulse/generate-wrapper.py 0.3 on 2021-02-20 00:08:59
+// flags: /home/hp/Projects/godot/pulse/generate-wrapper.py --include /usr/include/libudev.h --sys-include <libudev.h> --soname libudev.so.1 --init-name libudev --omit-prefix gnu_ --output-header libudev-so_wrap.h --output-implementation libudev-so_wrap.c
+//
+#include <stdint.h>
+
+#define udev_ref udev_ref_dylibloader_orig_libudev
+#define udev_unref udev_unref_dylibloader_orig_libudev
+#define udev_new udev_new_dylibloader_orig_libudev
+#define udev_set_log_fn udev_set_log_fn_dylibloader_orig_libudev
+#define udev_get_log_priority udev_get_log_priority_dylibloader_orig_libudev
+#define udev_set_log_priority udev_set_log_priority_dylibloader_orig_libudev
+#define udev_get_userdata udev_get_userdata_dylibloader_orig_libudev
+#define udev_set_userdata udev_set_userdata_dylibloader_orig_libudev
+#define udev_list_entry_get_next udev_list_entry_get_next_dylibloader_orig_libudev
+#define udev_list_entry_get_by_name udev_list_entry_get_by_name_dylibloader_orig_libudev
+#define udev_list_entry_get_name udev_list_entry_get_name_dylibloader_orig_libudev
+#define udev_list_entry_get_value udev_list_entry_get_value_dylibloader_orig_libudev
+#define udev_device_ref udev_device_ref_dylibloader_orig_libudev
+#define udev_device_unref udev_device_unref_dylibloader_orig_libudev
+#define udev_device_get_udev udev_device_get_udev_dylibloader_orig_libudev
+#define udev_device_new_from_syspath udev_device_new_from_syspath_dylibloader_orig_libudev
+#define udev_device_new_from_devnum udev_device_new_from_devnum_dylibloader_orig_libudev
+#define udev_device_new_from_subsystem_sysname udev_device_new_from_subsystem_sysname_dylibloader_orig_libudev
+#define udev_device_new_from_device_id udev_device_new_from_device_id_dylibloader_orig_libudev
+#define udev_device_new_from_environment udev_device_new_from_environment_dylibloader_orig_libudev
+#define udev_device_get_parent udev_device_get_parent_dylibloader_orig_libudev
+#define udev_device_get_parent_with_subsystem_devtype udev_device_get_parent_with_subsystem_devtype_dylibloader_orig_libudev
+#define udev_device_get_devpath udev_device_get_devpath_dylibloader_orig_libudev
+#define udev_device_get_subsystem udev_device_get_subsystem_dylibloader_orig_libudev
+#define udev_device_get_devtype udev_device_get_devtype_dylibloader_orig_libudev
+#define udev_device_get_syspath udev_device_get_syspath_dylibloader_orig_libudev
+#define udev_device_get_sysname udev_device_get_sysname_dylibloader_orig_libudev
+#define udev_device_get_sysnum udev_device_get_sysnum_dylibloader_orig_libudev
+#define udev_device_get_devnode udev_device_get_devnode_dylibloader_orig_libudev
+#define udev_device_get_is_initialized udev_device_get_is_initialized_dylibloader_orig_libudev
+#define udev_device_get_devlinks_list_entry udev_device_get_devlinks_list_entry_dylibloader_orig_libudev
+#define udev_device_get_properties_list_entry udev_device_get_properties_list_entry_dylibloader_orig_libudev
+#define udev_device_get_tags_list_entry udev_device_get_tags_list_entry_dylibloader_orig_libudev
+#define udev_device_get_sysattr_list_entry udev_device_get_sysattr_list_entry_dylibloader_orig_libudev
+#define udev_device_get_property_value udev_device_get_property_value_dylibloader_orig_libudev
+#define udev_device_get_driver udev_device_get_driver_dylibloader_orig_libudev
+#define udev_device_get_devnum udev_device_get_devnum_dylibloader_orig_libudev
+#define udev_device_get_action udev_device_get_action_dylibloader_orig_libudev
+#define udev_device_get_seqnum udev_device_get_seqnum_dylibloader_orig_libudev
+#define udev_device_get_usec_since_initialized udev_device_get_usec_since_initialized_dylibloader_orig_libudev
+#define udev_device_get_sysattr_value udev_device_get_sysattr_value_dylibloader_orig_libudev
+#define udev_device_set_sysattr_value udev_device_set_sysattr_value_dylibloader_orig_libudev
+#define udev_device_has_tag udev_device_has_tag_dylibloader_orig_libudev
+#define udev_monitor_ref udev_monitor_ref_dylibloader_orig_libudev
+#define udev_monitor_unref udev_monitor_unref_dylibloader_orig_libudev
+#define udev_monitor_get_udev udev_monitor_get_udev_dylibloader_orig_libudev
+#define udev_monitor_new_from_netlink udev_monitor_new_from_netlink_dylibloader_orig_libudev
+#define udev_monitor_enable_receiving udev_monitor_enable_receiving_dylibloader_orig_libudev
+#define udev_monitor_set_receive_buffer_size udev_monitor_set_receive_buffer_size_dylibloader_orig_libudev
+#define udev_monitor_get_fd udev_monitor_get_fd_dylibloader_orig_libudev
+#define udev_monitor_receive_device udev_monitor_receive_device_dylibloader_orig_libudev
+#define udev_monitor_filter_add_match_subsystem_devtype udev_monitor_filter_add_match_subsystem_devtype_dylibloader_orig_libudev
+#define udev_monitor_filter_add_match_tag udev_monitor_filter_add_match_tag_dylibloader_orig_libudev
+#define udev_monitor_filter_update udev_monitor_filter_update_dylibloader_orig_libudev
+#define udev_monitor_filter_remove udev_monitor_filter_remove_dylibloader_orig_libudev
+#define udev_enumerate_ref udev_enumerate_ref_dylibloader_orig_libudev
+#define udev_enumerate_unref udev_enumerate_unref_dylibloader_orig_libudev
+#define udev_enumerate_get_udev udev_enumerate_get_udev_dylibloader_orig_libudev
+#define udev_enumerate_new udev_enumerate_new_dylibloader_orig_libudev
+#define udev_enumerate_add_match_subsystem udev_enumerate_add_match_subsystem_dylibloader_orig_libudev
+#define udev_enumerate_add_nomatch_subsystem udev_enumerate_add_nomatch_subsystem_dylibloader_orig_libudev
+#define udev_enumerate_add_match_sysattr udev_enumerate_add_match_sysattr_dylibloader_orig_libudev
+#define udev_enumerate_add_nomatch_sysattr udev_enumerate_add_nomatch_sysattr_dylibloader_orig_libudev
+#define udev_enumerate_add_match_property udev_enumerate_add_match_property_dylibloader_orig_libudev
+#define udev_enumerate_add_match_sysname udev_enumerate_add_match_sysname_dylibloader_orig_libudev
+#define udev_enumerate_add_match_tag udev_enumerate_add_match_tag_dylibloader_orig_libudev
+#define udev_enumerate_add_match_parent udev_enumerate_add_match_parent_dylibloader_orig_libudev
+#define udev_enumerate_add_match_is_initialized udev_enumerate_add_match_is_initialized_dylibloader_orig_libudev
+#define udev_enumerate_add_syspath udev_enumerate_add_syspath_dylibloader_orig_libudev
+#define udev_enumerate_scan_devices udev_enumerate_scan_devices_dylibloader_orig_libudev
+#define udev_enumerate_scan_subsystems udev_enumerate_scan_subsystems_dylibloader_orig_libudev
+#define udev_enumerate_get_list_entry udev_enumerate_get_list_entry_dylibloader_orig_libudev
+#define udev_queue_ref udev_queue_ref_dylibloader_orig_libudev
+#define udev_queue_unref udev_queue_unref_dylibloader_orig_libudev
+#define udev_queue_get_udev udev_queue_get_udev_dylibloader_orig_libudev
+#define udev_queue_new udev_queue_new_dylibloader_orig_libudev
+#define udev_queue_get_kernel_seqnum udev_queue_get_kernel_seqnum_dylibloader_orig_libudev
+#define udev_queue_get_udev_seqnum udev_queue_get_udev_seqnum_dylibloader_orig_libudev
+#define udev_queue_get_udev_is_active udev_queue_get_udev_is_active_dylibloader_orig_libudev
+#define udev_queue_get_queue_is_empty udev_queue_get_queue_is_empty_dylibloader_orig_libudev
+#define udev_queue_get_seqnum_is_finished udev_queue_get_seqnum_is_finished_dylibloader_orig_libudev
+#define udev_queue_get_seqnum_sequence_is_finished udev_queue_get_seqnum_sequence_is_finished_dylibloader_orig_libudev
+#define udev_queue_get_fd udev_queue_get_fd_dylibloader_orig_libudev
+#define udev_queue_flush udev_queue_flush_dylibloader_orig_libudev
+#define udev_queue_get_queued_list_entry udev_queue_get_queued_list_entry_dylibloader_orig_libudev
+#define udev_hwdb_new udev_hwdb_new_dylibloader_orig_libudev
+#define udev_hwdb_ref udev_hwdb_ref_dylibloader_orig_libudev
+#define udev_hwdb_unref udev_hwdb_unref_dylibloader_orig_libudev
+#define udev_hwdb_get_properties_list_entry udev_hwdb_get_properties_list_entry_dylibloader_orig_libudev
+#define udev_util_encode_string udev_util_encode_string_dylibloader_orig_libudev
+#include <libudev.h>
+#undef udev_ref
+#undef udev_unref
+#undef udev_new
+#undef udev_set_log_fn
+#undef udev_get_log_priority
+#undef udev_set_log_priority
+#undef udev_get_userdata
+#undef udev_set_userdata
+#undef udev_list_entry_get_next
+#undef udev_list_entry_get_by_name
+#undef udev_list_entry_get_name
+#undef udev_list_entry_get_value
+#undef udev_device_ref
+#undef udev_device_unref
+#undef udev_device_get_udev
+#undef udev_device_new_from_syspath
+#undef udev_device_new_from_devnum
+#undef udev_device_new_from_subsystem_sysname
+#undef udev_device_new_from_device_id
+#undef udev_device_new_from_environment
+#undef udev_device_get_parent
+#undef udev_device_get_parent_with_subsystem_devtype
+#undef udev_device_get_devpath
+#undef udev_device_get_subsystem
+#undef udev_device_get_devtype
+#undef udev_device_get_syspath
+#undef udev_device_get_sysname
+#undef udev_device_get_sysnum
+#undef udev_device_get_devnode
+#undef udev_device_get_is_initialized
+#undef udev_device_get_devlinks_list_entry
+#undef udev_device_get_properties_list_entry
+#undef udev_device_get_tags_list_entry
+#undef udev_device_get_sysattr_list_entry
+#undef udev_device_get_property_value
+#undef udev_device_get_driver
+#undef udev_device_get_devnum
+#undef udev_device_get_action
+#undef udev_device_get_seqnum
+#undef udev_device_get_usec_since_initialized
+#undef udev_device_get_sysattr_value
+#undef udev_device_set_sysattr_value
+#undef udev_device_has_tag
+#undef udev_monitor_ref
+#undef udev_monitor_unref
+#undef udev_monitor_get_udev
+#undef udev_monitor_new_from_netlink
+#undef udev_monitor_enable_receiving
+#undef udev_monitor_set_receive_buffer_size
+#undef udev_monitor_get_fd
+#undef udev_monitor_receive_device
+#undef udev_monitor_filter_add_match_subsystem_devtype
+#undef udev_monitor_filter_add_match_tag
+#undef udev_monitor_filter_update
+#undef udev_monitor_filter_remove
+#undef udev_enumerate_ref
+#undef udev_enumerate_unref
+#undef udev_enumerate_get_udev
+#undef udev_enumerate_new
+#undef udev_enumerate_add_match_subsystem
+#undef udev_enumerate_add_nomatch_subsystem
+#undef udev_enumerate_add_match_sysattr
+#undef udev_enumerate_add_nomatch_sysattr
+#undef udev_enumerate_add_match_property
+#undef udev_enumerate_add_match_sysname
+#undef udev_enumerate_add_match_tag
+#undef udev_enumerate_add_match_parent
+#undef udev_enumerate_add_match_is_initialized
+#undef udev_enumerate_add_syspath
+#undef udev_enumerate_scan_devices
+#undef udev_enumerate_scan_subsystems
+#undef udev_enumerate_get_list_entry
+#undef udev_queue_ref
+#undef udev_queue_unref
+#undef udev_queue_get_udev
+#undef udev_queue_new
+#undef udev_queue_get_kernel_seqnum
+#undef udev_queue_get_udev_seqnum
+#undef udev_queue_get_udev_is_active
+#undef udev_queue_get_queue_is_empty
+#undef udev_queue_get_seqnum_is_finished
+#undef udev_queue_get_seqnum_sequence_is_finished
+#undef udev_queue_get_fd
+#undef udev_queue_flush
+#undef udev_queue_get_queued_list_entry
+#undef udev_hwdb_new
+#undef udev_hwdb_ref
+#undef udev_hwdb_unref
+#undef udev_hwdb_get_properties_list_entry
+#undef udev_util_encode_string
+#include <dlfcn.h>
+#include <stdio.h>
+struct udev* (*udev_ref_dylibloader_wrapper_libudev)(struct udev*);
+struct udev* (*udev_unref_dylibloader_wrapper_libudev)(struct udev*);
+struct udev* (*udev_new_dylibloader_wrapper_libudev)( void);
+void (*udev_set_log_fn_dylibloader_wrapper_libudev)(struct udev*, void*);
+int (*udev_get_log_priority_dylibloader_wrapper_libudev)(struct udev*);
+void (*udev_set_log_priority_dylibloader_wrapper_libudev)(struct udev*, int);
+void* (*udev_get_userdata_dylibloader_wrapper_libudev)(struct udev*);
+void (*udev_set_userdata_dylibloader_wrapper_libudev)(struct udev*, void*);
+struct udev_list_entry* (*udev_list_entry_get_next_dylibloader_wrapper_libudev)(struct udev_list_entry*);
+struct udev_list_entry* (*udev_list_entry_get_by_name_dylibloader_wrapper_libudev)(struct udev_list_entry*,const char*);
+const char* (*udev_list_entry_get_name_dylibloader_wrapper_libudev)(struct udev_list_entry*);
+const char* (*udev_list_entry_get_value_dylibloader_wrapper_libudev)(struct udev_list_entry*);
+struct udev_device* (*udev_device_ref_dylibloader_wrapper_libudev)(struct udev_device*);
+struct udev_device* (*udev_device_unref_dylibloader_wrapper_libudev)(struct udev_device*);
+struct udev* (*udev_device_get_udev_dylibloader_wrapper_libudev)(struct udev_device*);
+struct udev_device* (*udev_device_new_from_syspath_dylibloader_wrapper_libudev)(struct udev*,const char*);
+struct udev_device* (*udev_device_new_from_devnum_dylibloader_wrapper_libudev)(struct udev*, char, dev_t);
+struct udev_device* (*udev_device_new_from_subsystem_sysname_dylibloader_wrapper_libudev)(struct udev*,const char*,const char*);
+struct udev_device* (*udev_device_new_from_device_id_dylibloader_wrapper_libudev)(struct udev*,const char*);
+struct udev_device* (*udev_device_new_from_environment_dylibloader_wrapper_libudev)(struct udev*);
+struct udev_device* (*udev_device_get_parent_dylibloader_wrapper_libudev)(struct udev_device*);
+struct udev_device* (*udev_device_get_parent_with_subsystem_devtype_dylibloader_wrapper_libudev)(struct udev_device*,const char*,const char*);
+const char* (*udev_device_get_devpath_dylibloader_wrapper_libudev)(struct udev_device*);
+const char* (*udev_device_get_subsystem_dylibloader_wrapper_libudev)(struct udev_device*);
+const char* (*udev_device_get_devtype_dylibloader_wrapper_libudev)(struct udev_device*);
+const char* (*udev_device_get_syspath_dylibloader_wrapper_libudev)(struct udev_device*);
+const char* (*udev_device_get_sysname_dylibloader_wrapper_libudev)(struct udev_device*);
+const char* (*udev_device_get_sysnum_dylibloader_wrapper_libudev)(struct udev_device*);
+const char* (*udev_device_get_devnode_dylibloader_wrapper_libudev)(struct udev_device*);
+int (*udev_device_get_is_initialized_dylibloader_wrapper_libudev)(struct udev_device*);
+struct udev_list_entry* (*udev_device_get_devlinks_list_entry_dylibloader_wrapper_libudev)(struct udev_device*);
+struct udev_list_entry* (*udev_device_get_properties_list_entry_dylibloader_wrapper_libudev)(struct udev_device*);
+struct udev_list_entry* (*udev_device_get_tags_list_entry_dylibloader_wrapper_libudev)(struct udev_device*);
+struct udev_list_entry* (*udev_device_get_sysattr_list_entry_dylibloader_wrapper_libudev)(struct udev_device*);
+const char* (*udev_device_get_property_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*);
+const char* (*udev_device_get_driver_dylibloader_wrapper_libudev)(struct udev_device*);
+dev_t (*udev_device_get_devnum_dylibloader_wrapper_libudev)(struct udev_device*);
+const char* (*udev_device_get_action_dylibloader_wrapper_libudev)(struct udev_device*);
+unsigned long long int (*udev_device_get_seqnum_dylibloader_wrapper_libudev)(struct udev_device*);
+unsigned long long int (*udev_device_get_usec_since_initialized_dylibloader_wrapper_libudev)(struct udev_device*);
+const char* (*udev_device_get_sysattr_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*);
+int (*udev_device_set_sysattr_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*,const char*);
+int (*udev_device_has_tag_dylibloader_wrapper_libudev)(struct udev_device*,const char*);
+struct udev_monitor* (*udev_monitor_ref_dylibloader_wrapper_libudev)(struct udev_monitor*);
+struct udev_monitor* (*udev_monitor_unref_dylibloader_wrapper_libudev)(struct udev_monitor*);
+struct udev* (*udev_monitor_get_udev_dylibloader_wrapper_libudev)(struct udev_monitor*);
+struct udev_monitor* (*udev_monitor_new_from_netlink_dylibloader_wrapper_libudev)(struct udev*,const char*);
+int (*udev_monitor_enable_receiving_dylibloader_wrapper_libudev)(struct udev_monitor*);
+int (*udev_monitor_set_receive_buffer_size_dylibloader_wrapper_libudev)(struct udev_monitor*, int);
+int (*udev_monitor_get_fd_dylibloader_wrapper_libudev)(struct udev_monitor*);
+struct udev_device* (*udev_monitor_receive_device_dylibloader_wrapper_libudev)(struct udev_monitor*);
+int (*udev_monitor_filter_add_match_subsystem_devtype_dylibloader_wrapper_libudev)(struct udev_monitor*,const char*,const char*);
+int (*udev_monitor_filter_add_match_tag_dylibloader_wrapper_libudev)(struct udev_monitor*,const char*);
+int (*udev_monitor_filter_update_dylibloader_wrapper_libudev)(struct udev_monitor*);
+int (*udev_monitor_filter_remove_dylibloader_wrapper_libudev)(struct udev_monitor*);
+struct udev_enumerate* (*udev_enumerate_ref_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+struct udev_enumerate* (*udev_enumerate_unref_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+struct udev* (*udev_enumerate_get_udev_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+struct udev_enumerate* (*udev_enumerate_new_dylibloader_wrapper_libudev)(struct udev*);
+int (*udev_enumerate_add_match_subsystem_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+int (*udev_enumerate_add_nomatch_subsystem_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+int (*udev_enumerate_add_match_sysattr_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*);
+int (*udev_enumerate_add_nomatch_sysattr_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*);
+int (*udev_enumerate_add_match_property_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*);
+int (*udev_enumerate_add_match_sysname_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+int (*udev_enumerate_add_match_tag_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+int (*udev_enumerate_add_match_parent_dylibloader_wrapper_libudev)(struct udev_enumerate*,struct udev_device*);
+int (*udev_enumerate_add_match_is_initialized_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+int (*udev_enumerate_add_syspath_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+int (*udev_enumerate_scan_devices_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+int (*udev_enumerate_scan_subsystems_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+struct udev_list_entry* (*udev_enumerate_get_list_entry_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+struct udev_queue* (*udev_queue_ref_dylibloader_wrapper_libudev)(struct udev_queue*);
+struct udev_queue* (*udev_queue_unref_dylibloader_wrapper_libudev)(struct udev_queue*);
+struct udev* (*udev_queue_get_udev_dylibloader_wrapper_libudev)(struct udev_queue*);
+struct udev_queue* (*udev_queue_new_dylibloader_wrapper_libudev)(struct udev*);
+unsigned long long int (*udev_queue_get_kernel_seqnum_dylibloader_wrapper_libudev)(struct udev_queue*);
+unsigned long long int (*udev_queue_get_udev_seqnum_dylibloader_wrapper_libudev)(struct udev_queue*);
+int (*udev_queue_get_udev_is_active_dylibloader_wrapper_libudev)(struct udev_queue*);
+int (*udev_queue_get_queue_is_empty_dylibloader_wrapper_libudev)(struct udev_queue*);
+int (*udev_queue_get_seqnum_is_finished_dylibloader_wrapper_libudev)(struct udev_queue*, unsigned long long int);
+int (*udev_queue_get_seqnum_sequence_is_finished_dylibloader_wrapper_libudev)(struct udev_queue*, unsigned long long int, unsigned long long int);
+int (*udev_queue_get_fd_dylibloader_wrapper_libudev)(struct udev_queue*);
+int (*udev_queue_flush_dylibloader_wrapper_libudev)(struct udev_queue*);
+struct udev_list_entry* (*udev_queue_get_queued_list_entry_dylibloader_wrapper_libudev)(struct udev_queue*);
+struct udev_hwdb* (*udev_hwdb_new_dylibloader_wrapper_libudev)(struct udev*);
+struct udev_hwdb* (*udev_hwdb_ref_dylibloader_wrapper_libudev)(struct udev_hwdb*);
+struct udev_hwdb* (*udev_hwdb_unref_dylibloader_wrapper_libudev)(struct udev_hwdb*);
+struct udev_list_entry* (*udev_hwdb_get_properties_list_entry_dylibloader_wrapper_libudev)(struct udev_hwdb*,const char*, unsigned);
+int (*udev_util_encode_string_dylibloader_wrapper_libudev)(const char*, char*, size_t);
+int initialize_libudev(int verbose) {
+ void *handle;
+ char *error;
+ handle = dlopen("libudev.so.1", RTLD_LAZY);
+ if (!handle) {
+ if (verbose) {
+ fprintf(stderr, "%s\n", dlerror());
+ }
+ return(1);
+ }
+ dlerror();
+// udev_ref
+ *(void **) (&udev_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_ref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_unref
+ *(void **) (&udev_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_unref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_new
+ *(void **) (&udev_new_dylibloader_wrapper_libudev) = dlsym(handle, "udev_new");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_set_log_fn
+ *(void **) (&udev_set_log_fn_dylibloader_wrapper_libudev) = dlsym(handle, "udev_set_log_fn");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_get_log_priority
+ *(void **) (&udev_get_log_priority_dylibloader_wrapper_libudev) = dlsym(handle, "udev_get_log_priority");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_set_log_priority
+ *(void **) (&udev_set_log_priority_dylibloader_wrapper_libudev) = dlsym(handle, "udev_set_log_priority");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_get_userdata
+ *(void **) (&udev_get_userdata_dylibloader_wrapper_libudev) = dlsym(handle, "udev_get_userdata");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_set_userdata
+ *(void **) (&udev_set_userdata_dylibloader_wrapper_libudev) = dlsym(handle, "udev_set_userdata");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_list_entry_get_next
+ *(void **) (&udev_list_entry_get_next_dylibloader_wrapper_libudev) = dlsym(handle, "udev_list_entry_get_next");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_list_entry_get_by_name
+ *(void **) (&udev_list_entry_get_by_name_dylibloader_wrapper_libudev) = dlsym(handle, "udev_list_entry_get_by_name");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_list_entry_get_name
+ *(void **) (&udev_list_entry_get_name_dylibloader_wrapper_libudev) = dlsym(handle, "udev_list_entry_get_name");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_list_entry_get_value
+ *(void **) (&udev_list_entry_get_value_dylibloader_wrapper_libudev) = dlsym(handle, "udev_list_entry_get_value");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_ref
+ *(void **) (&udev_device_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_ref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_unref
+ *(void **) (&udev_device_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_unref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_udev
+ *(void **) (&udev_device_get_udev_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_udev");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_new_from_syspath
+ *(void **) (&udev_device_new_from_syspath_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_syspath");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_new_from_devnum
+ *(void **) (&udev_device_new_from_devnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_devnum");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_new_from_subsystem_sysname
+ *(void **) (&udev_device_new_from_subsystem_sysname_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_subsystem_sysname");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_new_from_device_id
+ *(void **) (&udev_device_new_from_device_id_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_device_id");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_new_from_environment
+ *(void **) (&udev_device_new_from_environment_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_environment");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_parent
+ *(void **) (&udev_device_get_parent_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_parent");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_parent_with_subsystem_devtype
+ *(void **) (&udev_device_get_parent_with_subsystem_devtype_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_parent_with_subsystem_devtype");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_devpath
+ *(void **) (&udev_device_get_devpath_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devpath");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_subsystem
+ *(void **) (&udev_device_get_subsystem_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_subsystem");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_devtype
+ *(void **) (&udev_device_get_devtype_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devtype");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_syspath
+ *(void **) (&udev_device_get_syspath_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_syspath");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_sysname
+ *(void **) (&udev_device_get_sysname_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_sysname");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_sysnum
+ *(void **) (&udev_device_get_sysnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_sysnum");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_devnode
+ *(void **) (&udev_device_get_devnode_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devnode");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_is_initialized
+ *(void **) (&udev_device_get_is_initialized_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_is_initialized");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_devlinks_list_entry
+ *(void **) (&udev_device_get_devlinks_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devlinks_list_entry");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_properties_list_entry
+ *(void **) (&udev_device_get_properties_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_properties_list_entry");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_tags_list_entry
+ *(void **) (&udev_device_get_tags_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_tags_list_entry");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_sysattr_list_entry
+ *(void **) (&udev_device_get_sysattr_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_sysattr_list_entry");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_property_value
+ *(void **) (&udev_device_get_property_value_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_property_value");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_driver
+ *(void **) (&udev_device_get_driver_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_driver");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_devnum
+ *(void **) (&udev_device_get_devnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devnum");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_action
+ *(void **) (&udev_device_get_action_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_action");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_seqnum
+ *(void **) (&udev_device_get_seqnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_seqnum");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_usec_since_initialized
+ *(void **) (&udev_device_get_usec_since_initialized_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_usec_since_initialized");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_get_sysattr_value
+ *(void **) (&udev_device_get_sysattr_value_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_sysattr_value");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_set_sysattr_value
+ *(void **) (&udev_device_set_sysattr_value_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_set_sysattr_value");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_device_has_tag
+ *(void **) (&udev_device_has_tag_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_has_tag");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_ref
+ *(void **) (&udev_monitor_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_ref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_unref
+ *(void **) (&udev_monitor_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_unref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_get_udev
+ *(void **) (&udev_monitor_get_udev_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_get_udev");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_new_from_netlink
+ *(void **) (&udev_monitor_new_from_netlink_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_new_from_netlink");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_enable_receiving
+ *(void **) (&udev_monitor_enable_receiving_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_enable_receiving");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_set_receive_buffer_size
+ *(void **) (&udev_monitor_set_receive_buffer_size_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_set_receive_buffer_size");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_get_fd
+ *(void **) (&udev_monitor_get_fd_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_get_fd");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_receive_device
+ *(void **) (&udev_monitor_receive_device_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_receive_device");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_filter_add_match_subsystem_devtype
+ *(void **) (&udev_monitor_filter_add_match_subsystem_devtype_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_filter_add_match_subsystem_devtype");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_filter_add_match_tag
+ *(void **) (&udev_monitor_filter_add_match_tag_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_filter_add_match_tag");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_filter_update
+ *(void **) (&udev_monitor_filter_update_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_filter_update");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_monitor_filter_remove
+ *(void **) (&udev_monitor_filter_remove_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_filter_remove");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_ref
+ *(void **) (&udev_enumerate_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_ref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_unref
+ *(void **) (&udev_enumerate_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_unref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_get_udev
+ *(void **) (&udev_enumerate_get_udev_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_get_udev");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_new
+ *(void **) (&udev_enumerate_new_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_new");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_match_subsystem
+ *(void **) (&udev_enumerate_add_match_subsystem_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_subsystem");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_nomatch_subsystem
+ *(void **) (&udev_enumerate_add_nomatch_subsystem_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_nomatch_subsystem");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_match_sysattr
+ *(void **) (&udev_enumerate_add_match_sysattr_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_sysattr");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_nomatch_sysattr
+ *(void **) (&udev_enumerate_add_nomatch_sysattr_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_nomatch_sysattr");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_match_property
+ *(void **) (&udev_enumerate_add_match_property_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_property");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_match_sysname
+ *(void **) (&udev_enumerate_add_match_sysname_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_sysname");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_match_tag
+ *(void **) (&udev_enumerate_add_match_tag_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_tag");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_match_parent
+ *(void **) (&udev_enumerate_add_match_parent_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_parent");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_match_is_initialized
+ *(void **) (&udev_enumerate_add_match_is_initialized_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_is_initialized");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_add_syspath
+ *(void **) (&udev_enumerate_add_syspath_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_syspath");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_scan_devices
+ *(void **) (&udev_enumerate_scan_devices_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_scan_devices");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_scan_subsystems
+ *(void **) (&udev_enumerate_scan_subsystems_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_scan_subsystems");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_enumerate_get_list_entry
+ *(void **) (&udev_enumerate_get_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_get_list_entry");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_ref
+ *(void **) (&udev_queue_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_ref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_unref
+ *(void **) (&udev_queue_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_unref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_get_udev
+ *(void **) (&udev_queue_get_udev_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_udev");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_new
+ *(void **) (&udev_queue_new_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_new");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_get_kernel_seqnum
+ *(void **) (&udev_queue_get_kernel_seqnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_kernel_seqnum");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_get_udev_seqnum
+ *(void **) (&udev_queue_get_udev_seqnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_udev_seqnum");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_get_udev_is_active
+ *(void **) (&udev_queue_get_udev_is_active_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_udev_is_active");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_get_queue_is_empty
+ *(void **) (&udev_queue_get_queue_is_empty_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_queue_is_empty");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_get_seqnum_is_finished
+ *(void **) (&udev_queue_get_seqnum_is_finished_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_seqnum_is_finished");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_get_seqnum_sequence_is_finished
+ *(void **) (&udev_queue_get_seqnum_sequence_is_finished_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_seqnum_sequence_is_finished");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_get_fd
+ *(void **) (&udev_queue_get_fd_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_fd");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_flush
+ *(void **) (&udev_queue_flush_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_flush");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_queue_get_queued_list_entry
+ *(void **) (&udev_queue_get_queued_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_queued_list_entry");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_hwdb_new
+ *(void **) (&udev_hwdb_new_dylibloader_wrapper_libudev) = dlsym(handle, "udev_hwdb_new");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_hwdb_ref
+ *(void **) (&udev_hwdb_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_hwdb_ref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_hwdb_unref
+ *(void **) (&udev_hwdb_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_hwdb_unref");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_hwdb_get_properties_list_entry
+ *(void **) (&udev_hwdb_get_properties_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_hwdb_get_properties_list_entry");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+// udev_util_encode_string
+ *(void **) (&udev_util_encode_string_dylibloader_wrapper_libudev) = dlsym(handle, "udev_util_encode_string");
+ if (verbose) {
+ error = dlerror();
+ if (error != NULL) {
+ fprintf(stderr, "%s\n", error);
+ }
+ }
+return 0;
+}
diff --git a/platform/linuxbsd/libudev-so_wrap.h b/platform/linuxbsd/libudev-so_wrap.h
new file mode 100644
index 0000000000..dd43fd1191
--- /dev/null
+++ b/platform/linuxbsd/libudev-so_wrap.h
@@ -0,0 +1,378 @@
+#ifndef DYLIBLOAD_WRAPPER_LIBUDEV
+#define DYLIBLOAD_WRAPPER_LIBUDEV
+// This file is generated. Do not edit!
+// see https://github.com/hpvb/dynload-wrapper for details
+// generated by /home/hp/Projects/godot/pulse/generate-wrapper.py 0.3 on 2021-02-20 00:08:59
+// flags: /home/hp/Projects/godot/pulse/generate-wrapper.py --include /usr/include/libudev.h --sys-include <libudev.h> --soname libudev.so.1 --init-name libudev --omit-prefix gnu_ --output-header libudev-so_wrap.h --output-implementation libudev-so_wrap.c
+//
+#include <stdint.h>
+
+#define udev_ref udev_ref_dylibloader_orig_libudev
+#define udev_unref udev_unref_dylibloader_orig_libudev
+#define udev_new udev_new_dylibloader_orig_libudev
+#define udev_set_log_fn udev_set_log_fn_dylibloader_orig_libudev
+#define udev_get_log_priority udev_get_log_priority_dylibloader_orig_libudev
+#define udev_set_log_priority udev_set_log_priority_dylibloader_orig_libudev
+#define udev_get_userdata udev_get_userdata_dylibloader_orig_libudev
+#define udev_set_userdata udev_set_userdata_dylibloader_orig_libudev
+#define udev_list_entry_get_next udev_list_entry_get_next_dylibloader_orig_libudev
+#define udev_list_entry_get_by_name udev_list_entry_get_by_name_dylibloader_orig_libudev
+#define udev_list_entry_get_name udev_list_entry_get_name_dylibloader_orig_libudev
+#define udev_list_entry_get_value udev_list_entry_get_value_dylibloader_orig_libudev
+#define udev_device_ref udev_device_ref_dylibloader_orig_libudev
+#define udev_device_unref udev_device_unref_dylibloader_orig_libudev
+#define udev_device_get_udev udev_device_get_udev_dylibloader_orig_libudev
+#define udev_device_new_from_syspath udev_device_new_from_syspath_dylibloader_orig_libudev
+#define udev_device_new_from_devnum udev_device_new_from_devnum_dylibloader_orig_libudev
+#define udev_device_new_from_subsystem_sysname udev_device_new_from_subsystem_sysname_dylibloader_orig_libudev
+#define udev_device_new_from_device_id udev_device_new_from_device_id_dylibloader_orig_libudev
+#define udev_device_new_from_environment udev_device_new_from_environment_dylibloader_orig_libudev
+#define udev_device_get_parent udev_device_get_parent_dylibloader_orig_libudev
+#define udev_device_get_parent_with_subsystem_devtype udev_device_get_parent_with_subsystem_devtype_dylibloader_orig_libudev
+#define udev_device_get_devpath udev_device_get_devpath_dylibloader_orig_libudev
+#define udev_device_get_subsystem udev_device_get_subsystem_dylibloader_orig_libudev
+#define udev_device_get_devtype udev_device_get_devtype_dylibloader_orig_libudev
+#define udev_device_get_syspath udev_device_get_syspath_dylibloader_orig_libudev
+#define udev_device_get_sysname udev_device_get_sysname_dylibloader_orig_libudev
+#define udev_device_get_sysnum udev_device_get_sysnum_dylibloader_orig_libudev
+#define udev_device_get_devnode udev_device_get_devnode_dylibloader_orig_libudev
+#define udev_device_get_is_initialized udev_device_get_is_initialized_dylibloader_orig_libudev
+#define udev_device_get_devlinks_list_entry udev_device_get_devlinks_list_entry_dylibloader_orig_libudev
+#define udev_device_get_properties_list_entry udev_device_get_properties_list_entry_dylibloader_orig_libudev
+#define udev_device_get_tags_list_entry udev_device_get_tags_list_entry_dylibloader_orig_libudev
+#define udev_device_get_sysattr_list_entry udev_device_get_sysattr_list_entry_dylibloader_orig_libudev
+#define udev_device_get_property_value udev_device_get_property_value_dylibloader_orig_libudev
+#define udev_device_get_driver udev_device_get_driver_dylibloader_orig_libudev
+#define udev_device_get_devnum udev_device_get_devnum_dylibloader_orig_libudev
+#define udev_device_get_action udev_device_get_action_dylibloader_orig_libudev
+#define udev_device_get_seqnum udev_device_get_seqnum_dylibloader_orig_libudev
+#define udev_device_get_usec_since_initialized udev_device_get_usec_since_initialized_dylibloader_orig_libudev
+#define udev_device_get_sysattr_value udev_device_get_sysattr_value_dylibloader_orig_libudev
+#define udev_device_set_sysattr_value udev_device_set_sysattr_value_dylibloader_orig_libudev
+#define udev_device_has_tag udev_device_has_tag_dylibloader_orig_libudev
+#define udev_monitor_ref udev_monitor_ref_dylibloader_orig_libudev
+#define udev_monitor_unref udev_monitor_unref_dylibloader_orig_libudev
+#define udev_monitor_get_udev udev_monitor_get_udev_dylibloader_orig_libudev
+#define udev_monitor_new_from_netlink udev_monitor_new_from_netlink_dylibloader_orig_libudev
+#define udev_monitor_enable_receiving udev_monitor_enable_receiving_dylibloader_orig_libudev
+#define udev_monitor_set_receive_buffer_size udev_monitor_set_receive_buffer_size_dylibloader_orig_libudev
+#define udev_monitor_get_fd udev_monitor_get_fd_dylibloader_orig_libudev
+#define udev_monitor_receive_device udev_monitor_receive_device_dylibloader_orig_libudev
+#define udev_monitor_filter_add_match_subsystem_devtype udev_monitor_filter_add_match_subsystem_devtype_dylibloader_orig_libudev
+#define udev_monitor_filter_add_match_tag udev_monitor_filter_add_match_tag_dylibloader_orig_libudev
+#define udev_monitor_filter_update udev_monitor_filter_update_dylibloader_orig_libudev
+#define udev_monitor_filter_remove udev_monitor_filter_remove_dylibloader_orig_libudev
+#define udev_enumerate_ref udev_enumerate_ref_dylibloader_orig_libudev
+#define udev_enumerate_unref udev_enumerate_unref_dylibloader_orig_libudev
+#define udev_enumerate_get_udev udev_enumerate_get_udev_dylibloader_orig_libudev
+#define udev_enumerate_new udev_enumerate_new_dylibloader_orig_libudev
+#define udev_enumerate_add_match_subsystem udev_enumerate_add_match_subsystem_dylibloader_orig_libudev
+#define udev_enumerate_add_nomatch_subsystem udev_enumerate_add_nomatch_subsystem_dylibloader_orig_libudev
+#define udev_enumerate_add_match_sysattr udev_enumerate_add_match_sysattr_dylibloader_orig_libudev
+#define udev_enumerate_add_nomatch_sysattr udev_enumerate_add_nomatch_sysattr_dylibloader_orig_libudev
+#define udev_enumerate_add_match_property udev_enumerate_add_match_property_dylibloader_orig_libudev
+#define udev_enumerate_add_match_sysname udev_enumerate_add_match_sysname_dylibloader_orig_libudev
+#define udev_enumerate_add_match_tag udev_enumerate_add_match_tag_dylibloader_orig_libudev
+#define udev_enumerate_add_match_parent udev_enumerate_add_match_parent_dylibloader_orig_libudev
+#define udev_enumerate_add_match_is_initialized udev_enumerate_add_match_is_initialized_dylibloader_orig_libudev
+#define udev_enumerate_add_syspath udev_enumerate_add_syspath_dylibloader_orig_libudev
+#define udev_enumerate_scan_devices udev_enumerate_scan_devices_dylibloader_orig_libudev
+#define udev_enumerate_scan_subsystems udev_enumerate_scan_subsystems_dylibloader_orig_libudev
+#define udev_enumerate_get_list_entry udev_enumerate_get_list_entry_dylibloader_orig_libudev
+#define udev_queue_ref udev_queue_ref_dylibloader_orig_libudev
+#define udev_queue_unref udev_queue_unref_dylibloader_orig_libudev
+#define udev_queue_get_udev udev_queue_get_udev_dylibloader_orig_libudev
+#define udev_queue_new udev_queue_new_dylibloader_orig_libudev
+#define udev_queue_get_kernel_seqnum udev_queue_get_kernel_seqnum_dylibloader_orig_libudev
+#define udev_queue_get_udev_seqnum udev_queue_get_udev_seqnum_dylibloader_orig_libudev
+#define udev_queue_get_udev_is_active udev_queue_get_udev_is_active_dylibloader_orig_libudev
+#define udev_queue_get_queue_is_empty udev_queue_get_queue_is_empty_dylibloader_orig_libudev
+#define udev_queue_get_seqnum_is_finished udev_queue_get_seqnum_is_finished_dylibloader_orig_libudev
+#define udev_queue_get_seqnum_sequence_is_finished udev_queue_get_seqnum_sequence_is_finished_dylibloader_orig_libudev
+#define udev_queue_get_fd udev_queue_get_fd_dylibloader_orig_libudev
+#define udev_queue_flush udev_queue_flush_dylibloader_orig_libudev
+#define udev_queue_get_queued_list_entry udev_queue_get_queued_list_entry_dylibloader_orig_libudev
+#define udev_hwdb_new udev_hwdb_new_dylibloader_orig_libudev
+#define udev_hwdb_ref udev_hwdb_ref_dylibloader_orig_libudev
+#define udev_hwdb_unref udev_hwdb_unref_dylibloader_orig_libudev
+#define udev_hwdb_get_properties_list_entry udev_hwdb_get_properties_list_entry_dylibloader_orig_libudev
+#define udev_util_encode_string udev_util_encode_string_dylibloader_orig_libudev
+#include <libudev.h>
+#undef udev_ref
+#undef udev_unref
+#undef udev_new
+#undef udev_set_log_fn
+#undef udev_get_log_priority
+#undef udev_set_log_priority
+#undef udev_get_userdata
+#undef udev_set_userdata
+#undef udev_list_entry_get_next
+#undef udev_list_entry_get_by_name
+#undef udev_list_entry_get_name
+#undef udev_list_entry_get_value
+#undef udev_device_ref
+#undef udev_device_unref
+#undef udev_device_get_udev
+#undef udev_device_new_from_syspath
+#undef udev_device_new_from_devnum
+#undef udev_device_new_from_subsystem_sysname
+#undef udev_device_new_from_device_id
+#undef udev_device_new_from_environment
+#undef udev_device_get_parent
+#undef udev_device_get_parent_with_subsystem_devtype
+#undef udev_device_get_devpath
+#undef udev_device_get_subsystem
+#undef udev_device_get_devtype
+#undef udev_device_get_syspath
+#undef udev_device_get_sysname
+#undef udev_device_get_sysnum
+#undef udev_device_get_devnode
+#undef udev_device_get_is_initialized
+#undef udev_device_get_devlinks_list_entry
+#undef udev_device_get_properties_list_entry
+#undef udev_device_get_tags_list_entry
+#undef udev_device_get_sysattr_list_entry
+#undef udev_device_get_property_value
+#undef udev_device_get_driver
+#undef udev_device_get_devnum
+#undef udev_device_get_action
+#undef udev_device_get_seqnum
+#undef udev_device_get_usec_since_initialized
+#undef udev_device_get_sysattr_value
+#undef udev_device_set_sysattr_value
+#undef udev_device_has_tag
+#undef udev_monitor_ref
+#undef udev_monitor_unref
+#undef udev_monitor_get_udev
+#undef udev_monitor_new_from_netlink
+#undef udev_monitor_enable_receiving
+#undef udev_monitor_set_receive_buffer_size
+#undef udev_monitor_get_fd
+#undef udev_monitor_receive_device
+#undef udev_monitor_filter_add_match_subsystem_devtype
+#undef udev_monitor_filter_add_match_tag
+#undef udev_monitor_filter_update
+#undef udev_monitor_filter_remove
+#undef udev_enumerate_ref
+#undef udev_enumerate_unref
+#undef udev_enumerate_get_udev
+#undef udev_enumerate_new
+#undef udev_enumerate_add_match_subsystem
+#undef udev_enumerate_add_nomatch_subsystem
+#undef udev_enumerate_add_match_sysattr
+#undef udev_enumerate_add_nomatch_sysattr
+#undef udev_enumerate_add_match_property
+#undef udev_enumerate_add_match_sysname
+#undef udev_enumerate_add_match_tag
+#undef udev_enumerate_add_match_parent
+#undef udev_enumerate_add_match_is_initialized
+#undef udev_enumerate_add_syspath
+#undef udev_enumerate_scan_devices
+#undef udev_enumerate_scan_subsystems
+#undef udev_enumerate_get_list_entry
+#undef udev_queue_ref
+#undef udev_queue_unref
+#undef udev_queue_get_udev
+#undef udev_queue_new
+#undef udev_queue_get_kernel_seqnum
+#undef udev_queue_get_udev_seqnum
+#undef udev_queue_get_udev_is_active
+#undef udev_queue_get_queue_is_empty
+#undef udev_queue_get_seqnum_is_finished
+#undef udev_queue_get_seqnum_sequence_is_finished
+#undef udev_queue_get_fd
+#undef udev_queue_flush
+#undef udev_queue_get_queued_list_entry
+#undef udev_hwdb_new
+#undef udev_hwdb_ref
+#undef udev_hwdb_unref
+#undef udev_hwdb_get_properties_list_entry
+#undef udev_util_encode_string
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define udev_ref udev_ref_dylibloader_wrapper_libudev
+#define udev_unref udev_unref_dylibloader_wrapper_libudev
+#define udev_new udev_new_dylibloader_wrapper_libudev
+#define udev_set_log_fn udev_set_log_fn_dylibloader_wrapper_libudev
+#define udev_get_log_priority udev_get_log_priority_dylibloader_wrapper_libudev
+#define udev_set_log_priority udev_set_log_priority_dylibloader_wrapper_libudev
+#define udev_get_userdata udev_get_userdata_dylibloader_wrapper_libudev
+#define udev_set_userdata udev_set_userdata_dylibloader_wrapper_libudev
+#define udev_list_entry_get_next udev_list_entry_get_next_dylibloader_wrapper_libudev
+#define udev_list_entry_get_by_name udev_list_entry_get_by_name_dylibloader_wrapper_libudev
+#define udev_list_entry_get_name udev_list_entry_get_name_dylibloader_wrapper_libudev
+#define udev_list_entry_get_value udev_list_entry_get_value_dylibloader_wrapper_libudev
+#define udev_device_ref udev_device_ref_dylibloader_wrapper_libudev
+#define udev_device_unref udev_device_unref_dylibloader_wrapper_libudev
+#define udev_device_get_udev udev_device_get_udev_dylibloader_wrapper_libudev
+#define udev_device_new_from_syspath udev_device_new_from_syspath_dylibloader_wrapper_libudev
+#define udev_device_new_from_devnum udev_device_new_from_devnum_dylibloader_wrapper_libudev
+#define udev_device_new_from_subsystem_sysname udev_device_new_from_subsystem_sysname_dylibloader_wrapper_libudev
+#define udev_device_new_from_device_id udev_device_new_from_device_id_dylibloader_wrapper_libudev
+#define udev_device_new_from_environment udev_device_new_from_environment_dylibloader_wrapper_libudev
+#define udev_device_get_parent udev_device_get_parent_dylibloader_wrapper_libudev
+#define udev_device_get_parent_with_subsystem_devtype udev_device_get_parent_with_subsystem_devtype_dylibloader_wrapper_libudev
+#define udev_device_get_devpath udev_device_get_devpath_dylibloader_wrapper_libudev
+#define udev_device_get_subsystem udev_device_get_subsystem_dylibloader_wrapper_libudev
+#define udev_device_get_devtype udev_device_get_devtype_dylibloader_wrapper_libudev
+#define udev_device_get_syspath udev_device_get_syspath_dylibloader_wrapper_libudev
+#define udev_device_get_sysname udev_device_get_sysname_dylibloader_wrapper_libudev
+#define udev_device_get_sysnum udev_device_get_sysnum_dylibloader_wrapper_libudev
+#define udev_device_get_devnode udev_device_get_devnode_dylibloader_wrapper_libudev
+#define udev_device_get_is_initialized udev_device_get_is_initialized_dylibloader_wrapper_libudev
+#define udev_device_get_devlinks_list_entry udev_device_get_devlinks_list_entry_dylibloader_wrapper_libudev
+#define udev_device_get_properties_list_entry udev_device_get_properties_list_entry_dylibloader_wrapper_libudev
+#define udev_device_get_tags_list_entry udev_device_get_tags_list_entry_dylibloader_wrapper_libudev
+#define udev_device_get_sysattr_list_entry udev_device_get_sysattr_list_entry_dylibloader_wrapper_libudev
+#define udev_device_get_property_value udev_device_get_property_value_dylibloader_wrapper_libudev
+#define udev_device_get_driver udev_device_get_driver_dylibloader_wrapper_libudev
+#define udev_device_get_devnum udev_device_get_devnum_dylibloader_wrapper_libudev
+#define udev_device_get_action udev_device_get_action_dylibloader_wrapper_libudev
+#define udev_device_get_seqnum udev_device_get_seqnum_dylibloader_wrapper_libudev
+#define udev_device_get_usec_since_initialized udev_device_get_usec_since_initialized_dylibloader_wrapper_libudev
+#define udev_device_get_sysattr_value udev_device_get_sysattr_value_dylibloader_wrapper_libudev
+#define udev_device_set_sysattr_value udev_device_set_sysattr_value_dylibloader_wrapper_libudev
+#define udev_device_has_tag udev_device_has_tag_dylibloader_wrapper_libudev
+#define udev_monitor_ref udev_monitor_ref_dylibloader_wrapper_libudev
+#define udev_monitor_unref udev_monitor_unref_dylibloader_wrapper_libudev
+#define udev_monitor_get_udev udev_monitor_get_udev_dylibloader_wrapper_libudev
+#define udev_monitor_new_from_netlink udev_monitor_new_from_netlink_dylibloader_wrapper_libudev
+#define udev_monitor_enable_receiving udev_monitor_enable_receiving_dylibloader_wrapper_libudev
+#define udev_monitor_set_receive_buffer_size udev_monitor_set_receive_buffer_size_dylibloader_wrapper_libudev
+#define udev_monitor_get_fd udev_monitor_get_fd_dylibloader_wrapper_libudev
+#define udev_monitor_receive_device udev_monitor_receive_device_dylibloader_wrapper_libudev
+#define udev_monitor_filter_add_match_subsystem_devtype udev_monitor_filter_add_match_subsystem_devtype_dylibloader_wrapper_libudev
+#define udev_monitor_filter_add_match_tag udev_monitor_filter_add_match_tag_dylibloader_wrapper_libudev
+#define udev_monitor_filter_update udev_monitor_filter_update_dylibloader_wrapper_libudev
+#define udev_monitor_filter_remove udev_monitor_filter_remove_dylibloader_wrapper_libudev
+#define udev_enumerate_ref udev_enumerate_ref_dylibloader_wrapper_libudev
+#define udev_enumerate_unref udev_enumerate_unref_dylibloader_wrapper_libudev
+#define udev_enumerate_get_udev udev_enumerate_get_udev_dylibloader_wrapper_libudev
+#define udev_enumerate_new udev_enumerate_new_dylibloader_wrapper_libudev
+#define udev_enumerate_add_match_subsystem udev_enumerate_add_match_subsystem_dylibloader_wrapper_libudev
+#define udev_enumerate_add_nomatch_subsystem udev_enumerate_add_nomatch_subsystem_dylibloader_wrapper_libudev
+#define udev_enumerate_add_match_sysattr udev_enumerate_add_match_sysattr_dylibloader_wrapper_libudev
+#define udev_enumerate_add_nomatch_sysattr udev_enumerate_add_nomatch_sysattr_dylibloader_wrapper_libudev
+#define udev_enumerate_add_match_property udev_enumerate_add_match_property_dylibloader_wrapper_libudev
+#define udev_enumerate_add_match_sysname udev_enumerate_add_match_sysname_dylibloader_wrapper_libudev
+#define udev_enumerate_add_match_tag udev_enumerate_add_match_tag_dylibloader_wrapper_libudev
+#define udev_enumerate_add_match_parent udev_enumerate_add_match_parent_dylibloader_wrapper_libudev
+#define udev_enumerate_add_match_is_initialized udev_enumerate_add_match_is_initialized_dylibloader_wrapper_libudev
+#define udev_enumerate_add_syspath udev_enumerate_add_syspath_dylibloader_wrapper_libudev
+#define udev_enumerate_scan_devices udev_enumerate_scan_devices_dylibloader_wrapper_libudev
+#define udev_enumerate_scan_subsystems udev_enumerate_scan_subsystems_dylibloader_wrapper_libudev
+#define udev_enumerate_get_list_entry udev_enumerate_get_list_entry_dylibloader_wrapper_libudev
+#define udev_queue_ref udev_queue_ref_dylibloader_wrapper_libudev
+#define udev_queue_unref udev_queue_unref_dylibloader_wrapper_libudev
+#define udev_queue_get_udev udev_queue_get_udev_dylibloader_wrapper_libudev
+#define udev_queue_new udev_queue_new_dylibloader_wrapper_libudev
+#define udev_queue_get_kernel_seqnum udev_queue_get_kernel_seqnum_dylibloader_wrapper_libudev
+#define udev_queue_get_udev_seqnum udev_queue_get_udev_seqnum_dylibloader_wrapper_libudev
+#define udev_queue_get_udev_is_active udev_queue_get_udev_is_active_dylibloader_wrapper_libudev
+#define udev_queue_get_queue_is_empty udev_queue_get_queue_is_empty_dylibloader_wrapper_libudev
+#define udev_queue_get_seqnum_is_finished udev_queue_get_seqnum_is_finished_dylibloader_wrapper_libudev
+#define udev_queue_get_seqnum_sequence_is_finished udev_queue_get_seqnum_sequence_is_finished_dylibloader_wrapper_libudev
+#define udev_queue_get_fd udev_queue_get_fd_dylibloader_wrapper_libudev
+#define udev_queue_flush udev_queue_flush_dylibloader_wrapper_libudev
+#define udev_queue_get_queued_list_entry udev_queue_get_queued_list_entry_dylibloader_wrapper_libudev
+#define udev_hwdb_new udev_hwdb_new_dylibloader_wrapper_libudev
+#define udev_hwdb_ref udev_hwdb_ref_dylibloader_wrapper_libudev
+#define udev_hwdb_unref udev_hwdb_unref_dylibloader_wrapper_libudev
+#define udev_hwdb_get_properties_list_entry udev_hwdb_get_properties_list_entry_dylibloader_wrapper_libudev
+#define udev_util_encode_string udev_util_encode_string_dylibloader_wrapper_libudev
+extern struct udev* (*udev_ref_dylibloader_wrapper_libudev)(struct udev*);
+extern struct udev* (*udev_unref_dylibloader_wrapper_libudev)(struct udev*);
+extern struct udev* (*udev_new_dylibloader_wrapper_libudev)( void);
+extern void (*udev_set_log_fn_dylibloader_wrapper_libudev)(struct udev*, void*);
+extern int (*udev_get_log_priority_dylibloader_wrapper_libudev)(struct udev*);
+extern void (*udev_set_log_priority_dylibloader_wrapper_libudev)(struct udev*, int);
+extern void* (*udev_get_userdata_dylibloader_wrapper_libudev)(struct udev*);
+extern void (*udev_set_userdata_dylibloader_wrapper_libudev)(struct udev*, void*);
+extern struct udev_list_entry* (*udev_list_entry_get_next_dylibloader_wrapper_libudev)(struct udev_list_entry*);
+extern struct udev_list_entry* (*udev_list_entry_get_by_name_dylibloader_wrapper_libudev)(struct udev_list_entry*,const char*);
+extern const char* (*udev_list_entry_get_name_dylibloader_wrapper_libudev)(struct udev_list_entry*);
+extern const char* (*udev_list_entry_get_value_dylibloader_wrapper_libudev)(struct udev_list_entry*);
+extern struct udev_device* (*udev_device_ref_dylibloader_wrapper_libudev)(struct udev_device*);
+extern struct udev_device* (*udev_device_unref_dylibloader_wrapper_libudev)(struct udev_device*);
+extern struct udev* (*udev_device_get_udev_dylibloader_wrapper_libudev)(struct udev_device*);
+extern struct udev_device* (*udev_device_new_from_syspath_dylibloader_wrapper_libudev)(struct udev*,const char*);
+extern struct udev_device* (*udev_device_new_from_devnum_dylibloader_wrapper_libudev)(struct udev*, char, dev_t);
+extern struct udev_device* (*udev_device_new_from_subsystem_sysname_dylibloader_wrapper_libudev)(struct udev*,const char*,const char*);
+extern struct udev_device* (*udev_device_new_from_device_id_dylibloader_wrapper_libudev)(struct udev*,const char*);
+extern struct udev_device* (*udev_device_new_from_environment_dylibloader_wrapper_libudev)(struct udev*);
+extern struct udev_device* (*udev_device_get_parent_dylibloader_wrapper_libudev)(struct udev_device*);
+extern struct udev_device* (*udev_device_get_parent_with_subsystem_devtype_dylibloader_wrapper_libudev)(struct udev_device*,const char*,const char*);
+extern const char* (*udev_device_get_devpath_dylibloader_wrapper_libudev)(struct udev_device*);
+extern const char* (*udev_device_get_subsystem_dylibloader_wrapper_libudev)(struct udev_device*);
+extern const char* (*udev_device_get_devtype_dylibloader_wrapper_libudev)(struct udev_device*);
+extern const char* (*udev_device_get_syspath_dylibloader_wrapper_libudev)(struct udev_device*);
+extern const char* (*udev_device_get_sysname_dylibloader_wrapper_libudev)(struct udev_device*);
+extern const char* (*udev_device_get_sysnum_dylibloader_wrapper_libudev)(struct udev_device*);
+extern const char* (*udev_device_get_devnode_dylibloader_wrapper_libudev)(struct udev_device*);
+extern int (*udev_device_get_is_initialized_dylibloader_wrapper_libudev)(struct udev_device*);
+extern struct udev_list_entry* (*udev_device_get_devlinks_list_entry_dylibloader_wrapper_libudev)(struct udev_device*);
+extern struct udev_list_entry* (*udev_device_get_properties_list_entry_dylibloader_wrapper_libudev)(struct udev_device*);
+extern struct udev_list_entry* (*udev_device_get_tags_list_entry_dylibloader_wrapper_libudev)(struct udev_device*);
+extern struct udev_list_entry* (*udev_device_get_sysattr_list_entry_dylibloader_wrapper_libudev)(struct udev_device*);
+extern const char* (*udev_device_get_property_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*);
+extern const char* (*udev_device_get_driver_dylibloader_wrapper_libudev)(struct udev_device*);
+extern dev_t (*udev_device_get_devnum_dylibloader_wrapper_libudev)(struct udev_device*);
+extern const char* (*udev_device_get_action_dylibloader_wrapper_libudev)(struct udev_device*);
+extern unsigned long long int (*udev_device_get_seqnum_dylibloader_wrapper_libudev)(struct udev_device*);
+extern unsigned long long int (*udev_device_get_usec_since_initialized_dylibloader_wrapper_libudev)(struct udev_device*);
+extern const char* (*udev_device_get_sysattr_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*);
+extern int (*udev_device_set_sysattr_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*,const char*);
+extern int (*udev_device_has_tag_dylibloader_wrapper_libudev)(struct udev_device*,const char*);
+extern struct udev_monitor* (*udev_monitor_ref_dylibloader_wrapper_libudev)(struct udev_monitor*);
+extern struct udev_monitor* (*udev_monitor_unref_dylibloader_wrapper_libudev)(struct udev_monitor*);
+extern struct udev* (*udev_monitor_get_udev_dylibloader_wrapper_libudev)(struct udev_monitor*);
+extern struct udev_monitor* (*udev_monitor_new_from_netlink_dylibloader_wrapper_libudev)(struct udev*,const char*);
+extern int (*udev_monitor_enable_receiving_dylibloader_wrapper_libudev)(struct udev_monitor*);
+extern int (*udev_monitor_set_receive_buffer_size_dylibloader_wrapper_libudev)(struct udev_monitor*, int);
+extern int (*udev_monitor_get_fd_dylibloader_wrapper_libudev)(struct udev_monitor*);
+extern struct udev_device* (*udev_monitor_receive_device_dylibloader_wrapper_libudev)(struct udev_monitor*);
+extern int (*udev_monitor_filter_add_match_subsystem_devtype_dylibloader_wrapper_libudev)(struct udev_monitor*,const char*,const char*);
+extern int (*udev_monitor_filter_add_match_tag_dylibloader_wrapper_libudev)(struct udev_monitor*,const char*);
+extern int (*udev_monitor_filter_update_dylibloader_wrapper_libudev)(struct udev_monitor*);
+extern int (*udev_monitor_filter_remove_dylibloader_wrapper_libudev)(struct udev_monitor*);
+extern struct udev_enumerate* (*udev_enumerate_ref_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+extern struct udev_enumerate* (*udev_enumerate_unref_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+extern struct udev* (*udev_enumerate_get_udev_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+extern struct udev_enumerate* (*udev_enumerate_new_dylibloader_wrapper_libudev)(struct udev*);
+extern int (*udev_enumerate_add_match_subsystem_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+extern int (*udev_enumerate_add_nomatch_subsystem_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+extern int (*udev_enumerate_add_match_sysattr_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*);
+extern int (*udev_enumerate_add_nomatch_sysattr_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*);
+extern int (*udev_enumerate_add_match_property_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*);
+extern int (*udev_enumerate_add_match_sysname_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+extern int (*udev_enumerate_add_match_tag_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+extern int (*udev_enumerate_add_match_parent_dylibloader_wrapper_libudev)(struct udev_enumerate*,struct udev_device*);
+extern int (*udev_enumerate_add_match_is_initialized_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+extern int (*udev_enumerate_add_syspath_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*);
+extern int (*udev_enumerate_scan_devices_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+extern int (*udev_enumerate_scan_subsystems_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+extern struct udev_list_entry* (*udev_enumerate_get_list_entry_dylibloader_wrapper_libudev)(struct udev_enumerate*);
+extern struct udev_queue* (*udev_queue_ref_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern struct udev_queue* (*udev_queue_unref_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern struct udev* (*udev_queue_get_udev_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern struct udev_queue* (*udev_queue_new_dylibloader_wrapper_libudev)(struct udev*);
+extern unsigned long long int (*udev_queue_get_kernel_seqnum_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern unsigned long long int (*udev_queue_get_udev_seqnum_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern int (*udev_queue_get_udev_is_active_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern int (*udev_queue_get_queue_is_empty_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern int (*udev_queue_get_seqnum_is_finished_dylibloader_wrapper_libudev)(struct udev_queue*, unsigned long long int);
+extern int (*udev_queue_get_seqnum_sequence_is_finished_dylibloader_wrapper_libudev)(struct udev_queue*, unsigned long long int, unsigned long long int);
+extern int (*udev_queue_get_fd_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern int (*udev_queue_flush_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern struct udev_list_entry* (*udev_queue_get_queued_list_entry_dylibloader_wrapper_libudev)(struct udev_queue*);
+extern struct udev_hwdb* (*udev_hwdb_new_dylibloader_wrapper_libudev)(struct udev*);
+extern struct udev_hwdb* (*udev_hwdb_ref_dylibloader_wrapper_libudev)(struct udev_hwdb*);
+extern struct udev_hwdb* (*udev_hwdb_unref_dylibloader_wrapper_libudev)(struct udev_hwdb*);
+extern struct udev_list_entry* (*udev_hwdb_get_properties_list_entry_dylibloader_wrapper_libudev)(struct udev_hwdb*,const char*, unsigned);
+extern int (*udev_util_encode_string_dylibloader_wrapper_libudev)(const char*, char*, size_t);
+int initialize_libudev(int verbose);
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 09a5eca914..09e1f9461c 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,8 +31,11 @@
#include "os_linuxbsd.h"
#include "core/os/dir_access.h"
-#include "core/print_string.h"
-#include "errno.h"
+#include "main/main.h"
+
+#ifdef X11_ENABLED
+#include "display_server_x11.h"
+#endif
#ifdef HAVE_MNTENT
#include <mntent.h>
@@ -48,12 +51,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include "main/main.h"
-
-#ifdef X11_ENABLED
-#include "display_server_x11.h"
-#endif
-
void OS_LinuxBSD::initialize() {
crash_handler.initialize();
@@ -68,9 +65,9 @@ void OS_LinuxBSD::initialize_joypads() {
String OS_LinuxBSD::get_unique_id() const {
static String machine_id;
- if (machine_id.empty()) {
+ if (machine_id.is_empty()) {
if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) {
- while (machine_id.empty() && !f->eof_reached()) {
+ while (machine_id.is_empty() && !f->eof_reached()) {
machine_id = f->get_line().strip_edges();
}
f->close();
@@ -91,7 +88,9 @@ void OS_LinuxBSD::finalize() {
#endif
#ifdef JOYDEV_ENABLED
- memdelete(joypad);
+ if (joypad) {
+ memdelete(joypad);
+ }
#endif
}
@@ -124,18 +123,39 @@ String OS_LinuxBSD::get_name() const {
Error OS_LinuxBSD::shell_open(String p_uri) {
Error ok;
+ int err_code;
List<String> args;
args.push_back(p_uri);
- ok = execute("xdg-open", args, false);
- if (ok == OK) {
+
+ // Agnostic
+ ok = execute("xdg-open", args, nullptr, &err_code);
+ if (ok == OK && !err_code) {
return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
}
- ok = execute("gnome-open", args, false);
- if (ok == OK) {
+ // GNOME
+ args.push_front("open"); // The command is `gio open`, so we need to add it to args
+ ok = execute("gio", args, nullptr, &err_code);
+ if (ok == OK && !err_code) {
return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
}
- ok = execute("kde-open", args, false);
- return ok;
+ args.pop_front();
+ ok = execute("gvfs-open", args, nullptr, &err_code);
+ if (ok == OK && !err_code) {
+ return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+ // KDE
+ ok = execute("kde-open5", args, nullptr, &err_code);
+ if (ok == OK && !err_code) {
+ return OK;
+ }
+ ok = execute("kde-open", args, nullptr, &err_code);
+ return !err_code ? ok : FAILED;
}
bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) {
@@ -212,7 +232,7 @@ String OS_LinuxBSD::get_system_dir(SystemDir p_dir) const {
String pipe;
List<String> arg;
arg.push_back(xdgparam);
- Error err = const_cast<OS_LinuxBSD *>(this)->execute("xdg-user-dir", arg, true, nullptr, &pipe);
+ Error err = const_cast<OS_LinuxBSD *>(this)->execute("xdg-user-dir", arg, &pipe);
if (err != OK) {
return ".";
}
@@ -226,7 +246,7 @@ void OS_LinuxBSD::run() {
return;
}
- main_loop->init();
+ main_loop->initialize();
//uint64_t last_ticks=get_ticks_usec();
@@ -243,7 +263,7 @@ void OS_LinuxBSD::run() {
}
};
- main_loop->finish();
+ main_loop->finalize();
}
void OS_LinuxBSD::disable_crash_handler() {
@@ -283,65 +303,147 @@ static String get_mountpoint(const String &p_path) {
}
Error OS_LinuxBSD::move_to_trash(const String &p_path) {
- String trash_can = "";
+ int err_code;
+ List<String> args;
+ args.push_back(p_path);
+ args.push_front("trash"); // The command is `gio trash <file_name>` so we need to add it to args.
+ Error result = execute("gio", args, nullptr, &err_code); // For GNOME based machines.
+ if (result == OK && !err_code) {
+ return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ args.pop_front();
+ args.push_front("move");
+ args.push_back("trash:/"); // The command is `kioclient5 move <file_name> trash:/`.
+ result = execute("kioclient5", args, nullptr, &err_code); // For KDE based machines.
+ if (result == OK && !err_code) {
+ return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ args.pop_front();
+ args.pop_back();
+ result = execute("gvfs-trash", args, nullptr, &err_code); // For older Linux machines.
+ if (result == OK && !err_code) {
+ return OK;
+ } else if (err_code == 2) {
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ // If the commands `kioclient5`, `gio` or `gvfs-trash` don't exist on the system we do it manually.
+ String trash_path = "";
String mnt = get_mountpoint(p_path);
- // If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can.
+ // If there is a directory "[Mountpoint]/.Trash-[UID], use it as the trash can.
if (mnt != "") {
- String path(mnt + "/.Trash-" + itos(getuid()) + "/files");
+ String path(mnt + "/.Trash-" + itos(getuid()));
struct stat s;
if (!stat(path.utf8().get_data(), &s)) {
- trash_can = path;
+ trash_path = path;
}
}
- // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can.
- if (trash_can == "") {
+ // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash" as the trash can.
+ if (trash_path == "") {
char *dhome = getenv("XDG_DATA_HOME");
if (dhome) {
- trash_can = String(dhome) + "/Trash/files";
+ trash_path = String(dhome) + "/Trash";
}
}
- // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can.
- if (trash_can == "") {
+ // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash" as the trash can.
+ if (trash_path == "") {
char *home = getenv("HOME");
if (home) {
- trash_can = String(home) + "/.local/share/Trash/files";
+ trash_path = String(home) + "/.local/share/Trash";
}
}
// Issue an error if none of the previous locations is appropriate for the trash can.
- if (trash_can == "") {
- ERR_PRINT("move_to_trash: Could not determine the trash can location");
- return FAILED;
- }
+ ERR_FAIL_COND_V_MSG(trash_path == "", FAILED, "Could not determine the trash can location");
// Create needed directories for decided trash can location.
- DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- Error err = dir_access->make_dir_recursive(trash_can);
- memdelete(dir_access);
+ {
+ DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Error err = dir_access->make_dir_recursive(trash_path);
+
+ // Issue an error if trash can is not created proprely.
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"");
+ err = dir_access->make_dir_recursive(trash_path + "/files");
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/files");
+ err = dir_access->make_dir_recursive(trash_path + "/info");
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/info");
+ memdelete(dir_access);
+ }
- // Issue an error if trash can is not created proprely.
- if (err != OK) {
- ERR_PRINT("move_to_trash: Could not create the trash can \"" + trash_can + "\"");
- return err;
+ // The trash can is successfully created, now we check that we don't exceed our file name length limit.
+ // If the file name is too long trim it so we can add the identifying number and ".trashinfo".
+ // Assumes that the file name length limit is 255 characters.
+ String file_name = basename(p_path.utf8().get_data());
+ if (file_name.length() > 240) {
+ file_name = file_name.substr(0, file_name.length() - 15);
+ }
+
+ String dest_path = trash_path + "/files/" + file_name;
+ struct stat buff;
+ int id_number = 0;
+ String fn = file_name;
+
+ // Checks if a resource with the same name already exist in the trash can,
+ // if there is, add an identifying number to our resource's name.
+ while (stat(dest_path.utf8().get_data(), &buff) == 0) {
+ id_number++;
+
+ // Added a limit to check for identically named files already on the trash can
+ // if there are too many it could make the editor unresponsive.
+ ERR_FAIL_COND_V_MSG(id_number > 99, FAILED, "Too many identically named resources already in the trash can.");
+ fn = file_name + "." + itos(id_number);
+ dest_path = trash_path + "/files/" + fn;
+ }
+ file_name = fn;
+
+ // Generates the .trashinfo file
+ OS::Date date = OS::get_singleton()->get_date(false);
+ OS::Time time = OS::get_singleton()->get_time(false);
+ String timestamp = vformat("%04d-%02d-%02dT%02d:%02d:", date.year, date.month, date.day, time.hour, time.min);
+ timestamp = vformat("%s%02d", timestamp, time.sec); // vformat only supports up to 6 arguments.
+ String trash_info = "[Trash Info]\nPath=" + p_path.uri_encode() + "\nDeletionDate=" + timestamp + "\n";
+ {
+ Error err;
+ FileAccess *file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't create trashinfo file:" + trash_path + "/info/" + file_name + ".trashinfo");
+ file->store_string(trash_info);
+ file->close();
+
+ // Rename our resource before moving it to the trash can.
+ DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ err = dir_access->rename(p_path, p_path.get_base_dir() + "/" + file_name);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't rename file \"" + p_path + "\"");
+ memdelete(dir_access);
}
- // The trash can is successfully created, now move the given resource to it.
+ // Move the given resource to the trash can.
// Do not use DirAccess:rename() because it can't move files across multiple mountpoints.
List<String> mv_args;
- mv_args.push_back(p_path);
- mv_args.push_back(trash_can);
- int retval;
- err = execute("mv", mv_args, true, nullptr, nullptr, &retval);
-
- // Issue an error if "mv" failed to move the given resource to the trash can.
- if (err != OK || retval != 0) {
- ERR_PRINT("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_can + "\"");
- return FAILED;
+ mv_args.push_back(p_path.get_base_dir() + "/" + file_name);
+ mv_args.push_back(trash_path + "/files");
+ {
+ int retval;
+ Error err = execute("mv", mv_args, nullptr, &retval);
+
+ // Issue an error if "mv" failed to move the given resource to the trash can.
+ if (err != OK || retval != 0) {
+ ERR_PRINT("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_path + "/files\"");
+ DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ err = dir_access->rename(p_path.get_base_dir() + "/" + file_name, p_path);
+ memdelete(dir_access);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not rename " + p_path.get_base_dir() + "/" + file_name + " back to its original name:" + p_path);
+ return FAILED;
+ }
}
-
return OK;
}
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 4295721c68..b6cf93c551 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,16 +39,16 @@
#include "drivers/unix/os_unix.h"
#include "joypad_linux.h"
#include "servers/audio_server.h"
-#include "servers/rendering/rasterizer.h"
+#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
class OS_LinuxBSD : public OS_Unix {
- virtual void delete_main_loop();
+ virtual void delete_main_loop() override;
bool force_quit;
#ifdef JOYDEV_ENABLED
- JoypadLinux *joypad;
+ JoypadLinux *joypad = nullptr;
#endif
#ifdef ALSA_ENABLED
@@ -68,36 +68,36 @@ class OS_LinuxBSD : public OS_Unix {
MainLoop *main_loop;
protected:
- virtual void initialize();
- virtual void finalize();
+ virtual void initialize() override;
+ virtual void finalize() override;
- virtual void initialize_joypads();
+ virtual void initialize_joypads() override;
- virtual void set_main_loop(MainLoop *p_main_loop);
+ virtual void set_main_loop(MainLoop *p_main_loop) override;
public:
- virtual String get_name() const;
+ virtual String get_name() const override;
- virtual MainLoop *get_main_loop() const;
+ virtual MainLoop *get_main_loop() const override;
- virtual String get_config_path() const;
- virtual String get_data_path() const;
- virtual String get_cache_path() const;
+ virtual String get_config_path() const override;
+ virtual String get_data_path() const override;
+ virtual String get_cache_path() const override;
- virtual String get_system_dir(SystemDir p_dir) const;
+ virtual String get_system_dir(SystemDir p_dir) const override;
- virtual Error shell_open(String p_uri);
+ virtual Error shell_open(String p_uri) override;
- virtual String get_unique_id() const;
+ virtual String get_unique_id() const override;
- virtual bool _check_internal_feature_support(const String &p_feature);
+ virtual bool _check_internal_feature_support(const String &p_feature) override;
void run();
- void disable_crash_handler();
- bool is_disable_crash_handler() const;
+ virtual void disable_crash_handler() override;
+ virtual bool is_disable_crash_handler() const override;
- virtual Error move_to_trash(const String &p_path);
+ virtual Error move_to_trash(const String &p_path) override;
OS_LinuxBSD();
};
diff --git a/platform/linuxbsd/platform_config.h b/platform/linuxbsd/platform_config.h
index ac30519132..3195d08935 100644
--- a/platform/linuxbsd/platform_config.h
+++ b/platform/linuxbsd/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,9 +31,15 @@
#ifdef __linux__
#include <alloca.h>
#endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__)
-#include <stdlib.h>
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#include <stdlib.h> // alloca
+// FreeBSD and OpenBSD use pthread_set_name_np, while other platforms,
+// include NetBSD, use pthread_setname_np. NetBSD's version however requires
+// a different format, we handle this directly in thread_posix.
+#ifdef __NetBSD__
+#define PTHREAD_NETBSD_SET_NAME
+#else
#define PTHREAD_BSD_SET_NAME
#endif
-
-#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h"
+#endif
diff --git a/platform/linuxbsd/vulkan_context_x11.cpp b/platform/linuxbsd/vulkan_context_x11.cpp
index 2eaa9f9446..021db630e0 100644
--- a/platform/linuxbsd/vulkan_context_x11.cpp
+++ b/platform/linuxbsd/vulkan_context_x11.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/linuxbsd/vulkan_context_x11.h b/platform/linuxbsd/vulkan_context_x11.h
index af3d923cfe..26472444ad 100644
--- a/platform/linuxbsd/vulkan_context_x11.h
+++ b/platform/linuxbsd/vulkan_context_x11.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index ad62db358b..46c13d8550 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -18,5 +18,5 @@ files = [
prog = env.add_program("#bin/godot", files)
-if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]:
+if env["debug_symbols"] and env["separate_debug_symbols"]:
env.AddPostAction(prog, run_in_subprocess(platform_osx_builders.make_debug_osx))
diff --git a/platform/osx/context_gl_osx.h b/platform/osx/context_gl_osx.h
index cce00fb35f..ac45559217 100644
--- a/platform/osx/context_gl_osx.h
+++ b/platform/osx/context_gl_osx.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,7 +33,7 @@
#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED)
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/os/os.h"
#include <AppKit/AppKit.h>
diff --git a/platform/osx/context_gl_osx.mm b/platform/osx/context_gl_osx.mm
index 2319e9eb1f..88db1a296e 100644
--- a/platform/osx/context_gl_osx.mm
+++ b/platform/osx/context_gl_osx.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/osx/crash_handler_osx.h b/platform/osx/crash_handler_osx.h
index 9970f6045a..1601bbaab6 100644
--- a/platform/osx/crash_handler_osx.h
+++ b/platform/osx/crash_handler_osx.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm
index 5da0118686..147ce26779 100644
--- a/platform/osx/crash_handler_osx.mm
+++ b/platform/osx/crash_handler_osx.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,8 +30,8 @@
#include "crash_handler_osx.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "main/main.h"
#include <string.h>
@@ -90,7 +90,7 @@ static void handle_crash(int sig) {
if (OS::get_singleton()->get_main_loop())
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
- fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
+ fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data());
char **strings = backtrace_symbols(bt_buffer, size);
if (strings) {
void *load_addr = (void *)load_address();
@@ -135,14 +135,14 @@ static void handle_crash(int sig) {
int ret;
String out = "";
- Error err = OS::get_singleton()->execute(String("atos"), args, true, NULL, &out, &ret);
+ Error err = OS::get_singleton()->execute(String("atos"), args, &out, &ret);
if (err == OK && out.substr(0, 2) != "0x") {
out.erase(out.length() - 1, 1);
output = out;
}
}
- fprintf(stderr, "[%zu] %ls\n", i, output.c_str());
+ fprintf(stderr, "[%zu] %s\n", i, output.utf8().get_data());
}
free(strings);
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 29aa8ece19..acea00c5ac 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -1,6 +1,5 @@
import os
import sys
-import subprocess
from methods import detect_darwin_sdk_path
@@ -13,7 +12,6 @@ def get_name():
def can_build():
-
if sys.platform == "darwin" or ("OSXCROSS_ROOT" in os.environ):
return True
@@ -24,14 +22,16 @@ def get_opts():
from SCons.Variables import BoolVariable, EnumVariable
return [
- ("osxcross_sdk", "OSXCross SDK version", "darwin14"),
+ ("osxcross_sdk", "OSXCross SDK version", "darwin16"),
("MACOS_SDK_PATH", "Path to the macOS SDK", ""),
BoolVariable(
"use_static_mvk",
- "Link MoltenVK statically as Level-0 driver (better portability) or use Vulkan ICD loader (enables validation layers)",
+ "Link MoltenVK statically as Level-0 driver (better portability) or use Vulkan ICD loader (enables"
+ " validation layers)",
False,
),
- EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
+ EnumVariable("macports_clang", "Build using Clang from MacPorts", "no", ("no", "5.0", "devel")),
+ BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
@@ -40,23 +40,21 @@ def get_opts():
def get_flags():
-
return []
def configure(env):
-
## Build type
if env["target"] == "release":
if env["optimize"] == "speed": # optimize for speed (default)
- env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize", "-msse2"])
+ env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize"])
else: # optimize for size
- env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize", "-msse2"])
+ env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize"])
+ if env["arch"] != "arm64":
+ env.Prepend(CCFLAGS=["-msse2"])
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
+ if env["debug_symbols"]:
env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "release_debug":
@@ -65,14 +63,13 @@ def configure(env):
else: # optimize for size
env.Prepend(CCFLAGS=["-Os"])
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
+ if env["debug_symbols"]:
env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "debug":
env.Prepend(CCFLAGS=["-g3"])
- env.Prepend(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Prepend(LINKFLAGS=["-Xlinker", "-no_deduplicate"])
## Architecture
@@ -86,14 +83,20 @@ def configure(env):
if "OSXCROSS_ROOT" in os.environ:
env["osxcross"] = True
+ if env["arch"] == "arm64":
+ print("Building for macOS 10.15+, platform arm64.")
+ env.Append(CCFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15"])
+ env.Append(LINKFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15"])
+ else:
+ print("Building for macOS 10.12+, platform x86-64.")
+ env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"])
+ env.Append(LINKFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"])
+
if not "osxcross" in env: # regular native build
- env.Append(CCFLAGS=["-arch", "x86_64"])
- env.Append(LINKFLAGS=["-arch", "x86_64"])
if env["macports_clang"] != "no":
mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local")
mpclangver = env["macports_clang"]
env["CC"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang"
- env["LINK"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang++"
env["CXX"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang++"
env["AR"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ar"
env["RANLIB"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ranlib"
@@ -109,7 +112,10 @@ def configure(env):
else: # osxcross build
root = os.environ.get("OSXCROSS_ROOT", 0)
- basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
+ if env["arch"] == "arm64":
+ basecmd = root + "/target/bin/arm64-apple-" + env["osxcross_sdk"] + "-"
+ else:
+ basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
ccache_path = os.environ.get("CCACHE")
if ccache_path is None:
@@ -125,11 +131,6 @@ def configure(env):
env["AS"] = basecmd + "as"
env.Append(CPPDEFINES=["__MACPORTS__"]) # hack to fix libvpx MM256_BROADCASTSI128_SI256 define
- if env["CXX"] == "clang++":
- env.Append(CPPDEFINES=["TYPED_METHOD_BIND"])
- env["CC"] = "clang"
- env["LINK"] = "clang++"
-
if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
env.extra_suffix += "s"
@@ -148,7 +149,8 @@ def configure(env):
## Dependencies
if env["builtin_libtheora"]:
- env["x86_libtheora_opt_gcc"] = True
+ if env["arch"] != "arm64":
+ env["x86_libtheora_opt_gcc"] = True
## Flags
@@ -189,6 +191,3 @@ def configure(env):
env.Append(LIBS=["vulkan"])
# env.Append(CPPDEFINES=['GLES_ENABLED', 'OPENGL_ENABLED'])
-
- env.Append(CCFLAGS=["-mmacosx-version-min=10.12"])
- env.Append(LINKFLAGS=["-mmacosx-version-min=10.12"])
diff --git a/platform/osx/dir_access_osx.h b/platform/osx/dir_access_osx.h
index d61ee181f0..f61581979f 100644
--- a/platform/osx/dir_access_osx.h
+++ b/platform/osx/dir_access_osx.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -47,6 +47,8 @@ protected:
virtual int get_drive_count();
virtual String get_drive(int p_drive);
+
+ virtual bool is_hidden(const String &p_name);
};
#endif //UNIX ENABLED
diff --git a/platform/osx/dir_access_osx.mm b/platform/osx/dir_access_osx.mm
index 7791ba5407..552c33d018 100644
--- a/platform/osx/dir_access_osx.mm
+++ b/platform/osx/dir_access_osx.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -68,4 +68,14 @@ String DirAccessOSX::get_drive(int p_drive) {
return volname;
}
+bool DirAccessOSX::is_hidden(const String &p_name) {
+ String f = get_current_dir().plus_file(p_name);
+ NSURL *url = [NSURL fileURLWithPath:@(f.utf8().get_data())];
+ NSNumber *hidden = nil;
+ if (![url getResourceValue:&hidden forKey:NSURLIsHiddenKey error:nil]) {
+ return DirAccessUnix::is_hidden(p_name);
+ }
+ return [hidden boolValue];
+}
+
#endif //posix_enabled
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index 8133dfe2c4..9fac99810b 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -77,15 +77,24 @@ public:
struct KeyEvent {
WindowID window_id;
- unsigned int osx_state;
- bool pressed;
- bool echo;
- bool raw;
- uint32_t keycode;
- uint32_t physical_keycode;
- uint32_t unicode;
+ unsigned int osx_state = false;
+ bool pressed = false;
+ bool echo = false;
+ bool raw = false;
+ uint32_t keycode = 0;
+ uint32_t physical_keycode = 0;
+ uint32_t unicode = 0;
};
+ struct WarpEvent {
+ NSTimeInterval timestamp;
+ NSPoint delta;
+ };
+
+ List<WarpEvent> warp_events;
+ NSTimeInterval last_warp = 0;
+ bool ignore_warp = false;
+
Vector<KeyEvent> key_event_buffer;
int key_event_pos;
@@ -94,6 +103,8 @@ public:
id window_object;
id window_view;
+ Vector<Vector2> mpath;
+
#if defined(OPENGL_ENABLED)
ContextGL_OSX *context_gles2 = nullptr;
#endif
@@ -124,6 +135,7 @@ public:
bool on_top = false;
bool borderless = false;
bool resize_disabled = false;
+ bool no_focus = false;
};
Point2i im_selection;
@@ -142,7 +154,6 @@ public:
void _set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window);
- float _display_scale(id screen) const;
Point2i _get_screens_origin() const;
Point2i _get_native_screen_position(int p_screen) const;
@@ -169,132 +180,139 @@ public:
bool in_dispatch_input_event = false;
public:
- virtual bool has_feature(Feature p_feature) const;
- virtual String get_name() const;
+ virtual bool has_feature(Feature p_feature) const override;
+ virtual String get_name() const override;
- virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant());
- virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant());
- virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu);
- virtual void global_menu_add_separator(const String &p_menu_root);
+ virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant()) override;
+ virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant()) override;
+ virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu) override;
+ virtual void global_menu_add_separator(const String &p_menu_root) override;
- virtual bool global_menu_is_item_checked(const String &p_menu_root, int p_idx) const;
- virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const;
- virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx);
- virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx);
- virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx);
- virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx);
+ virtual bool global_menu_is_item_checked(const String &p_menu_root, int p_idx) const override;
+ virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const override;
+ virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) override;
+ virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) override;
+ virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) override;
+ virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) override;
- virtual void global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked);
- virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable);
- virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback);
- virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag);
- virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text);
- virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu);
+ virtual void global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) override;
+ virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override;
+ virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) override;
+ virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) override;
+ virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) override;
+ virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) override;
- virtual int global_menu_get_item_count(const String &p_menu_root) const;
+ virtual int global_menu_get_item_count(const String &p_menu_root) const override;
- virtual void global_menu_remove_item(const String &p_menu_root, int p_idx);
- virtual void global_menu_clear(const String &p_menu_root);
+ virtual void global_menu_remove_item(const String &p_menu_root, int p_idx) override;
+ virtual void global_menu_clear(const String &p_menu_root) override;
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
- virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback);
- virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback);
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
+ virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override;
+ virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
- virtual void mouse_set_mode(MouseMode p_mode);
- virtual MouseMode mouse_get_mode() const;
+ virtual void mouse_set_mode(MouseMode p_mode) override;
+ virtual MouseMode mouse_get_mode() const override;
- virtual void mouse_warp_to_position(const Point2i &p_to);
- virtual Point2i mouse_get_position() const;
- virtual Point2i mouse_get_absolute_position() const;
- virtual int mouse_get_button_state() const;
+ virtual void mouse_warp_to_position(const Point2i &p_to) override;
+ virtual Point2i mouse_get_position() const override;
+ virtual Point2i mouse_get_absolute_position() const override;
+ virtual int mouse_get_button_state() const override;
- virtual void clipboard_set(const String &p_text);
- virtual String clipboard_get() const;
+ virtual void clipboard_set(const String &p_text) override;
+ virtual String clipboard_get() const override;
- virtual int get_screen_count() const;
- virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
- virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
- virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
- virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
- virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual int get_screen_count() const override;
+ virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_max_scale() const override;
+ virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
- virtual Vector<int> get_window_list() const;
+ virtual Vector<int> get_window_list() const override;
- virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i & = Rect2i());
- virtual void delete_sub_window(WindowID p_id);
+ virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override;
+ virtual void show_window(WindowID p_id) override;
+ virtual void delete_sub_window(WindowID p_id) override;
- virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
- virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
- virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
- virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
- virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const;
- virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID);
+ virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const;
- virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual void window_set_transient(WindowID p_window, WindowID p_parent);
+ virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
- virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
- virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
- virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
- virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const;
- virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID);
- virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
- virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID);
- virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
- virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual bool can_any_window_draw() const;
+ virtual bool can_any_window_draw() const override;
- virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID);
- virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID) override;
- virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
+ virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
- virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID);
- virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
+ virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual Point2i ime_get_selection() const;
- virtual String ime_get_text() const;
+ virtual Point2i ime_get_selection() const override;
+ virtual String ime_get_text() const override;
- virtual void cursor_set_shape(CursorShape p_shape);
- virtual CursorShape cursor_get_shape() const;
- virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
+ virtual void cursor_set_shape(CursorShape p_shape) override;
+ virtual CursorShape cursor_get_shape() const override;
+ virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
- virtual bool get_swap_ok_cancel();
+ virtual bool get_swap_cancel_ok() override;
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+ virtual int keyboard_get_layout_count() const override;
+ virtual int keyboard_get_current_layout() const override;
+ virtual void keyboard_set_current_layout(int p_index) override;
+ virtual String keyboard_get_layout_language(int p_index) const override;
+ virtual String keyboard_get_layout_name(int p_index) const override;
- virtual void process_events();
- virtual void force_process_and_drop_events();
+ virtual void process_events() override;
+ virtual void force_process_and_drop_events() override;
- virtual void release_rendering_thread();
- virtual void make_rendering_thread();
- virtual void swap_buffers();
+ virtual void release_rendering_thread() override;
+ virtual void make_rendering_thread() override;
+ virtual void swap_buffers() override;
- virtual void set_native_icon(const String &p_filename);
- virtual void set_icon(const Ref<Image> &p_icon);
+ virtual void set_native_icon(const String &p_filename) override;
+ virtual void set_icon(const Ref<Image> &p_icon) override;
- virtual void console_set_visible(bool p_enabled);
- virtual bool is_console_visible() const;
+ virtual void console_set_visible(bool p_enabled) override;
+ virtual bool is_console_visible() const override;
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
static Vector<String> get_rendering_drivers_func();
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 9a1191490c..ed7d89009f 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,6 +33,7 @@
#include "os_osx.h"
#include "core/io/marshalls.h"
+#include "core/math/geometry_2d.h"
#include "core/os/keyboard.h"
#include "main/main.h"
#include "scene/resources/texture.h"
@@ -45,12 +46,13 @@
#include <IOKit/hid/IOHIDLib.h>
#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
//TODO - reimplement OpenGLES
+
+#import <AppKit/NSOpenGLView.h>
#endif
#if defined(VULKAN_ENABLED)
-#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include <QuartzCore/CAMetalLayer.h>
#endif
@@ -61,6 +63,8 @@
#define DS_OSX ((DisplayServerOSX *)(DisplayServerOSX::get_singleton()))
+static bool ignore_momentum_scroll = false;
+
static void _get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) {
r_state->set_shift((p_osx_state & NSEventModifierFlagShift));
r_state->set_control((p_osx_state & NSEventModifierFlagControl));
@@ -68,11 +72,11 @@ static void _get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWith
r_state->set_metakey((p_osx_state & NSEventModifierFlagCommand));
}
-static Vector2i _get_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_locationInWindow, CGFloat p_backingScaleFactor) {
+static Vector2i _get_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_locationInWindow) {
const NSRect contentRect = [p_wd.window_view frame];
- const NSPoint p = p_locationInWindow;
- p_wd.mouse_pos.x = p.x * p_backingScaleFactor;
- p_wd.mouse_pos.y = (contentRect.size.height - p.y) * p_backingScaleFactor;
+ const float scale = DS_OSX->screen_get_max_scale();
+ p_wd.mouse_pos.x = p_locationInWindow.x * scale;
+ p_wd.mouse_pos.y = (contentRect.size.height - p_locationInWindow.y) * scale;
DS_OSX->last_mouse_pos = p_wd.mouse_pos;
Input::get_singleton()->set_mouse_position(p_wd.mouse_pos);
return p_wd.mouse_pos;
@@ -131,10 +135,11 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
// This works around an AppKit bug, where key up events while holding
// down the command key don't get sent to the key window.
- if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand))
+ if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) {
[[self keyWindow] sendEvent:event];
- else
+ } else {
[super sendEvent:event];
+ }
}
@end
@@ -197,9 +202,22 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
}
+- (void)applicationDidResignActive:(NSNotification *)notification {
+ if (OS_OSX::get_singleton()->get_main_loop()) {
+ OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
+ }
+}
+
+- (void)applicationDidBecomeActive:(NSNotification *)notification {
+ if (OS_OSX::get_singleton()->get_main_loop()) {
+ OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
+ }
+}
+
- (void)globalMenuCallback:(id)sender {
- if (![sender representedObject])
+ if (![sender representedObject]) {
return;
+ }
GlobalMenuItem *value = [sender representedObject];
@@ -238,9 +256,7 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
List<String> args;
args.push_back(((OS_OSX *)(OS_OSX::get_singleton()))->open_with_filename);
String exec = OS::get_singleton()->get_executable_path();
-
- OS::ProcessID pid = 0;
- OS::get_singleton()->execute(exec, args, false, &pid);
+ OS::get_singleton()->create_process(exec, args);
}
#endif
return YES;
@@ -252,8 +268,9 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
- (void)showAbout:(id)sender {
- if (OS_OSX::get_singleton()->get_main_loop())
+ if (OS_OSX::get_singleton()->get_main_loop()) {
OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
+ }
}
@end
@@ -295,8 +312,6 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
DS_OSX->window_set_transient(wd.transient_children.front()->get(), DisplayServerOSX::INVALID_WINDOW_ID);
}
- DS_OSX->windows.erase(window_id);
-
if (wd.transient_parent != DisplayServerOSX::INVALID_WINDOW_ID) {
DisplayServerOSX::WindowData &pwd = DS_OSX->windows[wd.transient_parent];
[pwd.window_object makeKeyAndOrderFront:nil]; // Move focus back to parent.
@@ -306,11 +321,18 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
[pwd.window_object makeKeyAndOrderFront:nil]; // Move focus back to main window if there is no parent or other windows left.
}
+#if defined(OPENGL_ENABLED)
+ if (DS_OSX->rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ }
+#endif
#ifdef VULKAN_ENABLED
if (DS_OSX->rendering_driver == "vulkan") {
DS_OSX->context_vulkan->window_destroy(window_id);
}
#endif
+
+ DS_OSX->windows.erase(window_id);
}
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
@@ -333,22 +355,25 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
wd.fullscreen = false;
+ const float scale = DS_OSX->screen_get_max_scale();
if (wd.min_size != Size2i()) {
- Size2i size = wd.min_size / DS_OSX->_display_scale([wd.window_object screen]);
+ Size2i size = wd.min_size / scale;
[wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
}
if (wd.max_size != Size2i()) {
- Size2i size = wd.max_size / DS_OSX->_display_scale([wd.window_object screen]);
+ Size2i size = wd.max_size / scale;
[wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
}
- if (wd.resize_disabled)
+ if (wd.resize_disabled) {
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
+ }
}
- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
- if (!DisplayServerOSX::get_singleton())
+ if (!DisplayServerOSX::get_singleton()) {
return;
+ }
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
@@ -356,34 +381,21 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
CGFloat newBackingScaleFactor = [wd.window_object backingScaleFactor];
CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
-#if defined(OPENGL_ENABLED)
- if (DS_OSX->rendering_driver == "opengl_es") {
- //TODO - reimplement OpenGLES
- if (OS_OSX::get_singleton()->is_hidpi_allowed()) {
- [wd.window_view setWantsBestResolutionOpenGLSurface:YES];
- } else {
- [wd.window_view setWantsBestResolutionOpenGLSurface:NO];
- }
- }
-#endif
-
if (newBackingScaleFactor != oldBackingScaleFactor) {
//Set new display scale and window size
- float newDisplayScale = OS_OSX::get_singleton()->is_hidpi_allowed() ? newBackingScaleFactor : 1.0;
-
+ const float scale = DS_OSX->screen_get_max_scale();
const NSRect contentRect = [wd.window_view frame];
- wd.size.width = contentRect.size.width * newDisplayScale;
- wd.size.height = contentRect.size.height * newDisplayScale;
+ wd.size.width = contentRect.size.width * scale;
+ wd.size.height = contentRect.size.height * scale;
DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_DPI_CHANGE);
-#if defined(VULKAN_ENABLED)
- if (DS_OSX->rendering_driver == "vulkan") {
- CALayer *layer = [wd.window_view layer];
- layer.contentsScale = DS_OSX->_display_scale([wd.window_object screen]);
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ layer.contentsScale = scale;
}
-#endif
+
//Force window resize event
[self windowDidResize:notification];
}
@@ -395,22 +407,24 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ const NSRect contentRect = [wd.window_view frame];
+
+ const float scale = DS_OSX->screen_get_max_scale();
+ wd.size.width = contentRect.size.width * scale;
+ wd.size.height = contentRect.size.height * scale;
+
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ layer.contentsScale = scale;
+ }
+
#if defined(OPENGL_ENABLED)
if (DS_OSX->rendering_driver == "opengl_es") {
//TODO - reimplement OpenGLES
- wd.context_gles2->update();
}
#endif
- const NSRect contentRect = [wd.window_view frame];
-
- float displayScale = DS_OSX->_display_scale([wd.window_object screen]);
- wd.size.width = contentRect.size.width * displayScale;
- wd.size.height = contentRect.size.height * displayScale;
-
#if defined(VULKAN_ENABLED)
if (DS_OSX->rendering_driver == "vulkan") {
- CALayer *layer = [wd.window_view layer];
- layer.contentsScale = displayScale;
DS_OSX->context_vulkan->window_resize(window_id, wd.size.width, wd.size.height);
}
#endif
@@ -455,8 +469,7 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
- const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [wd.window_view backingScaleFactor] : 1.0;
- _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream], backingScaleFactor);
+ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
Input::get_singleton()->set_mouse_position(wd.mouse_pos);
DS_OSX->window_focused = true;
@@ -531,6 +544,12 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
- (CALayer *)makeBackingLayer {
+#if defined(OPENGL_ENABLED)
+ if (DS_OSX->rendering_driver == "opengl_es") {
+ CALayer *layer = [[NSOpenGLLayer class] layer];
+ return layer;
+ }
+#endif
#if defined(VULKAN_ENABLED)
if (DS_OSX->rendering_driver == "vulkan") {
CALayer *layer = [[CAMetalLayer class] layer];
@@ -541,20 +560,17 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
- (void)updateLayer {
-#if defined(VULKAN_ENABLED)
- if (DS_OSX->rendering_driver == "vulkan") {
- [super updateLayer];
- }
-#endif
#if defined(OPENGL_ENABLED)
if (DS_OSX->rendering_driver == "opengl_es") {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- wd.context_gles2->update();
+ [super updateLayer];
//TODO - reimplement OpenGLES
}
#endif
+#if defined(VULKAN_ENABLED)
+ if (DS_OSX->rendering_driver == "vulkan") {
+ [super updateLayer];
+ }
+#endif
}
- (BOOL)wantsUpdateLayer {
@@ -566,7 +582,11 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
trackingArea = nil;
imeInputEventInProgress = false;
[self updateTrackingAreas];
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
+ [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
+#else
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
+#endif
markedText = [[NSMutableAttributedString alloc] init];
return self;
}
@@ -615,8 +635,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
}
- (void)doCommandBySelector:(SEL)aSelector {
- if ([self respondsToSelector:aSelector])
+ if ([self respondsToSelector:aSelector]) {
[self performSelector:aSelector];
+ }
}
- (void)unmarkText {
@@ -651,8 +672,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
const NSRect contentRect = [wd.window_view frame];
- float displayScale = DS_OSX->_display_scale([wd.window_object screen]);
- NSRect pointInWindowRect = NSMakeRect(wd.im_position.x / displayScale, contentRect.size.height - (wd.im_position.y / displayScale) - 1, 0, 0);
+ const float scale = DS_OSX->screen_get_max_scale();
+ NSRect pointInWindowRect = NSMakeRect(wd.im_position.x / scale, contentRect.size.height - (wd.im_position.y / scale) - 1, 0, 0);
NSPoint pointOnScreen = [wd.window_object convertRectToScreen:pointInWindowRect].origin;
return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0);
@@ -678,8 +699,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
characters = (NSString *)aString;
}
- NSUInteger i, length = [characters length];
-
NSCharacterSet *ctrlChars = [NSCharacterSet controlCharacterSet];
NSCharacterSet *wsnlChars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
if ([characters rangeOfCharacterFromSet:ctrlChars].length && [characters rangeOfCharacterFromSet:wsnlChars].length == 0) {
@@ -689,10 +708,18 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
return;
}
- for (i = 0; i < length; i++) {
- const unichar codepoint = [characters characterAtIndex:i];
- if ((codepoint & 0xFF00) == 0xF700)
+ Char16String text;
+ text.resize([characters length] + 1);
+ [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
+
+ String u32text;
+ u32text.parse_utf16(text.ptr(), text.length());
+
+ for (int i = 0; i < u32text.length(); i++) {
+ const char32_t codepoint = u32text[i];
+ if ((codepoint & 0xFF00) == 0xF700) {
continue;
+ }
DisplayServerOSX::KeyEvent ke;
@@ -722,20 +749,32 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
ERR_FAIL_COND_V(!DS_OSX->windows.has(window_id), NO);
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
- NSPasteboard *pboard = [sender draggingPasteboard];
- NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
-
- Vector<String> files;
- for (NSUInteger i = 0; i < filenames.count; i++) {
- NSString *ns = [filenames objectAtIndex:i];
- char *utfs = strdup([ns UTF8String]);
- String ret;
- ret.parse_utf8(utfs);
- free(utfs);
- files.push_back(ret);
- }
-
if (!wd.drop_files_callback.is_null()) {
+ Vector<String> files;
+ NSPasteboard *pboard = [sender draggingPasteboard];
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
+ NSArray *items = pboard.pasteboardItems;
+ for (NSPasteboardItem *item in items) {
+ NSString *path = [item stringForType:NSPasteboardTypeFileURL];
+ NSString *ns = [NSURL URLWithString:path].path;
+ char *utfs = strdup([ns UTF8String]);
+ String ret;
+ ret.parse_utf8(utfs);
+ free(utfs);
+ files.push_back(ret);
+ }
+#else
+ NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
+ for (NSString *ns in filenames) {
+ char *utfs = strdup([ns UTF8String]);
+ String ret;
+ ret.parse_utf8(utfs);
+ free(utfs);
+ files.push_back(ret);
+ }
+#endif
+
Variant v = files;
Variant *vp = &v;
Variant ret;
@@ -751,6 +790,12 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
}
- (BOOL)canBecomeKeyView {
+ if (DS_OSX->windows.has(window_id)) {
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ if (wd.no_focus) {
+ return NO;
+ }
+ }
return YES;
}
@@ -777,8 +822,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
Ref<InputEventMouseButton> mb;
mb.instance();
mb->set_window_id(window_id);
- const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0;
- const Vector2 pos = _get_mouse_pos(wd, [event locationInWindow], backingScaleFactor);
+ const Vector2 pos = _get_mouse_pos(wd, [event locationInWindow]);
_get_key_modifier_state([event modifierFlags], mb);
mb->set_button_index(index);
mb->set_pressed(pressed);
@@ -824,13 +868,67 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ NSPoint delta = NSMakePoint([event deltaX], [event deltaY]);
+ NSPoint mpos = [event locationInWindow];
+
+ if (DS_OSX->ignore_warp) {
+ // Discard late events, before warp
+ if (([event timestamp]) < DS_OSX->last_warp) {
+ return;
+ }
+ DS_OSX->ignore_warp = false;
+ return;
+ }
+
+ if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED) {
+ // Discard late events
+ if (([event timestamp]) < DS_OSX->last_warp) {
+ return;
+ }
+
+ // Warp affects next event delta, subtract previous warp deltas
+ List<DisplayServerOSX::WarpEvent>::Element *F = DS_OSX->warp_events.front();
+ while (F) {
+ if (F->get().timestamp < [event timestamp]) {
+ List<DisplayServerOSX::WarpEvent>::Element *E = F;
+ delta.x -= E->get().delta.x;
+ delta.y -= E->get().delta.y;
+ F = F->next();
+ DS_OSX->warp_events.erase(E);
+ } else {
+ F = F->next();
+ }
+ }
+
+ // Confine mouse position to the window, and update delta
+ NSRect frame = [wd.window_object frame];
+ NSPoint conf_pos = mpos;
+ conf_pos.x = CLAMP(conf_pos.x + delta.x, 0.f, frame.size.width);
+ conf_pos.y = CLAMP(conf_pos.y - delta.y, 0.f, frame.size.height);
+ delta.x = conf_pos.x - mpos.x;
+ delta.y = mpos.y - conf_pos.y;
+ mpos = conf_pos;
+
+ // Move mouse cursor
+ NSRect pointInWindowRect = NSMakeRect(conf_pos.x, conf_pos.y, 0, 0);
+ conf_pos = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
+ conf_pos.y = CGDisplayBounds(CGMainDisplayID()).size.height - conf_pos.y;
+ CGWarpMouseCursorPosition(conf_pos);
+
+ // Save warp data
+ DS_OSX->last_warp = [[NSProcessInfo processInfo] systemUptime];
+ DisplayServerOSX::WarpEvent ev;
+ ev.timestamp = DS_OSX->last_warp;
+ ev.delta = delta;
+ DS_OSX->warp_events.push_back(ev);
+ }
+
Ref<InputEventMouseMotion> mm;
mm.instance();
mm->set_window_id(window_id);
mm->set_button_mask(DS_OSX->last_button_state);
- const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0;
- const Vector2i pos = _get_mouse_pos(wd, [event locationInWindow], backingScaleFactor);
+ const Vector2i pos = _get_mouse_pos(wd, mpos);
mm->set_position(pos);
mm->set_pressure([event pressure]);
if ([event subtype] == NSEventSubtypeTabletPoint) {
@@ -839,9 +937,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
}
mm->set_global_position(pos);
mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
- Vector2i relativeMotion = Vector2i();
- relativeMotion.x = [event deltaX] * backingScaleFactor;
- relativeMotion.y = [event deltaY] * backingScaleFactor;
+ const Vector2i relativeMotion = Vector2i(delta.x, delta.y) * DS_OSX->screen_get_max_scale();
mm->set_relative(relativeMotion);
_get_key_modifier_state([event modifierFlags], mm);
@@ -893,16 +989,18 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
- if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED)
+ if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED) {
DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_EXIT);
+ }
}
- (void)mouseEntered:(NSEvent *)event {
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
- if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED)
+ if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED) {
DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_ENTER);
+ }
DisplayServer::CursorShape p_shape = DS_OSX->cursor_shape;
DS_OSX->cursor_shape = DisplayServer::CURSOR_MAX;
@@ -917,8 +1015,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
ev.instance();
ev->set_window_id(window_id);
_get_key_modifier_state([event modifierFlags], ev);
- const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0;
- ev->set_position(_get_mouse_pos(wd, [event locationInWindow], backingScaleFactor));
+ ev->set_position(_get_mouse_pos(wd, [event locationInWindow]));
ev->set_factor([event magnification] + 1.0);
Input::get_singleton()->accumulate_input_event(ev);
@@ -934,17 +1031,8 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
[trackingArea release];
}
- NSTrackingAreaOptions options =
- NSTrackingMouseEnteredAndExited |
- NSTrackingActiveInKeyWindow |
- NSTrackingCursorUpdate |
- NSTrackingInVisibleRect;
-
- trackingArea = [[NSTrackingArea alloc]
- initWithRect:[self bounds]
- options:options
- owner:self
- userInfo:nil];
+ NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingCursorUpdate | NSTrackingInVisibleRect;
+ trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
[super updateTrackingAreas];
@@ -974,8 +1062,9 @@ static bool isNumpadKey(unsigned int key) {
0x00
};
for (int i = 0; table[i] != 0; i++) {
- if (key == table[i])
+ if (key == table[i]) {
return true;
+ }
}
return false;
}
@@ -1115,8 +1204,9 @@ static int translateKey(unsigned int key) {
/* 7f */ KEY_UNKNOWN,
};
- if (key >= 128)
+ if (key >= 128) {
return KEY_UNKNOWN;
+ }
return table[key];
}
@@ -1185,16 +1275,19 @@ static const _KeyCodeMap _keycodes[55] = {
};
static int remapKey(unsigned int key, unsigned int state) {
- if (isNumpadKey(key))
+ if (isNumpadKey(key)) {
return translateKey(key);
+ }
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- if (!currentKeyboard)
+ if (!currentKeyboard) {
return translateKey(key);
+ }
CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (!layoutData)
+ if (!layoutData) {
return translateKey(key);
+ }
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
@@ -1229,6 +1322,8 @@ static int remapKey(unsigned int key, unsigned int state) {
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ ignore_momentum_scroll = true;
+
// Ignore all input if IME input is in progress
if (!imeInputEventInProgress) {
NSString *characters = [event characters];
@@ -1236,7 +1331,16 @@ static int remapKey(unsigned int key, unsigned int state) {
if (!wd.im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
// Fallback unicode character handler used if IME is not active
- for (NSUInteger i = 0; i < length; i++) {
+ Char16String text;
+ text.resize([characters length] + 1);
+ [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
+
+ String u32text;
+ u32text.parse_utf16(text.ptr(), text.length());
+
+ for (int i = 0; i < u32text.length(); i++) {
+ const char32_t codepoint = u32text[i];
+
DisplayServerOSX::KeyEvent ke;
ke.window_id = window_id;
@@ -1246,7 +1350,7 @@ static int remapKey(unsigned int key, unsigned int state) {
ke.keycode = remapKey([event keyCode], [event modifierFlags]);
ke.physical_keycode = translateKey([event keyCode]);
ke.raw = true;
- ke.unicode = [characters characterAtIndex:i];
+ ke.unicode = codepoint;
_push_to_key_event_buffer(ke);
}
@@ -1267,11 +1371,14 @@ static int remapKey(unsigned int key, unsigned int state) {
}
// Pass events to IME handler
- if (wd.im_active)
+ if (wd.im_active) {
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
+ }
}
- (void)flagsChanged:(NSEvent *)event {
+ ignore_momentum_scroll = true;
+
// Ignore all input if IME input is in progress
if (!imeInputEventInProgress) {
DisplayServerOSX::KeyEvent ke;
@@ -1335,7 +1442,15 @@ static int remapKey(unsigned int key, unsigned int state) {
// Fallback unicode character handler used if IME is not active
if (!wd.im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
- for (NSUInteger i = 0; i < length; i++) {
+ Char16String text;
+ text.resize([characters length] + 1);
+ [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
+
+ String u32text;
+ u32text.parse_utf16(text.ptr(), text.length());
+
+ for (int i = 0; i < u32text.length(); i++) {
+ const char32_t codepoint = u32text[i];
DisplayServerOSX::KeyEvent ke;
ke.window_id = window_id;
@@ -1345,7 +1460,7 @@ static int remapKey(unsigned int key, unsigned int state) {
ke.keycode = remapKey([event keyCode], [event modifierFlags]);
ke.physical_keycode = translateKey([event keyCode]);
ke.raw = true;
- ke.unicode = [characters characterAtIndex:i];
+ ke.unicode = codepoint;
_push_to_key_event_buffer(ke);
}
@@ -1421,8 +1536,7 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy
double deltaX, deltaY;
- const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0;
- _get_mouse_pos(wd, [event locationInWindow], backingScaleFactor);
+ _get_mouse_pos(wd, [event locationInWindow]);
deltaX = [event scrollingDeltaX];
deltaY = [event scrollingDeltaY];
@@ -1432,6 +1546,14 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy
deltaY *= 0.03;
}
+ if ([event momentumPhase] != NSEventPhaseNone) {
+ if (ignore_momentum_scroll) {
+ return;
+ }
+ } else {
+ ignore_momentum_scroll = false;
+ }
+
if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) {
sendPanEvent(window_id, deltaX, deltaY, [event modifierFlags]);
} else {
@@ -1459,6 +1581,25 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy
- (BOOL)canBecomeKeyWindow {
// Required for NSBorderlessWindowMask windows
+ for (Map<DisplayServer::WindowID, DisplayServerOSX::WindowData>::Element *E = DS_OSX->windows.front(); E; E = E->next()) {
+ if (E->get().window_object == self) {
+ if (E->get().no_focus) {
+ return NO;
+ }
+ }
+ }
+ return YES;
+}
+
+- (BOOL)canBecomeMainWindow {
+ // Required for NSBorderlessWindowMask windows
+ for (Map<DisplayServer::WindowID, DisplayServerOSX::WindowData>::Element *E = DS_OSX->windows.front(); E; E = E->next()) {
+ if (E->get().window_object == self) {
+ if (E->get().no_focus) {
+ return NO;
+ }
+ }
+ }
return YES;
}
@@ -1485,6 +1626,7 @@ bool DisplayServerOSX::has_feature(Feature p_feature) const {
case FEATURE_HIDPI:
case FEATURE_ICON:
case FEATURE_NATIVE_ICON:
+ //case FEATURE_KEEP_SCREEN_ON:
case FEATURE_SWAP_BUFFERS:
return true;
default: {
@@ -1849,8 +1991,12 @@ void DisplayServerOSX::alert(const String &p_alert, const String &p_title) {
[window setInformativeText:ns_alert];
[window setAlertStyle:NSAlertStyleWarning];
+ id key_window = [[NSApplication sharedApplication] keyWindow];
[window runModal];
[window release];
+ if (key_window) {
+ [key_window makeKeyAndOrderFront:nil];
+ }
}
Error DisplayServerOSX::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
@@ -1930,23 +2076,40 @@ Error DisplayServerOSX::dialog_input_text(String p_title, String p_description,
void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
_THREAD_SAFE_METHOD_
- if (p_mode == mouse_mode)
+ if (p_mode == mouse_mode) {
return;
+ }
if (p_mode == MOUSE_MODE_CAPTURED) {
// Apple Docs state that the display parameter is not used.
// "This parameter is not used. By default, you may pass kCGDirectMainDisplay."
// https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html
- CGDisplayHideCursor(kCGDirectMainDisplay);
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ CGDisplayHideCursor(kCGDirectMainDisplay);
+ }
CGAssociateMouseAndMouseCursorPosition(false);
+ WindowData &wd = windows[MAIN_WINDOW_ID];
+ const NSRect contentRect = [wd.window_view frame];
+ NSRect pointInWindowRect = NSMakeRect(contentRect.size.width / 2, contentRect.size.height / 2, 0, 0);
+ NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
+ CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
+ CGWarpMouseCursorPosition(lMouseWarpPos);
} else if (p_mode == MOUSE_MODE_HIDDEN) {
- CGDisplayHideCursor(kCGDirectMainDisplay);
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ CGDisplayHideCursor(kCGDirectMainDisplay);
+ }
CGAssociateMouseAndMouseCursorPosition(true);
+ } else if (p_mode == MOUSE_MODE_CONFINED) {
+ CGDisplayShowCursor(kCGDirectMainDisplay);
+ CGAssociateMouseAndMouseCursorPosition(false);
} else {
CGDisplayShowCursor(kCGDirectMainDisplay);
CGAssociateMouseAndMouseCursorPosition(true);
}
+ last_warp = [[NSProcessInfo processInfo] systemUptime];
+ ignore_warp = true;
+ warp_events.clear();
mouse_mode = p_mode;
}
@@ -1964,8 +2127,8 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
//local point in window coords
const NSRect contentRect = [wd.window_view frame];
- float displayScale = _display_scale([wd.window_object screen]);
- NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0);
+ const float scale = screen_get_max_scale();
+ NSRect pointInWindowRect = NSMakeRect(p_to.x / scale, contentRect.size.height - (p_to.y / scale - 1), 0, 0);
NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
//point in scren coords
@@ -1976,7 +2139,9 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
CGAssociateMouseAndMouseCursorPosition(false);
CGWarpMouseCursorPosition(lMouseWarpPos);
- CGAssociateMouseAndMouseCursorPosition(true);
+ if (mouse_mode != MOUSE_MODE_CONFINED) {
+ CGAssociateMouseAndMouseCursorPosition(true);
+ }
}
}
@@ -1988,11 +2153,12 @@ Point2i DisplayServerOSX::mouse_get_absolute_position() const {
_THREAD_SAFE_METHOD_
const NSPoint mouse_pos = [NSEvent mouseLocation];
+ const float scale = screen_get_max_scale();
for (NSScreen *screen in [NSScreen screens]) {
NSRect frame = [screen frame];
if (NSMouseInRect(mouse_pos, frame, NO)) {
- return Vector2i((int)mouse_pos.x, (int)-mouse_pos.y) + _get_screens_origin();
+ return Vector2i((int)mouse_pos.x, (int)-mouse_pos.y) * scale + _get_screens_origin();
}
}
return Vector2i();
@@ -2050,17 +2216,10 @@ int DisplayServerOSX::get_screen_count() const {
// to convert between OS X native screen coordinates and the ones expected by Godot
static bool displays_arrangement_dirty = true;
+static bool displays_scale_dirty = true;
static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
displays_arrangement_dirty = true;
-}
-
-float DisplayServerOSX::_display_scale(id screen) const {
- if (OS_OSX::get_singleton()->is_hidpi_allowed()) {
- if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
- return fmax(1.0, [screen backingScaleFactor]);
- }
- }
- return 1.0;
+ displays_scale_dirty = true;
}
Point2i DisplayServerOSX::_get_screens_origin() const {
@@ -2087,10 +2246,9 @@ Point2i DisplayServerOSX::_get_screens_origin() const {
Point2i DisplayServerOSX::_get_native_screen_position(int p_screen) const {
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
- float display_scale = _display_scale([screenArray objectAtIndex:p_screen]);
NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
// Return the top-left corner of the screen, for OS X the y starts at the bottom
- return Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale;
+ return Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * screen_get_max_scale();
}
return Point2i();
@@ -2119,10 +2277,9 @@ Size2i DisplayServerOSX::screen_get_size(int p_screen) const {
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
- float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
// Note: Use frame to get the whole screen size
NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
- return Size2i(nsrect.size.width, nsrect.size.height) * displayScale;
+ return Size2i(nsrect.size.width, nsrect.size.height) * screen_get_max_scale();
}
return Size2i();
@@ -2137,16 +2294,19 @@ int DisplayServerOSX::screen_get_dpi(int p_screen) const {
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
- float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
- NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
- CGSize displayPhysicalSize = CGDisplayScreenSize(
- [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
- return (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale;
+ const NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
+ const CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
+ float scale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
+
+ float den2 = (displayPhysicalSize.width / 25.4f) * (displayPhysicalSize.width / 25.4f) + (displayPhysicalSize.height / 25.4f) * (displayPhysicalSize.height / 25.4f);
+ if (den2 > 0.0f) {
+ return ceil(sqrt(displayPixelSize.width * displayPixelSize.width + displayPixelSize.height * displayPixelSize.height) / sqrt(den2) * scale);
+ }
}
- return 96;
+ return 72;
}
float DisplayServerOSX::screen_get_scale(int p_screen) const {
@@ -2155,14 +2315,32 @@ float DisplayServerOSX::screen_get_scale(int p_screen) const {
if (p_screen == SCREEN_OF_MAIN_WINDOW) {
p_screen = window_get_current_screen();
}
- NSArray *screenArray = [NSScreen screens];
- if ((NSUInteger)p_screen < [screenArray count]) {
- return _display_scale([screenArray objectAtIndex:p_screen]);
+ if (OS::get_singleton()->is_hidpi_allowed()) {
+ NSArray *screenArray = [NSScreen screens];
+ if ((NSUInteger)p_screen < [screenArray count]) {
+ if ([[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) {
+ return fmax(1.0, [[screenArray objectAtIndex:p_screen] backingScaleFactor]);
+ }
+ }
}
return 1.f;
}
+float DisplayServerOSX::screen_get_max_scale() const {
+ _THREAD_SAFE_METHOD_
+
+ static float scale = 1.f;
+ if (displays_scale_dirty) {
+ int screen_count = get_screen_count();
+ for (int i = 0; i < screen_count; i++) {
+ scale = fmax(scale, screen_get_scale(i));
+ }
+ displays_scale_dirty = false;
+ }
+ return scale;
+}
+
Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const {
_THREAD_SAFE_METHOD_
@@ -2172,12 +2350,12 @@ Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const {
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
- float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
+ const float scale = screen_get_max_scale();
NSRect nsrect = [[screenArray objectAtIndex:p_screen] visibleFrame];
- Point2i position = Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * displayScale - _get_screens_origin();
+ Point2i position = Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * scale - _get_screens_origin();
position.y *= -1;
- Size2i size = Size2i(nsrect.size.width, nsrect.size.height) * displayScale;
+ Size2i size = Size2i(nsrect.size.width, nsrect.size.height) * scale;
return Rect2i(position, size);
}
@@ -2199,16 +2377,25 @@ DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, u
_THREAD_SAFE_METHOD_
WindowID id = _create_window(p_mode, p_rect);
- WindowData &wd = windows[id];
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, id);
}
}
- [wd.window_object makeKeyAndOrderFront:nil];
+
return id;
}
+void DisplayServerOSX::show_window(WindowID p_id) {
+ WindowData &wd = windows[p_id];
+
+ if (wd.no_focus) {
+ [wd.window_object orderFront:nil];
+ } else {
+ [wd.window_object makeKeyAndOrderFront:nil];
+ }
+}
+
void DisplayServerOSX::_send_window_event(const WindowData &wd, WindowEvent p_event) {
_THREAD_SAFE_METHOD_
@@ -2223,8 +2410,9 @@ void DisplayServerOSX::_send_window_event(const WindowData &wd, WindowEvent p_ev
DisplayServerOSX::WindowID DisplayServerOSX::_find_window_id(id p_window) {
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (E->get().window_object == p_window)
+ if (E->get().window_object == p_window) {
return E->key();
+ }
}
return INVALID_WINDOW_ID;
}
@@ -2249,7 +2437,11 @@ void DisplayServerOSX::_update_window(WindowData p_wd) {
[p_wd.window_object setHidesOnDeactivate:YES];
} else {
// Reset these when our window is not a borderless window that covers up the screen
- [p_wd.window_object setLevel:NSNormalWindowLevel];
+ if (p_wd.on_top) {
+ [p_wd.window_object setLevel:NSFloatingWindowLevel];
+ } else {
+ [p_wd.window_object setLevel:NSNormalWindowLevel];
+ }
[p_wd.window_object setHidesOnDeactivate:NO];
}
}
@@ -2275,6 +2467,15 @@ void DisplayServerOSX::window_set_title(const String &p_title, WindowID p_window
[wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
}
+void DisplayServerOSX::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.mpath = p_region;
+}
+
void DisplayServerOSX::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -2348,7 +2549,7 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent
wd_window.transient_parent = INVALID_WINDOW_ID;
wd_parent.transient_children.erase(p_window);
- [wd_window.window_object setParentWindow:nil];
+ [wd_parent.window_object removeChildWindow:wd_window.window_object];
} else {
ERR_FAIL_COND(!windows.has(p_parent));
ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
@@ -2357,7 +2558,7 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent
wd_window.transient_parent = p_parent;
wd_parent.transient_children.insert(p_window);
- [wd_window.window_object setParentWindow:wd_parent.window_object];
+ [wd_parent.window_object addChildWindow:wd_window.window_object ordered:NSWindowAbove];
}
}
@@ -2369,11 +2570,12 @@ Point2i DisplayServerOSX::window_get_position(WindowID p_window) const {
NSRect nsrect = [wd.window_object frame];
Point2i pos;
- float display_scale = _display_scale([wd.window_object screen]);
// Return the position of the top-left corner, for OS X the y starts at the bottom
- pos.x = nsrect.origin.x * display_scale;
- pos.y = (nsrect.origin.y + nsrect.size.height) * display_scale;
+ const float scale = screen_get_max_scale();
+ pos.x = nsrect.origin.x;
+ pos.y = (nsrect.origin.y + nsrect.size.height);
+ pos *= scale;
pos -= _get_screens_origin();
// OS X native y-coordinate relative to _get_screens_origin() is negative,
// Godot expects a positive value
@@ -2392,17 +2594,12 @@ void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p
// Godot passes a positive value
position.y *= -1;
position += _get_screens_origin();
+ position /= screen_get_max_scale();
- NSPoint pos;
- float displayScale = _display_scale([wd.window_object screen]);
-
- pos.x = position.x / displayScale;
- pos.y = position.y / displayScale;
-
- [wd.window_object setFrameTopLeftPoint:pos];
+ [wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x, position.y)];
_update_window(wd);
- _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream], displayScale);
+ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
}
void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_window) {
@@ -2418,7 +2615,7 @@ void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_windo
wd.max_size = p_size;
if ((wd.max_size != Size2i()) && !wd.fullscreen) {
- Size2i size = wd.max_size / _display_scale([wd.window_object screen]);
+ Size2i size = wd.max_size / screen_get_max_scale();
[wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
} else {
[wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
@@ -2446,7 +2643,7 @@ void DisplayServerOSX::window_set_min_size(const Size2i p_size, WindowID p_windo
wd.min_size = p_size;
if ((wd.min_size != Size2i()) && !wd.fullscreen) {
- Size2i size = wd.min_size / _display_scale([wd.window_object screen]);
+ Size2i size = wd.min_size / screen_get_max_scale();
[wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
} else {
[wd.window_object setContentMinSize:NSMakeSize(0, 0)];
@@ -2468,18 +2665,20 @@ void DisplayServerOSX::window_set_size(const Size2i p_size, WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- Size2i size = p_size / _display_scale([wd.window_object screen]);
+ Size2i size = p_size / screen_get_max_scale();
- if (!wd.borderless) {
- // NSRect used by setFrame includes the title bar, so add it to our size.y
- CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
- if (menuBarHeight != 0.f) {
- size.y += menuBarHeight;
- }
- }
+ NSPoint top_left;
+ NSRect old_frame = [wd.window_object frame];
+ top_left.x = old_frame.origin.x;
+ top_left.y = NSMaxY(old_frame);
- NSRect frame = [wd.window_object frame];
- [wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, size.x, size.y) display:YES];
+ NSRect new_frame = NSMakeRect(0, 0, size.x, size.y);
+ new_frame = [wd.window_object frameRectForContentRect:new_frame];
+
+ new_frame.origin.x = top_left.x;
+ new_frame.origin.y = top_left.y - new_frame.size.height;
+
+ [wd.window_object setFrame:new_frame display:YES];
_update_window(wd);
}
@@ -2498,7 +2697,7 @@ Size2i DisplayServerOSX::window_get_real_size(WindowID p_window) const {
ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
const WindowData &wd = windows[p_window];
NSRect frame = [wd.window_object frame];
- return Size2i(frame.size.width, frame.size.height) * _display_scale([wd.window_object screen]);
+ return Size2i(frame.size.width, frame.size.height) * screen_get_max_scale();
}
bool DisplayServerOSX::window_is_maximize_allowed(WindowID p_window) const {
@@ -2509,24 +2708,26 @@ void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- if (!OS_OSX::get_singleton()->is_layered_allowed())
+ if (!OS_OSX::get_singleton()->is_layered_allowed()) {
return;
+ }
if (wd.layered_window != p_enabled) {
if (p_enabled) {
[wd.window_object setBackgroundColor:[NSColor clearColor]];
[wd.window_object setOpaque:NO];
[wd.window_object setHasShadow:NO];
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ [layer setOpaque:NO];
+ }
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
- CALayer *layer = [wd.window_view layer];
- [layer setOpaque:NO];
//TODO - implement transparency for Vulkan
}
#endif
#if defined(OPENGL_ENABLED)
if (rendering_driver == "opengl_es") {
//TODO - reimplement OpenGLES
- wd.context_gles2->set_opacity(0);
}
#endif
wd.layered_window = true;
@@ -2534,17 +2735,18 @@ void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled
[wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
[wd.window_object setOpaque:YES];
[wd.window_object setHasShadow:YES];
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ [layer setOpaque:YES];
+ }
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
- CALayer *layer = [wd.window_view layer];
- [layer setOpaque:YES];
//TODO - implement transparency for Vulkan
}
#endif
#if defined(OPENGL_ENABLED)
if (rendering_driver == "opengl_es") {
//TODO - reimplement OpenGLES
- wd.context_gles2->set_opacity(1);
}
#endif
wd.layered_window = false;
@@ -2552,7 +2754,11 @@ void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled
#if defined(OPENGL_ENABLED)
if (rendering_driver == "opengl_es") {
//TODO - reimplement OpenGLES
- wd.context_gles2->update();
+ }
+#endif
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ //TODO - implement transparency for Vulkan
}
#endif
NSRect frameRect = [wd.window_object frame];
@@ -2580,16 +2786,18 @@ void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) {
[wd.window_object deminiaturize:nil];
} break;
case WINDOW_MODE_FULLSCREEN: {
- if (wd.layered_window)
+ if (wd.layered_window) {
_set_window_per_pixel_transparency_enabled(true, p_window);
- if (wd.resize_disabled) //restore resize disabled
+ }
+ if (wd.resize_disabled) { //restore resize disabled
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
+ }
if (wd.min_size != Size2i()) {
- Size2i size = wd.min_size / _display_scale([wd.window_object screen]);
+ Size2i size = wd.min_size / screen_get_max_scale();
[wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
}
if (wd.max_size != Size2i()) {
- Size2i size = wd.max_size / _display_scale([wd.window_object screen]);
+ Size2i size = wd.max_size / screen_get_max_scale();
[wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
}
[wd.window_object toggleFullScreen:nil];
@@ -2658,8 +2866,9 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
switch (p_flag) {
case WINDOW_FLAG_RESIZE_DISABLED: {
wd.resize_disabled = p_enabled;
- if (wd.fullscreen) //fullscreen window should be resizable, style will be applyed on exiting fs
+ if (wd.fullscreen) { //fullscreen window should be resizable, style will be applied on exiting fs
return;
+ }
if (p_enabled) {
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
} else {
@@ -2668,7 +2877,9 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
} break;
case WINDOW_FLAG_BORDERLESS: {
// OrderOut prevents a lose focus bug with the window
- [wd.window_object orderOut:nil];
+ if ([wd.window_object isVisible]) {
+ [wd.window_object orderOut:nil];
+ }
wd.borderless = p_enabled;
if (p_enabled) {
[wd.window_object setStyleMask:NSWindowStyleMaskBorderless];
@@ -2682,7 +2893,13 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
[wd.window_object setFrame:frameRect display:NO];
}
_update_window(wd);
- [wd.window_object makeKeyAndOrderFront:nil];
+ if ([wd.window_object isVisible]) {
+ if (wd.no_focus) {
+ [wd.window_object orderFront:nil];
+ } else {
+ [wd.window_object makeKeyAndOrderFront:nil];
+ }
+ }
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
wd.on_top = p_enabled;
@@ -2700,7 +2917,9 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
[wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)];
}
_set_window_per_pixel_transparency_enabled(p_enabled, p_window);
-
+ } break;
+ case WINDOW_FLAG_NO_FOCUS: {
+ wd.no_focus = p_enabled;
} break;
default: {
}
@@ -2726,6 +2945,9 @@ bool DisplayServerOSX::window_get_flag(WindowFlags p_flag, WindowID p_window) co
case WINDOW_FLAG_TRANSPARENT: {
return wd.layered_window;
} break;
+ case WINDOW_FLAG_NO_FOCUS: {
+ return wd.no_focus;
+ } break;
default: {
}
}
@@ -2745,7 +2967,11 @@ void DisplayServerOSX::window_move_to_foreground(WindowID p_window) {
const WindowData &wd = windows[p_window];
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
- [wd.window_object makeKeyAndOrderFront:nil];
+ if (wd.no_focus) {
+ [wd.window_object orderFront:nil];
+ } else {
+ [wd.window_object makeKeyAndOrderFront:nil];
+ }
}
bool DisplayServerOSX::window_can_draw(WindowID p_window) const {
@@ -2771,8 +2997,9 @@ void DisplayServerOSX::window_set_ime_active(const bool p_active, WindowID p_win
wd.im_active = p_active;
- if (!p_active)
+ if (!p_active) {
[wd.window_view cancelComposition];
+ }
}
void DisplayServerOSX::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
@@ -2784,8 +3011,8 @@ void DisplayServerOSX::window_set_ime_position(const Point2i &p_pos, WindowID p_
wd.im_position = p_pos;
}
-bool DisplayServerOSX::get_swap_ok_cancel() {
- return true;
+bool DisplayServerOSX::get_swap_cancel_ok() {
+ return false;
}
void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
@@ -2793,8 +3020,9 @@ void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
- if (cursor_shape == p_shape)
+ if (cursor_shape == p_shape) {
return;
+ }
if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
cursor_shape = p_shape;
@@ -2987,85 +3215,129 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
}
}
+struct LayoutInfo {
+ String name;
+ String code;
+};
+
+static Vector<LayoutInfo> kbd_layouts;
+static int current_layout = 0;
static bool keyboard_layout_dirty = true;
static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
+ kbd_layouts.clear();
+ current_layout = 0;
keyboard_layout_dirty = true;
}
-// Returns string representation of keys, if they are printable.
-static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) {
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- if (!currentKeyboard)
- return nil;
+void _update_keyboard_layouts() {
+ @autoreleasepool {
+ TISInputSourceRef cur_source = TISCopyCurrentKeyboardInputSource();
+ NSString *cur_name = (NSString *)TISGetInputSourceProperty(cur_source, kTISPropertyLocalizedName);
+ CFRelease(cur_source);
- CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (!layoutData)
- return nil;
+ // Enum IME layouts
+ NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode };
+ NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false);
+ for (NSUInteger i = 0; i < [list_ime count]; i++) {
+ LayoutInfo ly;
+ NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
+ ly.name.parse_utf8([name UTF8String]);
- const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+ NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages);
+ ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
+ kbd_layouts.push_back(ly);
- OSStatus err;
- CFMutableStringRef output = CFStringCreateMutable(NULL, 0);
+ if ([name isEqualToString:cur_name]) {
+ current_layout = kbd_layouts.size() - 1;
+ }
+ }
+ [list_ime release];
- for (int i = 0; i < length; ++i) {
- UInt32 keysDown = 0;
- UniChar chars[4];
- UniCharCount realLength;
+ // Enum plain keyboard layouts
+ NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout };
+ NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false);
+ for (NSUInteger i = 0; i < [list_kbd count]; i++) {
+ LayoutInfo ly;
+ NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
+ ly.name.parse_utf8([name UTF8String]);
- err = UCKeyTranslate(keyboardLayout,
- keyCode[i],
- kUCKeyActionDisplay,
- 0,
- LMGetKbdType(),
- kUCKeyTranslateNoDeadKeysBit,
- &keysDown,
- sizeof(chars) / sizeof(chars[0]),
- &realLength,
- chars);
+ NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyInputSourceLanguages);
+ ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
+ kbd_layouts.push_back(ly);
- if (err != noErr) {
- CFRelease(output);
- return nil;
+ if ([name isEqualToString:cur_name]) {
+ current_layout = kbd_layouts.size() - 1;
+ }
}
-
- CFStringAppendCharacters(output, chars, 1);
+ [list_kbd release];
}
- return (NSString *)output;
+ keyboard_layout_dirty = false;
}
-DisplayServerOSX::LatinKeyboardVariant DisplayServerOSX::get_latin_keyboard_variant() const {
- _THREAD_SAFE_METHOD_
-
- static LatinKeyboardVariant layout = LATIN_KEYBOARD_QWERTY;
+int DisplayServerOSX::keyboard_get_layout_count() const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
+ }
+ return kbd_layouts.size();
+}
+void DisplayServerOSX::keyboard_set_current_layout(int p_index) {
if (keyboard_layout_dirty) {
- layout = LATIN_KEYBOARD_QWERTY;
+ _update_keyboard_layouts();
+ }
+
+ ERR_FAIL_INDEX(p_index, kbd_layouts.size());
+
+ NSString *cur_name = [NSString stringWithUTF8String:kbd_layouts[p_index].name.utf8().get_data()];
- CGKeyCode keys[] = { kVK_ANSI_Q, kVK_ANSI_W, kVK_ANSI_E, kVK_ANSI_R, kVK_ANSI_T, kVK_ANSI_Y };
- NSString *test = createStringForKeys(keys, 6);
+ NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout };
+ NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false);
+ for (NSUInteger i = 0; i < [list_kbd count]; i++) {
+ NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
+ if ([name isEqualToString:cur_name]) {
+ TISSelectInputSource((TISInputSourceRef)[list_kbd objectAtIndex:i]);
+ break;
+ }
+ }
+ [list_kbd release];
- if ([test isEqualToString:@"qwertz"]) {
- layout = LATIN_KEYBOARD_QWERTZ;
- } else if ([test isEqualToString:@"azerty"]) {
- layout = LATIN_KEYBOARD_AZERTY;
- } else if ([test isEqualToString:@"qzerty"]) {
- layout = LATIN_KEYBOARD_QZERTY;
- } else if ([test isEqualToString:@"',.pyf"]) {
- layout = LATIN_KEYBOARD_DVORAK;
- } else if ([test isEqualToString:@"xvlcwk"]) {
- layout = LATIN_KEYBOARD_NEO;
- } else if ([test isEqualToString:@"qwfpgj"]) {
- layout = LATIN_KEYBOARD_COLEMAK;
+ NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode };
+ NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false);
+ for (NSUInteger i = 0; i < [list_ime count]; i++) {
+ NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
+ if ([name isEqualToString:cur_name]) {
+ TISSelectInputSource((TISInputSourceRef)[list_ime objectAtIndex:i]);
+ break;
}
+ }
+ [list_ime release];
+}
+
+int DisplayServerOSX::keyboard_get_current_layout() const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
+ }
+
+ return current_layout;
+}
+
+String DisplayServerOSX::keyboard_get_layout_language(int p_index) const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
+ }
- [test release];
+ ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
+ return kbd_layouts[p_index].code;
+}
- keyboard_layout_dirty = false;
- return layout;
+String DisplayServerOSX::keyboard_get_layout_name(int p_index) const {
+ if (keyboard_layout_dirty) {
+ _update_keyboard_layouts();
}
- return layout;
+ ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
+ return kbd_layouts[p_index].name;
}
void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) {
@@ -3144,8 +3416,9 @@ void DisplayServerOSX::process_events() {
inMode:NSDefaultRunLoopMode
dequeue:YES];
- if (event == nil)
+ if (event == nil) {
break;
+ }
[NSApp sendEvent:event];
}
@@ -3155,6 +3428,26 @@ void DisplayServerOSX::process_events() {
Input::get_singleton()->flush_accumulated_events();
}
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ WindowData &wd = E->get();
+ if (wd.mpath.size() > 0) {
+ const Vector2 mpos = _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
+ if (Geometry2D::is_point_in_polygon(mpos, wd.mpath)) {
+ if ([wd.window_object ignoresMouseEvents]) {
+ [wd.window_object setIgnoresMouseEvents:NO];
+ }
+ } else {
+ if (![wd.window_object ignoresMouseEvents]) {
+ [wd.window_object setIgnoresMouseEvents:YES];
+ }
+ }
+ } else {
+ if ([wd.window_object ignoresMouseEvents]) {
+ [wd.window_object setIgnoresMouseEvents:NO];
+ }
+ }
+ }
+
[autoreleasePool drain];
autoreleasePool = [[NSAutoreleasePool alloc] init];
}
@@ -3233,11 +3526,11 @@ void DisplayServerOSX::set_icon(const Ref<Image> &p_icon) {
Vector<String> DisplayServerOSX::get_rendering_drivers_func() {
Vector<String> drivers;
-#ifdef VULKAN_ENABLED
+#if defined(VULKAN_ENABLED)
drivers.push_back("vulkan");
#endif
-#ifdef OPENGL_ENABLED
- drivers.push_back("opengl");
+#if defined(OPENGL_ENABLED)
+ drivers.push_back("opengl_es");
#endif
return drivers;
@@ -3252,9 +3545,14 @@ String DisplayServerOSX::ime_get_text() const {
}
DisplayServer::WindowID DisplayServerOSX::get_window_at_screen_position(const Point2i &p_position) const {
+ Point2i position = p_position;
+ position.y *= -1;
+ position += _get_screens_origin();
+ position /= screen_get_max_scale();
+
+ NSInteger wnum = [NSWindow windowNumberAtPoint:NSMakePoint(position.x, position.y) belowWindowWithWindowNumber:0 /*topmost*/];
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- Rect2i win_rect = Rect2i(window_get_position(E->key()), window_get_size(E->key()));
- if (win_rect.has_point(p_position)) {
+ if ([E->get().window_object windowNumber] == wnum) {
return E->key();
}
}
@@ -3276,23 +3574,19 @@ ObjectID DisplayServerOSX::window_get_attached_instance_id(WindowID p_window) co
}
DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- return memnew(DisplayServerOSX(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ DisplayServer *ds = memnew(DisplayServerOSX(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ if (r_error != OK) {
+ ds->alert("Your video card driver does not support any of the supported Metal versions.", "Unable to initialize Video driver");
+ }
+ return ds;
}
DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, const Rect2i &p_rect) {
WindowID id;
+ const float scale = screen_get_max_scale();
{
WindowData wd;
- float displayScale = 1.0;
- if (OS_OSX::get_singleton()->is_hidpi_allowed()) {
- // note that mainScreen is not screen #0 but the one with the keyboard focus.
- NSScreen *screen = [NSScreen mainScreen];
- if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
- displayScale = fmax(displayScale, [screen backingScaleFactor]);
- }
- }
-
wd.window_delegate = [[GodotWindowDelegate alloc] init];
ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate");
[wd.window_delegate setWindowID:window_id_counter];
@@ -3305,7 +3599,7 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, c
// initWithContentRect uses bottom-left corner of the window’s frame as origin.
wd.window_object = [[GodotWindow alloc]
- initWithContentRect:NSMakeRect(position.x / displayScale, (position.y - p_rect.size.height) / displayScale, p_rect.size.width / displayScale, p_rect.size.height / displayScale)
+ initWithContentRect:NSMakeRect(position.x / scale, (position.y - p_rect.size.height) / scale, p_rect.size.width / scale, p_rect.size.height / scale)
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
backing:NSBackingStoreBuffered
defer:NO];
@@ -3314,24 +3608,7 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, c
wd.window_view = [[GodotContentView alloc] init];
ERR_FAIL_COND_V_MSG(wd.window_view == nil, INVALID_WINDOW_ID, "Can't create a window view");
[wd.window_view setWindowID:window_id_counter];
- if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_14) {
- [wd.window_view setWantsLayer:TRUE];
- }
-
- if (displayScale > 1.0) {
-#if defined(OPENGL_ENABLED)
- if (rendering_driver == "opengl_es") {
- [wd.window_view setWantsBestResolutionOpenGLSurface:YES];
- }
-#endif
- [wd.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
- } else {
-#if defined(OPENGL_ENABLED)
- if (rendering_driver == "opengl_es") {
- [wd.window_view setWantsBestResolutionOpenGLSurface:NO];
- }
-#endif
- }
+ [wd.window_view setWantsLayer:TRUE];
[wd.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
[wd.window_object setContentView:wd.window_view];
@@ -3339,33 +3616,26 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, c
[wd.window_object setAcceptsMouseMovedEvents:YES];
[wd.window_object setRestorable:NO];
- if ([wd.window_object respondsToSelector:@selector(setTabbingMode:)])
+ if ([wd.window_object respondsToSelector:@selector(setTabbingMode:)]) {
[wd.window_object setTabbingMode:NSWindowTabbingModeDisallowed];
+ }
+
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ layer.contentsScale = scale;
+ }
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
if (context_vulkan) {
- CALayer *layer = [wd.window_view layer];
- layer.contentsScale = displayScale;
Error err = context_vulkan->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height);
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan context");
}
}
#endif
-#ifdef OPENGL_ENABLED
+#if defined(OPENGL_ENABLED)
if (rendering_driver == "opengl_es") {
//TODO - reimplement OpenGLES
- wd.context_gles2 = memnew(ContextGL_OSX(wd.window_view, false));
-
- if (wd.context_gles2->initialize() != OK) {
- memdelete(wd.context_gles2);
- ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a OpenGL context");
- }
-
- //if (RasterizerGLES2::is_viable() == OK) {
- // RasterizerGLES2::register_config();
- // RasterizerGLES2::make_current();
- //}
}
#endif
id = window_id_counter++;
@@ -3375,25 +3645,22 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, c
WindowData &wd = windows[id];
window_set_mode(p_mode, id);
- float displayScale = _display_scale([wd.window_object screen]);
const NSRect contentRect = [wd.window_view frame];
- wd.size.width = contentRect.size.width * displayScale;
- wd.size.height = contentRect.size.height * displayScale;
+ wd.size.width = contentRect.size.width * scale;
+ wd.size.height = contentRect.size.height * scale;
+
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ layer.contentsScale = scale;
+ }
#if defined(OPENGL_ENABLED)
if (rendering_driver == "opengl_es") {
- if (OS_OSX::singleton->is_hidpi_allowed()) {
- [wd.window_view setWantsBestResolutionOpenGLSurface:YES];
- } else {
- [wd.window_view setWantsBestResolutionOpenGLSurface:NO];
- }
- wd.context_gles2->update();
+ //TODO - reimplement OpenGLES
}
#endif
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
- CALayer *layer = [wd.window_view layer];
- layer.contentsScale = displayScale;
context_vulkan->window_resize(id, wd.size.width, wd.size.height);
}
#endif
@@ -3488,6 +3755,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
keyboard_layout_dirty = true;
displays_arrangement_dirty = true;
+ displays_scale_dirty = true;
// Register to be notified on keyboard layout changes
CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
@@ -3505,8 +3773,9 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
NSString *title;
NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
- if (nsappname == nil)
+ if (nsappname == nil) {
nsappname = [[NSProcessInfo processInfo] processName];
+ }
// Setup Dock menu
dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"];
@@ -3559,8 +3828,9 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
inMode:NSDefaultRunLoopMode
dequeue:YES];
- if (event == nil)
+ if (event == nil) {
break;
+ }
[NSApp sendEvent:event];
}
@@ -3592,34 +3862,32 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
#endif
Point2i window_position(
- (screen_get_size(0).width - p_resolution.width) / 2,
- (screen_get_size(0).height - p_resolution.height) / 2);
+ screen_get_position(0).x + (screen_get_size(0).width - p_resolution.width) / 2,
+ screen_get_position(0).y + (screen_get_size(0).height - p_resolution.height) / 2);
WindowID main_window = _create_window(p_mode, Rect2i(window_position, p_resolution));
+ ERR_FAIL_COND(main_window == INVALID_WINDOW_ID);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, main_window);
}
}
- [windows[main_window].window_object makeKeyAndOrderFront:nil];
+ show_window(MAIN_WINDOW_ID);
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ }
+#endif
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
rendering_device_vulkan = memnew(RenderingDeviceVulkan);
rendering_device_vulkan->initialize(context_vulkan);
- RasterizerRD::make_current();
+ RendererCompositorRD::make_current();
}
#endif
[NSApp activateIgnoringOtherApps:YES];
-
- /*
- visual_server = memnew(VisualServerRaster);
- if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
- }
- visual_server->init();
- */
}
DisplayServerOSX::~DisplayServerOSX() {
@@ -3632,12 +3900,19 @@ DisplayServerOSX::~DisplayServerOSX() {
}
//destroy all windows
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- [E->get().window_object setContentView:nil];
- [E->get().window_object close];
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E;) {
+ Map<WindowID, WindowData>::Element *F = E;
+ E = E->next();
+ [F->get().window_object setContentView:nil];
+ [F->get().window_object close];
}
//destroy drivers
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ }
+#endif
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
if (rendering_device_vulkan) {
@@ -3645,8 +3920,9 @@ DisplayServerOSX::~DisplayServerOSX() {
memdelete(rendering_device_vulkan);
}
- if (context_vulkan)
+ if (context_vulkan) {
memdelete(context_vulkan);
+ }
}
#endif
@@ -3654,9 +3930,6 @@ DisplayServerOSX::~DisplayServerOSX() {
CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
cursors_cache.clear();
-
- //visual_server->finish();
- //memdelete(visual_server);
}
void DisplayServerOSX::register_osx_driver() {
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 784fba75ec..337cfd6808 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,31 +30,32 @@
#include "export.h"
+#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
#include "core/io/zip_io.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "core/version.h"
#include "editor/editor_export.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "platform/osx/logo.gen.h"
-#include "string.h"
+
#include <sys/stat.h>
class EditorExportPlatformOSX : public EditorExportPlatform {
GDCLASS(EditorExportPlatformOSX, EditorExportPlatform);
- int version_code;
+ int version_code = 0;
Ref<ImageTexture> logo;
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
+ Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
@@ -66,17 +67,39 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
bool use_codesign() const { return false; }
bool use_dmg() const { return false; }
#endif
+ bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
+ String pname = p_package;
+
+ if (pname.length() == 0) {
+ if (r_error) {
+ *r_error = TTR("Identifier is missing.");
+ }
+ return false;
+ }
+
+ for (int i = 0; i < pname.length(); i++) {
+ char32_t c = pname[i];
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) {
+ if (r_error) {
+ *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
+ }
+ return false;
+ }
+ }
+
+ return true;
+ }
protected:
- virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
- virtual void get_export_options(List<ExportOption> *r_options);
+ virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override;
+ virtual void get_export_options(List<ExportOption> *r_options) override;
public:
- virtual String get_name() const { return "Mac OSX"; }
- virtual String get_os_name() const { return "OSX"; }
- virtual Ref<Texture2D> get_logo() const { return logo; }
+ virtual String get_name() const override { return "macOS"; }
+ virtual String get_os_name() const override { return "macOS"; }
+ virtual Ref<Texture2D> get_logo() const override { return logo; }
- virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
List<String> list;
if (use_dmg()) {
list.push_back("dmg");
@@ -84,17 +107,17 @@ public:
list.push_back("zip");
return list;
}
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
- virtual void get_platform_features(List<String> *r_features) {
+ virtual void get_platform_features(List<String> *r_features) override {
r_features->push_back("pc");
r_features->push_back("s3tc");
r_features->push_back("OSX");
}
- virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
}
EditorExportPlatformOSX();
@@ -122,7 +145,7 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
@@ -138,6 +161,11 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), ""));
#endif
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
@@ -326,8 +354,8 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
strnew += lines[i].replace("$name", p_binary) + "\n";
} else if (lines[i].find("$info") != -1) {
strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n";
- } else if (lines[i].find("$identifier") != -1) {
- strnew += lines[i].replace("$identifier", p_preset->get("application/identifier")) + "\n";
+ } else if (lines[i].find("$bundle_identifier") != -1) {
+ strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n";
} else if (lines[i].find("$short_version") != -1) {
strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
} else if (lines[i].find("$version") != -1) {
@@ -363,6 +391,52 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
- and then wrap it up in a DMG
**/
+Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
+#ifdef OSX_ENABLED
+ List<String> args;
+
+ args.push_back("altool");
+ args.push_back("--notarize-app");
+
+ args.push_back("--primary-bundle-id");
+ args.push_back(p_preset->get("application/bundle_identifier"));
+
+ args.push_back("--username");
+ args.push_back(p_preset->get("notarization/apple_id_name"));
+
+ args.push_back("--password");
+ args.push_back(p_preset->get("notarization/apple_id_password"));
+
+ args.push_back("--type");
+ args.push_back("osx");
+
+ if (p_preset->get("notarization/apple_team_id")) {
+ args.push_back("--asc-provider");
+ args.push_back(p_preset->get("notarization/apple_team_id"));
+ }
+
+ args.push_back("--file");
+ args.push_back(p_path);
+
+ String str;
+ Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ print_line("altool (" + p_path + "):\n" + str);
+ if (str.find("RequestUUID") == -1) {
+ EditorNode::add_io_error("altool: " + str);
+ return FAILED;
+ } else {
+ print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email.");
+ print_line(" You can check progress manually by opening a Terminal and running the following command:");
+ print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\"");
+ }
+
+#endif
+
+ return OK;
+}
+
Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
#ifdef OSX_ENABLED
List<String> args;
@@ -383,7 +457,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
PackedStringArray user_args = p_preset->get("codesign/custom_options");
for (int i = 0; i < user_args.size(); i++) {
String user_arg = user_args[i].strip_edges();
- if (!user_arg.empty()) {
+ if (!user_arg.is_empty()) {
args.push_back(user_arg);
}
}
@@ -396,10 +470,10 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
args.push_back(p_path);
String str;
- Error err = OS::get_singleton()->execute("codesign", args, true, nullptr, &str, nullptr, true);
+ Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
- print_line("codesign (" + p_path + "): " + str);
+ print_line("codesign (" + p_path + "):\n" + str);
if (str.find("no identity found") != -1) {
EditorNode::add_io_error("codesign: no identity found");
return FAILED;
@@ -430,7 +504,7 @@ Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const Strin
args.push_back(p_app_path_name);
String str;
- Error err = OS::get_singleton()->execute("hdiutil", args, true, nullptr, &str, nullptr, true);
+ Error err = OS::get_singleton()->execute("hdiutil", args, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
print_line("hdiutil returned: " + str);
@@ -498,36 +572,36 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
pkg_name = "Unnamed";
}
- String pkg_name_safe = OS::get_singleton()->get_safe_dir_name(pkg_name);
-
- Error err = OK;
- String tmp_app_path_name = "";
+ pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
- DirAccess *tmp_app_path = nullptr;
String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip";
// Create our application bundle.
- tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".app");
+ String tmp_app_dir_name = pkg_name + ".app";
+ String tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name);
print_line("Exporting to " + tmp_app_path_name);
- tmp_app_path = DirAccess::create_for_path(tmp_app_path_name);
- if (!tmp_app_path) {
+
+ Error err = OK;
+
+ DirAccessRef tmp_app_dir = DirAccess::create_for_path(tmp_app_path_name);
+ if (!tmp_app_dir) {
err = ERR_CANT_CREATE;
}
// Create our folder structure.
if (err == OK) {
print_line("Creating " + tmp_app_path_name + "/Contents/MacOS");
- err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
+ err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
}
if (err == OK) {
print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks");
- err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks");
+ err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks");
}
if (err == OK) {
print_line("Creating " + tmp_app_path_name + "/Contents/Resources");
- err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
+ err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
}
// Now process our template.
@@ -591,7 +665,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
Ref<Image> icon;
icon.instance();
icon->load(iconpath);
- if (!icon->empty()) {
+ if (!icon->is_empty()) {
_make_icon(icon, data);
}
}
@@ -604,14 +678,14 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
ret = unzGoToNextFile(src_pkg_zip);
continue; // skip
}
- file = file.replace("/data.mono.osx.64.release_debug/", "/data_" + pkg_name_safe + "/");
+ file = file.replace("/data.mono.osx.64.release_debug/", "/data_" + pkg_name + "/");
}
if (file.find("/data.mono.osx.64.release/") != -1) {
if (p_debug) {
ret = unzGoToNextFile(src_pkg_zip);
continue; // skip
}
- file = file.replace("/data.mono.osx.64.release/", "/data_" + pkg_name_safe + "/");
+ file = file.replace("/data.mono.osx.64.release/", "/data_" + pkg_name + "/");
}
print_line("ADDING: " + file + " size: " + itos(data.size()));
@@ -620,7 +694,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
// Write it into our application bundle.
file = tmp_app_path_name.plus_file(file);
if (err == OK) {
- err = tmp_app_path->make_dir_recursive(file.get_base_dir());
+ err = tmp_app_dir->make_dir_recursive(file.get_base_dir());
}
if (err == OK) {
FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
@@ -714,8 +788,19 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
}
}
+ bool noto_enabled = p_preset->get("notarization/enable");
+ if (err == OK && noto_enabled) {
+ if (ep.step("Sending archive for notarization", 4)) {
+ return ERR_SKIP;
+ }
+ err = _notarize(p_preset, p_path);
+ }
+
// Clean up temporary .app dir.
- OS::get_singleton()->move_to_trash(tmp_app_path_name);
+ tmp_app_dir->change_dir(tmp_app_path_name);
+ tmp_app_dir->erase_contents_recursive();
+ tmp_app_dir->change_dir("..");
+ tmp_app_dir->remove(tmp_app_dir_name);
}
return err;
@@ -743,11 +828,15 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String
zipfi.tmz_date.tm_hour = time.hour;
zipfi.tmz_date.tm_mday = date.day;
zipfi.tmz_date.tm_min = time.min;
- zipfi.tmz_date.tm_mon = date.month;
+ zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/
zipfi.tmz_date.tm_sec = time.sec;
zipfi.tmz_date.tm_year = date.year;
zipfi.dosDate = 0;
- zipfi.external_fa = (is_executable ? 0755 : 0644) << 16L;
+ // 0100000: regular file type
+ // 0000755: permissions rwxr-xr-x
+ // 0000644: permissions rw-r--r--
+ uint32_t _mode = (is_executable ? 0100755 : 0100644);
+ zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
zipfi.internal_fa = 0;
zipOpenNewFileInZip4(p_zip,
@@ -803,7 +892,42 @@ bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset
valid = dvalid || rvalid;
r_missing_templates = !valid;
- if (!err.empty()) {
+ String identifier = p_preset->get("application/bundle_identifier");
+ String pn_err;
+ if (!is_package_name_valid(identifier, &pn_err)) {
+ err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n";
+ valid = false;
+ }
+
+ bool sign_enabled = p_preset->get("codesign/enable");
+ if (sign_enabled) {
+ if (p_preset->get("codesign/identity") == "") {
+ err += TTR("Codesign: identity not specified.") + "\n";
+ valid = false;
+ }
+ }
+ bool noto_enabled = p_preset->get("notarization/enable");
+ if (noto_enabled) {
+ if (!sign_enabled) {
+ err += TTR("Notarization: code signing required.") + "\n";
+ valid = false;
+ }
+ bool hr_enabled = p_preset->get("codesign/hardened_runtime");
+ if (!hr_enabled) {
+ err += TTR("Notarization: hardened runtime required.") + "\n";
+ valid = false;
+ }
+ if (p_preset->get("notarization/apple_id_name") == "") {
+ err += TTR("Notarization: Apple ID name not specified.") + "\n";
+ valid = false;
+ }
+ if (p_preset->get("notarization/apple_id_password") == "") {
+ err += TTR("Notarization: Apple ID password not specified.") + "\n";
+ valid = false;
+ }
+ }
+
+ if (!err.is_empty()) {
r_error = err;
}
return valid;
diff --git a/platform/osx/export/export.h b/platform/osx/export/export.h
index 4ddcec09fb..f8cf41c0e7 100644
--- a/platform/osx/export/export.h
+++ b/platform/osx/export/export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm
index 93d0d6168c..7e7dbf6afb 100644
--- a/platform/osx/godot_main_osx.mm
+++ b/platform/osx/godot_main_osx.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,7 +37,7 @@
int main(int argc, char **argv) {
#if defined(VULKAN_ENABLED)
- //MoltenVK - enable full component swizzling support
+ // MoltenVK - enable full component swizzling support
setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
#endif
@@ -60,6 +60,9 @@ int main(int argc, char **argv) {
OS_OSX os;
Error err;
+ // We must override main when testing is enabled
+ TEST_MAIN_OVERRIDE
+
if (os.open_with_filename != "") {
char *argv_c = (char *)malloc(os.open_with_filename.utf8().size());
memcpy(argv_c, os.open_with_filename.utf8().get_data(), os.open_with_filename.utf8().size());
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index d342d30097..0b6a0e20a6 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,14 +37,6 @@
static JoypadOSX *self = nullptr;
joypad::joypad() {
- device_ref = nullptr;
- ff_device = nullptr;
- ff_axes = nullptr;
- ff_directions = nullptr;
- ffservice = 0;
- ff_timestamp = 0;
- id = 0;
-
ff_constant_force.lMagnitude = 10000;
ff_effect.dwDuration = 0;
ff_effect.dwSamplePeriod = 0;
@@ -311,9 +303,16 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
if (refCF) {
CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &product_id);
}
+
+ int version = 0;
+ refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVersionNumberKey));
+ if (refCF) {
+ CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &version);
+ }
+
if (vendor && product_id) {
char uid[128];
- sprintf(uid, "%04x%08x%04x%08x", OSSwapHostToBigInt32(vendor), 0, OSSwapHostToBigInt32(product_id), 0);
+ sprintf(uid, "%08x%08x%08x%08x", OSSwapHostToBigInt32(3), OSSwapHostToBigInt32(vendor), OSSwapHostToBigInt32(product_id), OSSwapHostToBigInt32(version));
input->joy_connection_changed(id, true, name, uid);
} else {
//bluetooth device
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index dc238e68e4..bf7e8949df 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -46,10 +46,10 @@ struct rec_element {
IOHIDElementRef ref;
IOHIDElementCookie cookie;
- uint32_t usage;
+ uint32_t usage = 0;
- int min;
- int max;
+ int min = 0;
+ int max = 0;
struct Comparator {
bool operator()(const rec_element p_a, const rec_element p_b) const { return p_a.usage < p_b.usage; }
@@ -57,22 +57,22 @@ struct rec_element {
};
struct joypad {
- IOHIDDeviceRef device_ref;
+ IOHIDDeviceRef device_ref = nullptr;
Vector<rec_element> axis_elements;
Vector<rec_element> button_elements;
Vector<rec_element> hat_elements;
- int id;
+ int id = 0;
- io_service_t ffservice; /* Interface for force feedback, 0 = no ff */
+ io_service_t ffservice = 0; /* Interface for force feedback, 0 = no ff */
FFCONSTANTFORCE ff_constant_force;
FFDeviceObjectReference ff_device;
FFEffectObjectReference ff_object;
- uint64_t ff_timestamp;
- LONG *ff_directions;
+ uint64_t ff_timestamp = 0;
+ LONG *ff_directions = nullptr;
FFEFFECT ff_effect;
- DWORD *ff_axes;
+ DWORD *ff_axes = nullptr;
void add_hid_elements(CFArrayRef p_array);
void add_hid_element(IOHIDElementRef p_element);
diff --git a/platform/osx/logo.png b/platform/osx/logo.png
index 834bbf3ba6..b5a660b165 100644
--- a/platform/osx/logo.png
+++ b/platform/osx/logo.png
Binary files differ
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 9204a145bf..d57940775d 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -40,11 +40,11 @@
#include "servers/audio_server.h"
class OS_OSX : public OS_Unix {
- virtual void delete_main_loop();
+ virtual void delete_main_loop() override;
bool force_quit;
- JoypadOSX *joypad_osx;
+ JoypadOSX *joypad_osx = nullptr;
#ifdef COREAUDIO_ENABLED
AudioDriverCoreAudio audio_driver;
@@ -61,45 +61,45 @@ public:
String open_with_filename;
protected:
- virtual void initialize_core();
- virtual void initialize();
- virtual void finalize();
+ virtual void initialize_core() override;
+ virtual void initialize() override;
+ virtual void finalize() override;
- virtual void initialize_joypads();
+ virtual void initialize_joypads() override;
- virtual void set_main_loop(MainLoop *p_main_loop);
+ virtual void set_main_loop(MainLoop *p_main_loop) override;
public:
- virtual String get_name() const;
+ virtual String get_name() const override;
- virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
+ virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override;
- virtual MainLoop *get_main_loop() const;
+ virtual MainLoop *get_main_loop() const override;
- virtual String get_config_path() const;
- virtual String get_data_path() const;
- virtual String get_cache_path() const;
- virtual String get_bundle_resource_dir() const;
- virtual String get_godot_dir_name() const;
+ virtual String get_config_path() const override;
+ virtual String get_data_path() const override;
+ virtual String get_cache_path() const override;
+ virtual String get_bundle_resource_dir() const override;
+ virtual String get_godot_dir_name() const override;
- virtual String get_system_dir(SystemDir p_dir) const;
+ virtual String get_system_dir(SystemDir p_dir) const override;
- Error shell_open(String p_uri);
+ Error shell_open(String p_uri) override;
- String get_locale() const;
+ String get_locale() const override;
- virtual String get_executable_path() const;
+ virtual String get_executable_path() const override;
- virtual String get_unique_id() const; //++
+ virtual String get_unique_id() const override; //++
- virtual bool _check_internal_feature_support(const String &p_feature);
+ virtual bool _check_internal_feature_support(const String &p_feature) override;
void run();
- void disable_crash_handler();
- bool is_disable_crash_handler() const;
+ virtual void disable_crash_handler() override;
+ virtual bool is_disable_crash_handler() const override;
- virtual Error move_to_trash(const String &p_path);
+ virtual Error move_to_trash(const String &p_path) override;
OS_OSX();
};
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index dba96ccfcd..7b5daf5bfb 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -99,7 +99,7 @@ public:
String OS_OSX::get_unique_id() const {
static String serial_number;
- if (serial_number.empty()) {
+ if (serial_number.is_empty()) {
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
CFStringRef serialNumberAsCFString = NULL;
if (platformExpert) {
@@ -145,7 +145,9 @@ void OS_OSX::finalize() {
delete_main_loop();
- memdelete(joypad_osx);
+ if (joypad_osx) {
+ memdelete(joypad_osx);
+ }
}
void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
@@ -272,13 +274,19 @@ String OS_OSX::get_system_dir(SystemDir p_dir) const {
}
Error OS_OSX::shell_open(String p_uri) {
- [[NSWorkspace sharedWorkspace] openURL:[[NSURL alloc] initWithString:[[NSString stringWithUTF8String:p_uri.utf8().get_data()] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]]];
+ NSString *string = [NSString stringWithUTF8String:p_uri.utf8().get_data()];
+ NSURL *uri = [[NSURL alloc] initWithString:string];
+ // Escape special characters in filenames
+ if (!uri || !uri.scheme || [uri.scheme isEqual:@"file"]) {
+ uri = [[NSURL alloc] initWithString:[string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]];
+ }
+ [[NSWorkspace sharedWorkspace] openURL:uri];
return OK;
}
String OS_OSX::get_locale() const {
NSString *locale_code = [[NSLocale preferredLanguages] objectAtIndex:0];
- return [locale_code UTF8String];
+ return String([locale_code UTF8String]).replace("-", "_");
}
String OS_OSX::get_executable_path() const {
@@ -304,7 +312,7 @@ void OS_OSX::run() {
if (!main_loop)
return;
- main_loop->init();
+ main_loop->initialize();
bool quit = false;
while (!force_quit && !quit) {
@@ -314,14 +322,14 @@ void OS_OSX::run() {
}
joypad_osx->process_joypads();
- if (Main::iteration() == true) {
+ if (Main::iteration()) {
quit = true;
}
} @catch (NSException *exception) {
ERR_PRINT("NSException: " + String([exception reason].UTF8String));
}
};
- main_loop->finish();
+ main_loop->finalize();
}
Error OS_OSX::move_to_trash(const String &p_path) {
diff --git a/platform/osx/platform_config.h b/platform/osx/platform_config.h
index 155f37ed55..2d0fd872dc 100644
--- a/platform/osx/platform_config.h
+++ b/platform/osx/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,5 +30,4 @@
#include <alloca.h>
-#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h"
#define PTHREAD_RENAME_SELF
diff --git a/platform/osx/vulkan_context_osx.h b/platform/osx/vulkan_context_osx.h
index e996f176a9..8b6a75adfb 100644
--- a/platform/osx/vulkan_context_osx.h
+++ b/platform/osx/vulkan_context_osx.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/osx/vulkan_context_osx.mm b/platform/osx/vulkan_context_osx.mm
index ec8745ff01..75a4fc990f 100644
--- a/platform/osx/vulkan_context_osx.mm
+++ b/platform/osx/vulkan_context_osx.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/register_platform_apis.h b/platform/register_platform_apis.h
index 6b962f5d91..4cf84b321f 100644
--- a/platform/register_platform_apis.h
+++ b/platform/register_platform_apis.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/server/detect.py b/platform/server/detect.py
index a73810cdf4..5be7e81e7a 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -21,7 +21,6 @@ def get_program_suffix():
def can_build():
-
if os.name != "posix":
return False
@@ -33,20 +32,19 @@ def get_opts():
return [
BoolVariable("use_llvm", "Use the LLVM compiler", False),
- BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", False),
+ BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True),
BoolVariable("use_coverage", "Test Godot coverage", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False),
BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False),
BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False),
- EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
+ BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False),
]
def get_flags():
-
return []
@@ -60,9 +58,7 @@ def configure(env):
else: # optimize for size
env.Prepend(CCFLAGS=["-Os"])
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
+ if env["debug_symbols"]:
env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "release_debug":
@@ -72,14 +68,12 @@ def configure(env):
env.Prepend(CCFLAGS=["-Os"])
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
+ if env["debug_symbols"]:
env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "debug":
env.Prepend(CCFLAGS=["-g3"])
- env.Prepend(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(LINKFLAGS=["-rdynamic"])
## Architecture
@@ -98,9 +92,8 @@ def configure(env):
if "clang++" not in os.path.basename(env["CXX"]):
env["CC"] = "clang"
env["CXX"] = "clang++"
- env["LINK"] = "clang++"
- env.Append(CPPDEFINES=["TYPED_METHOD_BIND"])
env.extra_suffix = ".llvm" + env.extra_suffix
+ env.Append(LIBS=["atomic"])
if env["use_coverage"]:
env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
@@ -144,14 +137,31 @@ def configure(env):
# freetype depends on libpng and zlib, so bundling one of them while keeping others
# as shared libraries leads to weird issues
- if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]:
+ if (
+ env["builtin_freetype"]
+ or env["builtin_libpng"]
+ or env["builtin_zlib"]
+ or env["builtin_graphite"]
+ or env["builtin_harfbuzz"]
+ ):
env["builtin_freetype"] = True
env["builtin_libpng"] = True
env["builtin_zlib"] = True
+ env["builtin_graphite"] = True
+ env["builtin_harfbuzz"] = True
if not env["builtin_freetype"]:
env.ParseConfig("pkg-config freetype2 --cflags --libs")
+ if not env["builtin_graphite"]:
+ env.ParseConfig("pkg-config graphite2 --cflags --libs")
+
+ if not env["builtin_icu"]:
+ env.ParseConfig("pkg-config icu-uc --cflags --libs")
+
+ if not env["builtin_harfbuzz"]:
+ env.ParseConfig("pkg-config harfbuzz harfbuzz-icu --cflags --libs")
+
if not env["builtin_libpng"]:
env.ParseConfig("pkg-config libpng16 --cflags --libs")
@@ -239,7 +249,17 @@ def configure(env):
env.Append(CPPDEFINES=["SERVER_ENABLED", "UNIX_ENABLED"])
if platform.system() == "Darwin":
- env.Append(LINKFLAGS=["-framework", "Cocoa", "-framework", "Carbon", "-lz", "-framework", "IOKit"])
+ env.Append(
+ LINKFLAGS=[
+ "-framework",
+ "Cocoa",
+ "-framework",
+ "Carbon",
+ "-lz",
+ "-framework",
+ "IOKit",
+ ]
+ )
env.Append(LIBS=["pthread"])
diff --git a/platform/server/godot_server.cpp b/platform/server/godot_server.cpp
index 32bd943ac3..1ced95fcbc 100644
--- a/platform/server/godot_server.cpp
+++ b/platform/server/godot_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -34,6 +34,9 @@
int main(int argc, char *argv[]) {
OS_Server os;
+ // We must override main when testing is enabled
+ TEST_MAIN_OVERRIDE
+
Error err = Main::setup(argv[0], argc - 1, &argv[1]);
if (err != OK)
return 255;
diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp
index fbe526ef6d..852ec7c4ef 100644
--- a/platform/server/os_server.cpp
+++ b/platform/server/os_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,10 +30,10 @@
#include "os_server.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include "drivers/dummy/rasterizer_dummy.h"
#include "drivers/dummy/texture_loader_dummy.h"
-#include "servers/rendering/rendering_server_raster.h"
+#include "servers/rendering/rendering_server_default.h"
#include "main/main.h"
@@ -49,14 +49,6 @@ const char *OS_Server::get_video_driver_name(int p_driver) const {
return "Dummy";
}
-int OS_Server::get_audio_driver_count() const {
- return 1;
-}
-
-const char *OS_Server::get_audio_driver_name(int p_driver) const {
- return "Dummy";
-}
-
int OS_Server::get_current_video_driver() const {
return video_driver_index;
}
@@ -76,7 +68,7 @@ Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int
video_driver_index = p_video_driver; // unused in server platform, but should still be initialized
- rendering_server = memnew(RenderingServerRaster);
+ rendering_server = memnew(RenderingServerDefault);
rendering_server->init();
AudioDriverManager::initialize(p_audio_driver);
@@ -158,10 +150,6 @@ void OS_Server::set_main_loop(MainLoop *p_main_loop) {
input->set_main_loop(p_main_loop);
}
-bool OS_Server::can_draw() const {
- return false; //can never draw
-};
-
String OS_Server::get_name() const {
return "Server";
}
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
index 06ea483fd4..61025fa14b 100644
--- a/platform/server/os_server.h
+++ b/platform/server/os_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -41,28 +41,28 @@
#include "platform/x11/crash_handler_linuxbsd.h"
#endif
#include "servers/audio_server.h"
-#include "servers/rendering/rasterizer.h"
+#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
#undef CursorShape
class OS_Server : public OS_Unix {
- RenderingServer *rendering_server;
+ RenderingServer *rendering_server = nullptr;
VideoMode current_videomode;
List<String> args;
- MainLoop *main_loop;
+ MainLoop *main_loop = nullptr;
- bool grab;
+ bool grab = false;
virtual void delete_main_loop();
- bool force_quit;
+ bool force_quit = false;
- InputDefault *input;
+ InputDefault *input = nullptr;
CrashHandler crash_handler;
- int video_driver_index;
+ int video_driver_index = 0;
Ref<ResourceFormatDummyTexture> resource_loader_dummy;
@@ -70,8 +70,6 @@ protected:
virtual int get_video_driver_count() const;
virtual const char *get_video_driver_name(int p_driver) const;
virtual int get_current_video_driver() const;
- virtual int get_audio_driver_count() const;
- virtual const char *get_audio_driver_name(int p_driver) const;
virtual void initialize_core();
virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
@@ -91,8 +89,6 @@ public:
virtual MainLoop *get_main_loop() const;
- virtual bool can_draw() const;
-
virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0);
virtual VideoMode get_video_mode(int p_screen = 0) const;
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const;
diff --git a/platform/server/platform_config.h b/platform/server/platform_config.h
index bdff93f02b..32a19d811b 100644
--- a/platform/server/platform_config.h
+++ b/platform/server/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,10 +31,19 @@
#if defined(__linux__) || defined(__APPLE__)
#include <alloca.h>
#endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__)
-#include <stdlib.h>
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+#include <stdlib.h> // alloca
+// FreeBSD and OpenBSD use pthread_set_name_np, while other platforms,
+// include NetBSD, use pthread_setname_np. NetBSD's version however requires
+// a different format, we handle this directly in thread_posix.
+#ifdef __NetBSD__
+#define PTHREAD_NETBSD_SET_NAME
+#else
#define PTHREAD_BSD_SET_NAME
#endif
+#endif
+
#ifdef __APPLE__
#define PTHREAD_RENAME_SELF
#endif
diff --git a/platform/uwp/app.cpp b/platform/uwp/app.cpp
index 6090d13854..dc4238bdd4 100644
--- a/platform/uwp/app.cpp
+++ b/platform/uwp/app.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/uwp/app.h b/platform/uwp/app.h
index 5cffe378b1..0b02527dae 100644
--- a/platform/uwp/app.h
+++ b/platform/uwp/app.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/uwp/context_egl_uwp.cpp b/platform/uwp/context_egl_uwp.cpp
index 2da6c5897a..bb2a14e9fc 100644
--- a/platform/uwp/context_egl_uwp.cpp
+++ b/platform/uwp/context_egl_uwp.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -204,7 +204,8 @@ ContextEGL_UWP::ContextEGL_UWP(CoreWindow ^ p_window, Driver p_driver) :
mEglContext(EGL_NO_CONTEXT),
mEglSurface(EGL_NO_SURFACE),
driver(p_driver),
- window(p_window) {}
+ window(p_window),
+ vsync(false) {}
ContextEGL_UWP::~ContextEGL_UWP() {
cleanup();
diff --git a/platform/uwp/context_egl_uwp.h b/platform/uwp/context_egl_uwp.h
index 6f333b8e6a..974faa3ac7 100644
--- a/platform/uwp/context_egl_uwp.h
+++ b/platform/uwp/context_egl_uwp.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,7 +35,7 @@
#include <EGL/egl.h>
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/os/os.h"
using namespace Windows::UI::Core;
diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py
index 669bfe6814..fda8fdec66 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -30,7 +30,6 @@ def get_opts():
def get_flags():
-
return [
("tools", False),
("xaudio2", True),
@@ -39,7 +38,6 @@ def get_flags():
def configure(env):
-
env.msvc = True
if env["bits"] != "default":
@@ -65,12 +63,14 @@ def configure(env):
env.Append(CCFLAGS=["/MD"])
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
+ env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
elif env["target"] == "debug":
env.Append(CCFLAGS=["/Zi"])
env.Append(CCFLAGS=["/MDd"])
- env.Append(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
+ env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
env.Append(LINKFLAGS=["/DEBUG"])
## Compiler configuration
@@ -78,6 +78,9 @@ def configure(env):
env["ENV"] = os.environ
vc_base_path = os.environ["VCTOOLSINSTALLDIR"] if "VCTOOLSINSTALLDIR" in os.environ else os.environ["VCINSTALLDIR"]
+ # Force to use Unicode encoding
+ env.AppendUnique(CCFLAGS=["/utf-8"])
+
# ANGLE
angle_root = os.getenv("ANGLE_SRC_PATH")
env.Prepend(CPPPATH=[angle_root + "/include"])
@@ -120,7 +123,9 @@ def configure(env):
print("Compiled program architecture will be a x86 executable. (forcing bits=32).")
else:
print(
- "Failed to detect MSVC compiler architecture version... Defaulting to 32-bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup."
+ "Failed to detect MSVC compiler architecture version... Defaulting to 32-bit executable settings"
+ " (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture"
+ " this build is compiled for. You should check your settings/compilation setup."
)
env["bits"] = "32"
@@ -160,7 +165,10 @@ def configure(env):
env.Append(CPPFLAGS=["/AI", vc_base_path + "lib/x86/store/references"])
env.Append(
- CCFLAGS='/FS /MP /GS /wd"4453" /wd"28204" /wd"4291" /Zc:wchar_t /Gm- /fp:precise /errorReport:prompt /WX- /Zc:forScope /Gd /EHsc /nologo'.split()
+ CCFLAGS=(
+ '/FS /MP /GS /wd"4453" /wd"28204" /wd"4291" /Zc:wchar_t /Gm- /fp:precise /errorReport:prompt /WX-'
+ " /Zc:forScope /Gd /EHsc /nologo".split()
+ )
)
env.Append(CPPDEFINES=["_UNICODE", "UNICODE", ("WINAPI_FAMILY", "WINAPI_FAMILY_APP")])
env.Append(CXXFLAGS=["/ZW"])
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index db42908f89..1aad2bfa1a 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,14 +29,15 @@
/*************************************************************************/
#include "export.h"
-#include "core/bind/core_bind.h"
+
+#include "core/config/project_settings.h"
+#include "core/core_bind.h"
#include "core/crypto/crypto_core.h"
#include "core/io/marshalls.h"
#include "core/io/zip_io.h"
-#include "core/object.h"
+#include "core/object/class_db.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
-#include "core/project_settings.h"
#include "core/version.h"
#include "editor/editor_export.h"
#include "editor/editor_node.h"
@@ -107,7 +108,7 @@ class AppxPackager {
struct BlockHash {
String base64_hash;
- size_t compressed_size;
+ size_t compressed_size = 0;
};
struct FileMeta {
@@ -119,12 +120,10 @@ class AppxPackager {
Vector<BlockHash> hashes;
uLong file_crc32 = 0;
ZPOS64_T zip_offset = 0;
-
- FileMeta() {}
};
String progress_task;
- FileAccess *package;
+ FileAccess *package = nullptr;
Set<String> mime_types;
@@ -249,7 +248,7 @@ void AppxPackager::make_content_types(const String &p_path) {
Map<String, String> types;
for (int i = 0; i < file_metadata.size(); i++) {
- String ext = file_metadata[i].name.get_extension();
+ String ext = file_metadata[i].name.get_extension().to_lower();
if (types.has(ext)) {
continue;
@@ -642,7 +641,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
};
bool _valid_resource_name(const String &p_name) const {
- if (p_name.empty()) {
+ if (p_name.is_empty()) {
return false;
}
if (p_name.ends_with(".")) {
@@ -688,7 +687,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
}
bool _valid_bgcolor(const String &p_color) const {
- if (p_color.empty()) {
+ if (p_color.is_empty()) {
return true;
}
if (p_color.begins_with("#") && p_color.is_valid_html_color()) {
@@ -743,23 +742,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
// TODO: Add resource creation or image rescaling to enable other scales:
// 1.25, 1.5, 2.0
- real_t scales[] = { 1.0 };
- bool valid_w = false;
- bool valid_h = false;
-
- for (int i = 0; i < 1; i++) {
- int w = ceil(p_width * scales[i]);
- int h = ceil(p_height * scales[i]);
-
- if (w == p_image->get_width()) {
- valid_w = true;
- }
- if (h == p_image->get_height()) {
- valid_h = true;
- }
- }
-
- return valid_w && valid_h;
+ return p_width == p_image->get_width() && p_height == p_image->get_height();
}
Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const {
@@ -777,10 +760,10 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
result = result.replace("$version_string$", version);
Platform arch = (Platform)(int)p_preset->get("architecture/target");
- String architecture = arch == ARM ? "arm" : arch == X86 ? "x86" : "x64";
+ String architecture = arch == ARM ? "arm" : (arch == X86 ? "x86" : "x64");
result = result.replace("$architecture$", architecture);
- result = result.replace("$display_name$", String(p_preset->get("package/display_name")).empty() ? (String)ProjectSettings::get_singleton()->get("application/config/name") : String(p_preset->get("package/display_name")));
+ result = result.replace("$display_name$", String(p_preset->get("package/display_name")).is_empty() ? (String)ProjectSettings::get_singleton()->get("application/config/name") : String(p_preset->get("package/display_name")));
result = result.replace("$publisher_display_name$", p_preset->get("package/publisher_display_name"));
result = result.replace("$app_description$", p_preset->get("package/description"));
@@ -799,7 +782,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
}
String show_name_on_tiles = "";
- if (!name_on_tiles.empty()) {
+ if (!name_on_tiles.is_empty()) {
show_name_on_tiles = "<uap:ShowNameOnTiles>\n" + name_on_tiles + " </uap:ShowNameOnTiles>";
}
@@ -820,7 +803,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
}
String rotation_preference = "";
- if (!rotations.empty()) {
+ if (!rotations.is_empty()) {
rotation_preference = "<uap:InitialRotationPreference>\n" + rotations + " </uap:InitialRotationPreference>";
}
@@ -854,7 +837,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
}
String capabilities_string = "<Capabilities />";
- if (!capabilities_elements.empty()) {
+ if (!capabilities_elements.is_empty()) {
capabilities_string = "<Capabilities>\n" + capabilities_elements + " </Capabilities>";
}
@@ -977,7 +960,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
return true;
}
- static Error save_appx_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
+ static Error save_appx_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
AppxPackager *packager = (AppxPackager *)p_userdata;
String dst_path = p_path.replace_first("res://", "game/");
@@ -985,24 +968,24 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
}
public:
- virtual String get_name() const {
- return "Windows Universal";
+ virtual String get_name() const override {
+ return "UWP";
}
- virtual String get_os_name() const {
+ virtual String get_os_name() const override {
return "UWP";
}
- virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
List<String> list;
list.push_back("appx");
return list;
}
- virtual Ref<Texture2D> get_logo() const {
+ virtual Ref<Texture2D> get_logo() const override {
return logo;
}
- virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
+ virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override {
r_features->push_back("s3tc");
r_features->push_back("etc");
switch ((int)p_preset->get("architecture/target")) {
@@ -1018,7 +1001,10 @@ public:
}
}
- virtual void get_export_options(List<ExportOption> *r_options) {
+ virtual void get_export_options(List<ExportOption> *r_options) override {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "arm,x86,x64"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
@@ -1060,9 +1046,6 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square310x310"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
-
// Capabilities
const char **basic = uwp_capabilities;
while (*basic) {
@@ -1083,7 +1066,7 @@ public:
}
}
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override {
String err;
bool valid = false;
@@ -1193,10 +1176,10 @@ public:
return valid;
}
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override {
String src_appx;
- EditorProgress ep("export", "Exporting for Windows Universal", 7, true);
+ EditorProgress ep("export", "Exporting for UWP", 7, true);
if (p_debug) {
src_appx = p_preset->get("custom_template/debug");
@@ -1428,18 +1411,18 @@ public:
args.push_back(cert_pass);
args.push_back(p_path);
- OS::get_singleton()->execute(signtool_path, args, true);
+ OS::get_singleton()->execute(signtool_path, args);
#endif // WINDOWS_ENABLED
return OK;
}
- virtual void get_platform_features(List<String> *r_features) {
+ virtual void get_platform_features(List<String> *r_features) override {
r_features->push_back("pc");
r_features->push_back("UWP");
}
- virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) {
+ virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override {
}
EditorExportPlatformUWP() {
diff --git a/platform/uwp/export/export.h b/platform/uwp/export/export.h
index 1a1555d8ee..bc23cad38c 100644
--- a/platform/uwp/export/export.h
+++ b/platform/uwp/export/export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/uwp/joypad_uwp.cpp b/platform/uwp/joypad_uwp.cpp
index 4fdfde9673..5da90db49d 100644
--- a/platform/uwp/joypad_uwp.cpp
+++ b/platform/uwp/joypad_uwp.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h
index 13f246a438..5df4a211ac 100644
--- a/platform/uwp/joypad_uwp.h
+++ b/platform/uwp/joypad_uwp.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -58,21 +58,12 @@ private:
struct ControllerDevice {
Windows::Gaming::Input::IGameController ^ controller_reference;
- int id;
- bool connected;
- ControllerType type;
- float ff_timestamp;
- float ff_end_timestamp;
- bool vibrating;
-
- ControllerDevice() {
- id = -1;
- connected = false;
- type = ControllerType::GAMEPAD_CONTROLLER;
- ff_timestamp = 0.0f;
- ff_end_timestamp = 0.0f;
- vibrating = false;
- }
+ int id = -1;
+ bool connected = false;
+ ControllerType type = ControllerType::GAMEPAD_CONTROLLER;
+ float ff_timestamp = 0;
+ float ff_end_timestamp = 0;
+ bool vibrating = false;
};
ControllerDevice controllers[MAX_CONTROLLERS];
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 7bd67d3726..fa97948395 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,21 +33,17 @@
#include "os_uwp.h"
+#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
-#include "core/project_settings.h"
-#include "drivers/gles2/rasterizer_gles2.h"
#include "drivers/unix/ip_unix.h"
#include "drivers/windows/dir_access_windows.h"
#include "drivers/windows/file_access_windows.h"
#include "drivers/windows/mutex_windows.h"
-#include "drivers/windows/rw_lock_windows.h"
#include "drivers/windows/semaphore_windows.h"
#include "main/main.h"
#include "platform/windows/windows_terminal_logger.h"
#include "servers/audio_server.h"
-#include "servers/rendering/rendering_server_raster.h"
-#include "servers/rendering/rendering_server_wrap_mt.h"
-#include "thread_uwp.h"
+#include "servers/rendering/rendering_server_default.h"
#include <ppltasks.h>
#include <wrl.h>
@@ -132,9 +128,6 @@ void OS_UWP::initialize_core() {
//RedirectIOToConsole();
- ThreadUWP::make_default();
- RWLockWindows::make_default();
-
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
@@ -157,10 +150,6 @@ void OS_UWP::initialize_core() {
cursor_shape = CURSOR_ARROW;
}
-bool OS_UWP::can_draw() const {
- return !minimized;
-};
-
void OS_UWP::set_window(Windows::UI::Core::CoreWindow ^ p_window) {
window = p_window;
}
@@ -241,7 +230,7 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
set_video_mode(vm);
- rendering_server = memnew(RenderingServerRaster);
+ rendering_server = memnew(RenderingServerDefault);
// FIXME: Reimplement threaded rendering
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
rendering_server = memnew(RenderingServerWrapMT(rendering_server, false));
@@ -297,7 +286,7 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
void OS_UWP::set_clipboard(const String &p_text) {
DataPackage ^ clip = ref new DataPackage();
clip->RequestedOperation = DataPackageOperation::Copy;
- clip->SetText(ref new Platform::String((const wchar_t *)p_text.c_str()));
+ clip->SetText(ref new Platform::String((LPCWSTR)(p_text.utf16().get_data())));
Clipboard::SetContent(clip);
};
@@ -347,8 +336,8 @@ void OS_UWP::finalize_core() {
}
void OS_UWP::alert(const String &p_alert, const String &p_title) {
- Platform::String ^ alert = ref new Platform::String(p_alert.c_str());
- Platform::String ^ title = ref new Platform::String(p_title.c_str());
+ Platform::String ^ alert = ref new Platform::String((LPCWSTR)(p_alert.utf16().get_data()));
+ Platform::String ^ title = ref new Platform::String((LPCWSTR)(p_title.utf16().get_data()));
MessageDialog ^ msg = ref new MessageDialog(alert, title);
@@ -535,11 +524,28 @@ void OS_UWP::delay_usec(uint32_t p_usec) const {
uint64_t OS_UWP::get_ticks_usec() const {
uint64_t ticks;
- uint64_t time;
+
// This is the number of clock ticks since start
QueryPerformanceCounter((LARGE_INTEGER *)&ticks);
+
// Divide by frequency to get the time in seconds
- time = ticks * 1000000L / ticks_per_second;
+ // original calculation shown below is subject to overflow
+ // with high ticks_per_second and a number of days since the last reboot.
+ // time = ticks * 1000000L / ticks_per_second;
+
+ // we can prevent this by either using 128 bit math
+ // or separating into a calculation for seconds, and the fraction
+ uint64_t seconds = ticks / ticks_per_second;
+
+ // compiler will optimize these two into one divide
+ uint64_t leftover = ticks % ticks_per_second;
+
+ // remainder
+ uint64_t time = (leftover * 1000000L) / ticks_per_second;
+
+ // seconds
+ time += seconds * 1000000L;
+
// Subtract the time at game start to get
// the time since the game started
time -= ticks_start;
@@ -626,7 +632,11 @@ void OS_UWP::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
// TODO
}
-Error OS_UWP::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+Error OS_UWP::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+ return FAILED;
+};
+
+Error OS_UWP::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
return FAILED;
};
@@ -698,7 +708,7 @@ bool OS_UWP::has_virtual_keyboard() const {
return UIViewSettings::GetForCurrentView()->UserInteractionMode == UserInteractionMode::Touch;
}
-void OS_UWP::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
+void OS_UWP::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
InputPane ^ pane = InputPane::GetForCurrentView();
pane->TryShow();
}
@@ -722,7 +732,7 @@ static String format_error_message(DWORD id) {
Error OS_UWP::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
String full_path = "game/" + p_path;
- p_library_handle = (void *)LoadPackagedLibrary(full_path.c_str(), 0);
+ p_library_handle = (void *)LoadPackagedLibrary((LPCWSTR)(full_path.utf16().get_data()), 0);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + full_path + ", error: " + format_error_message(GetLastError()) + ".");
return OK;
}
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 95359c68b0..a4d3d6d52a 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,11 +35,11 @@
#include "core/input/input.h"
#include "core/math/transform_2d.h"
#include "core/os/os.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
#include "drivers/xaudio2/audio_driver_xaudio2.h"
#include "joypad_uwp.h"
#include "servers/audio_server.h"
-#include "servers/rendering/rasterizer.h"
+#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
#include <fcntl.h>
@@ -55,13 +55,13 @@ public:
CHAR_EVENT_MESSAGE
};
- bool alt, shift, control;
- MessageType type;
- bool pressed;
- unsigned int keycode;
- unsigned int physical_keycode;
- unsigned int unicode;
- bool echo;
+ bool alt = false, shift = false, control = false;
+ MessageType type = KEY_EVENT_MESSAGE;
+ bool pressed = false;
+ unsigned int keycode = 0;
+ unsigned int physical_keycode = 0;
+ unsigned int unicode = 0;
+ bool echo = false;
CorePhysicalKeyStatus status;
};
@@ -194,13 +194,13 @@ public:
virtual TimeZoneInfo get_time_zone_info() const;
virtual uint64_t get_unix_time() const;
- virtual bool can_draw() const;
virtual Error set_cwd(const String &p_cwd);
virtual void delay_usec(uint32_t p_usec) const;
virtual uint64_t get_ticks_usec() const;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr);
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr);
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr);
virtual Error kill(const ProcessID &p_pid);
virtual bool has_environment(const String &p_var) const;
@@ -234,7 +234,7 @@ public:
virtual bool has_touchscreen_ui_hint() const;
virtual bool has_virtual_keyboard() const;
- virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1);
+ virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1);
virtual void hide_virtual_keyboard();
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
@@ -245,7 +245,7 @@ public:
void run();
- virtual bool get_swap_ok_cancel() { return true; }
+ virtual bool get_swap_cancel_ok() { return true; }
void input_event(const Ref<InputEvent> &p_event);
diff --git a/platform/uwp/platform_config.h b/platform/uwp/platform_config.h
index 09a16614e0..481f583f6f 100644
--- a/platform/uwp/platform_config.h
+++ b/platform/uwp/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/uwp/thread_uwp.cpp b/platform/uwp/thread_uwp.cpp
deleted file mode 100644
index 8e7bb144be..0000000000
--- a/platform/uwp/thread_uwp.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*************************************************************************/
-/* thread_uwp.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "thread_uwp.h"
-
-#include "core/os/memory.h"
-
-Thread *ThreadUWP::create_func_uwp(ThreadCreateCallback p_callback, void *p_user, const Settings &) {
- ThreadUWP *thread = memnew(ThreadUWP);
-
- std::thread new_thread(p_callback, p_user);
- std::swap(thread->thread, new_thread);
-
- return thread;
-};
-
-Thread::ID ThreadUWP::get_thread_id_func_uwp() {
- return std::hash<std::thread::id>()(std::this_thread::get_id());
-};
-
-void ThreadUWP::wait_to_finish_func_uwp(Thread *p_thread) {
- ThreadUWP *tp = static_cast<ThreadUWP *>(p_thread);
- tp->thread.join();
-};
-
-Thread::ID ThreadUWP::get_id() const {
- return std::hash<std::thread::id>()(thread.get_id());
-};
-
-void ThreadUWP::make_default() {
- create_func = create_func_uwp;
- get_thread_id_func = get_thread_id_func_uwp;
- wait_to_finish_func = wait_to_finish_func_uwp;
-};
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index daffe59f34..47d8e14680 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -26,11 +26,11 @@ prog = env.add_program("#bin/godot", common_win + res_obj, PROGSUFFIX=env["PROGS
# Microsoft Visual Studio Project Generation
if env["vsproj"]:
- env.vs_srcs = env.vs_srcs + ["platform/windows/" + res_file]
- env.vs_srcs = env.vs_srcs + ["platform/windows/godot.natvis"]
+ env.vs_srcs += ["platform/windows/" + res_file]
+ env.vs_srcs += ["platform/windows/godot.natvis"]
for x in common_win:
- env.vs_srcs = env.vs_srcs + ["platform/windows/" + str(x)]
+ env.vs_srcs += ["platform/windows/" + str(x)]
if not os.getenv("VCINSTALLDIR"):
- if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]:
+ if env["debug_symbols"] and env["separate_debug_symbols"]:
env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw))
diff --git a/platform/windows/context_gl_windows.cpp b/platform/windows/context_gl_windows.cpp
index 1c32639a38..207b0a1168 100644
--- a/platform/windows/context_gl_windows.cpp
+++ b/platform/windows/context_gl_windows.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -211,6 +211,7 @@ ContextGL_Windows::ContextGL_Windows(HWND hwnd, bool p_opengl_3_context) {
hWnd = hwnd;
use_vsync = false;
vsync_via_compositor = false;
+ pixel_format = 0;
}
ContextGL_Windows::~ContextGL_Windows() {
diff --git a/platform/windows/context_gl_windows.h b/platform/windows/context_gl_windows.h
index 046e3437ea..e44e2945ca 100644
--- a/platform/windows/context_gl_windows.h
+++ b/platform/windows/context_gl_windows.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,7 +35,7 @@
#ifndef CONTEXT_GL_WIN_H
#define CONTEXT_GL_WIN_H
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/os/os.h"
#include <windows.h>
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp
index 996d9722f5..e24e466f88 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,8 +30,8 @@
#include "crash_handler_windows.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "main/main.h"
#ifdef CRASH_HANDLER_EXCEPTION
@@ -57,7 +57,7 @@
struct module_data {
std::string image_name;
std::string module_name;
- void *base_address;
+ void *base_address = nullptr;
DWORD load_size;
};
@@ -175,7 +175,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
msg = proj_settings->get("debug/settings/crash_handler/message");
}
- fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str());
+ fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data());
int n = 0;
do {
diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h
index 66a4cac296..e1ec8e6787 100644
--- a/platform/windows/crash_handler_windows.h
+++ b/platform/windows/crash_handler_windows.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 9f79e92dcb..f26dea8d35 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -64,22 +64,22 @@ def get_opts():
# XP support dropped after EOL due to missing API for IPv6 and other issues
# Vista support dropped after EOL due to GH-10243
("target_win_version", "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601"),
- EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")),
+ BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
+ EnumVariable("windows_subsystem", "Windows subsystem", "default", ("default", "console", "gui")),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
("msvc_version", "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.", None),
- BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed. Only used on Windows.", False),
+ BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
BoolVariable("use_llvm", "Use the LLVM compiler", False),
BoolVariable("use_thinlto", "Use ThinLTO", False),
+ BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
]
def get_flags():
-
return []
def build_res_file(target, source, env):
-
if env["bits"] == "32":
cmdbase = env["mingw_prefix_32"]
else:
@@ -93,7 +93,7 @@ def build_res_file(target, source, env):
out = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
if len(out[1]):
return 1
- except:
+ except Exception:
return 1
return 0
@@ -128,7 +128,9 @@ def setup_msvc_manual(env):
print("Compiled program architecture will be a 32 bit executable. (forcing bits=32).")
else:
print(
- "Failed to manually detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup, or avoid setting VCINSTALLDIR."
+ "Failed to manually detect MSVC compiler architecture version... Defaulting to 32bit executable settings"
+ " (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this"
+ " build is compiled for. You should check your settings/compilation setup, or avoid setting VCINSTALLDIR."
)
@@ -176,12 +178,20 @@ def configure_msvc(env, manual_msvc_config):
# Build type
+ if env["tests"]:
+ env["windows_subsystem"] = "console"
+ elif env["windows_subsystem"] == "default":
+ # Default means we use console for debug, gui for release.
+ if "debug" in env["target"]:
+ env["windows_subsystem"] = "console"
+ else:
+ env["windows_subsystem"] = "gui"
+
if env["target"] == "release":
if env["optimize"] == "speed": # optimize for speed (default)
env.Append(CCFLAGS=["/O2"])
else: # optimize for size
env.Append(CCFLAGS=["/O1"])
- env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"])
env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
env.Append(LINKFLAGS=["/OPT:REF"])
@@ -191,24 +201,33 @@ def configure_msvc(env, manual_msvc_config):
else: # optimize for size
env.Append(CCFLAGS=["/O1"])
env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED"])
- env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
env.Append(LINKFLAGS=["/OPT:REF"])
elif env["target"] == "debug":
- env.AppendUnique(CCFLAGS=["/Z7", "/Od", "/EHsc"])
- env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED", "D3D_DEBUG_INFO"])
- env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
+ env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"])
+ env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(LINKFLAGS=["/DEBUG"])
- if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes":
- env.AppendUnique(CCFLAGS=["/Z7"])
+ if env["debug_symbols"]:
+ env.AppendUnique(CCFLAGS=["/Zi", "/FS"])
env.AppendUnique(LINKFLAGS=["/DEBUG"])
+ if env["windows_subsystem"] == "gui":
+ env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"])
+ else:
+ env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
+ env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
+
## Compile/link flags
- env.AppendUnique(CCFLAGS=["/MT", "/Gd", "/GR", "/nologo"])
- if int(env["MSVC_VERSION"].split(".")[0]) >= 14: # vs2015 and later
- env.AppendUnique(CCFLAGS=["/utf-8"])
+ if env["use_static_cpp"]:
+ env.AppendUnique(CCFLAGS=["/MT"])
+ else:
+ env.AppendUnique(CCFLAGS=["/MD"])
+
+ env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"])
+ # Force to use Unicode encoding
+ env.AppendUnique(CCFLAGS=["/utf-8"])
env.AppendUnique(CXXFLAGS=["/TP"]) # assume all sources are C++
if manual_msvc_config: # should be automatic if SCons found it
if os.getenv("WindowsSdkDir") is not None:
@@ -301,6 +320,15 @@ def configure_mingw(env):
## Build type
+ if env["tests"]:
+ env["windows_subsystem"] = "console"
+ elif env["windows_subsystem"] == "default":
+ # Default means we use console for debug, gui for release.
+ if "debug" in env["target"]:
+ env["windows_subsystem"] = "console"
+ else:
+ env["windows_subsystem"] = "gui"
+
if env["target"] == "release":
env.Append(CCFLAGS=["-msse2"])
@@ -312,19 +340,13 @@ def configure_mingw(env):
else: # optimize for size
env.Prepend(CCFLAGS=["-Os"])
- env.Append(LINKFLAGS=["-Wl,--subsystem,windows"])
-
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
+ if env["debug_symbols"]:
env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "release_debug":
env.Append(CCFLAGS=["-O2"])
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
- if env["debug_symbols"] == "yes":
- env.Prepend(CCFLAGS=["-g1"])
- if env["debug_symbols"] == "full":
+ if env["debug_symbols"]:
env.Prepend(CCFLAGS=["-g2"])
if env["optimize"] == "speed": # optimize for speed (default)
env.Append(CCFLAGS=["-O2"])
@@ -333,7 +355,13 @@ def configure_mingw(env):
elif env["target"] == "debug":
env.Append(CCFLAGS=["-g3"])
- env.Append(CPPDEFINES=["DEBUG_ENABLED", "DEBUG_MEMORY_ENABLED"])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+
+ if env["windows_subsystem"] == "gui":
+ env.Append(LINKFLAGS=["-Wl,--subsystem,windows"])
+ else:
+ env.Append(LINKFLAGS=["-Wl,--subsystem,console"])
+ env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
## Compiler configuration
@@ -349,28 +377,29 @@ def configure_mingw(env):
mingw_prefix = ""
if env["bits"] == "32":
- env.Append(LINKFLAGS=["-static"])
- env.Append(LINKFLAGS=["-static-libgcc"])
- env.Append(LINKFLAGS=["-static-libstdc++"])
+ if env["use_static_cpp"]:
+ env.Append(LINKFLAGS=["-static"])
+ env.Append(LINKFLAGS=["-static-libgcc"])
+ env.Append(LINKFLAGS=["-static-libstdc++"])
mingw_prefix = env["mingw_prefix_32"]
else:
- env.Append(LINKFLAGS=["-static"])
+ if env["use_static_cpp"]:
+ env.Append(LINKFLAGS=["-static"])
mingw_prefix = env["mingw_prefix_64"]
if env["use_llvm"]:
env["CC"] = mingw_prefix + "clang"
- env["AS"] = mingw_prefix + "as"
env["CXX"] = mingw_prefix + "clang++"
+ env["AS"] = mingw_prefix + "as"
env["AR"] = mingw_prefix + "ar"
env["RANLIB"] = mingw_prefix + "ranlib"
- env["LINK"] = mingw_prefix + "clang++"
else:
env["CC"] = mingw_prefix + "gcc"
- env["AS"] = mingw_prefix + "as"
env["CXX"] = mingw_prefix + "g++"
+ env["AS"] = mingw_prefix + "as"
env["AR"] = mingw_prefix + "gcc-ar"
env["RANLIB"] = mingw_prefix + "gcc-ranlib"
- env["LINK"] = mingw_prefix + "g++"
+
env["x86_libtheora_opt_gcc"] = True
if env["use_lto"]:
@@ -424,7 +453,7 @@ def configure_mingw(env):
else:
env.Append(LIBS=["cfgmgr32"])
- ## TODO !!! Reenable when OpenGLES Rendering Device is implemented !!!
+ ## TODO !!! Re-enable when OpenGLES Rendering Device is implemented !!!
# env.Append(CPPDEFINES=['OPENGL_ENABLED'])
env.Append(LIBS=["opengl32"])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 30a3ad5a01..b9b78f7bd4 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,7 +29,9 @@
/*************************************************************************/
#include "display_server_windows.h"
+
#include "core/io/marshalls.h"
+#include "core/math/geometry_2d.h"
#include "main/main.h"
#include "os_windows.h"
#include "scene/resources/texture.h"
@@ -42,7 +44,7 @@ static String format_error_message(DWORD id) {
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);
- String msg = "Error " + itos(id) + ": " + String(messageBuffer, size);
+ String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size);
LocalFree(messageBuffer);
@@ -78,7 +80,7 @@ String DisplayServerWindows::get_name() const {
}
void DisplayServerWindows::alert(const String &p_alert, const String &p_title) {
- MessageBoxW(nullptr, p_alert.c_str(), p_title.c_str(), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
+ MessageBoxW(nullptr, (LPCWSTR)(p_alert.utf16().get_data()), (LPCWSTR)(p_title.utf16().get_data()), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
}
void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
@@ -103,7 +105,11 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
}
if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
- hCursor = SetCursor(nullptr);
+ if (hCursor == nullptr) {
+ hCursor = SetCursor(nullptr);
+ } else {
+ SetCursor(nullptr);
+ }
} else {
CursorShape c = cursor_shape;
cursor_shape = CURSOR_MAX;
@@ -117,9 +123,9 @@ void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
if (mouse_mode == p_mode)
return;
- _set_mouse_mode_impl(p_mode);
-
mouse_mode = p_mode;
+
+ _set_mouse_mode_impl(p_mode);
}
DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {
@@ -166,18 +172,19 @@ void DisplayServerWindows::clipboard_set(const String &p_text) {
// Convert LF line endings to CRLF in clipboard content
// Otherwise, line endings won't be visible when pasted in other software
- String text = p_text.replace("\n", "\r\n");
+ String text = p_text.replace("\r\n", "\n").replace("\n", "\r\n"); // avoid \r\r\n
if (!OpenClipboard(windows[last_focused_window].hWnd)) {
ERR_FAIL_MSG("Unable to open clipboard.");
}
EmptyClipboard();
- HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1) * sizeof(CharType));
+ Char16String utf16 = text.utf16();
+ HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (utf16.length() + 1) * sizeof(WCHAR));
ERR_FAIL_COND_MSG(mem == nullptr, "Unable to allocate memory for clipboard contents.");
LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);
- memcpy(lptstrCopy, text.c_str(), (text.length() + 1) * sizeof(CharType));
+ memcpy(lptstrCopy, utf16.get_data(), (utf16.length() + 1) * sizeof(WCHAR));
GlobalUnlock(mem);
SetClipboardData(CF_UNICODETEXT, mem);
@@ -214,7 +221,7 @@ String DisplayServerWindows::clipboard_get() const {
if (mem != nullptr) {
LPWSTR ptr = (LPWSTR)GlobalLock(mem);
if (ptr != nullptr) {
- ret = String((CharType *)ptr);
+ ret = String::utf16((const char16_t *)ptr);
GlobalUnlock(mem);
};
};
@@ -472,6 +479,7 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod
_THREAD_SAFE_METHOD_
WindowID window_id = _create_window(p_mode, p_flags, p_rect);
+ ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window.");
WindowData &wd = windows[window_id];
@@ -488,15 +496,21 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod
wd.no_focus = true;
}
- _update_window_style(window_id);
+ return window_id;
+}
+
+void DisplayServerWindows::show_window(WindowID p_id) {
+ WindowData &wd = windows[p_id];
+
+ if (p_id != MAIN_WINDOW_ID) {
+ _update_window_style(p_id);
+ }
- ShowWindow(wd.hWnd, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show The Window
- if (!(p_flags & WINDOW_FLAG_NO_FOCUS_BIT)) {
+ ShowWindow(wd.hWnd, wd.no_focus ? SW_SHOWNOACTIVATE : SW_SHOW); // Show The Window
+ if (!wd.no_focus) {
SetForegroundWindow(wd.hWnd); // Slightly Higher Priority
SetFocus(wd.hWnd); // Sets Keyboard Focus To
}
-
- return window_id;
}
void DisplayServerWindows::delete_sub_window(WindowID p_window) {
@@ -521,7 +535,7 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
}
#endif
- if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[p_window].wtctx) {
+ if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window].wtctx) {
wintab_WTClose(windows[p_window].wtctx);
windows[p_window].wtctx = 0;
}
@@ -582,7 +596,37 @@ void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_wi
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
- SetWindowTextW(windows[p_window].hWnd, p_title.c_str());
+ SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data()));
+}
+
+void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].mpath = p_region;
+ _update_window_mouse_passthrough(p_window);
+}
+
+void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) {
+ if (windows[p_window].mpath.size() == 0) {
+ SetWindowRgn(windows[p_window].hWnd, nullptr, TRUE);
+ } else {
+ POINT *points = (POINT *)memalloc(sizeof(POINT) * windows[p_window].mpath.size());
+ for (int i = 0; i < windows[p_window].mpath.size(); i++) {
+ if (windows[p_window].borderless) {
+ points[i].x = windows[p_window].mpath[i].x;
+ points[i].y = windows[p_window].mpath[i].y;
+ } else {
+ points[i].x = windows[p_window].mpath[i].x + GetSystemMetrics(SM_CXSIZEFRAME);
+ points[i].y = windows[p_window].mpath[i].y + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);
+ }
+ }
+
+ HRGN region = CreatePolygonRgn(points, windows[p_window].mpath.size(), ALTERNATE);
+ SetWindowRgn(windows[p_window].hWnd, region, TRUE);
+ DeleteObject(region);
+ memfree(points);
+ }
}
int DisplayServerWindows::window_get_current_screen(WindowID p_window) const {
@@ -859,6 +903,9 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre
r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
}
}
+ if (!p_borderless) {
+ r_style |= WS_VISIBLE;
+ }
if (p_no_activate_focus) {
r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;
@@ -866,7 +913,7 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre
r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
}
-void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint, bool p_maximized) {
+void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
@@ -899,6 +946,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
RECT rect;
wd.fullscreen = false;
+ wd.maximized = wd.was_maximized;
if (wd.pre_fs_valid) {
rect = wd.pre_fs_rect;
@@ -907,13 +955,16 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
rect.right = wd.width;
rect.top = 0;
rect.bottom = wd.height;
+ wd.pre_fs_valid = true;
}
- _update_window_style(p_window, false, wd.was_maximized);
+ _update_window_style(p_window, false);
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
-
- wd.pre_fs_valid = true;
+ } else if (p_mode == WINDOW_MODE_WINDOWED) {
+ ShowWindow(wd.hWnd, SW_RESTORE);
+ wd.maximized = false;
+ wd.minimized = false;
}
if (p_mode == WINDOW_MODE_MAXIMIZED) {
@@ -922,12 +973,6 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
wd.minimized = false;
}
- if (p_mode == WINDOW_MODE_WINDOWED) {
- ShowWindow(wd.hWnd, SW_RESTORE);
- wd.maximized = false;
- wd.minimized = false;
- }
-
if (p_mode == WINDOW_MODE_MINIMIZED) {
ShowWindow(wd.hWnd, SW_MINIMIZE);
wd.maximized = false;
@@ -998,6 +1043,7 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
case WINDOW_FLAG_BORDERLESS: {
wd.borderless = p_enabled;
_update_window_style(p_window);
+ _update_window_mouse_passthrough(p_window);
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top");
@@ -1362,7 +1408,7 @@ void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorSh
}
}
-bool DisplayServerWindows::get_swap_ok_cancel() {
+bool DisplayServerWindows::get_swap_cancel_ok() {
return true;
}
@@ -1372,70 +1418,99 @@ void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
AllowSetForegroundWindow(pid);
}
-DisplayServer::LatinKeyboardVariant DisplayServerWindows::get_latin_keyboard_variant() const {
- _THREAD_SAFE_METHOD_
+int DisplayServerWindows::keyboard_get_layout_count() const {
+ return GetKeyboardLayoutList(0, NULL);
+}
- unsigned long azerty[] = {
- 0x00020401, // Arabic (102) AZERTY
- 0x0001080c, // Belgian (Comma)
- 0x0000080c, // Belgian French
- 0x0000040c, // French
- 0 // <--- STOP MARK
- };
- unsigned long qwertz[] = {
- 0x0000041a, // Croation
- 0x00000405, // Czech
- 0x00000407, // German
- 0x00010407, // German (IBM)
- 0x0000040e, // Hungarian
- 0x0000046e, // Luxembourgish
- 0x00010415, // Polish (214)
- 0x00000418, // Romanian (Legacy)
- 0x0000081a, // Serbian (Latin)
- 0x0000041b, // Slovak
- 0x00000424, // Slovenian
- 0x0001042e, // Sorbian Extended
- 0x0002042e, // Sorbian Standard
- 0x0000042e, // Sorbian Standard (Legacy)
- 0x0000100c, // Swiss French
- 0x00000807, // Swiss German
- 0 // <--- STOP MARK
- };
- unsigned long dvorak[] = {
- 0x00010409, // US-Dvorak
- 0x00030409, // US-Dvorak for left hand
- 0x00040409, // US-Dvorak for right hand
- 0 // <--- STOP MARK
- };
+int DisplayServerWindows::keyboard_get_current_layout() const {
+ HKL cur_layout = GetKeyboardLayout(0);
+
+ int layout_count = GetKeyboardLayoutList(0, NULL);
+ HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
+ GetKeyboardLayoutList(layout_count, layouts);
+
+ for (int i = 0; i < layout_count; i++) {
+ if (cur_layout == layouts[i]) {
+ memfree(layouts);
+ return i;
+ }
+ }
+ memfree(layouts);
+ return -1;
+}
- char name[KL_NAMELENGTH + 1];
- name[0] = 0;
- GetKeyboardLayoutNameA(name);
+void DisplayServerWindows::keyboard_set_current_layout(int p_index) {
+ int layout_count = GetKeyboardLayoutList(0, NULL);
- unsigned long hex = strtoul(name, nullptr, 16);
+ ERR_FAIL_INDEX(p_index, layout_count);
- int i = 0;
- while (azerty[i] != 0) {
- if (azerty[i] == hex)
- return LATIN_KEYBOARD_AZERTY;
- i++;
+ HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
+ GetKeyboardLayoutList(layout_count, layouts);
+ ActivateKeyboardLayout(layouts[p_index], KLF_SETFORPROCESS);
+ memfree(layouts);
+}
+
+String DisplayServerWindows::keyboard_get_layout_language(int p_index) const {
+ int layout_count = GetKeyboardLayoutList(0, NULL);
+
+ ERR_FAIL_INDEX_V(p_index, layout_count, "");
+
+ HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
+ GetKeyboardLayoutList(layout_count, layouts);
+
+ WCHAR buf[LOCALE_NAME_MAX_LENGTH];
+ memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));
+ LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);
+
+ memfree(layouts);
+
+ return String::utf16((const char16_t *)buf).substr(0, 2);
+}
+
+String _get_full_layout_name_from_registry(HKL p_layout) {
+ String id = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + String::num_int64((int64_t)p_layout, 16, false).lpad(8, "0");
+ String ret;
+
+ HKEY hkey;
+ WCHAR layout_text[1024];
+ memset(layout_text, 0, 1024 * sizeof(WCHAR));
+
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(id.utf16().get_data()), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) {
+ return ret;
}
- i = 0;
- while (qwertz[i] != 0) {
- if (qwertz[i] == hex)
- return LATIN_KEYBOARD_QWERTZ;
- i++;
+ DWORD buffer = 1024;
+ DWORD vtype = REG_SZ;
+ if (RegQueryValueExW(hkey, L"Layout Text", NULL, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) {
+ ret = String::utf16((const char16_t *)layout_text);
}
+ RegCloseKey(hkey);
+ return ret;
+}
+
+String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
+ int layout_count = GetKeyboardLayoutList(0, NULL);
+
+ ERR_FAIL_INDEX_V(p_index, layout_count, "");
+
+ HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL));
+ GetKeyboardLayoutList(layout_count, layouts);
+
+ String ret = _get_full_layout_name_from_registry(layouts[p_index]); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine).
+ if (ret == String()) {
+ WCHAR buf[LOCALE_NAME_MAX_LENGTH];
+ memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR));
+ LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0);
- i = 0;
- while (dvorak[i] != 0) {
- if (dvorak[i] == hex)
- return LATIN_KEYBOARD_DVORAK;
- i++;
+ WCHAR name[1024];
+ memset(name, 0, 1024 * sizeof(WCHAR));
+ GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024);
+
+ ret = String::utf16((const char16_t *)name);
}
+ memfree(layouts);
- return LATIN_KEYBOARD_QWERTY;
+ return ret;
}
void DisplayServerWindows::process_events() {
@@ -1709,7 +1784,10 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)
Ref<InputEventFromWindow> event_from_window = p_event;
if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {
//send to a window
- ERR_FAIL_COND(!windows.has(event_from_window->get_window_id()));
+ if (!windows.has(event_from_window->get_window_id())) {
+ in_dispatch_input_event = false;
+ ERR_FAIL_MSG("DisplayServerWindows: Invalid window id in input event.");
+ }
Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
if (callable.is_null()) {
in_dispatch_input_event = false;
@@ -1740,14 +1818,22 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
};
WindowID window_id = INVALID_WINDOW_ID;
+ bool window_created = false;
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
if (E->get().hWnd == hWnd) {
window_id = E->key();
+ window_created = true;
break;
}
}
+ if (!window_created) {
+ // Window creation in progress.
+ window_id = window_id_counter;
+ ERR_FAIL_COND_V(!windows.has(window_id), 0);
+ }
+
switch (uMsg) // Check For Windows Messages
{
case WM_SETFOCUS: {
@@ -1757,6 +1843,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Restore mouse mode
_set_mouse_mode_impl(mouse_mode);
+ if (!app_focused) {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
+ }
+ app_focused = true;
+ }
break;
}
case WM_KILLFOCUS: {
@@ -1772,29 +1864,31 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
touch_state.clear();
+ bool self_steal = false;
+ HWND new_hwnd = (HWND)wParam;
+ if (IsWindow(new_hwnd)) {
+ self_steal = true;
+ }
+
+ if (!self_steal) {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
+ }
+ app_focused = false;
+ }
+
break;
}
- case WM_ACTIVATE: // Watch For Window Activate Message
- {
- windows[window_id].minimized = HIWORD(wParam) != 0;
-
- if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
- _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_IN);
- windows[window_id].window_focused = true;
- alt_mem = false;
- control_mem = false;
- shift_mem = false;
- } else { // WM_INACTIVE
- Input::get_singleton()->release_pressed_events();
- _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT);
- windows[window_id].window_focused = false;
- alt_mem = false;
- };
+ case WM_ACTIVATE: { // Watch For Window Activate Message
+ if (!windows[window_id].window_focused) {
+ _process_activate_event(window_id, wParam, lParam);
+ } else {
+ windows[window_id].saved_wparam = wParam;
+ windows[window_id].saved_lparam = lParam;
- if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
- wintab_WTEnable(windows[window_id].wtctx, GET_WM_ACTIVATE_STATE(wParam, lParam));
+ // Run a timer to prevent event catching warning if the focused window is closing.
+ windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
}
-
return 0; // Return To The Message Loop
}
case WM_GETMINMAXINFO: {
@@ -1835,6 +1929,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_CLOSE: // Did We Receive A Close Message?
{
+ if (windows[window_id].focus_timer_id != 0U) {
+ KillTimer(windows[window_id].hWnd, windows[window_id].focus_timer_id);
+ }
_send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
return 0; // Jump Back
@@ -1924,7 +2021,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} break;
case WT_CSRCHANGE:
case WT_PROXIMITY: {
- if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
+ if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
AXIS pressure;
if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {
windows[window_id].min_pressure = int(pressure.axMin);
@@ -1938,7 +2035,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
} break;
case WT_PACKET: {
- if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
+ if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
PACKET packet;
if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) {
float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);
@@ -1965,8 +2062,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
Ref<InputEventMouseMotion> mm;
mm.instance();
mm->set_window_id(window_id);
- mm->set_control(GetKeyState(VK_CONTROL) != 0);
- mm->set_shift(GetKeyState(VK_SHIFT) != 0);
+ mm->set_control(GetKeyState(VK_CONTROL) < 0);
+ mm->set_shift(GetKeyState(VK_SHIFT) < 0);
mm->set_alt(alt_mem);
mm->set_pressure(windows[window_id].last_pressure);
@@ -2007,17 +2104,43 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
old_x = mm->get_position().x;
old_y = mm->get_position().y;
if (windows[window_id].window_has_focus)
- Input::get_singleton()->parse_input_event(mm);
+ Input::get_singleton()->accumulate_input_event(mm);
}
return 0;
}
} break;
+ case WM_POINTERENTER: {
+ if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
+ break;
+ }
+
+ if ((tablet_get_current_driver() != "winink") || !winink_available) {
+ break;
+ }
+
+ uint32_t pointer_id = LOWORD(wParam);
+ POINTER_INPUT_TYPE pointer_type = PT_POINTER;
+ if (!win8p_GetPointerType(pointer_id, &pointer_type)) {
+ break;
+ }
+
+ if (pointer_type != PT_PEN) {
+ break;
+ }
+
+ windows[window_id].block_mm = true;
+ return 0;
+ } break;
+ case WM_POINTERLEAVE: {
+ windows[window_id].block_mm = false;
+ return 0;
+ } break;
case WM_POINTERUPDATE: {
if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
break;
}
- if ((OS::get_singleton()->get_current_tablet_driver() != "winink") || !winink_available) {
+ if ((tablet_get_current_driver() != "winink") || !winink_available) {
break;
}
@@ -2082,8 +2205,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90));
}
- mm->set_control((wParam & MK_CONTROL) != 0);
- mm->set_shift((wParam & MK_SHIFT) != 0);
+ mm->set_control(GetKeyState(VK_CONTROL) < 0);
+ mm->set_shift(GetKeyState(VK_SHIFT) < 0);
mm->set_alt(alt_mem);
mm->set_button_mask(last_button_state);
@@ -2127,12 +2250,16 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
old_x = mm->get_position().x;
old_y = mm->get_position().y;
if (windows[window_id].window_has_focus) {
- Input::get_singleton()->parse_input_event(mm);
+ Input::get_singleton()->accumulate_input_event(mm);
}
return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event
} break;
case WM_MOUSEMOVE: {
+ if (windows[window_id].block_mm) {
+ break;
+ }
+
if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
break;
}
@@ -2177,7 +2304,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_shift((wParam & MK_SHIFT) != 0);
mm->set_alt(alt_mem);
- if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
+ if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
// Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not update recently.
if (windows[window_id].last_pressure_update < 10) {
windows[window_id].last_pressure_update++;
@@ -2430,7 +2557,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
windows[window_id].height = window_h;
#if defined(VULKAN_ENABLED)
- if (rendering_driver == "vulkan") {
+ if ((rendering_driver == "vulkan") && window_created) {
context_vulkan->window_resize(window_id, windows[window_id].width, windows[window_id].height);
}
#endif
@@ -2487,17 +2614,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_ENTERSIZEMOVE: {
Input::get_singleton()->release_pressed_events();
- move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
+ windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
} break;
case WM_EXITSIZEMOVE: {
- KillTimer(windows[window_id].hWnd, move_timer_id);
+ KillTimer(windows[window_id].hWnd, windows[window_id].move_timer_id);
} break;
case WM_TIMER: {
- if (wParam == move_timer_id) {
+ if (wParam == windows[window_id].move_timer_id) {
_process_key_events();
if (!Main::is_iterating()) {
Main::iteration();
}
+ } else if (wParam == windows[window_id].focus_timer_id) {
+ _process_activate_event(window_id, windows[window_id].saved_wparam, windows[window_id].saved_lparam);
+ KillTimer(windows[window_id].hWnd, wParam);
+ windows[window_id].focus_timer_id = 0U;
}
} break;
@@ -2506,9 +2637,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_KEYUP:
case WM_KEYDOWN: {
if (wParam == VK_SHIFT)
- shift_mem = uMsg == WM_KEYDOWN;
+ shift_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
if (wParam == VK_CONTROL)
- control_mem = uMsg == WM_KEYDOWN;
+ control_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
if (wParam == VK_MENU) {
alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
if (lParam & (1 << 24))
@@ -2595,10 +2726,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if (LOWORD(lParam) == HTCLIENT) {
if (windows[window_id].window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED)) {
//Hide the cursor
- if (hCursor == nullptr)
+ if (hCursor == nullptr) {
hCursor = SetCursor(nullptr);
- else
+ } else {
SetCursor(nullptr);
+ }
} else {
if (hCursor != nullptr) {
CursorShape c = cursor_shape;
@@ -2613,7 +2745,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_DROPFILES: {
HDROP hDropInfo = (HDROP)wParam;
const int buffsize = 4096;
- wchar_t buf[buffsize];
+ WCHAR buf[buffsize];
int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, nullptr, 0);
@@ -2621,7 +2753,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
for (int i = 0; i < fcount; i++) {
DragQueryFileW(hDropInfo, i, buf, buffsize);
- String file = buf;
+ String file = String::utf16((const char16_t *)buf);
files.push_back(file);
}
@@ -2653,6 +2785,25 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
+void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam) {
+ if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
+ _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_IN);
+ windows[p_window_id].window_focused = true;
+ alt_mem = false;
+ control_mem = false;
+ shift_mem = false;
+ } else { // WM_INACTIVE
+ Input::get_singleton()->release_pressed_events();
+ _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT);
+ windows[p_window_id].window_focused = false;
+ alt_mem = false;
+ }
+
+ if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window_id].wtctx) {
+ wintab_WTEnable(windows[p_window_id].wtctx, GET_WM_ACTIVATE_STATE(wParam, lParam));
+ }
+}
+
void DisplayServerWindows::_process_key_events() {
for (int i = 0; i < key_event_pos; i++) {
KeyEvent &ke = key_event_buffer[i];
@@ -2660,6 +2811,24 @@ void DisplayServerWindows::_process_key_events() {
case WM_CHAR: {
// extended keys should only be processed as WM_KEYDOWN message.
if (!KeyMappingWindows::is_extended_key(ke.wParam) && ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR))) {
+ static char32_t prev_wc = 0;
+ char32_t unicode = ke.wParam;
+ if ((unicode & 0xfffffc00) == 0xd800) {
+ if (prev_wc != 0) {
+ ERR_PRINT("invalid utf16 surrogate input");
+ }
+ prev_wc = unicode;
+ break; // Skip surrogate.
+ } else if ((unicode & 0xfffffc00) == 0xdc00) {
+ if (prev_wc == 0) {
+ ERR_PRINT("invalid utf16 surrogate input");
+ break; // Skip invalid surrogate.
+ }
+ unicode = (prev_wc << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev_wc = 0;
+ } else {
+ prev_wc = 0;
+ }
Ref<InputEventKey> k;
k.instance();
@@ -2671,7 +2840,7 @@ void DisplayServerWindows::_process_key_events() {
k->set_pressed(true);
k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam));
k->set_physical_keycode(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)));
- k->set_unicode(ke.wParam);
+ k->set_unicode(unicode);
if (k->get_unicode() && gr_mem) {
k->set_alt(false);
k->set_control(false);
@@ -2708,7 +2877,25 @@ void DisplayServerWindows::_process_key_events() {
k->set_physical_keycode(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)));
if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) {
- k->set_unicode(key_event_buffer[i + 1].wParam);
+ char32_t unicode = key_event_buffer[i + 1].wParam;
+ static char32_t prev_wck = 0;
+ if ((unicode & 0xfffffc00) == 0xd800) {
+ if (prev_wck != 0) {
+ ERR_PRINT("invalid utf16 surrogate input");
+ }
+ prev_wck = unicode;
+ break; // Skip surrogate.
+ } else if ((unicode & 0xfffffc00) == 0xdc00) {
+ if (prev_wck == 0) {
+ ERR_PRINT("invalid utf16 surrogate input");
+ break; // Skip invalid surrogate.
+ }
+ unicode = (prev_wck << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev_wck = 0;
+ } else {
+ prev_wck = 0;
+ }
+ k->set_unicode(unicode);
}
if (k->get_unicode() && gr_mem) {
k->set_alt(false);
@@ -2732,6 +2919,7 @@ void DisplayServerWindows::_process_key_events() {
void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const String &p_new_driver) {
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
WindowData &wd = E->get();
+ wd.block_mm = false;
if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) {
wintab_WTEnable(wd.wtctx, false);
wintab_WTClose(wd.wtctx);
@@ -2780,11 +2968,32 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
WindowRect.top = p_rect.position.y;
WindowRect.bottom = p_rect.position.y + p_rect.size.y;
+ if (p_mode == WINDOW_MODE_FULLSCREEN) {
+ int nearest_area = 0;
+ Rect2i screen_rect;
+ for (int i = 0; i < get_screen_count(); i++) {
+ Rect2i r;
+ r.position = screen_get_position(i);
+ r.size = screen_get_size(i);
+ Rect2 inters = r.intersection(p_rect);
+ int area = inters.size.width * inters.size.height;
+ if (area >= nearest_area) {
+ screen_rect = r;
+ nearest_area = area;
+ }
+ }
+
+ WindowRect.left = screen_rect.position.x;
+ WindowRect.right = screen_rect.position.x + screen_rect.size.x;
+ WindowRect.top = screen_rect.position.y;
+ WindowRect.bottom = screen_rect.position.y + screen_rect.size.y;
+ }
+
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
WindowID id = window_id_counter;
{
- WindowData wd;
+ WindowData &wd = windows[id];
wd.hWnd = CreateWindowExW(
dwExStyle,
@@ -2799,15 +3008,20 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
nullptr, nullptr, hInstance, nullptr);
if (!wd.hWnd) {
MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
+ windows.erase(id);
return INVALID_WINDOW_ID;
}
+ if (p_mode != WINDOW_MODE_FULLSCREEN) {
+ wd.pre_fs_valid = true;
+ }
#ifdef VULKAN_ENABLED
if (rendering_driver == "vulkan") {
if (context_vulkan->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) {
memdelete(context_vulkan);
context_vulkan = nullptr;
- ERR_FAIL_V(INVALID_WINDOW_ID);
+ windows.erase(id);
+ ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Vulkan Window.");
}
}
#endif
@@ -2823,7 +3037,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
DragAcceptFiles(wd.hWnd, true);
- if ((OS::get_singleton()->get_current_tablet_driver() == "wintab") && wintab_available) {
+ if ((tablet_get_current_driver() == "wintab") && wintab_available) {
wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);
wd.wtlc.lcOptions |= CXO_MESSAGES;
wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;
@@ -2865,8 +3079,6 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
wd.width = p_rect.size.width;
wd.height = p_rect.size.height;
- windows[id] = wd;
-
window_id_counter++;
}
@@ -2892,6 +3104,40 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
} SHC_PROCESS_DPI_AWARENESS;
+int DisplayServerWindows::tablet_get_driver_count() const {
+ return tablet_drivers.size();
+}
+
+String DisplayServerWindows::tablet_get_driver_name(int p_driver) const {
+ if (p_driver < 0 || p_driver >= tablet_drivers.size()) {
+ return "";
+ } else {
+ return tablet_drivers[p_driver];
+ }
+}
+
+String DisplayServerWindows::tablet_get_current_driver() const {
+ return tablet_driver;
+}
+
+void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {
+ if (tablet_get_driver_count() == 0) {
+ return;
+ }
+ bool found = false;
+ for (int i = 0; i < tablet_get_driver_count(); i++) {
+ if (p_driver == tablet_get_driver_name(i)) {
+ found = true;
+ }
+ }
+ if (found) {
+ _update_tablet_ctx(tablet_driver, p_driver);
+ tablet_driver = p_driver;
+ } else {
+ ERR_PRINT("Unknown tablet driver " + p_driver + ".");
+ }
+}
+
DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
drop_events = false;
key_event_pos = 0;
@@ -2910,6 +3156,35 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
outside = true;
+ //Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
+ HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
+ if (wintab_lib) {
+ wintab_WTOpen = (WTOpenPtr)GetProcAddress(wintab_lib, "WTOpenW");
+ wintab_WTClose = (WTClosePtr)GetProcAddress(wintab_lib, "WTClose");
+ wintab_WTInfo = (WTInfoPtr)GetProcAddress(wintab_lib, "WTInfoW");
+ wintab_WTPacket = (WTPacketPtr)GetProcAddress(wintab_lib, "WTPacket");
+ wintab_WTEnable = (WTEnablePtr)GetProcAddress(wintab_lib, "WTEnable");
+
+ wintab_available = wintab_WTOpen && wintab_WTClose && wintab_WTInfo && wintab_WTPacket && wintab_WTEnable;
+ }
+
+ if (wintab_available) {
+ tablet_drivers.push_back("wintab");
+ }
+
+ //Note: Windows Ink API for pen input, available on Windows 8+ only.
+ HMODULE user32_lib = LoadLibraryW(L"user32.dll");
+ if (user32_lib) {
+ win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
+ win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
+
+ winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo;
+ }
+
+ if (winink_available) {
+ tablet_drivers.push_back("winink");
+ }
+
if (OS::get_singleton()->is_hidpi_allowed()) {
HMODULE Shcore = LoadLibraryW(L"Shcore.dll");
@@ -2997,16 +3272,17 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
Point2i window_position(
(screen_get_size(0).width - p_resolution.width) / 2,
(screen_get_size(0).height - p_resolution.height) / 2);
+
WindowID main_window = _create_window(p_mode, 0, Rect2i(window_position, p_resolution));
+ ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window.");
+
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, main_window);
}
}
- ShowWindow(windows[MAIN_WINDOW_ID].hWnd, SW_SHOW); // Show The Window
- SetForegroundWindow(windows[MAIN_WINDOW_ID].hWnd); // Slightly Higher Priority
- SetFocus(windows[MAIN_WINDOW_ID].hWnd); // Sets Keyboard Focus To
+ show_window(MAIN_WINDOW_ID);
#if defined(VULKAN_ENABLED)
@@ -3014,12 +3290,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
rendering_device_vulkan = memnew(RenderingDeviceVulkan);
rendering_device_vulkan->initialize(context_vulkan);
- RasterizerRD::make_current();
+ RendererCompositorRD::make_current();
}
#endif
- move_timer_id = 1;
-
//set_ime_active(false);
if (!OS::get_singleton()->is_in_low_processor_usage_mode()) {
@@ -3061,7 +3335,13 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
}
DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- return memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+ if (r_error != OK) {
+ ds->alert("Your video card driver does not support any of the supported Vulkan versions.\n"
+ "Please update your drivers or if you have a very old or integrated GPU upgrade it.",
+ "Unable to initialize Video driver");
+ }
+ return ds;
}
void DisplayServerWindows::register_windows_driver() {
@@ -3074,18 +3354,6 @@ DisplayServerWindows::~DisplayServerWindows() {
cursors_cache.clear();
-#if defined(VULKAN_ENABLED)
- if (rendering_driver == "vulkan") {
- if (rendering_device_vulkan) {
- rendering_device_vulkan->finalize();
- memdelete(rendering_device_vulkan);
- }
-
- if (context_vulkan)
- memdelete(context_vulkan);
- }
-#endif
-
if (user_proc) {
SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
};
@@ -3102,4 +3370,16 @@ DisplayServerWindows::~DisplayServerWindows() {
}
DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
}
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ if (rendering_device_vulkan) {
+ rendering_device_vulkan->finalize();
+ memdelete(rendering_device_vulkan);
+ }
+
+ if (context_vulkan)
+ memdelete(context_vulkan);
+ }
+#endif
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 8bed2ad843..a734077e59 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,9 +33,9 @@
#include "servers/display_server.h"
+#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "crash_handler_windows.h"
#include "drivers/unix/ip_unix.h"
#include "drivers/wasapi/audio_driver_wasapi.h"
@@ -43,8 +43,8 @@
#include "joypad_windows.h"
#include "key_mapping_windows.h"
#include "servers/audio_server.h"
-#include "servers/rendering/rasterizer.h"
-#include "servers/rendering/rasterizer_rd/rasterizer_rd.h"
+#include "servers/rendering/renderer_compositor.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include "servers/rendering_server.h"
#ifdef XAUDIO2_ENABLED
@@ -229,6 +229,14 @@ typedef struct tagPOINTER_PEN_INFO {
#define WM_POINTERUPDATE 0x0245
#endif
+#ifndef WM_POINTERENTER
+#define WM_POINTERENTER 0x0249
+#endif
+
+#ifndef WM_POINTERLEAVE
+#define WM_POINTERLEAVE 0x024A
+#endif
+
typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type);
typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info);
@@ -256,7 +264,6 @@ class DisplayServerWindows : public DisplayServer {
_THREAD_SAFE_CLASS_
-public:
// WinTab API
static bool wintab_available;
static WTOpenPtr wintab_WTOpen;
@@ -271,8 +278,9 @@ public:
static GetPointerPenInfoPtr win8p_GetPointerPenInfo;
void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver);
+ String tablet_driver;
+ Vector<String> tablet_drivers;
-private:
void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap);
enum {
@@ -309,11 +317,14 @@ private:
int pressrc;
HINSTANCE hInstance; // Holds The Instance Of The Application
String rendering_driver;
+ bool app_focused = false;
struct WindowData {
HWND hWnd;
//layered window
+ Vector<Vector2> mpath;
+
bool preserve_window_size = false;
bool pre_fs_valid = false;
RECT pre_fs_rect;
@@ -328,11 +339,20 @@ private:
bool no_focus = false;
bool window_has_focus = false;
+ // Used to transfer data between events using timer.
+ WPARAM saved_wparam;
+ LPARAM saved_lparam;
+
+ // Timers.
+ uint32_t move_timer_id = 0U;
+ uint32_t focus_timer_id = 0U;
+
HANDLE wtctx;
LOGCONTEXTW wtlc;
int min_pressure;
int max_pressure;
bool tilt_supported;
+ bool block_mm = false;
int last_pressure_update;
float last_pressure;
@@ -375,8 +395,6 @@ private:
WindowID last_focused_window = INVALID_WINDOW_ID;
- uint32_t move_timer_id;
-
HCURSOR hCursor;
WNDPROC user_proc = nullptr;
@@ -399,18 +417,20 @@ private:
WNDCLASSEXW wc;
HCURSOR cursors[CURSOR_MAX] = { nullptr };
- CursorShape cursor_shape;
+ CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
Map<CursorShape, Vector<Variant>> cursors_cache;
void _drag_event(WindowID p_window, float p_x, float p_y, int idx);
void _touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx);
- void _update_window_style(WindowID p_window, bool p_repaint = true, bool p_maximized = false);
+ void _update_window_style(WindowID p_window, bool p_repaint = true);
+ void _update_window_mouse_passthrough(WindowID p_window);
void _update_real_mouse_position(WindowID p_window);
void _set_mouse_mode_impl(MouseMode p_mode);
+ void _process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam);
void _process_key_events();
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
@@ -450,6 +470,7 @@ public:
virtual Vector<DisplayServer::WindowID> get_window_list() const;
virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
+ virtual void show_window(WindowID p_window);
virtual void delete_sub_window(WindowID p_window);
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
@@ -466,6 +487,7 @@ public:
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID);
virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const;
virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID);
@@ -510,11 +532,20 @@ public:
virtual CursorShape cursor_get_shape() const;
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
- virtual bool get_swap_ok_cancel();
+ virtual bool get_swap_cancel_ok();
virtual void enable_for_stealing_focus(OS::ProcessID pid);
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+ virtual int keyboard_get_layout_count() const;
+ virtual int keyboard_get_current_layout() const;
+ virtual void keyboard_set_current_layout(int p_index);
+ virtual String keyboard_get_layout_language(int p_index) const;
+ virtual String keyboard_get_layout_name(int p_index) const;
+
+ virtual int tablet_get_driver_count() const;
+ virtual String tablet_get_driver_name(int p_driver) const;
+ virtual String tablet_get_current_driver() const;
+ virtual void tablet_set_current_driver(const String &p_driver);
virtual void process_events();
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp
index c2436e8b64..222597b3ff 100644
--- a/platform/windows/export/export.cpp
+++ b/platform/windows/export/export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -173,11 +173,11 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset>
}
#ifdef WINDOWS_ENABLED
- OS::get_singleton()->execute(rcedit_path, args, true);
+ OS::get_singleton()->execute(rcedit_path, args);
#else
// On non-Windows we need WINE to run rcedit
args.push_front(rcedit_path);
- OS::get_singleton()->execute(wine_path, args, true);
+ OS::get_singleton()->execute(wine_path, args);
#endif
}
@@ -299,7 +299,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
PackedStringArray user_args = p_preset->get("codesign/custom_options");
for (int i = 0; i < user_args.size(); i++) {
String user_arg = user_args[i].strip_edges();
- if (!user_arg.empty()) {
+ if (!user_arg.is_empty()) {
args.push_back(user_arg);
}
}
@@ -314,7 +314,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
#endif
String str;
- Error err = OS::get_singleton()->execute(signtool_path, args, true, nullptr, &str, nullptr, true);
+ Error err = OS::get_singleton()->execute(signtool_path, args, &str, nullptr, true);
ERR_FAIL_COND_V(err != OK, err);
print_line("codesign (" + p_path + "): " + str);
diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h
index d669192831..6a7131c73f 100644
--- a/platform/windows/export/export.h
+++ b/platform/windows/export/export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis
index 593557cc69..857c6a88f1 100644
--- a/platform/windows/godot.natvis
+++ b/platform/windows/godot.natvis
@@ -10,16 +10,16 @@
</Expand>
</Type>
- <Type Name="PoolVector&lt;*&gt;">
+ <Type Name="LocalVector&lt;*&gt;">
<Expand>
- <Item Name="[size]">alloc ? (alloc-&gt;size / sizeof($T1)) : 0</Item>
+ <Item Name="[size]">count</Item>
<ArrayItems>
- <Size>alloc ? (alloc-&gt;size / sizeof($T1)) : 0</Size>
- <ValuePointer>alloc ? (($T1 *)alloc-&gt;mem) : 0</ValuePointer>
+ <Size>count</Size>
+ <ValuePointer>data</ValuePointer>
</ArrayItems>
</Expand>
</Type>
-
+
<Type Name="List&lt;*&gt;">
<Expand>
<Item Name="[size]">_data ? (_data->size_cache) : 0</Item>
@@ -36,7 +36,7 @@
<DisplayString Condition="type == Variant::NIL">nil</DisplayString>
<DisplayString Condition="type == Variant::BOOL">{_data._bool}</DisplayString>
<DisplayString Condition="type == Variant::INT">{_data._int}</DisplayString>
- <DisplayString Condition="type == Variant::REAL">{_data._real}</DisplayString>
+ <DisplayString Condition="type == Variant::FLOAT">{_data._float}</DisplayString>
<DisplayString Condition="type == Variant::TRANSFORM2D">{_data._transform2d}</DisplayString>
<DisplayString Condition="type == Variant::AABB">{_data._aabb}</DisplayString>
<DisplayString Condition="type == Variant::BASIS">{_data._basis}</DisplayString>
@@ -49,24 +49,26 @@
<DisplayString Condition="type == Variant::QUAT">{*(Quat *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::NODE_PATH">{*(NodePath *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::_RID">{*(RID *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::RID">{*(::RID *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::OBJECT">{*(Object *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::POOL_BYTE_ARRAY">{*(PoolByteArray *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::POOL_INT_ARRAY">{*(PoolIntArray *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::POOL_REAL_ARRAY">{*(PoolRealArray *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::POOL_STRING_ARRAY">{*(PoolStringArray *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::POOL_VECTOR2_ARRAY">{*(PoolVector2Array *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::POOL_VECTOR3_ARRAY">{*(PoolVector3Array *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::POOL_COLOR_ARRAY">{*(PoolColorArray *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{*(PackedByteArray *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{*(PackedInt32Array *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*(PackedInt64Array *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{*(PackedFloat32Array *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{*(PackedFloat64Array *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{*(PackedStringArray *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{*(PackedVector2Array *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{*(PackedVector3Array *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{*(PackedColorArray *)_data._mem}</DisplayString>
+
+ <StringView Condition="type == Variant::STRING &amp;&amp; ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,s32</StringView>
- <StringView Condition="type == Variant::STRING &amp;&amp; ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,su</StringView>
-
<Expand>
<Item Name="[value]" Condition="type == Variant::BOOL">_data._bool</Item>
<Item Name="[value]" Condition="type == Variant::INT">_data._int</Item>
- <Item Name="[value]" Condition="type == Variant::REAL">_data._real</Item>
+ <Item Name="[value]" Condition="type == Variant::FLOAT">_data._float</Item>
<Item Name="[value]" Condition="type == Variant::TRANSFORM2D">_data._transform2d</Item>
<Item Name="[value]" Condition="type == Variant::AABB">_data._aabb</Item>
<Item Name="[value]" Condition="type == Variant::BASIS">_data._basis</Item>
@@ -79,32 +81,34 @@
<Item Name="[value]" Condition="type == Variant::QUAT">*(Quat *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::COLOR">*(Color *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::NODE_PATH">*(NodePath *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::_RID">*(RID *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::RID">*(::RID *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::OBJECT">*(Object *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::DICTIONARY">*(Dictionary *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::ARRAY">*(Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::POOL_BYTE_ARRAY">*(PoolByteArray *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::POOL_INT_ARRAY">*(PoolIntArray *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::POOL_REAL_ARRAY">*(PoolRealArray *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::POOL_STRING_ARRAY">*(PoolStringArray *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::POOL_VECTOR2_ARRAY">*(PoolVector2Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::POOL_VECTOR3_ARRAY">*(PoolVector3Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::POOL_COLOR_ARRAY">*(PoolColorArray *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">*(PackedByteArray *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">*(PackedInt32Array *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*(PackedInt64Array *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">*(PackedFloat32Array *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">*(PackedFloat64Array *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">*(PackedStringArray *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">*(PackedVector2Array *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">*(PackedVector3Array *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">*(PackedColorArray *)_data._mem</Item>
</Expand>
</Type>
<Type Name="String">
<DisplayString Condition="_cowdata._ptr == 0">[empty]</DisplayString>
- <DisplayString Condition="_cowdata._ptr != 0">{_cowdata._ptr,su}</DisplayString>
- <StringView Condition="_cowdata._ptr != 0">_cowdata._ptr,su</StringView>
+ <DisplayString Condition="_cowdata._ptr != 0">{_cowdata._ptr,s32}</DisplayString>
+ <StringView Condition="_cowdata._ptr != 0">_cowdata._ptr,s32</StringView>
</Type>
<Type Name="StringName">
<DisplayString Condition="_data &amp;&amp; _data->cname">{_data->cname}</DisplayString>
- <DisplayString Condition="_data &amp;&amp; !_data->cname">{_data->name,su}</DisplayString>
+ <DisplayString Condition="_data &amp;&amp; !_data->cname">{_data->name,s32}</DisplayString>
<DisplayString Condition="!_data">[empty]</DisplayString>
<StringView Condition="_data &amp;&amp; _data->cname">_data->cname</StringView>
- <StringView Condition="_data &amp;&amp; !_data->cname">_data->name,su</StringView>
+ <StringView Condition="_data &amp;&amp; !_data->cname">_data->name,s32</StringView>
</Type>
<Type Name="Vector2">
@@ -143,7 +147,7 @@
<Item Name="alpha">a</Item>
</Expand>
</Type>
-
+
<Type Name="Node" Inheritable="false">
<Expand>
<Item Name="Object">(Object*)this</Item>
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index 910059a9fc..22e2e5f7e5 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -146,6 +146,8 @@ int widechar_main(int argc, wchar_t **argv) {
argv_utf8[i] = wc_to_utf8(argv[i]);
}
+ TEST_MAIN_PARAM_OVERRIDE(argc, argv_utf8)
+
Error err = Main::setup(argv_utf8[0], argc - 1, &argv_utf8[1]);
if (err != OK) {
@@ -186,10 +188,12 @@ int _main() {
return result;
}
-int main(int _argc, char **_argv) {
+int main(int argc, char **argv) {
+ // override the arguments for the test handler / if symbol is provided
+ // TEST_MAIN_OVERRIDE
+
// _argc and _argv are ignored
// we are going to use the WideChar version of them instead
-
#ifdef CRASH_HANDLER_EXCEPTION
__try {
return _main();
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 0cff12ca8c..f46a0dbe2e 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,10 +33,6 @@
#include <oleauto.h>
#include <wbemidl.h>
-#ifndef __GNUC__
-#define __builtin_bswap32 _byteswap_ulong
-#endif
-
#if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
#define GetProcAddress (void *)GetProcAddress
@@ -67,17 +63,25 @@ JoypadWindows::JoypadWindows(HWND *hwnd) {
for (int i = 0; i < JOYPADS_MAX; i++)
attached_joypads[i] = false;
- HRESULT result;
- result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
- if (FAILED(result)) {
- printf("failed init DINPUT: %ld\n", result);
+ HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
+ if (result == DI_OK) {
+ probe_joypads();
+ } else {
+ ERR_PRINT("Couldn't initialize DirectInput. Error: " + itos(result));
+ if (result == DIERR_OUTOFMEMORY) {
+ ERR_PRINT("The Windows DirectInput subsystem could not allocate sufficient memory.");
+ ERR_PRINT("Rebooting your PC may solve this issue.");
+ }
+ // Ensure dinput is still a nullptr.
+ dinput = nullptr;
}
- probe_joypads();
}
JoypadWindows::~JoypadWindows() {
close_joypad();
- dinput->Release();
+ if (dinput) {
+ dinput->Release();
+ }
unload_xinput();
}
@@ -135,18 +139,19 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
}
bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
+ ERR_FAIL_NULL_V_MSG(dinput, false, "DirectInput not initialized. Rebooting your PC may solve this issue.");
HRESULT hr;
int num = input->get_unused_joy_id();
if (have_device(instance->guidInstance) || num == -1)
return false;
- d_joypads[joypad_count] = dinput_gamepad();
- dinput_gamepad *joy = &d_joypads[joypad_count];
+ d_joypads[num] = dinput_gamepad();
+ dinput_gamepad *joy = &d_joypads[num];
const DWORD devtype = (instance->dwDevType & 0xFF);
- if ((devtype != DI8DEVTYPE_JOYSTICK) && (devtype != DI8DEVTYPE_GAMEPAD) && (devtype != DI8DEVTYPE_1STPERSON)) {
+ if ((devtype != DI8DEVTYPE_JOYSTICK) && (devtype != DI8DEVTYPE_GAMEPAD) && (devtype != DI8DEVTYPE_1STPERSON) && (devtype != DI8DEVTYPE_DRIVING)) {
return false;
}
@@ -158,12 +163,16 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
const GUID &guid = instance->guidProduct;
char uid[128];
- sprintf_s(uid, "%08lx%04hx%04hx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
- __builtin_bswap32(guid.Data1), guid.Data2, guid.Data3,
- guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
- guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
- id_to_change = joypad_count;
+ ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognised.");
+ WORD type = BSWAP16(0x03);
+ WORD vendor = BSWAP16(LOWORD(guid.Data1));
+ WORD product = BSWAP16(HIWORD(guid.Data1));
+ WORD version = 0;
+ sprintf_s(uid, "%04x%04x%04x%04x%04x%04x%04x%04x", type, 0, vendor, 0, product, 0, version, 0);
+
+ id_to_change = num;
+ slider_count = 0;
joy->di_joy->SetDataFormat(&c_dfDIJoystick2);
joy->di_joy->SetCooperativeLevel(*hWnd, DISCL_FOREGROUND);
@@ -185,7 +194,7 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_
HRESULT res;
DIPROPRANGE prop_range;
DIPROPDWORD dilong;
- DWORD ofs;
+ LONG ofs;
if (ob->guidType == GUID_XAxis)
ofs = DIJOFS_X;
else if (ob->guidType == GUID_YAxis)
@@ -198,9 +207,14 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_
ofs = DIJOFS_RY;
else if (ob->guidType == GUID_RzAxis)
ofs = DIJOFS_RZ;
- else if (ob->guidType == GUID_Slider)
- ofs = DIJOFS_SLIDER(0);
- else
+ else if (ob->guidType == GUID_Slider) {
+ if (slider_count < 2) {
+ ofs = DIJOFS_SLIDER(slider_count);
+ slider_count++;
+ } else {
+ return;
+ }
+ } else
return;
prop_range.diph.dwSize = sizeof(DIPROPRANGE);
prop_range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
@@ -266,6 +280,7 @@ void JoypadWindows::close_joypad(int id) {
}
void JoypadWindows::probe_joypads() {
+ ERR_FAIL_NULL_MSG(dinput, "DirectInput not initialized. Rebooting your PC may solve this issue.");
DWORD dwResult;
for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) {
ZeroMemory(&x_joypads[i].state, sizeof(XINPUT_STATE));
@@ -379,9 +394,9 @@ void JoypadWindows::process_joypads() {
}
// on mingw, these constants are not constants
- int count = 6;
- unsigned int axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ };
- int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz };
+ int count = 8;
+ LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) };
+ int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz, js.rglSlider[0], js.rglSlider[1] };
for (int j = 0; j < joy->joy_axis.size(); j++) {
for (int k = 0; k < count; k++) {
diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h
index 6c06b3f6f0..4727b4a14c 100644
--- a/platform/windows/joypad_windows.h
+++ b/platform/windows/joypad_windows.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -77,7 +77,7 @@ private:
DWORD last_pad;
LPDIRECTINPUTDEVICE8 di_joy;
- List<DWORD> joy_axis;
+ List<LONG> joy_axis;
GUID guid;
dinput_gamepad() {
@@ -92,21 +92,13 @@ private:
};
struct xinput_gamepad {
- int id;
- bool attached;
- bool vibrating;
- DWORD last_packet;
+ int id = 0;
+ bool attached = false;
+ bool vibrating = false;
+ DWORD last_packet = 0;
XINPUT_STATE state;
- uint64_t ff_timestamp;
- uint64_t ff_end_timestamp;
-
- xinput_gamepad() {
- attached = false;
- vibrating = false;
- ff_timestamp = 0;
- ff_end_timestamp = 0;
- last_packet = 0;
- }
+ uint64_t ff_timestamp = 0;
+ uint64_t ff_end_timestamp = 0;
};
typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState);
@@ -118,6 +110,7 @@ private:
Input *input;
int id_to_change;
+ int slider_count;
int joypad_count;
bool attached_joypads[JOYPADS_MAX];
dinput_gamepad d_joypads[JOYPADS_MAX];
diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp
index d8d0b13068..3312c91932 100644
--- a/platform/windows/key_mapping_windows.cpp
+++ b/platform/windows/key_mapping_windows.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,7 +38,6 @@ struct _WinTranslatePair {
};
static _WinTranslatePair _vk_to_keycode[] = {
-
{ KEY_BACKSPACE, VK_BACK }, // (0x08) // backspace
{ KEY_TAB, VK_TAB }, //(0x09)
@@ -238,7 +237,6 @@ VK_OEM_CLEAR (0xFE)
*/
static _WinTranslatePair _scancode_to_keycode[] = {
-
{ KEY_ESCAPE, 0x01 },
{ KEY_1, 0x02 },
{ KEY_2, 0x03 },
diff --git a/platform/windows/key_mapping_windows.h b/platform/windows/key_mapping_windows.h
index f64f1feb9f..fb07227014 100644
--- a/platform/windows/key_mapping_windows.h
+++ b/platform/windows/key_mapping_windows.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/windows/lang_table.h b/platform/windows/lang_table.h
index f81bab13a4..51583cc11e 100644
--- a/platform/windows/lang_table.h
+++ b/platform/windows/lang_table.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index a2941dd43e..3280a36e9b 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,15 +39,12 @@
#include "core/version_generated.gen.h"
#include "drivers/windows/dir_access_windows.h"
#include "drivers/windows/file_access_windows.h"
-#include "drivers/windows/rw_lock_windows.h"
-#include "drivers/windows/thread_windows.h"
#include "joypad_windows.h"
#include "lang_table.h"
#include "main/main.h"
#include "platform/windows/display_server_windows.h"
#include "servers/audio_server.h"
-#include "servers/rendering/rendering_server_raster.h"
-#include "servers/rendering/rendering_server_wrap_mt.h"
+#include "servers/rendering/rendering_server_default.h"
#include "windows_terminal_logger.h"
#include <avrt.h>
@@ -84,7 +81,7 @@ static String format_error_message(DWORD id) {
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);
- String msg = "Error " + itos(id) + ": " + String(messageBuffer, size);
+ String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size);
LocalFree(messageBuffer);
@@ -107,15 +104,11 @@ void RedirectIOToConsole() {
// set the screen buffer to be big enough to let us scroll text
- GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
-
- &coninfo);
+ GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
- SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
-
- coninfo.dwSize);
+ SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
// redirect unbuffered STDOUT to the console
@@ -181,13 +174,9 @@ void OS_Windows::initialize() {
//RedirectIOToConsole();
- ThreadWindows::make_default();
- RWLockWindows::make_default();
-
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
- //FileAccessBufferedFA<FileAccessWindows>::make_default();
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_RESOURCES);
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM);
@@ -208,6 +197,13 @@ void OS_Windows::initialize() {
process_map = memnew((Map<ProcessID, ProcessInfo>));
+ // Add current Godot PID to the list of known PIDs
+ ProcessInfo current_pi = {};
+ PROCESS_INFORMATION current_pi_pi = {};
+ current_pi.pi = current_pi_pi;
+ current_pi.pi.hProcess = GetCurrentProcess();
+ process_map->insert(GetCurrentProcessId(), current_pi);
+
IP_Unix::make_default();
main_loop = nullptr;
}
@@ -241,7 +237,7 @@ void OS_Windows::finalize_core() {
}
Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
- String path = p_path;
+ String path = p_path.replace("/", "\\");
if (!FileAccess::exists(path)) {
//this code exists so gdnative can load .dll files from within the executable path
@@ -258,10 +254,10 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han
DLL_DIRECTORY_COOKIE cookie = nullptr;
if (p_also_set_library_path && has_dll_directory_api) {
- cookie = add_dll_directory(path.get_base_dir().c_str());
+ cookie = add_dll_directory((LPCWSTR)(path.get_base_dir().utf16().get_data()));
}
- p_library_handle = (void *)LoadLibraryExW(path.c_str(), nullptr, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0);
+ p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(path.utf16().get_data()), nullptr, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + format_error_message(GetLastError()) + ".");
if (cookie) {
@@ -343,55 +339,21 @@ OS::TimeZoneInfo OS_Windows::get_time_zone_info() const {
return ret;
}
-uint64_t OS_Windows::get_unix_time() const {
- FILETIME ft;
- SYSTEMTIME st;
- GetSystemTime(&st);
- SystemTimeToFileTime(&st, &ft);
-
- SYSTEMTIME ep;
- ep.wYear = 1970;
- ep.wMonth = 1;
- ep.wDayOfWeek = 4;
- ep.wDay = 1;
- ep.wHour = 0;
- ep.wMinute = 0;
- ep.wSecond = 0;
- ep.wMilliseconds = 0;
- FILETIME fep;
- SystemTimeToFileTime(&ep, &fep);
-
- // Type punning through unions (rather than pointer cast) as per:
- // https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime#remarks
- ULARGE_INTEGER ft_punning;
- ft_punning.LowPart = ft.dwLowDateTime;
- ft_punning.HighPart = ft.dwHighDateTime;
-
- ULARGE_INTEGER fep_punning;
- fep_punning.LowPart = fep.dwLowDateTime;
- fep_punning.HighPart = fep.dwHighDateTime;
-
- return (ft_punning.QuadPart - fep_punning.QuadPart) / 10000000;
-};
-
-uint64_t OS_Windows::get_system_time_secs() const {
- return get_system_time_msecs() / 1000;
-}
-
-uint64_t OS_Windows::get_system_time_msecs() const {
- const uint64_t WINDOWS_TICK = 10000;
- const uint64_t MSEC_TO_UNIX_EPOCH = 11644473600000LL;
+double OS_Windows::get_unix_time() const {
+ // 1 Windows tick is 100ns
+ const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000;
+ const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL;
SYSTEMTIME st;
GetSystemTime(&st);
FILETIME ft;
SystemTimeToFileTime(&st, &ft);
- uint64_t ret;
- ret = ft.dwHighDateTime;
- ret <<= 32;
- ret |= ft.dwLowDateTime;
+ uint64_t ticks_time;
+ ticks_time = ft.dwHighDateTime;
+ ticks_time <<= 32;
+ ticks_time |= ft.dwLowDateTime;
- return (uint64_t)(ret / WINDOWS_TICK - MSEC_TO_UNIX_EPOCH);
+ return (double)(ticks_time - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND;
}
void OS_Windows::delay_usec(uint32_t p_usec) const {
@@ -403,12 +365,29 @@ void OS_Windows::delay_usec(uint32_t p_usec) const {
uint64_t OS_Windows::get_ticks_usec() const {
uint64_t ticks;
- uint64_t time;
+
// This is the number of clock ticks since start
if (!QueryPerformanceCounter((LARGE_INTEGER *)&ticks))
ticks = (UINT64)timeGetTime();
+
// Divide by frequency to get the time in seconds
- time = ticks * 1000000L / ticks_per_second;
+ // original calculation shown below is subject to overflow
+ // with high ticks_per_second and a number of days since the last reboot.
+ // time = ticks * 1000000L / ticks_per_second;
+
+ // we can prevent this by either using 128 bit math
+ // or separating into a calculation for seconds, and the fraction
+ uint64_t seconds = ticks / ticks_per_second;
+
+ // compiler will optimize these two into one divide
+ uint64_t leftover = ticks % ticks_per_second;
+
+ // remainder
+ uint64_t time = (leftover * 1000000L) / ticks_per_second;
+
+ // seconds
+ time += seconds * 1000000L;
+
// Subtract the time at game start to get
// the time since the game started
time -= ticks_start;
@@ -417,7 +396,7 @@ uint64_t OS_Windows::get_ticks_usec() const {
String OS_Windows::_quote_command_line_argument(const String &p_text) const {
for (int i = 0; i < p_text.size(); i++) {
- CharType c = p_text[i];
+ char32_t c = p_text[i];
if (c == ' ' || c == '&' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '=' || c == ';' || c == '!' || c == '\'' || c == '+' || c == ',' || c == '`' || c == '~') {
return "\"" + p_text + "\"";
}
@@ -425,22 +404,23 @@ String OS_Windows::_quote_command_line_argument(const String &p_text) const {
return p_text;
}
-Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
- if (p_blocking && r_pipe) {
- String argss = _quote_command_line_argument(p_path);
- for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) {
- argss += " " + _quote_command_line_argument(E->get());
- }
+Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+ String path = p_path.replace("/", "\\");
+ String command = _quote_command_line_argument(path);
+ for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) {
+ command += " " + _quote_command_line_argument(E->get());
+ }
+ if (r_pipe) {
if (read_stderr) {
- argss += " 2>&1"; // Read stderr too
+ command += " 2>&1"; // Include stderr
}
- // Note: _wpopen is calling command as "cmd.exe /c argss", instead of executing it directly, add extra quotes around full command, to prevent it from stripping quotes in the command.
- argss = _quote_command_line_argument(argss);
-
- FILE *f = _wpopen(argss.c_str(), L"r");
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+ // Add extra quotes around the full command, to prevent it from stripping quotes in the command,
+ // because _wpopen calls command as "cmd.exe /c command", instead of executing it directly
+ command = _quote_command_line_argument(command);
+ FILE *f = _wpopen((LPCWSTR)(command.utf16().get_data()), L"r");
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot create pipe from command: " + command);
char buf[65535];
while (fgets(buf, 65535, f)) {
if (p_pipe_mutex) {
@@ -451,52 +431,57 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
p_pipe_mutex->unlock();
}
}
-
int rv = _pclose(f);
+
if (r_exitcode) {
*r_exitcode = rv;
}
-
return OK;
}
- String cmdline = _quote_command_line_argument(p_path);
- const List<String>::Element *I = p_arguments.front();
- while (I) {
- cmdline += " " + _quote_command_line_argument(I->get());
- I = I->next();
- }
-
ProcessInfo pi;
ZeroMemory(&pi.si, sizeof(pi.si));
pi.si.cb = sizeof(pi.si);
ZeroMemory(&pi.pi, sizeof(pi.pi));
LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
- Vector<CharType> modstr; // Windows wants to change this no idea why.
- modstr.resize(cmdline.size());
- for (int i = 0; i < cmdline.size(); i++) {
- modstr.write[i] = cmdline[i];
+ int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, nullptr, nullptr, si_w, &pi.pi);
+ ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command);
+
+ WaitForSingleObject(pi.pi.hProcess, INFINITE);
+ if (r_exitcode) {
+ DWORD ret2;
+ GetExitCodeProcess(pi.pi.hProcess, &ret2);
+ *r_exitcode = ret2;
}
+ CloseHandle(pi.pi.hProcess);
+ CloseHandle(pi.pi.hThread);
- int ret = CreateProcessW(nullptr, modstr.ptrw(), nullptr, nullptr, 0, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, nullptr, nullptr, si_w, &pi.pi);
- ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK);
+ return OK;
+};
- if (p_blocking) {
- DWORD ret2 = WaitForSingleObject(pi.pi.hProcess, INFINITE);
- if (r_exitcode) {
- *r_exitcode = ret2;
- }
+Error OS_Windows::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
+ String path = p_path.replace("/", "\\");
+ String command = _quote_command_line_argument(path);
+ for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) {
+ command += " " + _quote_command_line_argument(E->get());
+ }
- CloseHandle(pi.pi.hProcess);
- CloseHandle(pi.pi.hThread);
- } else {
- ProcessID pid = pi.pi.dwProcessId;
- if (r_child_id) {
- *r_child_id = pid;
- }
- process_map->insert(pid, pi);
+ ProcessInfo pi;
+ ZeroMemory(&pi.si, sizeof(pi.si));
+ pi.si.cb = sizeof(pi.si);
+ ZeroMemory(&pi.pi, sizeof(pi.pi));
+ LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
+
+ int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, nullptr, nullptr, si_w, &pi.pi);
+ ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command);
+
+ ProcessID pid = pi.pi.dwProcessId;
+ if (r_child_id) {
+ *r_child_id = pid;
}
+ process_map->insert(pid, pi);
+
return OK;
};
@@ -519,26 +504,26 @@ int OS_Windows::get_process_id() const {
}
Error OS_Windows::set_cwd(const String &p_cwd) {
- if (_wchdir(p_cwd.c_str()) != 0)
+ if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0)
return ERR_CANT_OPEN;
return OK;
}
String OS_Windows::get_executable_path() const {
- wchar_t bufname[4096];
+ WCHAR bufname[4096];
GetModuleFileNameW(nullptr, bufname, 4096);
- String s = bufname;
+ String s = String::utf16((const char16_t *)bufname).replace("\\", "/");
return s;
}
bool OS_Windows::has_environment(const String &p_var) const {
#ifdef MINGW_ENABLED
- return _wgetenv(p_var.c_str()) != nullptr;
+ return _wgetenv((LPCWSTR)(p_var.utf16().get_data())) != nullptr;
#else
- wchar_t *env;
+ WCHAR *env;
size_t len;
- _wdupenv_s(&env, &len, p_var.c_str());
+ _wdupenv_s(&env, &len, (LPCWSTR)(p_var.utf16().get_data()));
const bool has_env = env != nullptr;
free(env);
return has_env;
@@ -546,16 +531,16 @@ bool OS_Windows::has_environment(const String &p_var) const {
};
String OS_Windows::get_environment(const String &p_var) const {
- wchar_t wval[0x7Fff]; // MSDN says 32767 char is the maximum
- int wlen = GetEnvironmentVariableW(p_var.c_str(), wval, 0x7Fff);
+ WCHAR wval[0x7fff]; // MSDN says 32767 char is the maximum
+ int wlen = GetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), wval, 0x7fff);
if (wlen > 0) {
- return wval;
+ return String::utf16((const char16_t *)wval);
}
return "";
}
bool OS_Windows::set_environment(const String &p_var, const String &p_value) const {
- return (bool)SetEnvironmentVariableW(p_var.c_str(), p_value.c_str());
+ return (bool)SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), (LPCWSTR)(p_value.utf16().get_data()));
}
String OS_Windows::get_stdin_string(bool p_block) {
@@ -568,7 +553,7 @@ String OS_Windows::get_stdin_string(bool p_block) {
}
Error OS_Windows::shell_open(String p_uri) {
- ShellExecuteW(nullptr, nullptr, p_uri.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
+ ShellExecuteW(nullptr, nullptr, (LPCWSTR)(p_uri.utf16().get_data()), nullptr, nullptr, SW_SHOWNORMAL);
return OK;
}
@@ -577,21 +562,21 @@ String OS_Windows::get_locale() const {
LANGID langid = GetUserDefaultUILanguage();
String neutral;
- int lang = langid & ((1 << 9) - 1);
- int sublang = langid & ~((1 << 9) - 1);
+ int lang = PRIMARYLANGID(langid);
+ int sublang = SUBLANGID(langid);
while (wl->locale) {
if (wl->main_lang == lang && wl->sublang == SUBLANG_NEUTRAL)
neutral = wl->locale;
if (lang == wl->main_lang && sublang == wl->sublang)
- return wl->locale;
+ return String(wl->locale).replace("-", "_");
wl++;
}
if (neutral != "")
- return neutral;
+ return String(neutral).replace("-", "_");
return "en";
}
@@ -630,7 +615,7 @@ void OS_Windows::run() {
if (!main_loop)
return;
- main_loop->init();
+ main_loop->initialize();
while (!force_quit) {
DisplayServer::get_singleton()->process_events(); // get rid of pending events
@@ -638,7 +623,7 @@ void OS_Windows::run() {
break;
};
- main_loop->finish();
+ main_loop->finalize();
}
MainLoop *OS_Windows::get_main_loop() const {
@@ -711,7 +696,7 @@ String OS_Windows::get_system_dir(SystemDir p_dir) const {
PWSTR szPath;
HRESULT res = SHGetKnownFolderPath(id, 0, nullptr, &szPath);
ERR_FAIL_COND_V(res != S_OK, String());
- String path = String(szPath);
+ String path = String::utf16((const char16_t *)szPath);
CoTaskMemFree(szPath);
return path;
}
@@ -737,7 +722,7 @@ String OS_Windows::get_user_data_dir() const {
String OS_Windows::get_unique_id() const {
HW_PROFILE_INFO HwProfInfo;
ERR_FAIL_COND_V(!GetCurrentHwProfile(&HwProfInfo), "");
- return String(HwProfInfo.szHwProfileGuid);
+ return String::utf16((const char16_t *)(HwProfInfo.szHwProfileGuid), HW_PROFILE_GUIDLEN);
}
bool OS_Windows::_check_internal_feature_support(const String &p_feature) {
@@ -754,9 +739,11 @@ bool OS_Windows::is_disable_crash_handler() const {
Error OS_Windows::move_to_trash(const String &p_path) {
SHFILEOPSTRUCTW sf;
- WCHAR *from = new WCHAR[p_path.length() + 2];
- wcscpy_s(from, p_path.length() + 1, p_path.c_str());
- from[p_path.length() + 1] = 0;
+
+ Char16String utf16 = p_path.utf16();
+ WCHAR *from = new WCHAR[utf16.length() + 2];
+ wcscpy_s(from, utf16.length() + 1, (LPCWSTR)(utf16.get_data()));
+ from[utf16.length() + 1] = 0;
sf.hwnd = main_window;
sf.wFunc = FO_DELETE;
@@ -778,68 +765,11 @@ Error OS_Windows::move_to_trash(const String &p_path) {
return OK;
}
-int OS_Windows::get_tablet_driver_count() const {
- return tablet_drivers.size();
-}
-
-String OS_Windows::get_tablet_driver_name(int p_driver) const {
- if (p_driver < 0 || p_driver >= tablet_drivers.size()) {
- return "";
- } else {
- return tablet_drivers[p_driver].utf8().get_data();
- }
-}
-
-String OS_Windows::get_current_tablet_driver() const {
- return tablet_driver;
-}
-
-void OS_Windows::set_current_tablet_driver(const String &p_driver) {
- bool found = false;
- for (int i = 0; i < get_tablet_driver_count(); i++) {
- if (p_driver == get_tablet_driver_name(i)) {
- found = true;
- }
- }
- if (found) {
- if (DisplayServerWindows::get_singleton()) {
- ((DisplayServerWindows *)DisplayServerWindows::get_singleton())->_update_tablet_ctx(tablet_driver, p_driver);
- }
- tablet_driver = p_driver;
- } else {
- ERR_PRINT("Unknown tablet driver " + p_driver + ".");
- }
-}
-
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
- //Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
- HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
- if (wintab_lib) {
- DisplayServerWindows::wintab_WTOpen = (WTOpenPtr)GetProcAddress(wintab_lib, "WTOpenW");
- DisplayServerWindows::wintab_WTClose = (WTClosePtr)GetProcAddress(wintab_lib, "WTClose");
- DisplayServerWindows::wintab_WTInfo = (WTInfoPtr)GetProcAddress(wintab_lib, "WTInfoW");
- DisplayServerWindows::wintab_WTPacket = (WTPacketPtr)GetProcAddress(wintab_lib, "WTPacket");
- DisplayServerWindows::wintab_WTEnable = (WTEnablePtr)GetProcAddress(wintab_lib, "WTEnable");
-
- DisplayServerWindows::wintab_available = DisplayServerWindows::wintab_WTOpen && DisplayServerWindows::wintab_WTClose && DisplayServerWindows::wintab_WTInfo && DisplayServerWindows::wintab_WTPacket && DisplayServerWindows::wintab_WTEnable;
- }
-
- if (DisplayServerWindows::wintab_available) {
- tablet_drivers.push_back("wintab");
- }
-
- //Note: Windows Ink API for pen input, available on Windows 8+ only.
- HMODULE user32_lib = LoadLibraryW(L"user32.dll");
- if (user32_lib) {
- DisplayServerWindows::win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
- DisplayServerWindows::win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
-
- DisplayServerWindows::winink_available = DisplayServerWindows::win8p_GetPointerType && DisplayServerWindows::win8p_GetPointerPenInfo;
- }
-
- if (DisplayServerWindows::winink_available) {
- tablet_drivers.push_back("winink");
- }
+ ticks_per_second = 0;
+ ticks_start = 0;
+ main_loop = nullptr;
+ process_map = nullptr;
force_quit = false;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 11e3533bfd..8f9ef254f1 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,16 +31,16 @@
#ifndef OS_WINDOWS_H
#define OS_WINDOWS_H
+#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "crash_handler_windows.h"
#include "drivers/unix/ip_unix.h"
#include "drivers/wasapi/audio_driver_wasapi.h"
#include "drivers/winmidi/midi_driver_winmidi.h"
#include "key_mapping_windows.h"
#include "servers/audio_server.h"
-#include "servers/rendering/rasterizer.h"
+#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
#ifdef XAUDIO2_ENABLED
#include "drivers/xaudio2/audio_driver_xaudio2.h"
@@ -73,9 +73,6 @@ class OS_Windows : public OS {
HINSTANCE hInstance;
MainLoop *main_loop;
- String tablet_driver;
- Vector<String> tablet_drivers;
-
#ifdef WASAPI_ENABLED
AudioDriverWASAPI driver_wasapi;
#endif
@@ -93,14 +90,14 @@ class OS_Windows : public OS {
// functions used by main to initialize/deinitialize the OS
protected:
- virtual void initialize();
+ virtual void initialize() override;
- virtual void set_main_loop(MainLoop *p_main_loop);
- virtual void delete_main_loop();
+ virtual void set_main_loop(MainLoop *p_main_loop) override;
+ virtual void delete_main_loop() override;
- virtual void finalize();
- virtual void finalize_core();
- virtual String get_stdin_string(bool p_block);
+ virtual void finalize() override;
+ virtual void finalize_core() override;
+ virtual String get_stdin_string(bool p_block) override;
String _quote_command_line_argument(const String &p_text) const;
@@ -111,68 +108,62 @@ protected:
Map<ProcessID, ProcessInfo> *process_map;
public:
- virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
- virtual Error close_dynamic_library(void *p_library_handle);
- virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false);
-
- virtual MainLoop *get_main_loop() const;
+ virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override;
+ virtual Error close_dynamic_library(void *p_library_handle) override;
+ virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override;
- virtual String get_name() const;
+ virtual MainLoop *get_main_loop() const override;
- virtual int get_tablet_driver_count() const;
- virtual String get_tablet_driver_name(int p_driver) const;
- virtual String get_current_tablet_driver() const;
- virtual void set_current_tablet_driver(const String &p_driver);
+ virtual String get_name() const override;
- virtual void initialize_joypads() {}
+ virtual void initialize_joypads() override {}
- virtual Date get_date(bool utc) const;
- virtual Time get_time(bool utc) const;
- virtual TimeZoneInfo get_time_zone_info() const;
- virtual uint64_t get_unix_time() const;
- virtual uint64_t get_system_time_secs() const;
- virtual uint64_t get_system_time_msecs() const;
+ virtual Date get_date(bool utc) const override;
+ virtual Time get_time(bool utc) const override;
+ virtual TimeZoneInfo get_time_zone_info() const override;
+ virtual double get_unix_time() const override;
- virtual Error set_cwd(const String &p_cwd);
+ virtual Error set_cwd(const String &p_cwd) override;
- virtual void delay_usec(uint32_t p_usec) const;
- virtual uint64_t get_ticks_usec() const;
+ virtual void delay_usec(uint32_t p_usec) const override;
+ virtual uint64_t get_ticks_usec() const override;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr);
- virtual Error kill(const ProcessID &p_pid);
- virtual int get_process_id() const;
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+ virtual Error kill(const ProcessID &p_pid) override;
+ virtual int get_process_id() const override;
- virtual bool has_environment(const String &p_var) const;
- virtual String get_environment(const String &p_var) const;
- virtual bool set_environment(const String &p_var, const String &p_value) const;
+ virtual bool has_environment(const String &p_var) const override;
+ virtual String get_environment(const String &p_var) const override;
+ virtual bool set_environment(const String &p_var, const String &p_value) const override;
- virtual String get_executable_path() const;
+ virtual String get_executable_path() const override;
- virtual String get_locale() const;
+ virtual String get_locale() const override;
- virtual int get_processor_count() const;
+ virtual int get_processor_count() const override;
- virtual String get_config_path() const;
- virtual String get_data_path() const;
- virtual String get_cache_path() const;
- virtual String get_godot_dir_name() const;
+ virtual String get_config_path() const override;
+ virtual String get_data_path() const override;
+ virtual String get_cache_path() const override;
+ virtual String get_godot_dir_name() const override;
- virtual String get_system_dir(SystemDir p_dir) const;
- virtual String get_user_data_dir() const;
+ virtual String get_system_dir(SystemDir p_dir) const override;
+ virtual String get_user_data_dir() const override;
- virtual String get_unique_id() const;
+ virtual String get_unique_id() const override;
- virtual Error shell_open(String p_uri);
+ virtual Error shell_open(String p_uri) override;
void run();
- virtual bool _check_internal_feature_support(const String &p_feature);
+ virtual bool _check_internal_feature_support(const String &p_feature) override;
- void disable_crash_handler();
- bool is_disable_crash_handler() const;
- virtual void initialize_debugging();
+ virtual void disable_crash_handler() override;
+ virtual bool is_disable_crash_handler() const override;
+ virtual void initialize_debugging() override;
- virtual Error move_to_trash(const String &p_path);
+ virtual Error move_to_trash(const String &p_path) override;
void set_main_window(HWND p_main_window) { main_window = p_main_window; }
diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h
index 290decac5f..481f583f6f 100644
--- a/platform/windows/platform_config.h
+++ b/platform/windows/platform_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,5 +29,3 @@
/*************************************************************************/
#include <malloc.h>
-
-#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h"
diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp
index 2c63281c49..e5e176ab93 100644
--- a/platform/windows/vulkan_context_win.cpp
+++ b/platform/windows/vulkan_context_win.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h
index 6e80db0286..4fe987218d 100644
--- a/platform/windows/vulkan_context_win.h
+++ b/platform/windows/vulkan_context_win.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp
index 0938b65b04..56b620a6d9 100644
--- a/platform/windows/windows_terminal_logger.cpp
+++ b/platform/windows/windows_terminal_logger.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/platform/windows/windows_terminal_logger.h b/platform/windows/windows_terminal_logger.h
index d4443a707d..aacfe5869e 100644
--- a/platform/windows/windows_terminal_logger.h
+++ b/platform/windows/windows_terminal_logger.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */